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 ( 6d277d...c73de3 )
by Robert
11:50
created

QueryBuilder::dropCommentFromColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 4
cp 0
cc 1
eloc 2
nc 1
nop 2
crap 2
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\mssql;
9
10
use yii\base\InvalidParamException;
11
use yii\base\NotSupportedException;
12
use yii\db\Expression;
13
14
/**
15
 * QueryBuilder is the query builder for MS SQL Server databases (version 2008 and above).
16
 *
17
 * @author Timur Ruziev <[email protected]>
18
 * @since 2.0
19
 */
20
class QueryBuilder extends \yii\db\QueryBuilder
21
{
22
    /**
23
     * @var array mapping from abstract column types (keys) to physical column types (values).
24
     */
25
    public $typeMap = [
26
        Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY',
27
        Schema::TYPE_UPK => 'int IDENTITY PRIMARY KEY',
28
        Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY',
29
        Schema::TYPE_UBIGPK => 'bigint IDENTITY PRIMARY KEY',
30
        Schema::TYPE_CHAR => 'nchar(1)',
31
        Schema::TYPE_STRING => 'nvarchar(255)',
32
        Schema::TYPE_TEXT => 'nvarchar(max)',
33
        Schema::TYPE_SMALLINT => 'smallint',
34
        Schema::TYPE_INTEGER => 'int',
35
        Schema::TYPE_BIGINT => 'bigint',
36
        Schema::TYPE_FLOAT => 'float',
37
        Schema::TYPE_DOUBLE => 'float',
38
        Schema::TYPE_DECIMAL => 'decimal',
39
        Schema::TYPE_DATETIME => 'datetime',
40
        Schema::TYPE_TIMESTAMP => 'datetime',
41
        Schema::TYPE_TIME => 'time',
42
        Schema::TYPE_DATE => 'date',
43
        Schema::TYPE_BINARY => 'varbinary(max)',
44
        Schema::TYPE_BOOLEAN => 'bit',
45
        Schema::TYPE_MONEY => 'decimal(19,4)',
46
    ];
47
48
49
    /**
50
     * @inheritdoc
51
     */
52
    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
53
    {
54
        if (!$this->hasOffset($offset) && !$this->hasLimit($limit)) {
55
            $orderBy = $this->buildOrderBy($orderBy);
56
            return $orderBy === '' ? $sql : $sql . $this->separator . $orderBy;
57
        }
58
59
        if ($this->isOldMssql()) {
60
            return $this->oldBuildOrderByAndLimit($sql, $orderBy, $limit, $offset);
61
        } else {
62
            return $this->newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset);
63
        }
64
    }
65
66
    /**
67
     * Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2012 or newer.
68
     * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
69
     * @param array $orderBy the order by columns. See [[\yii\db\Query::orderBy]] for more details on how to specify this parameter.
70
     * @param int $limit the limit number. See [[\yii\db\Query::limit]] for more details.
71
     * @param int $offset the offset number. See [[\yii\db\Query::offset]] for more details.
72
     * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
73
     */
74
    protected function newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset)
75
    {
76
        $orderBy = $this->buildOrderBy($orderBy);
77
        if ($orderBy === '') {
78
            // ORDER BY clause is required when FETCH and OFFSET are in the SQL
79
            $orderBy = 'ORDER BY (SELECT NULL)';
80
        }
81
        $sql .= $this->separator . $orderBy;
82
83
        // http://technet.microsoft.com/en-us/library/gg699618.aspx
84
        $offset = $this->hasOffset($offset) ? $offset : '0';
85
        $sql .= $this->separator . "OFFSET $offset ROWS";
86
        if ($this->hasLimit($limit)) {
87
            $sql .= $this->separator . "FETCH NEXT $limit ROWS ONLY";
88
        }
89
90
        return $sql;
91
    }
92
93
    /**
94
     * Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2005 to 2008.
95
     * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET)
96
     * @param array $orderBy the order by columns. See [[\yii\db\Query::orderBy]] for more details on how to specify this parameter.
97
     * @param int $limit the limit number. See [[\yii\db\Query::limit]] for more details.
98
     * @param int $offset the offset number. See [[\yii\db\Query::offset]] for more details.
99
     * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any)
100
     */
101
    protected function oldBuildOrderByAndLimit($sql, $orderBy, $limit, $offset)
