Completed
Pull Request — master (#15643)
by Dmitry
10:34
created

QueryBuilder   F

Complexity

Total Complexity 198

Size/Duplication

Total Lines 1666
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 91.65%

Importance

Changes 0
Metric Value
wmc 198
lcom 1
cbo 14
dl 0
loc 1666
ccs 406
cts 443
cp 0.9165
rs 1.913
c 0
b 0
f 0

73 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A init() 0 7 1
A defaultConditionClasses() 0 18 1
A defaultExpressionBuilders() 0 19 1
A setExpressionBuilders() 0 4 1
D build() 0 40 9
A buildExpression() 0 6 1
C getExpressionBuilder() 0 27 7
A insert() 0 7 3
C prepareInsertValues() 0 27 7
B prepareInsertSelectSubQuery() 0 21 7
C batchInsert() 0 45 12
A upsert() 0 4 1
A prepareUpsertColumns() 0 15 3
B getTableUniqueColumnNames() 0 36 6
A update() 0 7 2
B prepareUpdateSets() 0 18 5
A delete() 0 7 2
A createTable() 0 14 4
A renameTable() 0 4 1
A dropTable() 0 4 1
A addPrimaryKey() 0 14 3
A dropPrimaryKey() 0 5 1
A truncateTable() 0 4 1
A addColumn() 0 6 1
A dropColumn() 0 5 1
A renameColumn() 0 6 1
A alterColumn() 0 7 1
A addForeignKey() 0 16 3
A dropForeignKey() 0 5 1
A createIndex() 0 7 2
A dropIndex() 0 4 1
A addUnique() 0 13 3
A dropUnique() 0 5 1
A addCheck() 0 5 1
A dropCheck() 0 5 1
A addDefaultValue() 0 4 1
A dropDefaultValue() 0 4 1
A resetSequence() 0 4 1
A checkIntegrity() 0 4 1
A addCommentOnColumn() 0 4 1
A addCommentOnTable() 0 4 1
A dropCommentFromColumn() 0 4 1
A dropCommentFromTable() 0 4 1
A createView() 0 15 2
A dropView() 0 4 1
B getColumnType() 0 20 7
C buildSelect() 0 37 12
A buildFrom() 0 10 2
C buildJoin() 0 25 7
C quoteTableNames() 0 22 7
A buildWhere() 0 6 2
B buildGroupBy() 0 15 5
A buildHaving() 0 6 2
A buildOrderByAndLimit() 0 13 3
B buildOrderBy() 0 16 5
A buildLimit() 0 12 3
A hasLimit() 0 4 2
A hasOffset() 0 4 3
B buildUnion() 0 19 5
C buildColumns() 0 23 7
A buildCondition() 0 14 4
A createConditionFromArray() 0 16 3
A buildHashCondition() 0 4 1
A buildAndCondition() 0 5 1
A buildNotCondition() 0 5 1
A buildBetweenCondition() 0 5 1
A buildInCondition() 0 5 1
A buildLikeCondition() 0 5 1
A buildExistsCondition() 0 5 1
A buildSimpleCondition() 0 5 1
A selectExists() 0 4 1
A bindParam() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like QueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db;
9
10
use yii\base\InvalidArgumentException;
11
use yii\base\NotSupportedException;
12
use yii\db\conditions\ConditionInterface;
13
use yii\db\conditions\HashCondition;
14
use yii\helpers\StringHelper;
15
16
/**
17
 * QueryBuilder builds a SELECT SQL statement based on the specification given as a [[Query]] object.
18
 *
19
 * SQL statements are created from [[Query]] objects using the [[build()]]-method.
20
 *
21
 * QueryBuilder is also used by [[Command]] to build SQL statements such as INSERT, UPDATE, DELETE, CREATE TABLE.
22
 *
23
 * For more details and usage information on QueryBuilder, see the [guide article on query builders](guide:db-query-builder).
24
 *
25
 * @author Qiang Xue <[email protected]>
26
 * @since 2.0
27
 */
28
class QueryBuilder extends \yii\base\BaseObject
29
{
30
    /**
31
     * The prefix for automatically generated query binding parameters.
32
     */
33
    const PARAM_PREFIX = ':qp';
34
35
    /**
36
     * @var Connection the database connection.
37
     */
38
    public $db;
39
    /**
40
     * @var string the separator between different fragments of a SQL statement.
41
     * Defaults to an empty space. This is mainly used by [[build()]] when generating a SQL statement.
42
     */
43
    public $separator = ' ';
44
    /**
45
     * @var array the abstract column types mapped to physical column types.
46
     * This is mainly used to support creating/modifying tables using DB-independent data type specifications.
47
     * Child classes should override this property to declare supported type mappings.
48
     */
49
    public $typeMap = [];
50
51
    /**
52
     * @var array map of query condition to builder methods.
53
     * These methods are used by [[buildCondition]] to build SQL conditions from array syntax.
54
     * @deprecated since 2.0.14. Is not used, will be dropped in 2.1.0
55
     */
56
    protected $conditionBuilders = [];
57
58
    /**
59
     * @var array map of condition aliases to condition classes. For example:
60
     *
61
     * ```php
62
     * return [
63
     *     'LIKE' => yii\db\condition\LikeCondition::class,
64
     * ];
65
     * ```
66
     *
67
     * This property is used by [[createConditionFromArray]] method.
68
     * See default condition classes list in [[defaultConditionClasses()]] method.
69
     *
70
     * In case you want to add custom conditions support, use the [[setConditionClasses()]] method.
71
     *
72
     * @see setConditonClasses()
73
     * @see defaultConditionClasses()
74
     * @since 2.0.14
75
     */
76
    protected $conditionClasses = [];
77
78
    /**
79
     * @var string[]|ExpressionBuilderInterface[] maps expression class to expression builder class.
80
     * For example:
81
     *
82
     * ```php
83
     * [
84
     *    yii\db\Expression::class => yii\db\ExpressionBuilder::class
85
     * ]
86
     * ```
87
     * This property is mainly used by [[buildExpression()]] to build SQL expressions form expression objects.
88
     * See default values in [[defaultExpressionBuilders()]] method.
89
     *
90
     *
91
     * To override existing builders or add custom, use [[setExpressionBuilder()]] method. New items will be added
92
     * to the end of this array.
93
     *
94
     * To find a builder, [[buildExpression()]] will check the expression class for its exact presence in this map.
95
     * In case it is NOT present, the array will be iterated in reverse direction, checking whether the expression
96
     * extends the class, defined in this map.
97
     *
98
     * @see setExpressionBuilders()
99
     * @see defaultExpressionBuilders()
100
     * @since 2.0.14
101
     */
102
    protected $expressionBuilders = [];
103
104
    /**
105
     * Constructor.
106
     * @param Connection $connection the database connection.
107
     * @param array $config name-value pairs that will be used to initialize the object properties
108
     */
109 1403
    public function __construct($connection, $config = [])
110
    {
111 1403
        $this->db = $connection;
112 1403
        parent::__construct($config);
113 1403
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 1403
    public function init()
119
    {
120 1403
        parent::init();
121
122 1403
        $this->expressionBuilders = array_merge($this->defaultExpressionBuilders(), $this->expressionBuilders);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->defau...is->expressionBuilders) of type array<string|integer,str...nsCondition":"string"}> is incompatible with the declared type array<integer,string|obj...ssionBuilderInterface>> of property $expressionBuilders.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
123 1403
        $this->conditionClasses = array_merge($this->defaultConditionClasses(), $this->conditionClasses);
124 1403
    }
125
126
    /**
127
     * Contains array of default condition classes. Extend this method, if you want to change
128
     * default condition classes for the query builder. See [[conditionClasses]] docs for details.
129
     *
130
     * @return array
131
     * @see conditionClasses
132
     * @since 2.0.14
133
     */
134 1403
    protected function defaultConditionClasses()
135
    {
136
        return [
137 1403
            'NOT' => 'yii\db\conditions\NotCondition',
138
            'AND' => 'yii\db\conditions\AndCondition',
139
            'OR' => 'yii\db\conditions\OrCondition',
140
            'BETWEEN' => 'yii\db\conditions\BetweenCondition',
141
            'NOT BETWEEN' => 'yii\db\conditions\BetweenCondition',
142
            'IN' => 'yii\db\conditions\InCondition',
143
            'NOT IN' => 'yii\db\conditions\InCondition',
144
            'LIKE' => 'yii\db\conditions\LikeCondition',
145
            'NOT LIKE' => 'yii\db\conditions\LikeCondition',
146
            'OR LIKE' => 'yii\db\conditions\LikeCondition',
147
            'OR NOT LIKE' => 'yii\db\conditions\LikeCondition',
148
            'EXISTS' => 'yii\db\conditions\ExistsCondition',
149
            'NOT EXISTS' => 'yii\db\conditions\ExistsCondition',
150
        ];
151
    }
152
153
    /**
154
     * Contains array of default expression builders. Extend this method and override it, if you want to change
155
     * default expression builders for this query builder. See [[expressionBuilders]] docs for details.
156
     *
157
     * @return array
158
     * @see $expressionBuilders
159
     * @since 2.0.14
160
     */
161 1403
    protected function defaultExpressionBuilders()
162
    {
163
        return [
164 1403
            'yii\db\Query' => 'yii\db\QueryExpressionBuilder',
165
            'yii\db\PdoValue' => 'yii\db\PdoValueBuilder',
166
            'yii\db\Expression' => 'yii\db\ExpressionBuilder',
167
            'yii\db\conditions\ConjunctionCondition' => 'yii\db\conditions\ConjunctionConditionBuilder',
168
            'yii\db\conditions\NotCondition' => 'yii\db\conditions\NotConditionBuilder',
169
            'yii\db\conditions\AndCondition' => 'yii\db\conditions\ConjunctionConditionBuilder',
170
            'yii\db\conditions\OrCondition' => 'yii\db\conditions\ConjunctionConditionBuilder',
171
            'yii\db\conditions\BetweenCondition' => 'yii\db\conditions\BetweenConditionBuilder',
172
            'yii\db\conditions\InCondition' => 'yii\db\conditions\InConditionBuilder',
173
            'yii\db\conditions\LikeCondition' => 'yii\db\conditions\LikeConditionBuilder',
174
            'yii\db\conditions\ExistsCondition' => 'yii\db\conditions\ExistsConditionBuilder',
175
            'yii\db\conditions\SimpleCondition' => 'yii\db\conditions\SimpleConditionBuilder',
176
            'yii\db\conditions\HashCondition' => 'yii\db\conditions\HashConditionBuilder',
177
            'yii\db\conditions\BetweenColumnsCondition' => 'yii\db\conditions\BetweenColumnsConditionBuilder',
178
        ];
179
    }
180
181
    /**
182
     * Setter for [[expressionBuilders]] property.
183
     *
184
     * @param string[] $builders array of builder that should be merged with [[expressionBuilders]]
185
     * @since 2.0.14
186
     * @see expressionBuilders
187
     */
188
    public function setExpressionBuilders($builders)
189
    {
190
        $this->expressionBuilders = array_merge($this->expressionBuilders, $builders);
191
    }
192
193
    /**
194
     * Generates a SELECT SQL statement from a [[Query]] object.
195
     *
196
     * @param Query $query the [[Query]] object from which the SQL statement will be generated.
197
     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will
198
     * be included in the result with the additional parameters generated during the query building process.
199
     * @return array the generated SQL statement (the first array element) and the corresponding
200
     * parameters to be bound to the SQL statement (the second array element). The parameters returned
201
     * include those provided in `$params`.
202
     */
203 804
    public function build($query, $params = [])
204
    {
205 804
        $query = $query->prepare($this);
206
207 804
        $params = empty($params) ? $query->params : array_merge($params, $query->params);
208
209
        $clauses = [
210 804
            $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
211 804
            $this->buildFrom($query->from, $params),
212 804
            $this->buildJoin($query->join, $params),
213 804
            $this->buildWhere($query->where, $params),
214 804
            $this->buildGroupBy($query->groupBy),
215 804
            $this->buildHaving($query->having, $params),
216
        ];
217
218 804
        $sql = implode($this->separator, array_filter($clauses));
219 804
        $sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset);
220
221 804
        if (!empty($query->orderBy)) {
222 108
            foreach ($query->orderBy as $expression) {
223 108
                if ($expression instanceof ExpressionInterface) {
224 108
                    $this->buildExpression($expression, $params);
225
                }
226
            }
227
        }
228 804
        if (!empty($query->groupBy)) {
229 18
            foreach ($query->groupBy as $expression) {
230 18
                if ($expression instanceof ExpressionInterface) {
231 18
                    $this->buildExpression($expression, $params);
232
                }
233
            }
234
        }
235
236 804
        $union = $this->buildUnion($query->union, $params);
237 804
        if ($union !== '') {
238 8
            $sql = "($sql){$this->separator}$union";
239
        }
240
241 804
        return [$sql, $params];
242
    }
243
244
    /**
245
     * Builds given $expression
246
     *
247
     * @param ExpressionInterface $expression the expression to be built
248
     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will
249
     * be included in the result with the additional parameters generated during the expression building process.
250
     * @return string the SQL statement that will not be neither quoted nor encoded before passing to DBMS
251
     * @see ExpressionInterface
252
     * @see ExpressionBuilderInterface
253
     * @see expressionBuilders
254
     * @since 2.0.14
255
     * @throws InvalidArgumentException when $expression building is not supported by this QueryBuilder.
256
     */
257 1029
    public function buildExpression(ExpressionInterface $expression, &$params = [])
258
    {
259 1029
        $builder = $this->getExpressionBuilder($expression);
260
261 1029
        return $builder->build($expression, $params);
262
    }
263
264
    /**
265
     * Gets object of [[ExpressionBuilderInterface]] that is suitable for $expression.
266
     * Uses [[expressionBuilders]] array to find a suitable builder class.
267
     *
268
     * @param ExpressionInterface $expression
269
     * @return ExpressionBuilderInterface
270
     * @see expressionBuilders
271
     * @since 2.0.14
272
     * @throws InvalidArgumentException when $expression building is not supported by this QueryBuilder.
273
     */
274 1029
    public function getExpressionBuilder(ExpressionInterface $expression)
275
    {
276 1029
        $className = get_class($expression);
277
278 1029
        if (!isset($this->expressionBuilders[$className])) {
279
            foreach (array_reverse($this->expressionBuilders) as $expressionClass => $builderClass) {
280
                if (is_subclass_of($expression, $expressionClass)) {
281
                    $this->expressionBuilders[$className] = $builderClass;
282
                    break;
283
                }
284
            }
285
286
            if (!isset($this->expressionBuilders[$className])) {
287
                throw new InvalidArgumentException('Expression of class ' . $className . ' can not be built in ' . get_class($this));
288
            }
289
        }
290
291 1029
        if ($this->expressionBuilders[$className] === __CLASS__) {
292
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (yii\db\QueryBuilder) is incompatible with the return type documented by yii\db\QueryBuilder::getExpressionBuilder of type yii\db\ExpressionBuilderInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
293
        }
294
295 1029
        if (!is_object($this->expressionBuilders[$className])) {
296 985
            $this->expressionBuilders[$className] = new $this->expressionBuilders[$className]($this);
297
        }
298
299 1029
        return $this->expressionBuilders[$className];
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->expressionBuilders[$className]; of type string|object adds the type string to the return on line 299 which is incompatible with the return type documented by yii\db\QueryBuilder::getExpressionBuilder of type yii\db\ExpressionBuilderInterface.
Loading history...
300
    }
301
302
    /**
303
     * Creates an INSERT SQL statement.
304
     * For example,
305
     * ```php
306
     * $sql = $queryBuilder->insert('user', [
307
     *     'name' => 'Sam',
308
     *     'age' => 30,
309
     * ], $params);
310
     * ```
311
     * The method will properly escape the table and column names.
312
     *
313
     * @param string $table the table that new rows will be inserted into.
314
     * @param array|Query $columns the column data (name => value) to be inserted into the table or instance
315
     * of [[yii\db\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.
316
     * Passing of [[yii\db\Query|Query]] is available since version 2.0.11.
317
     * @param array $params the binding parameters that will be generated by this method.
318
     * They should be bound to the DB command later.
319
     * @return string the INSERT SQL
320
     */
321 530
    public function insert($table, $columns, &$params)
322
    {
323 530
        list($names, $placeholders, $values, $params) = $this->prepareInsertValues($table, $columns, $params);
324 521
        return 'INSERT INTO ' . $this->db->quoteTableName($table)
325 521
            . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '')
326 521
            . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : $values);
