Test Failed
Pull Request — dev (#118)
by Def
04:47 queued 02:21
created

DMLQueryBuilder::prepareInsertValues()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 23
ccs 0
cts 0
cp 0
rs 9.2222
cc 6
nc 4
nop 3
crap 42
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Mysql;
6
7
use JsonException;
8
use Throwable;
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\ExpressionInterface;
15
use Yiisoft\Db\Query\DMLQueryBuilder as AbstractDMLQueryBuilder;
16
use Yiisoft\Db\Query\Query;
17
use Yiisoft\Db\Query\QueryBuilderInterface;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Query\QueryBuilderInterface 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...
18
use Yiisoft\Db\Query\QueryInterface;
19
use Yiisoft\Db\Schema\QuoterInterface;
20
use Yiisoft\Db\Schema\SchemaInterface;
21 355
22
use function implode;
23 355
use function reset;
24
25
final class DMLQueryBuilder extends AbstractDMLQueryBuilder
26
{
27
    public function __construct(private QueryBuilderInterface $queryBuilder)
28
    {
29
        parent::__construct($queryBuilder);
30
    }
31
32
    /**
33
     * Creates a SQL statement for resetting the sequence value of a table's primary key.
34
     *
35
     * The sequence will be reset such that the primary key of the next new row inserted will have the specified value
36
     * or 1.
37
     *
38
     * @param string $tableName the name of the table whose primary key sequence will be reset.
39
     * @param array|int|string|null $value the value for the primary key of the next new row inserted. If this is not
40 3
     * set, the next new row's primary key will have a value 1.
41
     *
42 3
     * @throws Exception|InvalidArgumentException|InvalidConfigException|Throwable
43
     *
44 3
     * @return string the SQL statement for resetting sequence.
45 1
     */
46
    public function resetSequence(string $tableName, array|int|string|null $value = null): string
47 1
    {
48 1
        $table = $this->schema->getTableSchema($tableName);
0 ignored issues
show
Bug Best Practice introduced by
The property schema does not exist on Yiisoft\Db\Mysql\DMLQueryBuilder. Did you maybe forget to declare it?
Loading history...
49 1
50 1
        if ($table === null) {
51 1
            throw new InvalidArgumentException("Table not found: $tableName");
52 1
        }
53 1
54
        $sequenceName = $table->getSequenceName();
55 1
        if ($sequenceName === null) {
56
            throw new InvalidArgumentException("There is no sequence associated with table '$tableName'.");
57
        }
58 1
59
        $tableName = $this->quoter->quoteTableName($tableName);
0 ignored issues
show
Bug Best Practice introduced by
The property quoter does not exist on Yiisoft\Db\Mysql\DMLQueryBuilder. Did you maybe forget to declare it?
Loading history...
60
61 2
        if ($value !== null) {
62 1
            return 'ALTER TABLE ' . $tableName . ' AUTO_INCREMENT=' . (int)$value . ';';
63
        }
64
65 1
        $pk = $table->getPrimaryKey();
66
        $key = (string) reset($pk);
67
68
        return "SET @new_autoincrement_value := (SELECT MAX(`$key`) + 1 FROM $tableName);
69
SET @sql = CONCAT('ALTER TABLE $tableName AUTO_INCREMENT =', @new_autoincrement_value);
70
PREPARE autoincrement_stmt FROM @sql;
71
EXECUTE autoincrement_stmt";
72
    }
73
74
    /**
75
     * Creates an SQL statement to insert rows into a database table if they do not already exist (matching unique
76
     * constraints), or update them if they do.
77
     *
78
     * For example,
79
     *
80
     * ```php
81
     * $sql = $queryBuilder->upsert('pages', [
82
     *     'name' => 'Front page',
83
     *     'url' => 'http://example.com/', // url is unique
84
     *     'visits' => 0,
85
     * ], [
86
     *     'visits' => new Expression('visits + 1'),
87
     * ], $params);
88
     * ```
89
     *
90
     * The method will properly escape the table and column names.
91
     *
92
     * @param string $table the table that new rows will be inserted into/updated in.
93
     * @param array|QueryInterface $insertColumns the column data (name => value) to be inserted into the table or
94
     * instance of {@see Query} to perform `INSERT INTO ... SELECT` SQL statement.
95
     * @param array|bool $updateColumns the column data (name => value) to be updated if they already exist. If `true`
96
     * is passed, the column data will be updated to match the insert column data. If `false` is passed, no update will
97
     * be performed if the column data already exists.
98
     * @param array $params the binding parameters that will be generated by this method. They should be bound to the DB
99
     * command later.
100 18
     *
101
     * @throws Exception|InvalidConfigException|JsonException|NotSupportedException if this is not supported by the
102
     * underlying DBMS.
103
     *
104
     * @return string the resulting SQL.
105
     */
106 18
    public function upsert(
107
        string $table,
108
        QueryInterface|array $insertColumns,
109 18
        bool|array $updateColumns,
110
        array &$params
111
    ): string {
112
        $insertSql = $this->insert($table, $insertColumns, $params);
113
114
        /** @var array $uniqueNames */
115 18
        [$uniqueNames, , $updateNames] = $this->prepareUpsertColumns(
0 ignored issues
show
Bug introduced by
The method prepareUpsertColumns() does not exist on Yiisoft\Db\Mysql\DMLQueryBuilder. ( Ignorable by Annotation )

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

115
        /** @scrutinizer ignore-call */ 
116
        [$uniqueNames, , $updateNames] = $this->prepareUpsertColumns(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
116 3
            $table,
117
            $insertColumns,
118
            $updateColumns,
119 15
        );
120 4
121
        if (empty($uniqueNames)) {
122 4
            return $insertSql;
123 4
        }
124 4
125
        if ($updateColumns === true) {
0 ignored issues
show
introduced by
The condition $updateColumns === true is always false.
Loading history...
126
            $updateColumns = [];
127 11
            /** @var string $name */
128 5
            foreach ($updateNames as $name) {
129 5
                $updateColumns[$name] = new Expression(
130 5
                    'VALUES(' . $this->quoter->quoteColumnName($name) . ')'
131
                );
132
            }
133
        } elseif ($updateColumns === false) {
0 ignored issues
show
introduced by
The condition $updateColumns === false is always false.
Loading history...
134
            $columnName = (string) reset($uniqueNames);
135
            $name = $this->quoter->quoteColumnName($columnName);
136
            $updateColumns = [$name => new Expression($this->quoter->quoteTableName($table) . '.' . $name)];
137 15
        }
138
139 15
        /**
140
         *  @psalm-var array<array-key, string> $updates
141
         *  @psalm-var array<string, ExpressionInterface|string> $updateColumns
142
         */
143
        [$updates, $params] = $this->prepareUpdateSets($table, $updateColumns, $params);
0 ignored issues
show
Bug introduced by
The method prepareUpdateSets() does not exist on Yiisoft\Db\Mysql\DMLQueryBuilder. ( Ignorable by Annotation )

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

143
        /** @scrutinizer ignore-call */ 
144
        [$updates, $params] = $this->prepareUpdateSets($table, $updateColumns, $params);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
144
145
        return $insertSql . ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updates);
146
    }
147
148
    /**
149
     * Prepares a `VALUES` part for an `INSERT` SQL statement.
150
     *
151
     * @param string $table the table that new rows will be inserted into.
152
     * @param array|QueryInterface $columns the column data (name => value) to be inserted into the table or instance of
153
     * {@see Query|Query} to perform INSERT INTO ... SELECT SQL statement.
154
     * @param array $params the binding parameters that will be generated by this method. They should be bound to the DB
155
     * command later.
156
     *
157
     * @throws Exception|InvalidArgumentException|InvalidConfigException|JsonException|NotSupportedException
158
     *
159
     * @return array array of column names, placeholders, values and params.
160
     */
161
    protected function prepareInsertValues(string $table, QueryInterface|array $columns, array $params = []): array
162
    {
163
        /**
164
         * @var array $names
165
         * @var array $placeholders
166
         */
167
        [$names, $placeholders, $values, $params] = parent::prepareInsertValues($table, $columns, $params);
168
169
        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...
170
            $tableSchema = $this->schema->getTableSchema($table);
0 ignored issues
show
Bug Best Practice introduced by
The property schema does not exist on Yiisoft\Db\Mysql\DMLQueryBuilder. Did you maybe forget to declare it?
Loading history...
171
172
            if ($tableSchema !== null) {
173
                $columns = $tableSchema->getColumns();
174
                $columns = !empty($tableSchema->getPrimaryKey())
175
                    ? $tableSchema->getPrimaryKey() : [reset($columns)->getName()];
176
                foreach ($columns as $name) {
177
                    $names[] = $this->quoter->quoteColumnName($name);
0 ignored issues
show
Bug Best Practice introduced by
The property quoter does not exist on Yiisoft\Db\Mysql\DMLQueryBuilder. Did you maybe forget to declare it?
Loading history...
178
                    $placeholders[] = 'DEFAULT';
179
                }
180
            }
181
        }
182
183
        return [$names, $placeholders, $values, $params];
184
    }
185
}
186