102
    {
103
        $orderBy = $this->buildOrderBy($orderBy);
104
        if ($orderBy === '') {
105
            // ROW_NUMBER() requires an ORDER BY clause
106
            $orderBy = 'ORDER BY (SELECT NULL)';
107
        }
108
109
        $sql = preg_replace('/^([\s(])*SELECT(\s+DISTINCT)?(?!\s*TOP\s*\()/i', "\\1SELECT\\2 rowNum = ROW_NUMBER() over ($orderBy),", $sql);
110
111
        if ($this->hasLimit($limit)) {
112
            $sql = "SELECT TOP $limit * FROM ($sql) sub";
113
        } else {
114
            $sql = "SELECT * FROM ($sql) sub";
115
        }
116
        if ($this->hasOffset($offset)) {
117
            $sql .= $this->separator . "WHERE rowNum > $offset";
118
        }
119
120
        return $sql;
121
    }
122
123
    /**
124
     * Builds a SQL statement for renaming a DB table.
125
     * @param string $oldName the table to be renamed. The name will be properly quoted by the method.
126
     * @param string $newName the new table name. The name will be properly quoted by the method.
127
     * @return string the SQL statement for renaming a DB table.
128
     */
129
    public function renameTable($oldName, $newName)
130
    {
131
        return 'sp_rename ' . $this->db->quoteTableName($oldName) . ', ' . $this->db->quoteTableName($newName);
132
    }
133
134
    /**
135
     * Builds a SQL statement for renaming a column.
136
     * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
137
     * @param string $oldName the old name of the column. The name will be properly quoted by the method.
138
     * @param string $newName the new name of the column. The name will be properly quoted by the method.
139
     * @return string the SQL statement for renaming a DB column.
140
     */
141
    public function renameColumn($table, $oldName, $newName)
142
    {
143
        $table = $this->db->quoteTableName($table);
144
        $oldName = $this->db->quoteColumnName($oldName);
145
        $newName = $this->db->quoteColumnName($newName);
146
        return "sp_rename '{$table}.{$oldName}', {$newName}, 'COLUMN'";
147
    }
148
149
    /**
150
     * Builds a SQL statement for changing the definition of a column.
151
     * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
152
     * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
153
     * @param string $type the new column type. The [[getColumnType]] method will be invoked to convert abstract column type (if any)
154
     * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
155
     * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
156
     * @return string the SQL statement for changing the definition of a column.
157
     */
158
    public function alterColumn($table, $column, $type)
159
    {
160
        $type = $this->getColumnType($type);
161
        $sql = 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
162
            . $this->db->quoteColumnName($column) . ' '
163
            . $this->getColumnType($type);
164
165
        return $sql;
166
    }
167
168
    /**
169
     * Builds a SQL statement for enabling or disabling integrity check.
170
     * @param bool $check whether to turn on or off the integrity check.
171
     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
172
     * @param string $table the table name. Defaults to empty string, meaning that no table will be changed.
173
     * @return string the SQL statement for checking integrity
174
     * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
175
     */
176
    public function checkIntegrity($check = true, $schema = '', $table = '')
177
    {
178
        if ($schema !== '') {
179
            $table = "{$schema}.{$table}";
180
        }
181
        $table = $this->db->quoteTableName($table);
182
        if ($this->db->getTableSchema($table) === null) {
183
            throw new InvalidParamException("Table not found: $table");
184
        }
185
        $enable = $check ? 'CHECK' : 'NOCHECK';
186
187
        return "ALTER TABLE {$table} {$enable} CONSTRAINT ALL";
188
    }
189
190
    /**
191
     * @inheritdoc
192
     * @since 2.0.8
193
     */
194
    public function addCommentOnColumn($table, $column, $comment)
195
    {
196
        return "sp_updateextendedproperty @name = N'MS_Description', @value = {$this->db->quoteValue($comment)}, @level1type = N'Table',  @level1name = {$this->db->quoteTableName($table)}, @level2type = N'Column', @level2name = {$this->db->quoteColumnName($column)}";
197
    }
198
199
    /**
200
     * @inheritdoc
201
     * @since 2.0.8
202
     */
203
    public function addCommentOnTable($table, $comment)
204
    {
205
        return "sp_updateextendedproperty @name = N'MS_Description', @value = {$this->db->quoteValue($comment)}, @level1type = N'Table',  @level1name = {$this->db->quoteTableName($table)}";
206
    }
207
208
    /**
209
     * @inheritdoc
210
     * @since 2.0.8
211
     */
212
    public function dropCommentFromColumn($table, $column)
213
    {
214
        return "sp_dropextendedproperty @name = N'MS_Description', @level1type = N'Table',  @level1name = {$this->db->quoteTableName($table)}, @level2type = N'Column', @level2name = {$this->db->quoteColumnName($column)}";
215
    }
216
217
    /**
218
     * @inheritdoc
219
     * @since 2.0.8
220
     */
221
    public function dropCommentFromTable($table)
