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

DMLQueryBuilder::resetSequence()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 16
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 26
rs 9.4222
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;
0 ignored issues
show
Bug introduced by
The type Yiisoft\Db\Query\DMLQueryBuilder 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...
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
19
final class DMLQueryBuilder extends AbstractDMLQueryBuilder
20
{
21
    public function __construct(
22
        private QueryBuilderInterface $queryBuilder,
23
    ) {
24
        parent::__construct($queryBuilder);
25
    }
26
27
    /**
28
     * Creates a SQL statement for resetting the sequence value of a table's primary key.
29
     *
30
     * The sequence will be reset such that the primary key of the next new row inserted will have the specified value
31
     * or 1.
32
     *
33
     * @param string $tableName the name of the table whose primary key sequence will be reset.
34
     * @param array|int|string|null $value the value for the primary key of the next new row inserted. If this is not
35
     * set, the next new row's primary key will have a value 1.
36
     *
37
     * @throws Exception|InvalidArgumentException|InvalidConfigException|Throwable
38
     *
39
     * @return string the SQL statement for resetting sequence.
40
     */
41
    public function resetSequence(string $tableName, array|int|string|null $value = null): string
42
    {
43
        $table = $this->queryBuilder->schema()->getTableSchema($tableName);
44
45
        if ($table !== null && $table->getSequenceName() !== null) {
46
            $tableName = $this->queryBuilder->quoter()->quoteTableName($tableName);
47
48
            if ($value === null) {
49
                $pk = $table->getPrimaryKey();
50
                $key = (string) reset($pk);
51
                $value = (int) $this->queryBuilder
52
                    ->command()
53
                    ->setSql("SELECT MAX(`$key`) FROM $tableName")
54
                    ->queryScalar() + 1;
55
            } else {
56
                $value = (int) $value;
57
            }
58
59
            return "ALTER TABLE $tableName AUTO_INCREMENT=$value";
60
        }
61
62
        if ($table === null) {
63
            throw new InvalidArgumentException("Table not found: $tableName");
64
        }
65
66
        throw new InvalidArgumentException("There is no sequence associated with table '$tableName'.");
67
    }
68
69
    /**
70
     * Creates an SQL statement to insert rows into a database table if they do not already exist (matching unique
71
     * constraints), or update them if they do.
72
     *
73
     * For example,
74
     *
75
     * ```php
76
     * $sql = $queryBuilder->upsert('pages', [
77
     *     'name' => 'Front page',
78
     *     'url' => 'http://example.com/', // url is unique
79
     *     'visits' => 0,
80
     * ], [
81
     *     'visits' => new Expression('visits + 1'),
82
     * ], $params);
83
     * ```
84
     *
85
     * The method will properly escape the table and column names.
86
     *
87
     * @param string $table the table that new rows will be inserted into/updated in.
88
     * @param array|Query $insertColumns the column data (name => value) to be inserted into the table or instance of
89
     * {@see Query} to perform `INSERT INTO ... SELECT` SQL statement.
90
     * @param bool|array $updateColumns the column data (name => value) to be updated if they already exist. If `true`
91
     * is passed, the column data will be updated to match the insert column data. If `false` is passed, no update will
92
     * be performed if the column data already exists.
93
     * @param array $params the binding parameters that will be generated by this method. They should be bound to the DB
94
     * command later.
95
     *
96
     * @throws Exception|InvalidConfigException|JsonException|NotSupportedException if this is not supported by the
97
     * underlying DBMS.
98
     *
99
     * @return string the resulting SQL.
100
     */
101
    public function upsert(string $table, Query|array $insertColumns, bool|array $updateColumns, array &$params): string
102
    {
103
        $insertSql = $this->insert($table, $insertColumns, $params);
104
105
        /** @var array $uniqueNames */
106
        [$uniqueNames, , $updateNames] = $this->queryBuilder->prepareUpsertColumns(
107
            $table,
108
            $insertColumns,
109
            $updateColumns,
110
        );
111
112
        if (empty($uniqueNames)) {
113
            return $insertSql;
114
        }
115
116
        if ($updateColumns === true) {
0 ignored issues
show
introduced by
The condition $updateColumns === true is always false.
Loading history...
117
            $updateColumns = [];
118
            /** @var string $name */
119
            foreach ($updateNames as $name) {
120
                $updateColumns[$name] = new Expression(
121
                    'VALUES(' . $this->queryBuilder->quoter()->quoteColumnName($name) . ')'
122
                );
123
            }
124
        } elseif ($updateColumns === false) {
0 ignored issues
show
introduced by
The condition $updateColumns === false is always false.
Loading history...
125
            $columnName = (string) reset($uniqueNames);
126
            $name = $this->queryBuilder->quoter()->quoteColumnName($columnName);
127
            $updateColumns = [$name => new Expression($this->queryBuilder->quoter()->quoteTableName($table) . '.' . $name)];
128
        }
129
130
        /**
131
         *  @psalm-var array<array-key, string> $updates
132
         *  @psalm-var array<string, ExpressionInterface|string> $updateColumns
133
         */
134
        [$updates, $params] = $this->queryBuilder->prepareUpdateSets($table, $updateColumns, $params);
135
136
        return $insertSql . ' ON DUPLICATE KEY UPDATE ' . implode(', ', $updates);
137
    }
138
}
139