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::prepareInsertValues()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 10
nc 4
nop 3
crap 6
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\mysql;
9
10
use yii\base\InvalidArgumentException;
11
use yii\base\NotSupportedException;
12
use yii\db\Exception;
13
use yii\db\Expression;
14
use yii\db\Query;
15
16
/**
17
 * QueryBuilder is the query builder for MySQL databases.
18
 *
19
 * @author Qiang Xue <[email protected]>
20
 * @since 2.0
21
 */
22
class QueryBuilder extends \yii\db\QueryBuilder
23
{
24
    /**
25
     * @var array mapping from abstract column types (keys) to physical column types (values).
26
     */
27
    public $typeMap = [
28
        Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
29
        Schema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
30
        Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',
31
        Schema::TYPE_UBIGPK => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
32
        Schema::TYPE_CHAR => 'char(1)',
33
        Schema::TYPE_STRING => 'varchar(255)',
34
        Schema::TYPE_TEXT => 'text',
35
        Schema::TYPE_SMALLINT => 'smallint(6)',
36
        Schema::TYPE_INTEGER => 'int(11)',
37
        Schema::TYPE_BIGINT => 'bigint(20)',
38
        Schema::TYPE_FLOAT => 'float',
39
        Schema::TYPE_DOUBLE => 'double',
40
        Schema::TYPE_DECIMAL => 'decimal(10,0)',
41
        Schema::TYPE_DATETIME => 'datetime',
42
        Schema::TYPE_TIMESTAMP => 'timestamp',
43
        Schema::TYPE_TIME => 'time',
44
        Schema::TYPE_DATE => 'date',
45
        Schema::TYPE_BINARY => 'blob',
46
        Schema::TYPE_BOOLEAN => 'tinyint(1)',
47
        Schema::TYPE_MONEY => 'decimal(19,4)',
48
        Schema::TYPE_JSON => 'json'
49
    ];
50
51
    /**
52
     * {@inheritdoc}
53
     */
54 476
    protected function defaultExpressionBuilders()
55
    {
56 476
        return array_merge(parent::defaultExpressionBuilders(), [
57 476
            'yii\db\JsonExpression' => 'yii\db\mysql\JsonExpressionBuilder',
58
        ]);
59
    }
60
61
    /**
62
     * Builds a SQL statement for renaming a column.
63
     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
64
     * @param string $oldName the old name of the column. The name will be properly quoted by the method.
65
     * @param string $newName the new name of the column. The name will be properly quoted by the method.
66
     * @return string the SQL statement for renaming a DB column.
67
     * @throws Exception
68
     */
69
    public function renameColumn($table, $oldName, $newName)
70
    {
71
        $quotedTable = $this->db->quoteTableName($table);
72
        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
73
        if ($row === false) {
74
            throw new Exception("Unable to find column '$oldName' in table '$table'.");
75
        }
76
        if (isset($row['Create Table'])) {
77
            $sql = $row['Create Table'];
78
        } else {
79
            $row = array_values($row);
80
            $sql = $row[1];
81
        }
82
        if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
83
            foreach ($matches[1] as $i => $c) {
84
                if ($c === $oldName) {
85
                    return "ALTER TABLE $quotedTable CHANGE "
86
                        . $this->db->quoteColumnName($oldName) . ' '
87
                        . $this->db->quoteColumnName($newName) . ' '
88
                        . $matches[2][$i];
89
                }
90
            }
91
        }
92
        // try to give back a SQL anyway
93
        return "ALTER TABLE $quotedTable CHANGE "
94
            . $this->db->quoteColumnName($oldName) . ' '
95
            . $this->db->quoteColumnName($newName);
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     * @see https://bugs.mysql.com/bug.php?id=48875
101
     */
102 12
    public function createIndex($name, $table, $columns, $unique = false)
103
    {
104
        return 'ALTER TABLE '
105 12
        . $this->db->quoteTableName($table)
106 12
        . ($unique ? ' ADD UNIQUE INDEX ' : ' ADD INDEX ')
107 12
        . $this->db->quoteTableName($name)
108 12
        . ' (' . $this->buildColumns($columns) . ')';
109
    }
110
111
    /**
112
     * Builds a SQL statement for dropping a foreign key constraint.
113
     * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
114
     * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
115
     * @return string the SQL statement for dropping a foreign key constraint.
116
     */
117 3
    public function dropForeignKey($name, $table)
118
    {
119 3
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
120 3
            . ' DROP FOREIGN KEY ' . $this->db->quoteColumnName($name);
121
    }
122
123
    /**
124
     * Builds a SQL statement for removing a primary key constraint to an existing table.
125
     * @param string $name the name of the primary key constraint to be removed.
126
     * @param string $table the table that the primary key constraint will be removed from.
127
     * @return string the SQL statement for removing a primary key constraint from an existing table.
128
     */
129 2
    public function dropPrimaryKey($name, $table)
130
    {
131 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' DROP PRIMARY KEY';
132
    }
133
134
    /**
135
     * @inheritDoc
136
     */
137 2
    public function dropUnique($name, $table)
138
    {
139 2
        return $this->dropIndex($name, $table);
140
    }
141
142
    /**
143
     * @inheritDoc
144
     * @throws NotSupportedException this is not supported by MySQL.
145
     */
146
    public function addCheck($name, $table, $expression)
147
    {
148
        throw new NotSupportedException(__METHOD__ . ' is not supported by MySQL.');
149
    }
150
151
    /**
152
     * @inheritDoc
153
     * @throws NotSupportedException this is not supported by MySQL.
154
     */
155
    public function dropCheck($name, $table)
156
    {
157
        throw new NotSupportedException(__METHOD__ . ' is not supported by MySQL.');
158
    }
159
160
    /**
161
     * Creates a SQL statement for resetting the sequence value of a table's primary key.
162
     * The sequence will be reset such that the primary key of the next new row inserted
163
     * will have the specified value or 1.
164
     * @param string $tableName the name of the table whose primary key sequence will be reset
165
     * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
166
     * the next new row's primary key will have a value 1.
167
     * @return string the SQL statement for resetting sequence
168
     * @throws InvalidArgumentException if the table does not exist or there is no sequence associated with the table.
169
     */
170 9
    public function resetSequence($tableName, $value = null)
171
    {
172 9
        $table = $this->db->getTableSchema($tableName);
173 9
        if ($table !== null && $table->sequenceName !== null) {
174 9
            $tableName = $this->db->quoteTableName($tableName);
175 9
            if ($value === null) {
176 1
                $key = reset($table->primaryKey);
177 1
                $value = $this->db->createCommand("SELECT MAX(`$key`) FROM $tableName")->queryScalar() + 1;
178
            } else {
179 9
                $value = (int) $value;
180
            }
181
182 9
            return "ALTER TABLE $tableName AUTO_INCREMENT=$value";
183
        } elseif ($table === null) {
184
            throw new InvalidArgumentException("Table not found: $tableName");
185
        }
186
187
        throw new InvalidArgumentException("There is no sequence associated with table '$tableName'.");
188
    }
189
190
    /**
191
     * Builds a SQL statement for enabling or disabling integrity check.
192
     * @param bool $check whether to turn on or off the integrity check.
193
     * @param string $schema the schema of the tables. Meaningless for MySQL.
194
     * @param string $table the table name. Meaningless for MySQL.
195
     * @return string the SQL statement for checking integrity
196
     */
197 4
    public function checkIntegrity($check = true, $schema = '', $table = '')
198
    {
199 4
        return 'SET FOREIGN_KEY_CHECKS = ' . ($check ? 1 : 0);
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205 379
    public function buildLimit($limit, $offset)
206
    {
207 379
        $sql = '';
208 379
        if ($this->hasLimit($limit)) {
209 30
            $sql = 'LIMIT ' . $limit;
210 30
            if ($this->hasOffset($offset)) {
211 30
                $sql .= ' OFFSET ' . $offset;
212
            }
213 372
        } elseif ($this->hasOffset($offset)) {
214
            // limit is not optional in MySQL
215
            // http://stackoverflow.com/a/271650/1106908
216
            // http://dev.mysql.com/doc/refman/5.0/en/select.html#idm47619502796240
217 2
            $sql = "LIMIT $offset, 18446744073709551615"; // 2^64-1
218
        }
219
220 379
        return $sql;
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226 379
    protected function hasLimit($limit)
227
    {
228
        // In MySQL limit argument must be nonnegative integer constant
229 379
        return ctype_digit((string) $limit);
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235 379
    protected function hasOffset($offset)
236
    {
237
        // In MySQL offset argument must be nonnegative integer constant
238 379
        $offset = (string) $offset;
239 379
        return ctype_digit($offset) && $offset !== '0';
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245 200
    protected function prepareInsertValues($table, $columns, $params = [])
246
    {
247 200
        list($names, $placeholders, $values, $params) = parent::prepareInsertValues($table, $columns, $params);
248 197
        if (!$columns instanceof Query && empty($names)) {
249 1
            $tableSchema = $this->db->getSchema()->getTableSchema($table);
250 1
            if ($tableSchema !== null) {
251 1
                $columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : [reset($tableSchema->columns)->name];
252 1
                foreach ($columns as $name) {
253 1
                    $names[] = $this->db->quoteColumnName($name);
254 1
                    $placeholders[] = 'DEFAULT';
255
                }
256
            }
257
        }
258 197
        return [$names, $placeholders, $values, $params];
259
    }
260
261
    /**
262
     * @inheritdoc
263
     * @see https://downloads.mysql.com/docs/refman-5.1-en.pdf
264
     */
265 22
    public function upsert($table, $insertColumns, $updateColumns, &$params)
266
    {
267 22
        $insertSql = $this->insert($table, $insertColumns, $params);
268 22
        list($uniqueNames, , $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns);
269 22
        if (empty($uniqueNames)) {
270 3
            return $insertSql;
271
        }
272
273 19
        if ($updateColumns === true) {
274 9
            $updateColumns = [];
275 9
            foreach ($updateNames as $name) {
276 9
                $updateColumns[$name] = new Expression('VALUES(' . $this->db->quoteColumnName($name) . ')');
277
            }
278 10
        } elseif ($updateColumns === false) {
279 4
            $name = $this->db->quoteColumnName(reset($uniqueNames));
280 4
            $updateColumns = [$name => new Expression($this->db->quoteTableName($table) . '.' . $name)];
281
        }
282 19
        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
283 19
        return $insertSql . ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updates);
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     * @since 2.0.8
289
     */
290 2
    public function addCommentOnColumn($table, $column, $comment)
291
    {
292 2
        $definition = $this->getColumnDefinition($table, $column);
293 2
        $definition = trim(preg_replace("/COMMENT '(.*?)'/i", '', $definition));
294
295 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table)
296 2
            . ' CHANGE ' . $this->db->quoteColumnName($column)
297 2
            . ' ' . $this->db->quoteColumnName($column)
298 2
            . (empty($definition) ? '' : ' ' . $definition)
299 2
            . ' COMMENT ' . $this->db->quoteValue($comment);
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     * @since 2.0.8
305
     */
306 1
    public function addCommentOnTable($table, $comment)
307
    {
308 1
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' COMMENT ' . $this->db->quoteValue($comment);
309
    }
310
311
    /**
312
     * {@inheritdoc}
313
     * @since 2.0.8
314
     */
315 2
    public function dropCommentFromColumn($table, $column)
316
    {
317 2
        return $this->addCommentOnColumn($table, $column, '');
318
    }
319
320
    /**
321
     * {@inheritdoc}
322
     * @since 2.0.8
323
     */
324 1
    public function dropCommentFromTable($table)
325
    {
326 1
        return $this->addCommentOnTable($table, '');
327
    }
328
329
330
    /**
331
     * Gets column definition.
332
     *
333
     * @param string $table table name
334
     * @param string $column column name
335
     * @return null|string the column definition
336
     * @throws Exception in case when table does not contain column
337
     */
338 2
    private function getColumnDefinition($table, $column)
339
    {
340 2
        $quotedTable = $this->db->quoteTableName($table);
341 2
        $row = $this->db->createCommand('SHOW CREATE TABLE ' . $quotedTable)->queryOne();
342 2
        if ($row === false) {
343
            throw new Exception("Unable to find column '$column' in table '$table'.");
344
        }
345 2
        if (isset($row['Create Table'])) {
346 2
            $sql = $row['Create Table'];
347
        } else {
348
            $row = array_values($row);
349
            $sql = $row[1];
350
        }
351 2
        if (preg_match_all('/^\s*`(.*?)`\s+(.*?),?$/m', $sql, $matches)) {
352 2
            foreach ($matches[1] as $i => $c) {
353 2
                if ($c === $column) {
354 2
                    return $matches[2][$i];
355
                }
356
            }
357
        }
358
359
        return null;
360
    }
361
362
}
363