327
    }
328
329
    /**
330
     * Prepares a `VALUES` part for an `INSERT` SQL statement.
331
     *
332
     * @param string $table the table that new rows will be inserted into.
333
     * @param array|Query $columns the column data (name => value) to be inserted into the table or instance
334
     * of [[yii\db\Query|Query]] to perform INSERT INTO ... SELECT SQL statement.
335
     * @param array $params the binding parameters that will be generated by this method.
336
     * They should be bound to the DB command later.
337
     * @return array array of column names, placeholders, values and params.
338
     * @since 2.0.14
339
     */
340 544
    protected function prepareInsertValues($table, $columns, $params = [])
341
    {
342 544
        $schema = $this->db->getSchema();
343 544
        $tableSchema = $schema->getTableSchema($table);
344 544
        $columnSchemas = $tableSchema !== null ? $tableSchema->columns : [];
345 544
        $names = [];
346 544
        $placeholders = [];
347 544
        $values = ' DEFAULT VALUES';
348 544
        if ($columns instanceof Query) {
349 42
            list($names, $values, $params) = $this->prepareInsertSelectSubQuery($columns, $schema, $params);
350
        } else {
351 508
            foreach ($columns as $name => $value) {
352 505
                $names[] = $schema->quoteColumnName($name);
353 505
                $value = isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value;
354
355 505
                if ($value instanceof ExpressionInterface) {
356 116
                    $placeholders[] = $this->buildExpression($value, $params);
357 499
                } elseif ($value instanceof \yii\db\Query) {
358
                    list($sql, $params) = $this->build($value, $params);
359
                    $placeholders[] = "($sql)";
360
                } else {
361 505
                    $placeholders[] = $this->bindParam($value, $params);
362
                }
363
            }
364
        }
365 535
        return [$names, $placeholders, $values, $params];
366
    }
