Passed
Push — dev ( 85c32d...c7af07 )
by Def
16:37 queued 08:07
created

DMLQueryBuilder   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Test Coverage

Coverage 97.96%

Importance

Changes 0
Metric Value
eloc 53
dl 0
loc 118
ccs 48
cts 49
cp 0.9796
rs 10
c 0
b 0
f 0
wmc 14

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
C upsert() 0 99 13
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Oracle;
6
7
use InvalidArgumentException;
8
use JsonException;
9
use Yiisoft\Db\Constraint\Constraint;
10
use Yiisoft\Db\Exception\Exception;
11
use Yiisoft\Db\Exception\InvalidConfigException;
12
use Yiisoft\Db\Exception\NotSupportedException;
13
use Yiisoft\Db\Expression\Expression;
14
use Yiisoft\Db\Query\DMLQueryBuilder as AbstractDMLQueryBuilder;
15
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...
16
use Yiisoft\Db\Query\QueryInterface;
17
18
use function implode;
19
use function ltrim;
20
use function strrpos;
21
22
final class DMLQueryBuilder extends AbstractDMLQueryBuilder
23
{
24 336
    public function __construct(private QueryBuilderInterface $queryBuilder)
25
    {
26 336
        parent::__construct($queryBuilder);
27
    }
28
29
    /**
30
     * @link https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606
31
     *
32
     * @param string $table
33
     * @param array|QueryInterface $insertColumns
34
     * @param array|bool $updateColumns
35
     * @param array $params
36
     *
37
     * @throws Exception|InvalidArgumentException|InvalidConfigException|JsonException|NotSupportedException
38
     *
39
     * @return string
40
     */
41 17
    public function upsert(
42
        string $table,
43
        QueryInterface|array $insertColumns,
44
        array|bool $updateColumns,
45
        array &$params = []
46
    ): string {
47 17
        $usingValues = null;
48 17
        $constraints = [];
49
50
        /** @var Constraint[] $constraints */
51 17
        [$uniqueNames, $insertNames, $updateNames] = $this->queryBuilder->prepareUpsertColumns(
52
            $table,
53
            $insertColumns,
54
            $updateColumns,
55
            $constraints
56
        );
57
58 17
        if (empty($uniqueNames)) {
59 3
            return $this->insert($table, $insertColumns, $params);
60
        }
61
62 14
        if ($updateNames === []) {
63
            /** there are no columns to update */
64
            $updateColumns = false;
65
        }
66
67 14
        $onCondition = ['or'];
68 14
        $quotedTableName = $this->queryBuilder->quoter()->quoteTableName($table);
69
70 14
        foreach ($constraints as $constraint) {
71 14
            $columnNames = $constraint->getColumnNames() ?? [];
72 14
            $constraintCondition = ['and'];
73
            /** @psalm-var string[] $columnNames */
74 14
            foreach ($columnNames as $name) {
75 14
                $quotedName = $this->queryBuilder->quoter()->quoteColumnName($name);
76 14
                $constraintCondition[] = "$quotedTableName.$quotedName=\"EXCLUDED\".$quotedName";
77
            }
78
79 14
            $onCondition[] = $constraintCondition;
80
        }
81
82 14
        $on = $this->queryBuilder->buildCondition($onCondition, $params);
83
        /** @psalm-var string[] $placeholders */
84 14
        [, $placeholders, $values, $params] = $this->queryBuilder->prepareInsertValues($table, $insertColumns, $params);
85
86 14
        if (!empty($placeholders)) {
87 6
            $usingSelectValues = [];
88
            /** @psalm-var string[] $insertNames */
89 6
            foreach ($insertNames as $index => $name) {
90 6
                $usingSelectValues[$name] = new Expression($placeholders[$index]);
91
            }
92
93
            /** @psalm-var array $params */
94 6
            $usingValues = $this->queryBuilder->buildSelect($usingSelectValues, $params) . ' ' . $this->queryBuilder->buildFrom(['DUAL'], $params);
95
        }
96
97 14
        $insertValues = [];
98 14
        $mergeSql = 'MERGE INTO '
99 14
            . $this->queryBuilder->quoter()->quoteTableName($table)
100
            . ' '
101 14
            . 'USING (' . ($usingValues ?? ltrim((string) $values, ' '))
102
            . ') "EXCLUDED" '
103 14
            . "ON ($on)";
104
105
        /** @psalm-var string[] $insertNames */
106 14
        foreach ($insertNames as $name) {
107 14
            $quotedName = $this->queryBuilder->quoter()->quoteColumnName($name);
108
109 14
            if (strrpos($quotedName, '.') === false) {
110 14
                $quotedName = '"EXCLUDED".' . $quotedName;
111
            }
112
113 14
            $insertValues[] = $quotedName;
114
        }
115
116 14
        $insertSql = 'INSERT (' . implode(', ', $insertNames) . ')' . ' VALUES (' . implode(', ', $insertValues) . ')';
117
118 14
        if ($updateColumns === false) {
119 4
            return "$mergeSql WHEN NOT MATCHED THEN $insertSql";
120
        }
121
122 10
        if ($updateColumns === true) {
0 ignored issues
show
introduced by
The condition $updateColumns === true is always false.
Loading history...
123 4
            $updateColumns = [];
124
            /** @psalm-var string[] $updateNames */
125 4
            foreach ($updateNames as $name) {
126 4
                $quotedName = $this->queryBuilder->quoter()->quoteColumnName($name);
127
128 4
                if (strrpos($quotedName, '.') === false) {
129 4
                    $quotedName = '"EXCLUDED".' . $quotedName;
130
                }
131 4
                $updateColumns[$name] = new Expression($quotedName);
132
            }
133
        }
134
135
        /** @psalm-var string[] $updates */
136 10
        [$updates, $params] = $this->queryBuilder->prepareUpdateSets($table, $updateColumns, (array) $params);
137 10
        $updateSql = 'UPDATE SET ' . implode(', ', $updates);
138
139 10
        return "$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql";
140
    }
141
}
142