Passed
Pull Request — dev (#52)
by Wilmer
09:10
created

DMLQueryBuilder   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 120
Duplicated Lines 0 %

Test Coverage

Coverage 98.04%

Importance

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