367
368
    /**
369
     * Prepare select-subquery and field names for INSERT INTO ... SELECT SQL statement.
370
     *
371
     * @param Query $columns Object, which represents select query.
372
     * @param \yii\db\Schema $schema Schema object to quote column name.
373
     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will
374
     * be included in the result with the additional parameters generated during the query building process.
375
     * @return array array of column names, values and params.
376
     * @throws InvalidArgumentException if query's select does not contain named parameters only.
377
     * @since 2.0.11
378
     */
379 42
    protected function prepareInsertSelectSubQuery($columns, $schema, $params = [])
380
    {
381 42
        if (!is_array($columns->select) || empty($columns->select) || in_array('*', $columns->select)) {
382 9
            throw new InvalidArgumentException('Expected select query object with enumerated (named) parameters');
383
        }
384
385 33
        list($values, $params) = $this->build($columns, $params);
386 33
        $names = [];
387 33
        $values = ' ' . $values;
388 33
        foreach ($columns->select as $title => $field) {
389 33
            if (is_string($title)) {
390 27
                $names[] = $schema->quoteColumnName($title);
391 24
            } elseif (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-_\.]+)$/', $field, $matches)) {
392 3
                $names[] = $schema->quoteColumnName($matches[2]);
393
            } else {
394 33
                $names[] = $schema->quoteColumnName($field);
395
            }
396
        }
397
398 33
        return [$names, $values, $params];
399
    }
400
401
    /**
402
     * Generates a batch INSERT SQL statement.
403
     *
404
     * For example,
405
     *
406
     * ```php
407
     * $sql = $queryBuilder->batchInsert('user', ['name', 'age'], [
408
     *     ['Tom', 30],
409
     *     ['Jane', 20],
410
     *     ['Linda', 25],
411
     * ]);
412
     * ```
413
     *
414
     * Note that the values in each row must match the corresponding column names.
415
     *
416
     * The method will properly escape the column names, and quote the values to be inserted.
417
     *
418
     * @param string $table the table that new rows will be inserted into.
419
     * @param array $columns the column names
420
     * @param array|\Generator $rows the rows to be batch inserted into the table
421
     * @return string the batch INSERT SQL statement
422
     */
423 26
    public function batchInsert($table, $columns, $rows)
424
    {
425 26
        if (empty($rows)) {
426 2
            return '';
427
        }
428
429 25
        $schema = $this->db->getSchema();
430 25
        if (($tableSchema = $schema->getTableSchema($table)) !== null) {
431 19
            $columnSchemas = $tableSchema->columns;
432
        } else {
433 6
            $columnSchemas = [];
434
        }
435
436 25
        $values = [];
437 25
        foreach ($rows as $row) {
438 23
            $vs = [];
439 23
            foreach ($row as $i => $value) {
440 23
                if (isset($columns[$i], $columnSchemas[$columns[$i]])) {
441 14
                    $value = $columnSchemas[$columns[$i]]->dbTypecast($value);
442
                }
443 23
                if (is_string($value)) {
444 17
                    $value = $schema->quoteValue($value);
445 13
                } elseif (is_float($value)) {
446
                    // ensure type cast always has . as decimal separator in all locales
447 2
                    $value = StringHelper::floatToString($value);
448 13
                } elseif ($value === false) {
449 4
                    $value = 0;
450 13
                } elseif ($value === null) {
451 8
                    $value = 'NULL';
452
                }
453 23
                $vs[] = $value;
454
            }
455 23
            $values[] = '(' . implode(', ', $vs) . ')';
456
        }
457 25
        if (empty($values)) {
458 2
            return '';
459
        }
460
461 23
        foreach ($columns as $i => $name) {
462 21
            $columns[$i] = $schema->quoteColumnName($name);
463
        }
464
465 23
        return 'INSERT INTO ' . $schema->quoteTableName($table)
466 23
        . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
467
    }
468
469
    /**
470
     * Creates an SQL statement to insert rows into a database table if
471
     * they do not already exist (matching unique constraints),
472
     * or update them if they do.
473
     *
474
     * For example,
475
     *
476
     * ```php
477
     * $sql = $queryBuilder->upsert('pages', [
478
     *     'name' => 'Front page',
479
     *     'url' => 'http://example.com/', // url is unique
480
     *     'visits' => 0,
481
     * ], [
482
     *     'visits' => new \yii\db\Expression('visits + 1'),
483
     * ], $params);
484
     * ```
485
     *
486
     * The method will properly escape the table and column names.
487
     *
488
     * @param string $table the table that new rows will be inserted into/updated in.
489
     * @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance
490
     * of [[Query]] to perform `INSERT INTO ... SELECT` SQL statement.
491
     * @param array|bool $updateColumns the column data (name => value) to be updated if they already exist.
492
     * If `true` is passed, the column data will be updated to match the insert column data.
493
     * If `false` is passed, no update will be performed if the column data already exists.
494
     * @param array $params the binding parameters that will be generated by this method.
495
     * They should be bound to the DB command later.
496
     * @return string the resulting SQL.
497
     * @throws NotSupportedException if this is not supported by the underlying DBMS.
498
     * @since 2.0.14
499
     */
500
    public function upsert($table, $insertColumns, $updateColumns, &$params)
501
    {
502
        throw new NotSupportedException($this->db->getDriverName() . ' does not support upsert statements.');
503
    }
504
505
    /**
506
     * @param string $table
507
     * @param array|Query $insertColumns
508
     * @param array|bool $updateColumns
509
     * @param Constraint[] $constraints this parameter recieves a matched constraint list.
510
     * The constraints will be unique by their column names.
511
     * @return array
512
     * @since 2.0.14
513
     */
514 66
    protected function prepareUpsertColumns($table, $insertColumns, $updateColumns, &$constraints = [])
515
    {
516 66
        if ($insertColumns instanceof Query) {
517 24
            list($insertNames) = $this->prepareInsertSelectSubQuery($insertColumns, $this->db->getSchema());
518
        } else {
519 42
            $insertNames = array_map([$this->db, 'quoteColumnName'], array_keys($insertColumns));
520
        }
521 66
        $uniqueNames = $this->getTableUniqueColumnNames($table, $insertNames, $constraints);
522 66
        $uniqueNames = array_map([$this->db, 'quoteColumnName'], $uniqueNames);
523 66
        if ($updateColumns !== true) {
524 36
            return [$uniqueNames, $insertNames, null];
525
        }
526
527 30
        return [$uniqueNames, $insertNames, array_diff($insertNames, $uniqueNames)];
528
    }
529
530
    /**
531
     * Returns all column names belonging to constraints enforcing uniqueness (`PRIMARY KEY`, `UNIQUE INDEX`, etc.)
532
     * for the named table removing constraints which did not cover the specified column list.
533
     * The column list will be unique by column names.
534
     *
535
     * @param string $name table name. The table name may contain schema name if any. Do not quote the table name.
536
     * @param string[] $columns source column list.
537
     * @param Constraint[] $constraints this parameter optionally recieves a matched constraint list.
538
     * The constraints will be unique by their column names.
539
     * @return string[] column list.
540
     */
541 66
    private function getTableUniqueColumnNames($name, $columns, &$constraints = [])
542
    {
543 66
        $schema = $this->db->getSchema();
544 66
        if (!$schema instanceof ConstraintFinderInterface) {
545
            return [];
546
        }
547
548 66
        $constraints = [];
549 66
        $primaryKey = $schema->getTablePrimaryKey($name);
550 66
        if ($primaryKey !== null) {
551 49
            $constraints[] = $primaryKey;
552
        }
553 66
        foreach ($schema->getTableIndexes($name) as $constraint) {
554 66
            if ($constraint->isUnique) {
555 66
                $constraints[] = $constraint;
556
            }
557
        }
558 66
        $constraints = array_merge($constraints, $schema->getTableUniques($name));
559
        // Remove duplicates
560
        $constraints = array_combine(array_map(function (Constraint $constraint) {
561 66
            $columns = $constraint->columnNames;
562 66
            sort($columns, SORT_STRING);
563 66
            return json_encode($columns);
564 66
        }, $constraints), $constraints);
565 66
        $columnNames = [];
566
        // Remove all constraints which do not cover the specified column list
567
        $constraints = array_values(array_filter($constraints, function (Constraint $constraint) use ($schema, $columns, &$columnNames) {
568 66
            $constraintColumnNames = array_map([$schema, 'quoteColumnName'], $constraint->columnNames);
569 66
            $result = !array_diff($constraintColumnNames, $columns);
570 66
            if ($result) {
571 57
                $columnNames = array_merge($columnNames, $constraintColumnNames);
572
            }
573 66
            return $result;
574 66
        }));
575 66
        return array_unique($columnNames);
576
    }
