Passed
Branch dev (1bbda8)
by Wilmer
14:10 queued 05:58
created

DMLQueryBuilder::upsert()   C

Complexity

Conditions 13
Paths 109

Size

Total Lines 88
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 13.0017

Importance

Changes 0
Metric Value
cc 13
eloc 51
nc 109
nop 4
dl 0
loc 88
ccs 45
cts 46
cp 0.9783
crap 13.0017
rs 6.5416
c 0
b 0
f 0

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