Passed
Push — master ( 6888e2...8a066f )
by Wilmer
06:35
created

Command::internalExecute()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 18
c 2
b 0
f 0
nc 9
nop 1
dl 0
loc 25
ccs 17
cts 17
cp 1
crap 9
rs 8.0555
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Oracle;
6
7
use PDO;
8
use PDOException;
9
use Throwable;
10
use Yiisoft\Db\Driver\Pdo\AbstractPdoCommand;
11
use Yiisoft\Db\Exception\ConvertException;
12
use Yiisoft\Db\QueryBuilder\AbstractQueryBuilder;
13
use Yiisoft\Db\QueryBuilder\QueryBuilderInterface;
14
use Yiisoft\Db\Schema\SchemaInterface;
15
16
use function array_keys;
17
use function count;
18
use function implode;
19
use function strlen;
20
21
/**
22
 * Implements a database command that can be executed against a PDO (PHP Data Object) database connection for Oracle
23
 * Server.
24
 */
25
final class Command extends AbstractPdoCommand
26
{
27 5
    public function insertWithReturningPks(string $table, array $columns): bool|array
28
    {
29 5
        $params = [];
30 5
        $sql = $this->getQueryBuilder()->insert($table, $columns, $params);
31
32 5
        $tableSchema = $this->db->getSchema()->getTableSchema($table);
33
34 5
        $returnColumns = $tableSchema?->getPrimaryKey() ?? [];
35 5
        $columnSchemas = $tableSchema?->getColumns() ?? [];
36
37 5
        $returnParams = [];
38 5
        $returning = [];
39
40 5
        foreach ($returnColumns as $name) {
41 5
            $phName = AbstractQueryBuilder::PARAM_PREFIX . (count($params) + count($returnParams));
42
43 5
            $returnParams[$phName] = [
44 5
                'column' => $name,
45 5
                'value' => '',
46 5
            ];
47
48 5
            if (!isset($columnSchemas[$name]) || $columnSchemas[$name]->getPhpType() !== SchemaInterface::PHP_TYPE_INTEGER) {
49 1
                $returnParams[$phName]['dataType'] = PDO::PARAM_STR;
50
            } else {
51 4
                $returnParams[$phName]['dataType'] = PDO::PARAM_INT;
52
            }
53
54 5
            $returnParams[$phName]['size'] = $columnSchemas[$name]->getSize() ?? -1;
55
56 5
            $returning[] = $this->db->getQuoter()->quoteColumnName($name);
57
        }
58
59 5
        $sql .= ' RETURNING ' . implode(', ', $returning) . ' INTO ' . implode(', ', array_keys($returnParams));
60
61 5
        $this->setSql($sql)->bindValues($params);
62 5
        $this->prepare(false);
63
64
        /** @psalm-var array<string, array{column: string, value: mixed, dataType: int, size: int}> $returnParams */
65 5
        foreach ($returnParams as $name => &$value) {
66 5
            $this->bindParam($name, $value['value'], $value['dataType'], $value['size']);
67
        }
68
69 5
        unset($value);
70
71 5
        if (!$this->execute()) {
72
            return false;
73
        }
74
75 5
        $result = [];
76
77 5
        foreach ($returnParams as $value) {
78
            /** @psalm-var mixed */
79 5
            $result[$value['column']] = $value['value'];
80
        }
81
82 5
        return $result;
83
    }
84
85 1
    public function showDatabases(): array
86
    {
87 1
        $sql = <<<SQL
88
        SELECT PDB_NAME FROM DBA_PDBS WHERE PDB_NAME NOT IN ('PDB\$SEED', 'PDB\$ROOT', 'ORCLPDB1', 'XEPDB1')
89 1
        SQL;
90
91 1
        return $this->setSql($sql)->queryColumn();
92
    }
93
94 265
    protected function getQueryBuilder(): QueryBuilderInterface
95
    {
96 265
        return $this->db->getQueryBuilder();
97
    }
98
99 242
    protected function bindPendingParams(): void
100
    {
101 242
        $paramsPassedByReference = [];
102
103 242
        $params = $this->params;
104
105 242
        foreach ($params as $name => $value) {
106 204
            if (PDO::PARAM_STR === $value->getType()) {
107
                /** @var mixed */
108 201
                $paramsPassedByReference[$name] = $value->getValue();
109 201
                $this->pdoStatement?->bindParam(
110 201
                    $name,
111 201
                    $paramsPassedByReference[$name],
112 201
                    $value->getType(),
113 201
                    strlen((string) $value->getValue())
114 201
                );
115
            } else {
116 34
                $this->pdoStatement?->bindValue($name, $value->getValue(), $value->getType());
117
            }
118
        }
119
    }
120
121
    /**
122
     * @psalm-suppress UnusedClosureParam
123
     *
124
     * @throws Throwable
125
     */
126 241
    protected function internalExecute(?string $rawSql): void
127
    {
128 241
        $attempt = 0;
129
130 241
        while (true) {
131
            try {
132
                if (
133 241
                    ++$attempt === 1
134 241
                    && $this->isolationLevel !== null
135 241
                    && $this->db->getTransaction() === null
136
                ) {
137 1
                    $this->db->transaction(
138 1
                        fn () => $this->internalExecute($rawSql),
139 1
                        $this->isolationLevel
140 1
                    );
141
                } else {
142 241
                    $this->pdoStatement?->execute();
143
                }
144 241
                break;
145 7
            } catch (PDOException $e) {
146 7
                $rawSql = $rawSql ?: $this->getRawSql();
147 7
                $e = (new ConvertException($e, $rawSql))->run();
148
149 7
                if ($this->retryHandler === null || !($this->retryHandler)($e, $attempt)) {
150 7
                    throw $e;
151
                }
152
            }
153
        }
154
    }
155
}
156