577
578
    /**
579
     * Creates an UPDATE SQL statement.
580
     *
581
     * For example,
582
     *
583
     * ```php
584
     * $params = [];
585
     * $sql = $queryBuilder->update('user', ['status' => 1], 'age > 30', $params);
586
     * ```
587
     *
588
     * The method will properly escape the table and column names.
589
     *
590
     * @param string $table the table to be updated.
591
     * @param array $columns the column data (name => value) to be updated.
592
     * @param array|string $condition the condition that will be put in the WHERE part. Please
593
     * refer to [[Query::where()]] on how to specify condition.
594
     * @param array $params the binding parameters that will be modified by this method
595
     * so that they can be bound to the DB command later.
596
     * @return string the UPDATE SQL
597
     */
598 126
    public function update($table, $columns, $condition, &$params)
599
    {
600 126
        list($lines, $params) = $this->prepareUpdateSets($table, $columns, $params);
601 126
        $sql = 'UPDATE ' . $this->db->quoteTableName($table) . ' SET ' . implode(', ', $lines);
602 126
        $where = $this->buildWhere($condition, $params);
603 126
        return $where === '' ? $sql : $sql . ' ' . $where;
604
    }
605
606
    /**
607
     * Prepares a `SET` parts for an `UPDATE` SQL statement.
608
     * @param string $table the table to be updated.
609
     * @param array $columns the column data (name => value) to be updated.
610
     * @param array $params the binding parameters that will be modified by this method
611
     * so that they can be bound to the DB command later.
612
     * @return array an array `SET` parts for an `UPDATE` SQL statement (the first array element) and params (the second array element).
613
     * @since 2.0.14
614
     */
615 158
    protected function prepareUpdateSets($table, $columns, $params = [])
616
    {
617 158
        $tableSchema = $this->db->getTableSchema($table);
618 158
        $columnSchemas = $tableSchema !== null ? $tableSchema->columns : [];
619 158
        $sets = [];
620 158
        foreach ($columns as $name => $value) {
621 158
            if ($value instanceof ExpressionInterface) {
622 85
                $sets[] = $this->db->quoteColumnName($name) . '=' . $this->buildExpression($value, $params);
623
            } else {
624 123
                $phName = $this->bindParam(
625 123
                    isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value,
626 123
                    $params
627
                );
628 158
                $sets[] = $this->db->quoteColumnName($name) . '=' . $phName;
629
            }
630
        }
631 158
        return [$sets, $params];
632
    }
633
634
    /**
635
     * Creates a DELETE SQL statement.
636
     *
637
     * For example,
638
     *
639
     * ```php
640
     * $sql = $queryBuilder->delete('user', 'status = 0');
641
     * ```
642
     *
643
     * The method will properly escape the table and column names.
644
     *
645
     * @param string $table the table where the data will be deleted from.
646
     * @param array|string $condition the condition that will be put in the WHERE part. Please
647
     * refer to [[Query::where()]] on how to specify condition.
648
     * @param array $params the binding parameters that will be modified by this method
649
     * so that they can be bound to the DB command later.
650
     * @return string the DELETE SQL
651
     */
652 353
    public function delete($table, $condition, &$params)
653
    {
654 353
        $sql = 'DELETE FROM ' . $this->db->quoteTableName($table);
655 353
        $where = $this->buildWhere($condition, $params);
656
657 353
        return $where === '' ? $sql : $sql . ' ' . $where;
658
    }
659
660
    /**
661
     * Builds a SQL statement for creating a new DB table.
662
     *
663
     * The columns in the new  table should be specified as name-definition pairs (e.g. 'name' => 'string'),
664
     * where name stands for a column name which will be properly quoted by the method, and definition
665
     * stands for the column type which can contain an abstract DB type.
666
     * The [[getColumnType()]] method will be invoked to convert any abstract type into a physical one.
667
     *
668
     * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly
669
     * inserted into the generated SQL.
670
     *
671
     * For example,
672
     *
673
     * ```php
674
     * $sql = $queryBuilder->createTable('user', [
675
     *  'id' => 'pk',
676
     *  'name' => 'string',
677
     *  'age' => 'integer',
678
     * ]);
679
     * ```
680
     *
681
     * @param string $table the name of the table to be created. The name will be properly quoted by the method.
682
     * @param array $columns the columns (name => definition) in the new table.
683
     * @param string $options additional SQL fragment that will be appended to the generated SQL.
684
     * @return string the SQL statement for creating a new DB table.
685
     */
686 134
    public function createTable($table, $columns, $options = null)
687
    {
688 134
        $cols = [];
689 134
        foreach ($columns as $name => $type) {
690 134
            if (is_string($name)) {
691 134
                $cols[] = "\t" . $this->db->quoteColumnName($name) . ' ' . $this->getColumnType($type);
692
            } else {
693 134
                $cols[] = "\t" . $type;
694
            }
695
        }
696 134
        $sql = 'CREATE TABLE ' . $this->db->quoteTableName($table) . " (\n" . implode(",\n", $cols) . "\n)";
697
698 134
        return $options === null ? $sql : $sql . ' ' . $options;
699
    }
700
701
    /**
702
     * Builds a SQL statement for renaming a DB table.
703
     * @param string $oldName the table to be renamed. The name will be properly quoted by the method.
704
     * @param string $newName the new table name. The name will be properly quoted by the method.
705
     * @return string the SQL statement for renaming a DB table.
706
     */
707 1
    public function renameTable($oldName, $newName)
708
    {
709 1
        return 'RENAME TABLE ' . $this->db->quoteTableName($oldName) . ' TO ' . $this->db->quoteTableName($newName);
710
    }
711
712
    /**
713
     * Builds a SQL statement for dropping a DB table.
714
     * @param string $table the table to be dropped. The name will be properly quoted by the method.
715
     * @return string the SQL statement for dropping a DB table.
716
     */
717 39
    public function dropTable($table)
718
    {
719 39
        return 'DROP TABLE ' . $this->db->quoteTableName($table);
720
    }
721
722
    /**
723
     * Builds a SQL statement for adding a primary key constraint to an existing table.
724
     * @param string $name the name of the primary key constraint.
725
     * @param string $table the table that the primary key constraint will be added to.
726
     * @param string|array $columns comma separated string or array of columns that the primary key will consist of.
727
     * @return string the SQL statement for adding a primary key constraint to an existing table.
728
     */
729 6
    public function addPrimaryKey($name, $table, $columns)
730
    {
731 6
        if (is_string($columns)) {
732 4
            $columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);
733
        }
734
735 6
        foreach ($columns as $i => $col) {
736 6
            $columns[$i] = $this->db->quoteColumnName($col);
737
        }
738
739 6
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '
740 6
            . $this->db->quoteColumnName($name) . ' PRIMARY KEY ('
741 6
            . implode(', ', $columns) . ')';
742
    }
743
744
    /**
745
     * Builds a SQL statement for removing a primary key constraint to an existing table.
746
     * @param string $name the name of the primary key constraint to be removed.
747
     * @param string $table the table that the primary key constraint will be removed from.
748
     * @return string the SQL statement for removing a primary key constraint from an existing table.
749
     */
750 2
    public function dropPrimaryKey($name, $table)
751
    {
752 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
753 2
            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);
754
    }
755
756
    /**
757
     * Builds a SQL statement for truncating a DB table.
758
     * @param string $table the table to be truncated. The name will be properly quoted by the method.
759
     * @return string the SQL statement for truncating a DB table.
760
     */
761 11
    public function truncateTable($table)
762
    {
763 11
        return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table);
764
    }
765
766
    /**
767
     * Builds a SQL statement for adding a new DB column.
768
     * @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.
769
     * @param string $column the name of the new column. The name will be properly quoted by the method.
770
     * @param string $type the column type. The [[getColumnType()]] method will be invoked to convert abstract column type (if any)
771
     * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
772
     * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
773
     * @return string the SQL statement for adding a new column.
774
     */
775 4
    public function addColumn($table, $column, $type)
776
    {
777 4
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
778 4
            . ' ADD ' . $this->db->quoteColumnName($column) . ' '
779 4
            . $this->getColumnType($type);
780
    }
781
782
    /**
783
     * Builds a SQL statement for dropping a DB column.
784
     * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.
785
     * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.
786
     * @return string the SQL statement for dropping a DB column.
787
     */
788
    public function dropColumn($table, $column)
789
    {
790
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
791
            . ' DROP COLUMN ' . $this->db->quoteColumnName($column);
792
    }
793
794
    /**
795
     * Builds a SQL statement for renaming a column.
796
     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
797
     * @param string $oldName the old name of the column. The name will be properly quoted by the method.
798
     * @param string $newName the new name of the column. The name will be properly quoted by the method.
799
     * @return string the SQL statement for renaming a DB column.
800
     */
801
    public function renameColumn($table, $oldName, $newName)
