GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( ecf3ef...78a151 )
by Robert
11:40
created

QueryBuilder::prepareUpsertColumns()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 9
cts 9
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 10
nc 4
nop 4
crap 3
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 1367
    public function __construct($connection, $config = [])
110
    {
111 1367
        $this->db = $connection;
112 1367
        parent::__construct($config);
113 1367
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 1367
    public function init()
119
    {
120 1367
        parent::init();
121
122 1367
        $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...shCondition":"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 1367
        $this->conditionClasses = array_merge($this->defaultConditionClasses(), $this->conditionClasses);
124 1367
    }
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 1367
    protected function defaultConditionClasses()
135
    {
136
        return [
137 1367
            '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 1367
    protected function defaultExpressionBuilders()
162
    {
163
        return [
164 1367
            'yii\db\PdoValue' => 'yii\db\PdoValueBuilder',
165
            'yii\db\Expression' => 'yii\db\ExpressionBuilder',
166
            'yii\db\conditions\ConjunctionCondition' => 'yii\db\conditions\ConjunctionConditionBuilder',
167
            'yii\db\conditions\NotCondition' => 'yii\db\conditions\NotConditionBuilder',
168
            'yii\db\conditions\AndCondition' => 'yii\db\conditions\ConjunctionConditionBuilder',
169
            'yii\db\conditions\OrCondition' => 'yii\db\conditions\ConjunctionConditionBuilder',
170
            'yii\db\conditions\BetweenCondition' => 'yii\db\conditions\BetweenConditionBuilder',
171
            'yii\db\conditions\InCondition' => 'yii\db\conditions\InConditionBuilder',
172
            'yii\db\conditions\LikeCondition' => 'yii\db\conditions\LikeConditionBuilder',
173
            'yii\db\conditions\ExistsCondition' => 'yii\db\conditions\ExistsConditionBuilder',
174
            'yii\db\conditions\SimpleCondition' => 'yii\db\conditions\SimpleConditionBuilder',
175
            'yii\db\conditions\HashCondition' => 'yii\db\conditions\HashConditionBuilder',
176
        ];
177
    }
178
179
    /**
180
     * Setter for [[expressionBuilders]] property.
181
     *
182
     * @param string[] $builders array of builder that should be merged with [[expressionBuilders]]
183
     * @since 2.0.14
184
     * @see expressionBuilders
185
     */
186
    public function setExpressionBuilders($builders)
187
    {
188
        $this->expressionBuilders = array_merge($this->expressionBuilders, $builders);
189
    }
190
191
    /**
192
     * Generates a SELECT SQL statement from a [[Query]] object.
193
     *
194
     * @param Query $query the [[Query]] object from which the SQL statement will be generated.
195
     * @param array $params the parameters to be bound to the generated SQL statement. These parameters will
196
     * be included in the result with the additional parameters generated during the query building process.
197
     * @return array the generated SQL statement (the first array element) and the corresponding
198
     * parameters to be bound to the SQL statement (the second array element). The parameters returned
199
     * include those provided in `$params`.
200
     */
201 780
    public function build($query, $params = [])
202
    {
203 780
        $query = $query->prepare($this);
204
205 780
        $params = empty($params) ? $query->params : array_merge($params, $query->params);
206
207
        $clauses = [
208 780
            $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
209 780
            $this->buildFrom($query->from, $params),
210 780
            $this->buildJoin($query->join, $params),
211 780
            $this->buildWhere($query->where, $params),
212 780
            $this->buildGroupBy($query->groupBy),
213 780
            $this->buildHaving($query->having, $params),
214
        ];
215
216 780
        $sql = implode($this->separator, array_filter($clauses));
217 780
        $sql = $this->buildOrderByAndLimit($sql, $query->orderBy, $query->limit, $query->offset);
0 ignored issues
show
Bug introduced by
It seems like $query->limit can also be of type object<yii\db\ExpressionInterface>; however, yii\db\QueryBuilder::buildOrderByAndLimit() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $query->offset can also be of type object<yii\db\ExpressionInterface>; however, yii\db\QueryBuilder::buildOrderByAndLimit() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

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