Test Failed
Pull Request — master (#171)
by Def
24:20 queued 20:08
created

DMLQueryBuilder::upsert()   C

Complexity

Conditions 12
Paths 77

Size

Total Lines 78
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 12

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 40
c 3
b 0
f 0
dl 0
loc 78
ccs 39
cts 39
cp 1
rs 6.9666
cc 12
nc 77
nop 4
crap 12

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 479
    public function __construct(
29
        QueryBuilderInterface $queryBuilder,
30
        private QuoterInterface $quoter,
31
        private SchemaInterface $schema
32
    ) {
33 479
        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
        /** @var Constraint[] $constraints */
76 21
        $constraints = [];
77
78
        /**
79 34
         * @psalm-var string[] $insertNames
80 10
         * @psalm-var string[] $updateNames
81
         * @psalm-var array<string, ExpressionInterface|string>|bool $updateColumns
82
         */
83
        [$uniqueNames, $insertNames, $updateNames] = $this->prepareUpsertColumns(
84 34
            $table,
85
            $insertColumns,
86
            $updateColumns,
87
            $constraints
88
        );
89
90
        if (empty($uniqueNames)) {
91 34
            return $this->insert($table, $insertColumns, $params);
92 34
        }
93 34
94 34
        /**
95 34
         * @psalm-var string[] $placeholders
96 34
         */
97
        [, $placeholders, $values, $params] = $this->prepareInsertValues($table, $insertColumns, $params);
98 34
99 2
        $insertSql = 'INSERT OR IGNORE INTO '
100
            . $this->quoter->quoteTableName($table)
101
            . (!empty($insertNames) ? ' (' . implode(', ', $insertNames) . ')' : '')
102
            . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : "$values");
103
104
        if ($updateColumns === false) {
0 ignored issues
show
introduced by
The condition $updateColumns === false is always false.
Loading history...
105 32
            return $insertSql;
106
        }
107 32
108 32
        $updateCondition = ['or'];
109 32
        $quotedTableName = $this->quoter->quoteTableName($table);
110 32
111
        foreach ($constraints as $constraint) {
112 32
            $constraintCondition = ['and'];
113 12
            /** @psalm-var string[] */
114
            $columnsNames = $constraint->getColumnNames();
115
            foreach ($columnsNames as $name) {
116 20
                $quotedName = $this->quoter->quoteColumnName($name);
117 20
                $constraintCondition[] = "$quotedTableName.$quotedName=(SELECT $quotedName FROM `EXCLUDED`)";
118
            }
119 20
            $updateCondition[] = $constraintCondition;
120 20
        }
121
122 20
        if ($updateColumns === true) {
0 ignored issues
show
introduced by
The condition $updateColumns === true is always false.
Loading history...
123 20
            $updateColumns = [];
124 20
            foreach ($updateNames as $name) {
125 20
                $quotedName = $this->quoter->quoteColumnName($name);
126
127 20
                if (strrpos($quotedName, '.') === false) {
128
                    $quotedName = "(SELECT $quotedName FROM `EXCLUDED`)";
129
                }
130 20
                $updateColumns[$name] = new Expression($quotedName);
131 10
            }
132 10
        }
133 8
134
        if ($updateColumns === []) {
135 8
            return $insertSql;
136 8
        }
137
138 8
        /** @var array $params */
139
        $updateSql = 'WITH "EXCLUDED" ('
140
            . implode(', ', $insertNames)
141
            . ') AS (' . (!empty($placeholders)
142 20
                ? 'VALUES (' . implode(', ', $placeholders) . ')'
143 2
                : ltrim("$values", ' ')) . ') ' .
144
                $this->update($table, $updateColumns, $updateCondition, $params);
145
146
        return "$updateSql; $insertSql;";
147 18
    }
148
}
149