802
    {
803
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
804
            . ' RENAME COLUMN ' . $this->db->quoteColumnName($oldName)
805
            . ' TO ' . $this->db->quoteColumnName($newName);
806
    }
807
808
    /**
809
     * Builds a SQL statement for changing the definition of a column.
810
     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
811
     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
812
     * @param string $type the new column type. The [[getColumnType()]] method will be invoked to convert abstract
813
     * column type (if any) into the physical one. Anything that is not recognized as abstract type will be kept
814
     * in the generated SQL. For example, 'string' will be turned into 'varchar(255)', while 'string not null'
815
     * will become 'varchar(255) not null'.
816
     * @return string the SQL statement for changing the definition of a column.
817
     */
818 1
    public function alterColumn($table, $column, $type)
819
    {
820 1
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' CHANGE '
821 1
            . $this->db->quoteColumnName($column) . ' '
822 1
            . $this->db->quoteColumnName($column) . ' '
823 1
            . $this->getColumnType($type);
824
    }
825
826
    /**
827
     * Builds a SQL statement for adding a foreign key constraint to an existing table.
828
     * The method will properly quote the table and column names.
829
     * @param string $name the name of the foreign key constraint.
830
     * @param string $table the table that the foreign key constraint will be added to.
831
     * @param string|array $columns the name of the column to that the constraint will be added on.
832
     * If there are multiple columns, separate them with commas or use an array to represent them.
833
     * @param string $refTable the table that the foreign key references to.
834
     * @param string|array $refColumns the name of the column that the foreign key references to.
835
     * If there are multiple columns, separate them with commas or use an array to represent them.
836
     * @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
837
     * @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
838
     * @return string the SQL statement for adding a foreign key constraint to an existing table.
839
     */
840 8
    public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
841
    {
842 8
        $sql = 'ALTER TABLE ' . $this->db->quoteTableName($table)
843 8
            . ' ADD CONSTRAINT ' . $this->db->quoteColumnName($name)
844 8
            . ' FOREIGN KEY (' . $this->buildColumns($columns) . ')'
845 8
            . ' REFERENCES ' . $this->db->quoteTableName($refTable)
846 8
            . ' (' . $this->buildColumns($refColumns) . ')';
847 8
        if ($delete !== null) {
848 4
            $sql .= ' ON DELETE ' . $delete;
849
        }
850 8
        if ($update !== null) {
851 4
            $sql .= ' ON UPDATE ' . $update;
852
        }
853
854 8
        return $sql;
855
    }
856
857
    /**
858
     * Builds a SQL statement for dropping a foreign key constraint.
859
     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
860
     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
861
     * @return string the SQL statement for dropping a foreign key constraint.
862
     */
863 3
    public function dropForeignKey($name, $table)
864
    {
865 3
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
866 3
            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);
867
    }
868
869
    /**
870
     * Builds a SQL statement for creating a new index.
871
     * @param string $name the name of the index. The name will be properly quoted by the method.
872
     * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
873
     * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns,
874
     * separate them with commas or use an array to represent them. Each column name will be properly quoted
875
     * by the method, unless a parenthesis is found in the name.
876
     * @param bool $unique whether to add UNIQUE constraint on the created index.
877
     * @return string the SQL statement for creating a new index.
878
     */
879 6
    public function createIndex($name, $table, $columns, $unique = false)
880
    {
881 6
        return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ')
882 6
            . $this->db->quoteTableName($name) . ' ON '
883 6
            . $this->db->quoteTableName($table)
884 6
            . ' (' . $this->buildColumns($columns) . ')';
885
    }
886
887
    /**
888
     * Builds a SQL statement for dropping an index.
889
     * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
890
     * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
891
     * @return string the SQL statement for dropping an index.
892
     */
893 4
    public function dropIndex($name, $table)
894
    {
895 4
        return 'DROP INDEX ' . $this->db->quoteTableName($name) . ' ON ' . $this->db->quoteTableName($table);
896
    }
897
898
    /**
899
     * Creates a SQL command for adding an unique constraint to an existing table.
900
     * @param string $name the name of the unique constraint.
901
     * The name will be properly quoted by the method.
902
     * @param string $table the table that the unique constraint will be added to.
903
     * The name will be properly quoted by the method.
904
     * @param string|array $columns the name of the column to that the constraint will be added on.
905
     * If there are multiple columns, separate them with commas.
906
     * The name will be properly quoted by the method.
907
     * @return string the SQL statement for adding an unique constraint to an existing table.
908
     * @since 2.0.13
909
     */
910 6
    public function addUnique($name, $table, $columns)
911
    {
912 6
        if (is_string($columns)) {
913 4
            $columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);
914
        }
915 6
        foreach ($columns as $i => $col) {
916 6
            $columns[$i] = $this->db->quoteColumnName($col);
917
        }
918
919 6
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '
920 6
            . $this->db->quoteColumnName($name) . ' UNIQUE ('
921 6
            . implode(', ', $columns) . ')';
922
    }
923
924
    /**
925
     * Creates a SQL command for dropping an unique constraint.
926
     * @param string $name the name of the unique constraint to be dropped.
927
     * The name will be properly quoted by the method.
928
     * @param string $table the table whose unique constraint is to be dropped.
929
     * The name will be properly quoted by the method.
930
     * @return string the SQL statement for dropping an unique constraint.
931
     * @since 2.0.13
932
     */
933 2
    public function dropUnique($name, $table)
934
    {
935 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
936 2
            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);
937
    }
938
939
    /**
940
     * Creates a SQL command for adding a check constraint to an existing table.
941
     * @param string $name the name of the check constraint.
942
     * The name will be properly quoted by the method.
943
     * @param string $table the table that the check constraint will be added to.
944
     * The name will be properly quoted by the method.
945
     * @param string $expression the SQL of the `CHECK` constraint.
946
     * @return string the SQL statement for adding a check constraint to an existing table.
947
     * @since 2.0.13
948
     */
949 2
    public function addCheck($name, $table, $expression)
950
    {
951 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ADD CONSTRAINT '
952 2
            . $this->db->quoteColumnName($name) . ' CHECK (' . $this->db->quoteSql($expression) . ')';
953
    }
954
955
    /**
956
     * Creates a SQL command for dropping a check constraint.
957
     * @param string $name the name of the check constraint to be dropped.
958
     * The name will be properly quoted by the method.
959
     * @param string $table the table whose check constraint is to be dropped.
960
     * The name will be properly quoted by the method.
961
     * @return string the SQL statement for dropping a check constraint.
962
     * @since 2.0.13
963
     */
964 2
    public function dropCheck($name, $table)
965
    {
966 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
967 2
            . ' DROP CONSTRAINT ' . $this->db->quoteColumnName($name);
968
    }
969
970
    /**
971
     * Creates a SQL command for adding a default value constraint to an existing table.
972
     * @param string $name the name of the default value constraint.
973
     * The name will be properly quoted by the method.
974
     * @param string $table the table that the default value constraint will be added to.
975
     * The name will be properly quoted by the method.
976
     * @param string $column the name of the column to that the constraint will be added on.
977
     * The name will be properly quoted by the method.
978
     * @param mixed $value default value.
979
     * @return string the SQL statement for adding a default value constraint to an existing table.
980
     * @throws NotSupportedException if this is not supported by the underlying DBMS.
981
     * @since 2.0.13
982
     */
983
    public function addDefaultValue($name, $table, $column, $value)
984
    {
985
        throw new NotSupportedException($this->db->getDriverName() . ' does not support adding default value constraints.');
986
    }
987
988
    /**
989
     * Creates a SQL command for dropping a default value constraint.
990
     * @param string $name the name of the default value constraint to be dropped.
991
     * The name will be properly quoted by the method.
992
     * @param string $table the table whose default value constraint is to be dropped.
993
     * The name will be properly quoted by the method.
994
     * @return string the SQL statement for dropping a default value constraint.
995
     * @throws NotSupportedException if this is not supported by the underlying DBMS.
996
     * @since 2.0.13
997
     */
998
    public function dropDefaultValue($name, $table)
999
    {
1000
        throw new NotSupportedException($this->db->getDriverName() . ' does not support dropping default value constraints.');
1001
    }
1002
1003
    /**
1004
     * Creates a SQL statement for resetting the sequence value of a table's primary key.
1005
     * The sequence will be reset such that the primary key of the next new row inserted
1006
     * will have the specified value or 1.
1007
     * @param string $table the name of the table whose primary key sequence will be reset
1008
     * @param array|string $value the value for the primary key of the next new row inserted. If this is not set,
1009
     * the next new row's primary key will have a value 1.
1010
     * @return string the SQL statement for resetting sequence
1011
     * @throws NotSupportedException if this is not supported by the underlying DBMS
1012
     */
1013
    public function resetSequence($table, $value = null)
1014
    {
1015
        throw new NotSupportedException($this->db->getDriverName() . ' does not support resetting sequence.');
1016
    }