222
    {
223
        return "sp_dropextendedproperty @name = N'MS_Description', @level1type = N'Table',  @level1name = {$this->db->quoteTableName($table)}";
224
    }
225
226
    /**
227
     * Returns an array of column names given model name
228
     *
229
     * @param string $modelClass name of the model class
230
     * @return array|null array of column names
231
     */
232
    protected function getAllColumnNames($modelClass = null)
233
    {
234
        if (!$modelClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $modelClass of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
235
            return null;
236
        }
237
        /* @var $model \yii\db\ActiveRecord */
238
        $model = new $modelClass;
239
        $schema = $model->getTableSchema();
240
        return array_keys($schema->columns);
241
    }
242
243
    /**
244
     * @var bool whether MSSQL used is old.
245
     */
246
    private $_oldMssql;
247
248
    /**
249
     * @return bool whether the version of the MSSQL being used is older than 2012.
250
     * @throws \yii\base\InvalidConfigException
251
     * @throws \yii\db\Exception
252
     */
253
    protected function isOldMssql()
254
    {
255
        if ($this->_oldMssql === null) {
256
            $pdo = $this->db->getSlavePdo();
257
            $version = explode('.', $pdo->getAttribute(\PDO::ATTR_SERVER_VERSION));
258
            $this->_oldMssql = $version[0] < 11;
259
        }
260
        return $this->_oldMssql;
261
    }
262
263
    /**
264
     * @inheritdoc
265
     * @throws NotSupportedException if `$columns` is an array
266
     */
267
    protected function buildSubqueryInCondition($operator, $columns, $values, &$params)
268
    {
269
        if (is_array($columns)) {
270
            throw new NotSupportedException(__METHOD__ . ' is not supported by MSSQL.');
271
        }
272
        return parent::buildSubqueryInCondition($operator, $columns, $values, $params);
273
    }
274
275
    /**
276
     * Builds SQL for IN condition
277
     *
278
     * @param string $operator
279
     * @param array $columns
280
     * @param array $values
281
     * @param array $params
282
     * @return string SQL
283
     */
284
    protected function buildCompositeInCondition($operator, $columns, $values, &$params)
285
    {
286
        $quotedColumns = [];
287
        foreach ($columns as $i => $column) {
288
            $quotedColumns[$i] = strpos($column, '(') === false ? $this->db->quoteColumnName($column) : $column;
289
        }
290
        $vss = [];
291
        foreach ($values as $value) {
292
            $vs = [];
293
            foreach ($columns as $i => $column) {
294
                if (isset($value[$column])) {
295
                    $phName = self::PARAM_PREFIX . count($params);
296
                    $params[$phName] = $value[$column];
297
                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' = ' : ' != ') . $phName;
298
                } else {
299
                    $vs[] = $quotedColumns[$i] . ($operator === 'IN' ? ' IS' : ' IS NOT') . ' NULL';
300
                }
301
            }
302
            $vss[] = '(' . implode($operator === 'IN' ? ' AND ' : ' OR ', $vs) . ')';
303
        }
304
305
        return '(' . implode($operator === 'IN' ? ' OR ' : ' AND ', $vss) . ')';
306
    }
307
308
    /**
309
     * @inheritdoc
310
     * @since 2.0.8
311
     */
312
    public function selectExists($rawSql)
313
    {
314
        return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';
315
    }
316
317
    /**
318
     * Normalizes data to be saved into the table, performing extra preparations and type converting, if necessary.
319
     * @param string $table the table that data will be saved into.
320
     * @param array $columns the column data (name => value) to be saved into the table.
321
     * @return array normalized columns
322
     */
323
    private function normalizeTableRowData($table, $columns, &$params)
324
    {
325
        if (($tableSchema = $this->db->getSchema()->getTableSchema($table)) !== null) {
326
            $columnSchemas = $tableSchema->columns;
327
            foreach ($columns as $name => $value) {
328
                // @see https://github.com/yiisoft/yii2/issues/12599
329
                if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && is_string($value)) {
330
                    $phName = self::PARAM_PREFIX . count($params);
331
                    $columns[$name] = new Expression("CONVERT(VARBINARY, $phName)", [$phName => $value]);
332
                }
333
            }
334
        }
335
        return $columns;
336
    }
337
338
    /**
339
     * @inheritdoc
340
     */
341
    public function insert($table, $columns, &$params)
342
    {
343
        return parent::insert($table, $this->normalizeTableRowData($table, $columns, $params), $params);
344
    }
345
346
    /**
347
     * @inheritdoc
348
     */
349
    public function update($table, $columns, $condition, &$params)
350
    {
351
        return parent::update($table, $this->normalizeTableRowData($table, $columns, $params), $condition, $params);
352
    }
353
}
354