Test Failed
Pull Request — master (#66)
by Wilmer
18:24 queued 07:05
created

QueryBuilderPDOMssql   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 68
c 1
b 0
f 0
dl 0
loc 209
rs 10
wmc 27

19 Methods

Rating   Name   Duplication   Size   Complexity  
A schema() 0 3 1
A command() 0 3 1
A renameTable() 0 4 1
A getColumnType() 0 7 1
A newBuildOrderByAndLimit() 0 27 4
A dropCommentFromColumn() 0 3 1
A checkIntegrity() 0 3 1
A defaultExpressionBuilders() 0 5 1
A renameColumn() 0 7 1
A alterColumn() 0 4 1
A __construct() 0 8 1
A getAllColumnNames() 0 9 2
A extractAlias() 0 7 2
A dropCommentFromTable() 0 3 1
A addCommentOnTable() 0 3 1
A quoter() 0 3 1
A buildOrderByAndLimit() 0 9 4
A selectExists() 0 3 1
A addCommentOnColumn() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Mssql\PDO;
6
7
use Yiisoft\Db\Command\CommandInterface;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Command\CommandInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Yiisoft\Db\Exception\Exception;
9
use Yiisoft\Db\Exception\InvalidArgumentException;
10
use Yiisoft\Db\Expression\Expression;
11
use Yiisoft\Db\Mssql\Condition\InConditionBuilder;
12
use Yiisoft\Db\Mssql\Condition\LikeConditionBuilder;
13
use Yiisoft\Db\Mssql\DDLQueryBuilder;
14
use Yiisoft\Db\Mssql\DMLQueryBuilder;
15
use Yiisoft\Db\Query\Conditions\InCondition;
16
use Yiisoft\Db\Query\Conditions\LikeCondition;
17
use Yiisoft\Db\Query\Query;
18
use Yiisoft\Db\Query\QueryBuilder;
19
use Yiisoft\Db\Schema\ColumnSchemaBuilder;
20
use Yiisoft\Db\Schema\QuoterInterface;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Schema\QuoterInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Yiisoft\Db\Schema\Schema;
22
use Yiisoft\Db\Schema\SchemaInterface;
23
24
use function array_keys;
25
use function preg_match;
26
use function preg_replace;
27
28
/**
29
 * QueryBuilder is the query builder for MS SQL Server databases (version 2008 and above).
30
 */
31
final class QueryBuilderPDOMssql extends QueryBuilder
32
{
33
    /**
34
     * @var array mapping from abstract column types (keys) to physical column types (values).
35
     */
36
    protected array $typeMap = [
37
        Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY',
38
        Schema::TYPE_UPK => 'int IDENTITY PRIMARY KEY',
39
        Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY',
40
        Schema::TYPE_UBIGPK => 'bigint IDENTITY PRIMARY KEY',
41
        Schema::TYPE_CHAR => 'nchar(1)',
42
        Schema::TYPE_STRING => 'nvarchar(255)',
43
        Schema::TYPE_TEXT => 'nvarchar(max)',
44
        Schema::TYPE_TINYINT => 'tinyint',
45
        Schema::TYPE_SMALLINT => 'smallint',
46
        Schema::TYPE_INTEGER => 'int',
47
        Schema::TYPE_BIGINT => 'bigint',
48
        Schema::TYPE_FLOAT => 'float',
49
        Schema::TYPE_DOUBLE => 'float',
50
        Schema::TYPE_DECIMAL => 'decimal(18,0)',
51
        Schema::TYPE_DATETIME => 'datetime',
52
        Schema::TYPE_TIMESTAMP => 'datetime',
53
        Schema::TYPE_TIME => 'time',
54
        Schema::TYPE_DATE => 'date',
55
        Schema::TYPE_BINARY => 'varbinary(max)',
56
        Schema::TYPE_BOOLEAN => 'bit',
57
        Schema::TYPE_MONEY => 'decimal(19,4)',
58
    ];
59
60
    public function __construct(
61
        private CommandInterface $command,
62
        private QuoterInterface $quoter,
63
        private SchemaInterface $schema
64
    ) {
65
        $this->ddlBuilder = new DDLQueryBuilder($this);
0 ignored issues
show
Bug Best Practice introduced by
The property ddlBuilder does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
66
        $this->dmlBuilder = new DMLQueryBuilder($this);
0 ignored issues
show
Bug Best Practice introduced by
The property dmlBuilder does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
67
        parent::__construct($quoter, $schema);
0 ignored issues
show
Unused Code introduced by
The call to Yiisoft\Db\Query\QueryBuilder::__construct() has too many arguments starting with $schema. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

67
        parent::/** @scrutinizer ignore-call */ 
68
                __construct($quoter, $schema);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
68
    }
69
70
    public function addCommentOnColumn(string $table, string $column, string $comment): string
71
    {
72
        return $this->ddlBuilder->addCommentOnColumn($table, $column, $comment);
73
    }
74
75
    public function addCommentOnTable(string $table, string $comment): string
76
    {
77
        return $this->ddlBuilder->addCommentOnTable($table, $comment);
78
    }
79
80
    public function alterColumn(string $table, string $column, string $type): string
81
    {
82
        $type = $this->getColumnType($type);
83
        return $this->ddlBuilder->alterColumn($table, $column, $type);
84
    }
85
86
    public function buildOrderByAndLimit(string $sql, array $orderBy, $limit, $offset, array &$params = []): string
87
    {
88
        if (!$this->hasOffset($offset) && !$this->hasLimit($limit)) {
89
            $orderBy = $this->buildOrderBy($orderBy, $params);
90
91
            return $orderBy === '' ? $sql : $sql . $this->separator . $orderBy;
92
        }
93
94
        return $this->newBuildOrderByAndLimit($sql, $orderBy, $limit, $offset, $params);
95
    }
96
97
    public function checkIntegrity(string $schema = '', string $table = '', bool $check = true): string
98
    {
99
        return $this->ddlBuilder->checkIntegrity($schema, $table, $check);
100
    }
101
102
    public function command(): CommandInterface
103
    {
104
        return $this->command;
105
    }
106
107
    public function dropCommentFromColumn(string $table, string $column): string
108
    {
109
        return $this->ddlBuilder->dropCommentFromColumn($table, $column);
110
    }
111
112
    public function dropCommentFromTable(string $table): string
113
    {
114
        return $this->ddlBuilder->dropCommentFromTable($table);
115
    }
116
117
    public function getColumnType(ColumnSchemaBuilder|string $type): string
118
    {
119
        $columnType = parent::getColumnType($type);
120
121
        /** remove unsupported keywords*/
122
        $columnType = preg_replace("/\s*comment '.*'/i", '', $columnType);
123
        return preg_replace('/ first$/i', '', $columnType);
124
    }
125
126
    public function quoter(): QuoterInterface
127
    {
128
        return $this->quoter;
129
    }
130
131
    public function renameTable(string $oldName, string $newName): string
132
    {
133
        return 'sp_rename ' .
134
            $this->quoter->quoteTableName($oldName) . ', ' . $this->quoter->quoteTableName($newName);
135
    }
136
137
    public function renameColumn(string $table, string $oldName, string $newName): string
138
    {
139
        $table = $this->quoter->quoteTableName($table);
140
        $oldName = $this->quoter->quoteColumnName($oldName);
141
        $newName = $this->quoter->quoteColumnName($newName);
142
143
        return "sp_rename '$table.$oldName', $newName, 'COLUMN'";
144
    }
145
146
    public function selectExists(string $rawSql): string
147
    {
148
        return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END';
149
    }
150
151
    public function schema(): SchemaInterface
152
    {
153
        return $this->schema;
154
    }
155
156
    protected function defaultExpressionBuilders(): array
157
    {
158
        return array_merge(parent::defaultExpressionBuilders(), [
159
            InCondition::class => InConditionBuilder::class,
160
            LikeCondition::class => LikeConditionBuilder::class,
161
        ]);
162
    }
163
164
    /**
165
     * Builds the ORDER BY/LIMIT/OFFSET clauses for SQL SERVER 2012 or newer.
166
     *
167
     * @param string $sql the existing SQL (without ORDER BY/LIMIT/OFFSET).
168
     * @param array $orderBy the order by columns. See {@see Query::orderBy} for more details on how to specify this
169
     * parameter.
170
     * @param Expression|Query|int|null $limit the limit number. See {@see Query::limit} for more details.
171
     * @param Expression|Query|int|null $offset the offset number. See {@see Query::offset} for more details.
172
     * @param array $params the binding parameters to be populated.
173
     *
174
     * @throws Exception|InvalidArgumentException
175
     *
176
     * @return string the SQL completed with ORDER BY/LIMIT/OFFSET (if any).
177
     */
178
    protected function newBuildOrderByAndLimit(
179
        string $sql,
180
        array $orderBy,
181
        Expression|Query|int|null $limit,
182
        Expression|Query|int|null $offset,
183
        array &$params = []
184
    ): string {
185
        $orderBy = $this->buildOrderBy($orderBy, $params);
186
187
        if ($orderBy === '') {
188
            /** ORDER BY clause is required when FETCH and OFFSET are in the SQL */
189
            $orderBy = 'ORDER BY (SELECT NULL)';
190
        }
191
192
        $sql .= $this->separator . $orderBy;
193
194
        /**
195
         * {@see http://technet.microsoft.com/en-us/library/gg699618.aspx}
196
         */
197
        $offset = $this->hasOffset($offset) ? $offset : '0';
198
        $sql .= $this->separator . "OFFSET $offset ROWS";
199
200
        if ($this->hasLimit($limit)) {
201
            $sql .= $this->separator . "FETCH NEXT $limit ROWS ONLY";
202
        }
203
204
        return $sql;
205
    }
206
207
    /**
208
     * Returns an array of column names given model name.
209
     *
210
     * @param string|null $modelClass name of the model class.
211
     *
212
     * @return array|null array of column names
213
     */
214
    protected function getAllColumnNames(string $modelClass = null): ?array
215
    {
216
        if (!$modelClass) {
217
            return null;
218
        }
219
220
        $schema = $modelClass::getTableSchema();
221
222
        return array_keys($schema->columns);
223
    }
224
225
    /**
226
     * Extracts table alias if there is one or returns false
227
     *
228
     * @param string $table
229
     *
230
     * @return array|bool
231
     * @psalm-return array<array-key, string>|bool
232
     */
233
    protected function extractAlias(string $table): array|bool
234
    {
235
        if (preg_match('/^\[.*]$/', $table)) {
236
            return false;
237
        }
238
239
        return parent::extractAlias($table);
240
    }
241
}
242