1017
1018
    /**
1019
     * Builds a SQL statement for enabling or disabling integrity check.
1020
     * @param bool $check whether to turn on or off the integrity check.
1021
     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
1022
     * @param string $table the table name. Defaults to empty string, meaning that no table will be changed.
1023
     * @return string the SQL statement for checking integrity
1024
     * @throws NotSupportedException if this is not supported by the underlying DBMS
1025
     */
1026
    public function checkIntegrity($check = true, $schema = '', $table = '')
1027
    {
1028
        throw new NotSupportedException($this->db->getDriverName() . ' does not support enabling/disabling integrity check.');
1029
    }
1030
1031
    /**
1032
     * Builds a SQL command for adding comment to column.
1033
     *
1034
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
1035
     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
1036
     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
1037
     * @return string the SQL statement for adding comment on column
1038
     * @since 2.0.8
1039
     */
1040 2
    public function addCommentOnColumn($table, $column, $comment)
1041
    {
1042 2
        return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . ' IS ' . $this->db->quoteValue($comment);
1043
    }
1044
1045
    /**
1046
     * Builds a SQL command for adding comment to table.
1047
     *
1048
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
1049
     * @param string $comment the text of the comment to be added. The comment will be properly quoted by the method.
1050
     * @return string the SQL statement for adding comment on table
1051
     * @since 2.0.8
1052
     */
1053 1
    public function addCommentOnTable($table, $comment)
1054
    {
1055 1
        return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . ' IS ' . $this->db->quoteValue($comment);
1056
    }
1057
1058
    /**
1059
     * Builds a SQL command for adding comment to column.
1060
     *
1061
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
1062
     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
1063
     * @return string the SQL statement for adding comment on column
1064
     * @since 2.0.8
1065
     */
1066 2
    public function dropCommentFromColumn($table, $column)
1067
    {
1068 2
        return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . ' IS NULL';
1069
    }
1070
1071
    /**
1072
     * Builds a SQL command for adding comment to table.
1073
     *
1074
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
1075
     * @return string the SQL statement for adding comment on column
1076
     * @since 2.0.8
1077
     */
1078 1
    public function dropCommentFromTable($table)
1079
    {
1080 1
        return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . ' IS NULL';
1081
    }
1082
1083
    /**
1084
     * Creates a SQL View.
1085
     *
1086
     * @param string $viewName the name of the view to be created.
1087
     * @param string|Query $subQuery the select statement which defines the view.
1088
     * This can be either a string or a [[Query]] object.
1089
     * @return string the `CREATE VIEW` SQL statement.
1090
     * @since 2.0.14
1091
     */
1092 3
    public function createView($viewName, $subQuery)
1093
    {
1094 3
        if ($subQuery instanceof Query) {
1095 3
            list($rawQuery, $params) = $this->build($subQuery);
1096 3
            array_walk(
1097 3
                $params,
1098 3
                function(&$param) {
1099 3
                    $param = $this->db->quoteValue($param);
1100 3
                }
1101
            );
1102 3
            $subQuery = strtr($rawQuery, $params);
1103
        }
1104
1105 3
        return 'CREATE VIEW ' . $this->db->quoteTableName($viewName) . ' AS ' . $subQuery;
1106
    }
1107
1108
    /**
1109
     * Drops a SQL View.
1110
     *
1111
     * @param string $viewName the name of the view to be dropped.
1112
     * @return string the `DROP VIEW` SQL statement.
1113
     * @since 2.0.14
1114
     */
1115 3
    public function dropView($viewName)
1116
    {
1117 3
        return 'DROP VIEW ' . $this->db->quoteTableName($viewName);
1118
    }
1119
1120
    /**
1121
     * Converts an abstract column type into a physical column type.
1122
     *
1123
     * The conversion is done using the type map specified in [[typeMap]].
1124
     * The following abstract column types are supported (using MySQL as an example to explain the corresponding
1125
     * physical types):
1126
     *
1127
     * - `pk`: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"
1128
     * - `bigpk`: an auto-incremental primary key type, will be converted into "bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY"
1129
     * - `upk`: an unsigned auto-incremental primary key type, will be converted into "int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY"
1130
     * - `char`: char type, will be converted into "char(1)"
1131
     * - `string`: string type, will be converted into "varchar(255)"
1132
     * - `text`: a long string type, will be converted into "text"
1133
     * - `smallint`: a small integer type, will be converted into "smallint(6)"
1134
     * - `integer`: integer type, will be converted into "int(11)"
1135
     * - `bigint`: a big integer type, will be converted into "bigint(20)"
1136
     * - `boolean`: boolean type, will be converted into "tinyint(1)"
1137
     * - `float``: float number type, will be converted into "float"
1138
     * - `decimal`: decimal number type, will be converted into "decimal"
1139
     * - `datetime`: datetime type, will be converted into "datetime"
1140
     * - `timestamp`: timestamp type, will be converted into "timestamp"
1141
     * - `time`: time type, will be converted into "time"
1142
     * - `date`: date type, will be converted into "date"
1143
     * - `money`: money type, will be converted into "decimal(19,4)"
1144
     * - `binary`: binary data type, will be converted into "blob"
1145
     *
1146
     * If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only
1147
     * the first part will be converted, and the rest of the parts will be appended to the converted result.
1148
     * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'.
1149
     *
1150
     * For some of the abstract types you can also specify a length or precision constraint
1151
     * by appending it in round brackets directly to the type.
1152
     * For example `string(32)` will be converted into "varchar(32)" on a MySQL database.
1153
     * If the underlying DBMS does not support these kind of constraints for a type it will
1154
     * be ignored.
1155
     *
1156
     * If a type cannot be found in [[typeMap]], it will be returned without any change.
1157
     * @param string|ColumnSchemaBuilder $type abstract column type
1158
     * @return string physical column type.
1159
     */
1160 138
    public function getColumnType($type)
1161
    {
1162 138
        if ($type instanceof ColumnSchemaBuilder) {
1163 33
            $type = $type->__toString();
1164
        }
1165
1166 138
        if (isset($this->typeMap[$type])) {
1167 125
            return $this->typeMap[$type];
1168 77
        } elseif (preg_match('/^(\w+)\((.+?)\)(.*)$/', $type, $matches)) {
1169 39
            if (isset($this->typeMap[$matches[1]])) {
1170 39
                return preg_replace('/\(.+\)/', '(' . $matches[2] . ')', $this->typeMap[$matches[1]]) . $matches[3];
1171
            }
1172 52
        } elseif (preg_match('/^(\w+)\s+/', $type, $matches)) {
1173 49
            if (isset($this->typeMap[$matches[1]])) {
1174 49
                return preg_replace('/^\w+/', $this->typeMap[$matches[1]], $type);
1175
            }
1176
        }
1177
1178 32
        return $type;
1179
    }
1180
1181
    /**
1182
     * @param array $columns
1183
     * @param array $params the binding parameters to be populated
1184
     * @param bool $distinct
1185
     * @param string $selectOption
1186
     * @return string the SELECT clause built from [[Query::$select]].
1187
     */
1188 1138
    public function buildSelect($columns, &$params, $distinct = false, $selectOption = null)
1189
    {
1190 1138
        $select = $distinct ? 'SELECT DISTINCT' : 'SELECT';
1191 1138
        if ($selectOption !== null) {
1192
            $select .= ' ' . $selectOption;
1193
        }
1194
1195 1138
        if (empty($columns)) {
1196 873
            return $select . ' *';
1197
        }
1198
1199 543
        foreach ($columns as $i => $column) {
1200 543
            if ($column instanceof ExpressionInterface) {
1201 42
                if (is_int($i)) {
1202 6
                    $columns[$i] = $this->buildExpression($column, $params);
1203
                } else {
1204 42
                    $columns[$i] = $this->buildExpression($column, $params) . ' AS ' . $this->db->quoteColumnName($i);
1205
                }
1206 534
            } elseif ($column instanceof Query) {
1207
                list($sql, $params) = $this->build($column, $params);
1208
                $columns[$i] = "($sql) AS " . $this->db->quoteColumnName($i);
1209 534
            } elseif (is_string($i)) {
1210 23
                if (strpos($column, '(') === false) {
1211 23
                    $column = $this->db->quoteColumnName($column);
1212
                }
1213 23
                $columns[$i] = "$column AS " . $this->db->quoteColumnName($i);
1214 529
            } elseif (strpos($column, '(') === false) {
1215 445
                if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-_\.]+)$/', $column, $matches)) {
1216 6
                    $columns[$i] = $this->db->quoteColumnName($matches[1]) . ' AS ' . $this->db->quoteColumnName($matches[2]);
1217
                } else {
1218 543
                    $columns[$i] = $this->db->quoteColumnName($column);
1219
                }
1220
            }
1221
        }
1222
1223 543
        return $select . ' ' . implode(', ', $columns);
1224
    }
1225
1226
    /**
1227
     * @param array $tables
1228
     * @param array $params the binding parameters to be populated
1229
     * @return string the FROM clause built from [[Query::$from]].
1230
     */
1231 1138
    public function buildFrom($tables, &$params)
1232
    {
1233 1138
        if (empty($tables)) {
1234 349
            return '';
1235
        }
1236
1237 826
        $tables = $this->quoteTableNames($tables, $params);
1238
1239 826
        return 'FROM ' . implode(', ', $tables);
1240
    }
