Test Failed
Pull Request — master (#94)
by Wilmer
14:06 queued 02:52
created

QueryBuilderPDOMysql::renameColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Mysql\PDO;
6
7
use JsonException;
8
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...
9
use Yiisoft\Db\Exception\Exception;
10
use Yiisoft\Db\Exception\InvalidArgumentException;
11
use Yiisoft\Db\Exception\InvalidConfigException;
12
use Yiisoft\Db\Exception\NotSupportedException;
13
use Yiisoft\Db\Expression\Expression;
14
use Yiisoft\Db\Expression\ExpressionBuilder;
15
use Yiisoft\Db\Expression\JsonExpression;
16
use Yiisoft\Db\Mysql\DDLQueryBuilder;
17
use Yiisoft\Db\Mysql\DMLQueryBuilder;
18
use Yiisoft\Db\Mysql\JsonExpressionBuilder;
19
use Yiisoft\Db\Query\Query;
20
use Yiisoft\Db\Query\QueryBuilder;
21
use Yiisoft\Db\Schema\ColumnSchemaBuilder;
22
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...
23
use Yiisoft\Db\Schema\Schema;
24
use Yiisoft\Db\Schema\SchemaInterface;
25
26
use function array_merge;
27
use function ctype_digit;
28
use function reset;
29
30
/**
31
 * The class QueryBuilder is the query builder for Mysql databases.
32
 */
33
final class QueryBuilderPDOMysql extends QueryBuilder
34
{
35
    /**
36
     * @var array<string, string> mapping from abstract column types (keys) to physical column types (values).
37
     */
38
    protected array $typeMap = [
39
        Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
40
        Schema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
41
        Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',
42
        Schema::TYPE_UBIGPK => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
43
        Schema::TYPE_CHAR => 'char(1)',
44
        Schema::TYPE_STRING => 'varchar(255)',
45
        Schema::TYPE_TEXT => 'text',
46
        Schema::TYPE_TINYINT => 'tinyint(3)',
47
        Schema::TYPE_SMALLINT => 'smallint(6)',
48
        Schema::TYPE_INTEGER => 'int(11)',
49
        Schema::TYPE_BIGINT => 'bigint(20)',
50
        Schema::TYPE_FLOAT => 'float',
51
        Schema::TYPE_DOUBLE => 'double',
52
        Schema::TYPE_DECIMAL => 'decimal(10,0)',
53
        Schema::TYPE_DATE => 'date',
54
        Schema::TYPE_BINARY => 'blob',
55
        Schema::TYPE_BOOLEAN => 'tinyint(1)',
56
        Schema::TYPE_MONEY => 'decimal(19,4)',
57
        Schema::TYPE_JSON => 'json',
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
    /**
71
     * @throws NotSupportedException
72
     */
73
    public function addCheck(string $name, string $table, string $expression): string
74
    {
75
        throw new NotSupportedException(QueryBuilderPDOMysql::class . '::addCheck is not supported by MySQL.');
76
    }
77
78
    public function addCommentOnColumn(string $table, string $column, string $comment): string
79
    {
80
        return $this->ddlBuilder->addCommentOnColumn($table, $column, $comment);
81
    }
82
83
    public function addCommentOnTable(string $table, string $comment): string
84
    {
85
        return $this->ddlBuilder->addCommentOnTable($table, $comment);
86
    }
87
88
    public function buildLimit(Expression|int|null $limit, Expression|int|null $offset): string
89
    {
90
        $sql = '';
91
92
        if ($this->hasLimit($limit)) {
93
            $sql = 'LIMIT ' . (string) $limit;
94
95
            if ($this->hasOffset($offset)) {
96
                $sql .= ' OFFSET ' . (string) $offset;
97
            }
98
        } elseif ($this->hasOffset($offset)) {
99
            /**
100
             * limit is not optional in MySQL.
101
             *
102
             * http://stackoverflow.com/a/271650/1106908
103
             * http://dev.mysql.com/doc/refman/5.0/en/select.html#idm47619502796240
104
             */
105
            $sql = "LIMIT $offset, 18446744073709551615"; // 2^64-1
106
        }
107
108
        return $sql;
109
    }
110
111
    public function checkIntegrity(string $schema = '', string $table = '', bool $check = true): string
112
    {
113
        return 'SET FOREIGN_KEY_CHECKS = ' . ($check ? 1 : 0);
114
    }
115
116
    public function createIndex(string $name, string $table, array|string $columns, bool $unique = false): string
117
    {
118
        return $this->ddlBuilder->createIndex($name, $table, $columns, $unique);
119
    }
120
121
    public function command(): CommandInterface
122
    {
123
        return $this->command;
124
    }
125
126
    /**
127
     * @throws NotSupportedException
128
     */
129
    public function dropCheck(string $name, string $table): string
130
    {
131
        throw new NotSupportedException(__METHOD__ . ' is not supported by MySQL.');
132
    }
133
134
    /**
135
     * @throws Exception
136
     */
137
    public function dropCommentFromColumn(string $table, string $column): string
138
    {
139
        return $this->addCommentOnColumn($table, $column, '');
140
    }
141
142
    /**
143
     * @throws Exception
144
     */
145
    public function dropCommentFromTable(string $table): string
146
    {
147
        return $this->addCommentOnTable($table, '');
148
    }
149
150
    public function dropForeignKey(string $name, string $table): string
151
    {
152
        return $this->ddlBuilder->dropForeignKey($name, $table);
153
    }
154
155
    public function dropPrimaryKey(string $name, string $table): string
156
    {
157
        return $this->ddlBuilder->dropPrimaryKey($name, $table);
158
    }
159
160
    public function dropUnique(string $name, string $table): string
161
    {
162
        return $this->dropIndex($name, $table);
163
    }
164
165
    public function getColumnType(ColumnSchemaBuilder|string $type): string
166
    {
167
        $this->typeMap = array_merge($this->typeMap, $this->defaultTimeTypeMap());
168
        return parent::getColumnType($type);
169
    }
170
171
    /**
172
     * Prepares a `VALUES` part for an `INSERT` SQL statement.
173
     *
174
     * @param string $table the table that new rows will be inserted into.
175
     * @param array|Query $columns the column data (name => value) to be inserted into the table or instance of
176
     * {@see Query|Query} to perform INSERT INTO ... SELECT SQL statement.
177
     * @param array $params the binding parameters that will be generated by this method. They should be bound to the DB
178
     * command later.
179
     *
180
     * @throws Exception|InvalidArgumentException|InvalidConfigException|JsonException|NotSupportedException
181
     *
182
     * @return array array of column names, placeholders, values and params.
183
     */
184
    public function prepareInsertValues(string $table, Query|array $columns, array $params = []): array
185
    {
186
        /**
187
         * @var array $names
188
         * @var array $placeholders
189
         */
190
        [$names, $placeholders, $values, $params] = parent::prepareInsertValues($table, $columns, $params);
191
192
        if (!$columns instanceof Query && empty($names)) {
0 ignored issues
show
introduced by
$columns is never a sub-type of Yiisoft\Db\Query\Query.
Loading history...
193
            $tableSchema = $this->schema->getTableSchema($table);
194
195
            if ($tableSchema !== null) {
196
                $columns = $tableSchema->getColumns();
197
                $columns = !empty($tableSchema->getPrimaryKey())
198
                    ? $tableSchema->getPrimaryKey() : [reset($columns)->getName()];
199
                /** @var string $name */
200
                foreach ($columns as $name) {
201
                    $names[] = $this->quoter->quoteColumnName($name);
202
                    $placeholders[] = 'DEFAULT';
203
                }
204
            }
205
        }
206
207
        return [$names, $placeholders, $values, $params];
208
    }
209
210
    public function quoter(): QuoterInterface
211
    {
212
        return $this->quoter;
213
    }
214
215
    public function renameColumn(string $table, string $oldName, string $newName): string
216
    {
217
        return $this->ddlBuilder->renameColumn($table, $oldName, $newName);
218
    }
219
220
    public function schema(): SchemaInterface
221
    {
222
        return $this->schema;
223
    }
224
225
    /**
226
     * Checks to see if the given limit is effective.
227
     *
228
     * @param mixed $limit the given limit.
229
     *
230
     * @return bool whether the limit is effective.
231
     */
232
    protected function hasLimit(mixed $limit): bool
233
    {
234
        /** In MySQL limit argument must be non-negative integer constant */
235
        return ctype_digit((string) $limit);
236
    }
237
238
    /**
239
     * Checks to see if the given offset is effective.
240
     *
241
     * @param mixed $offset the given offset.
242
     *
243
     * @return bool whether the offset is effective.
244
     */
245
    protected function hasOffset(mixed $offset): bool
246
    {
247
        /** In MySQL offset argument must be non-negative integer constant */
248
        $offset = (string) $offset;
249
        return ctype_digit($offset) && $offset !== '0';
250
    }
251
252
    /**
253
     * Contains array of default expression builders. Extend this method and override it, if you want to change default
254
     * expression builders for this query builder.
255
     *
256
     * @return array
257
     *
258
     * See {@see ExpressionBuilder} docs for details.
259
     */
260
    protected function defaultExpressionBuilders(): array
261
    {
262
        return array_merge(
263
            parent::defaultExpressionBuilders(),
264
            [
265
                JsonExpression::class => JsonExpressionBuilder::class,
266
            ]
267
        );
268
    }
269
270
    /**
271
     * Returns the map for default time type.
272
     *
273
     * If the version of MySQL is lower than 5.6.4, then the types will be without fractional seconds, otherwise with
274
     * fractional seconds.
275
     *
276
     * @return array
277
     * @psalm-return array<string, string>
278
     */
279
    private function defaultTimeTypeMap(): array
280
    {
281
        return [
282
            Schema::TYPE_DATETIME => 'datetime(0)',
283
            Schema::TYPE_TIMESTAMP => 'timestamp(0)',
284
            Schema::TYPE_TIME => 'time(0)',
285
        ];
286
    }
287
}
288