Passed
Branch master (5430e1)
by Wilmer
12:07 queued 08:14
created

DMLQueryBuilder   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 19
eloc 59
c 2
b 0
f 0
dl 0
loc 129
ccs 62
cts 62
cp 1
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A resetSequence() 0 25 4
D upsert() 0 86 14
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Sqlite;
6
7
use JsonException;
8
use Throwable;
9
use Yiisoft\Db\Constraint\Constraint;
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\ExpressionInterface;
16
use Yiisoft\Db\QueryBuilder\DMLQueryBuilder as AbstractDMLQueryBuilder;
17
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
18
use Yiisoft\Db\Query\QueryInterface;
19
use Yiisoft\Db\Schema\QuoterInterface;
20
use Yiisoft\Db\Schema\SchemaInterface;
21
22
use function implode;
23
use function ltrim;
24
use function reset;
25
26
final class DMLQueryBuilder extends AbstractDMLQueryBuilder
27
{
28 475
    public function __construct(
29
        QueryBuilderInterface $queryBuilder,
30
        private QuoterInterface $quoter,
31
        private SchemaInterface $schema
32
    ) {
33 475
        parent::__construct($queryBuilder, $quoter, $schema);
34
    }
35
36
    /**
37
     * @throws Exception|Throwable
38
     */
39 4
    public function resetSequence(string $tableName, int|string $value = null): string
40
    {
41 4
        $table = $this->schema->getTableSchema($tableName);
42
43 4
        if ($table === null) {
44 1
            throw new InvalidArgumentException("Table not found: '$tableName'.");
45
        }
46
47 3
        $sequenceName = $table->getSequenceName();
48
49 3
        if ($sequenceName === null) {
50 1
            throw new InvalidArgumentException("There is not sequence associated with table '$tableName'.'");
51
        }
52
53 2
        $tableName = $this->quoter->quoteTableName($tableName);
54
55 2
        if ($value !== null) {
56 2
            $value = "'" . ((int) $value - 1) . "'";
57
        } else {
58 2
            $pk = $table->getPrimaryKey();
59 2
            $key = $this->quoter->quoteColumnName(reset($pk));
60 2
            $value = '(SELECT MAX(' . $key . ') FROM ' . $tableName . ')';
61
        }
62
63 2
        return 'UPDATE sqlite_sequence SET seq=' . $value . " WHERE name='" . $table->getName() . "'";
64
    }
65
66
    /**
67
     * @throws Exception|InvalidArgumentException|InvalidConfigException|JsonException|NotSupportedException
68
     */
69 34
    public function upsert(
70
        string $table,
71
        QueryInterface|array $insertColumns,
72
        bool|array $updateColumns,
73
        array &$params
74
    ): string {
75 34
        if (!$insertColumns instanceof QueryInterface) {
0 ignored issues
show
introduced by
$insertColumns is never a sub-type of Yiisoft\Db\Query\QueryInterface.
Loading history...
76 21
            $insertColumns = $this->normalizeColumnNames($table, $insertColumns);
77
        }
78
79 34
        if (!is_bool($updateColumns)) {
0 ignored issues
show
introduced by
The condition is_bool($updateColumns) is always false.
Loading history...
80 10
            $updateColumns = $this->normalizeColumnNames($table, $updateColumns);
81
        }
82
83
        /** @var Constraint[] $constraints */
84 34
        $constraints = [];
85
86
        /**
87
         * @psalm-var string[] $insertNames
88
         * @psalm-var string[] $updateNames
89
         * @psalm-var array<string, ExpressionInterface|string>|bool $updateColumns
90
         */
91 34
        [$uniqueNames, $insertNames, $updateNames] = $this->prepareUpsertColumns(
92 34
            $table,
93 34
            $insertColumns,
94 34
            $updateColumns,
95 34
            $constraints
96 34
        );
97
98 34
        if (empty($uniqueNames)) {
99 2
            return $this->insert($table, $insertColumns, $params);
100
        }
101
102
        /**
103
         * @psalm-var string[] $placeholders
104
         */
105 32
        [, $placeholders, $values, $params] = $this->prepareInsertValues($table, $insertColumns, $params);
106
107 32
        $insertSql = 'INSERT OR IGNORE INTO '
108 32
            . $this->quoter->quoteTableName($table)
109 32
            . (!empty($insertNames) ? ' (' . implode(', ', $insertNames) . ')' : '')
110 32
            . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : "$values");
111
112 32
        if ($updateColumns === false) {
0 ignored issues
show
introduced by
The condition $updateColumns === false is always false.
Loading history...
113 12
            return $insertSql;
114
        }
115
116 20
        $updateCondition = ['or'];
117 20
        $quotedTableName = $this->quoter->quoteTableName($table);
118
119 20
        foreach ($constraints as $constraint) {
120 20
            $constraintCondition = ['and'];
121
            /** @psalm-var string[] */
122 20
            $columnsNames = $constraint->getColumnNames();
123 20
            foreach ($columnsNames as $name) {
124 20
                $quotedName = $this->quoter->quoteColumnName($name);
125 20
                $constraintCondition[] = "$quotedTableName.$quotedName=(SELECT $quotedName FROM `EXCLUDED`)";
126
            }
127 20
            $updateCondition[] = $constraintCondition;
128
        }
129
130 20
        if ($updateColumns === true) {
0 ignored issues
show
introduced by
The condition $updateColumns === true is always false.
Loading history...
131 10
            $updateColumns = [];
132 10
            foreach ($updateNames as $name) {
133 8
                $quotedName = $this->quoter->quoteColumnName($name);
134
135 8
                if (strrpos($quotedName, '.') === false) {
136 8
                    $quotedName = "(SELECT $quotedName FROM `EXCLUDED`)";
137
                }
138 8
                $updateColumns[$name] = new Expression($quotedName);
139
            }
140
        }
141
142 20
        if ($updateColumns === []) {
143 2
            return $insertSql;
144
        }
145
146
        /** @var array $params */
147 18
        $updateSql = 'WITH "EXCLUDED" ('
148 18
            . implode(', ', $insertNames)
149 18
            . ') AS (' . (!empty($placeholders)
150 10
                ? 'VALUES (' . implode(', ', $placeholders) . ')'
151 18
                : ltrim("$values", ' ')) . ') ' .
152 18
                $this->update($table, $updateColumns, $updateCondition, $params);
153
154 18
        return "$updateSql; $insertSql;";
155
    }
156
}
157