Passed
Branch dev (68ca04)
by Wilmer
02:34
created

QueryBuilderPDOMysql::dropCheck()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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