Passed
Branch master (a8cc09)
by Wilmer
20:05 queued 15:54
created

DMLQueryBuilder   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 18
eloc 56
c 3
b 0
f 0
dl 0
loc 135
ccs 60
cts 60
cp 1
rs 10

4 Methods

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