1241
1242
    /**
1243
     * @param array $joins
1244
     * @param array $params the binding parameters to be populated
1245
     * @return string the JOIN clause built from [[Query::$join]].
1246
     * @throws Exception if the $joins parameter is not in proper format
1247
     */
1248 1138
    public function buildJoin($joins, &$params)
1249
    {
1250 1138
        if (empty($joins)) {
1251 1126
            return '';
1252
        }
1253
1254 54
        foreach ($joins as $i => $join) {
1255 54
            if (!is_array($join) || !isset($join[0], $join[1])) {
1256
                throw new Exception('A join clause must be specified as an array of join type, join table, and optionally join condition.');
1257
            }
1258
            // 0:join type, 1:join table, 2:on-condition (optional)
1259 54
            list($joinType, $table) = $join;
1260 54
            $tables = $this->quoteTableNames((array) $table, $params);
1261 54
            $table = reset($tables);
1262 54
            $joins[$i] = "$joinType $table";
1263 54
            if (isset($join[2])) {
1264 54
                $condition = $this->buildCondition($join[2], $params);
1265 54
                if ($condition !== '') {
1266 54
                    $joins[$i] .= ' ON ' . $condition;
1267
                }
1268
            }
1269
        }
1270
1271 54
        return implode($this->separator, $joins);
1272
    }
1273
1274
    /**
1275
     * Quotes table names passed.
1276
     *
1277
     * @param array $tables
1278
     * @param array $params
1279
     * @return array
1280
     */
1281 826
    private function quoteTableNames($tables, &$params)
1282
    {
1283 826
        foreach ($tables as $i => $table) {
1284 826
            if ($table instanceof Query) {
1285 10
                list($sql, $params) = $this->build($table, $params);
1286 10
                $tables[$i] = "($sql) " . $this->db->quoteTableName($i);
1287 826
            } elseif (is_string($i)) {
1288 79
                if (strpos($table, '(') === false) {
1289 70
                    $table = $this->db->quoteTableName($table);
1290
                }
1291 79
                $tables[$i] = "$table " . $this->db->quoteTableName($i);
1292 805
            } elseif (strpos($table, '(') === false) {
1293 798
                if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias
1294 21
                    $tables[$i] = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
1295
                } else {
1296 826
                    $tables[$i] = $this->db->quoteTableName($table);
1297
                }
1298
            }
1299
        }
1300
1301 826
        return $tables;
1302
    }
1303
1304
    /**
1305
     * @param string|array $condition
1306
     * @param array $params the binding parameters to be populated
1307
     * @return string the WHERE clause built from [[Query::$where]].
1308
     */
1309 1215
    public function buildWhere($condition, &$params)
1310
    {
1311 1215
        $where = $this->buildCondition($condition, $params);
1312
1313 1215
        return $where === '' ? '' : 'WHERE ' . $where;
1314
    }
1315
1316
    /**
1317
     * @param array $columns
1318
     * @return string the GROUP BY clause
1319
     */
1320 1138
    public function buildGroupBy($columns)
1321
    {
1322 1138
        if (empty($columns)) {
1323 1132
            return '';
1324
        }
1325 21
        foreach ($columns as $i => $column) {
1326 21
            if ($column instanceof ExpressionInterface) {
1327 3
                $columns[$i] = $this->buildExpression($column);
1328 21
            } elseif (strpos($column, '(') === false) {
1329 21
                $columns[$i] = $this->db->quoteColumnName($column);
1330
            }
1331
        }
1332
1333 21
        return 'GROUP BY ' . implode(', ', $columns);
1334
    }
1335
1336
    /**
1337
     * @param string|array $condition
1338
     * @param array $params the binding parameters to be populated
1339
     * @return string the HAVING clause built from [[Query::$having]].
1340
     */
1341 1138
    public function buildHaving($condition, &$params)
1342
    {
1343 1138
        $having = $this->buildCondition($condition, $params);
1344
1345 1138
        return $having === '' ? '' : 'HAVING ' . $having;
1346
    }
1347
1348
    /**
1349
     * Builds the ORDER BY and LIMIT/OFFSET clauses and appends them to the given SQL.
1350
     * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
1351
     * @param array $orderBy the order by columns. See [[Query::orderBy]] for more details on how to specify this parameter.
1352
     * @param int $limit the limit number. See [[Query::limit]] for more details.
1353
     * @param int $offset the offset number. See [[Query::offset]] for more details.
1354
     * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
1355
     */
1356 1138
    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
1357
    {
1358 1138
        $orderBy = $this->buildOrderBy($orderBy);
1359 1138
        if ($orderBy !== '') {
1360 180
            $sql .= $this->separator . $orderBy;
1361
        }
1362 1138
        $limit = $this->buildLimit($limit, $offset);
1363 1138
        if ($limit !== '') {
1364 88
            $sql .= $this->separator . $limit;
1365
        }
1366
1367 1138
        return $sql;
1368
    }
1369
1370
    /**
1371
     * @param array $columns
1372
     * @return string the ORDER BY clause built from [[Query::$orderBy]].
1373
     */
1374 1138
    public function buildOrderBy($columns)
1375
    {
1376 1138
        if (empty($columns)) {
1377 1103
            return '';
1378
        }
1379 180
        $orders = [];
1380 180
        foreach ($columns as $name => $direction) {
1381 180
            if ($direction instanceof ExpressionInterface) {
1382 3
                $orders[] = $this->buildExpression($direction);
1383
            } else {
1384 180
                $orders[] = $this->db->quoteColumnName($name) . ($direction === SORT_DESC ? ' DESC' : '');
1385
            }
1386
        }
1387
1388 180
        return 'ORDER BY ' . implode(', ', $orders);
1389
    }
1390
1391
    /**
1392
     * @param int $limit
1393
     * @param int $offset
1394
     * @return string the LIMIT and OFFSET clauses
1395
     */
1396 413
    public function buildLimit($limit, $offset)
1397
    {
1398 413
        $sql = '';
1399 413
        if ($this->hasLimit($limit)) {
1400 24
            $sql = 'LIMIT ' . $limit;
1401
        }
1402 413
        if ($this->hasOffset($offset)) {
1403 3
            $sql .= ' OFFSET ' . $offset;
1404
        }
1405
1406 413
        return ltrim($sql);
1407
    }
1408
1409
    /**
1410
     * Checks to see if the given limit is effective.
1411
     * @param mixed $limit the given limit
1412
     * @return bool whether the limit is effective
1413
     */
1414 747
    protected function hasLimit($limit)
1415
    {
1416 747
        return ($limit instanceof ExpressionInterface) || ctype_digit((string) $limit);
1417
    }
1418
1419
    /**
1420
     * Checks to see if the given offset is effective.
1421
     * @param mixed $offset the given offset
1422
     * @return bool whether the offset is effective
1423
     */
1424 747
    protected function hasOffset($offset)
1425
    {
1426 747
        return ($offset instanceof ExpressionInterface) || ctype_digit((string) $offset) && (string) $offset !== '0';
1427
    }
1428
1429
    /**
1430
     * @param array $unions
1431
     * @param array $params the binding parameters to be populated
1432
     * @return string the UNION clause built from [[Query::$union]].
1433
     */
1434 804
    public function buildUnion($unions, &$params)
1435
    {
1436 804
        if (empty($unions)) {
1437 804
            return '';
1438
        }
1439
1440 8
        $result = '';
1441
1442 8
        foreach ($unions as $i => $union) {
1443 8
            $query = $union['query'];
1444 8
            if ($query instanceof Query) {
1445 8
                list($unions[$i]['query'], $params) = $this->build($query, $params);
1446
            }
1447
1448 8
            $result .= 'UNION ' . ($union['all'] ? 'ALL ' : '') . '( ' . $unions[$i]['query'] . ' ) ';
1449
        }
1450
1451 8
        return trim($result);
1452
    }
1453
1454
    /**
1455
     * Processes columns and properly quotes them if necessary.
1456
     * It will join all columns into a string with comma as separators.
1457
     * @param string|array $columns the columns to be processed
1458
     * @return string the processing result
1459
     */
1460 32
    public function buildColumns($columns)
1461
    {
1462 32
        if (!is_array($columns)) {
1463 27
            if (strpos($columns, '(') !== false) {
1464
                return $columns;
1465
            }
1466
1467 27
            $rawColumns = $columns;
1468 27
            $columns = preg_split('/\s*,\s*/', $columns, -1, PREG_SPLIT_NO_EMPTY);
1469 27
            if ($columns === false) {
1470
                throw new InvalidArgumentException("$rawColumns is not valid columns.");
1471
            }
1472
        }
1473 32
        foreach ($columns as $i => $column) {
1474 32
            if ($column instanceof ExpressionInterface) {
1475
                $columns[$i] = $this->buildExpression($column);
1476 32
            } elseif (strpos($column, '(') === false) {
1477 32
                $columns[$i] = $this->db->quoteColumnName($column);
1478
            }
1479
        }
1480
1481 32
        return implode(', ', $columns);
1482
    }
1483
1484
    /**
1485
     * Parses the condition specification and generates the corresponding SQL expression.
1486
     * @param string|array|ExpressionInterface $condition the condition specification. Please refer to [[Query::where()]]
1487
     * on how to specify a condition.
1488
     * @param array $params the binding parameters to be populated
1489
     * @return string the generated SQL expression
1490
     */
1491 1215
    public function buildCondition($condition, &$params)
1492
    {
1493 1215
        if (is_array($condition)) {
1494 951
            $condition = $this->createConditionFromArray($condition);
1495
        }
1496
1497 1215
        if ($condition instanceof ExpressionInterface) {
1498 972
            return $this->buildExpression($condition, $params);
1499
        }
1500 1200
        if (empty($condition)) {
1501 1200
            return '';
1502
        }
1503 131
        return (string) $condition;
1504
    }
1505
1506
    /**
1507
     * Transforms $condition defined in array format (as described in [[Query::where()]]
1508
     * to instance of [[yii\db\condition\ConditionInterface|ConditionInterface]] according to
1509
     * [[conditionClasses]] map.
1510
     *
1511
     * @param string|array $condition
1512
     * @see conditionClasses
1513
     * @return ConditionInterface
1514
     * @since 2.0.14
1515
     */
1516 951
    public function createConditionFromArray($condition)
1517
    {
1518 951
        if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
1519 569
            $operator = strtoupper(array_shift($condition));
1520 569
            if (isset($this->conditionClasses[$operator])) {
1521 491
                $className = $this->conditionClasses[$operator];
1522
            } else {
1523 84
                $className = 'yii\db\conditions\SimpleCondition';
1524
            }
1525
            /** @var ConditionInterface $className */
1526 569
            return $className::fromArrayDefinition($operator, $condition);
1527
        }
1528
1529
        // hash format: 'column1' => 'value1', 'column2' => 'value2', ...
1530 668
        return new HashCondition($condition);
0 ignored issues
show
Bug introduced by
It seems like $condition defined by parameter $condition on line 1516 can also be of type string; however, yii\db\conditions\HashCondition::__construct() does only seem to accept array|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1531
    }
1532
1533
    /**
1534
     * Creates a condition based on column-value pairs.
1535
     * @param array $condition the condition specification.
1536
     * @param array $params the binding parameters to be populated
1537
     * @return string the generated SQL expression
1538
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1539
     */
1540
    public function buildHashCondition($condition, &$params)
1541
    {
1542
        return $this->buildCondition(new HashCondition($condition), $params);
1543
    }
1544
1545
    /**
1546
     * Connects two or more SQL expressions with the `AND` or `OR` operator.
1547
     * @param string $operator the operator to use for connecting the given operands
1548
     * @param array $operands the SQL expressions to connect.
1549
     * @param array $params the binding parameters to be populated
1550
     * @return string the generated SQL expression
1551
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1552
     */
1553
    public function buildAndCondition($operator, $operands, &$params)
1554
    {
1555
        array_unshift($operands, $operator);
1556
        return $this->buildCondition($operands, $params);
1557
    }
1558
1559
    /**
1560
     * Inverts an SQL expressions with `NOT` operator.
1561
     * @param string $operator the operator to use for connecting the given operands
1562
     * @param array $operands the SQL expressions to connect.
1563
     * @param array $params the binding parameters to be populated
1564
     * @return string the generated SQL expression
1565
     * @throws InvalidArgumentException if wrong number of operands have been given.
1566
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1567
     */
1568
    public function buildNotCondition($operator, $operands, &$params)
1569
    {
1570
        array_unshift($operands, $operator);
1571
        return $this->buildCondition($operands, $params);
1572
    }
1573
1574
    /**
1575
     * Creates an SQL expressions with the `BETWEEN` operator.
1576
     * @param string $operator the operator to use (e.g. `BETWEEN` or `NOT BETWEEN`)
1577
     * @param array $operands the first operand is the column name. The second and third operands
1578
     * describe the interval that column value should be in.
1579
     * @param array $params the binding parameters to be populated
1580
     * @return string the generated SQL expression
1581
     * @throws InvalidArgumentException if wrong number of operands have been given.
1582
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1583
     */
1584
    public function buildBetweenCondition($operator, $operands, &$params)
1585
    {
1586
        array_unshift($operands, $operator);
1587
        return $this->buildCondition($operands, $params);
1588
    }
1589
1590
    /**
1591
     * Creates an SQL expressions with the `IN` operator.
1592
     * @param string $operator the operator to use (e.g. `IN` or `NOT IN`)
1593
     * @param array $operands the first operand is the column name. If it is an array
1594
     * a composite IN condition will be generated.
1595
     * The second operand is an array of values that column value should be among.
1596
     * If it is an empty array the generated expression will be a `false` value if
1597
     * operator is `IN` and empty if operator is `NOT IN`.
1598
     * @param array $params the binding parameters to be populated
1599
     * @return string the generated SQL expression
1600
     * @throws Exception if wrong number of operands have been given.
1601
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1602
     */
1603
    public function buildInCondition($operator, $operands, &$params)
1604
    {
1605
        array_unshift($operands, $operator);
1606
        return $this->buildCondition($operands, $params);
1607
    }
1608
1609
    /**
1610
     * Creates an SQL expressions with the `LIKE` operator.
1611
     * @param string $operator the operator to use (e.g. `LIKE`, `NOT LIKE`, `OR LIKE` or `OR NOT LIKE`)
1612
     * @param array $operands an array of two or three operands
1613
     *
1614
     * - The first operand is the column name.
1615
     * - The second operand is a single value or an array of values that column value
1616
     *   should be compared with. If it is an empty array the generated expression will
1617
     *   be a `false` value if operator is `LIKE` or `OR LIKE`, and empty if operator
1618
     *   is `NOT LIKE` or `OR NOT LIKE`.
1619
     * - An optional third operand can also be provided to specify how to escape special characters
1620
     *   in the value(s). The operand should be an array of mappings from the special characters to their
1621
     *   escaped counterparts. If this operand is not provided, a default escape mapping will be used.
1622
     *   You may use `false` or an empty array to indicate the values are already escaped and no escape
1623
     *   should be applied. Note that when using an escape mapping (or the third operand is not provided),
1624
     *   the values will be automatically enclosed within a pair of percentage characters.
1625
     * @param array $params the binding parameters to be populated
1626
     * @return string the generated SQL expression
1627
     * @throws InvalidArgumentException if wrong number of operands have been given.
1628
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1629
     */
1630
    public function buildLikeCondition($operator, $operands, &$params)
1631
    {
1632
        array_unshift($operands, $operator);
1633
        return $this->buildCondition($operands, $params);
1634
    }
1635
1636
    /**
1637
     * Creates an SQL expressions with the `EXISTS` operator.
1638
     * @param string $operator the operator to use (e.g. `EXISTS` or `NOT EXISTS`)
1639
     * @param array $operands contains only one element which is a [[Query]] object representing the sub-query.
1640
     * @param array $params the binding parameters to be populated
1641
     * @return string the generated SQL expression
1642
     * @throws InvalidArgumentException if the operand is not a [[Query]] object.
1643
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1644
     */
1645
    public function buildExistsCondition($operator, $operands, &$params)
1646
    {
1647
        array_unshift($operands, $operator);
1648
        return $this->buildCondition($operands, $params);
1649
    }
1650
1651
    /**
1652
     * Creates an SQL expressions like `"column" operator value`.
1653
     * @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
1654
     * @param array $operands contains two column names.
1655
     * @param array $params the binding parameters to be populated
1656
     * @return string the generated SQL expression
1657
     * @throws InvalidArgumentException if wrong number of operands have been given.
1658
     * @deprecated since 2.0.14. Use `buildCondition()` instead.
1659
     */
1660
    public function buildSimpleCondition($operator, $operands, &$params)
1661
    {
1662
        array_unshift($operands, $operator);
1663
        return $this->buildCondition($operands, $params);
1664
    }
1665
1666
    /**
1667
     * Creates a SELECT EXISTS() SQL statement.
1668
     * @param string $rawSql the subquery in a raw form to select from.
1669
     * @return string the SELECT EXISTS() SQL statement.
1670
     * @since 2.0.8
1671
     */
1672 61
    public function selectExists($rawSql)
1673
    {
1674 61
        return 'SELECT EXISTS(' . $rawSql . ')';
1675
    }
1676
1677
    /**
1678
     * Helper method to add $value to $params array using [[PARAM_PREFIX]].
1679
     *
1680
     * @param string|null $value
1681
     * @param array $params passed by reference
1682
     * @return string the placeholder name in $params array
1683
     *
1684
     * @since 2.0.14
1685
     */
1686 1003
    public function bindParam($value, &$params)
1687
    {
1688 1003
        $phName = self::PARAM_PREFIX . count($params);
1689 1003
        $params[$phName] = $value;
1690
1691 1003
        return $phName;
1692
    }
1693
}
1694