1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file is part of Cycle ORM package. |
||
5 | * |
||
6 | * For the full copyright and license information, please view the LICENSE |
||
7 | * file that was distributed with this source code. |
||
8 | */ |
||
9 | |||
10 | declare(strict_types=1); |
||
11 | |||
12 | namespace Cycle\Database\Driver\SQLServer; |
||
13 | |||
14 | use Cycle\Database\Config\DriverConfig; |
||
15 | use Cycle\Database\Config\SQLServerDriverConfig; |
||
16 | use Cycle\Database\Driver\Driver; |
||
17 | use Cycle\Database\Driver\PDOStatementInterface; |
||
18 | use Cycle\Database\Driver\SQLServer\Query\SQLServerDeleteQuery; |
||
19 | use Cycle\Database\Driver\SQLServer\Query\SQLServerInsertQuery; |
||
20 | use Cycle\Database\Driver\SQLServer\Query\SQLServerSelectQuery; |
||
21 | use Cycle\Database\Driver\SQLServer\Query\SQLServerUpdateQuery; |
||
22 | use Cycle\Database\Exception\DriverException; |
||
23 | use Cycle\Database\Exception\StatementException; |
||
24 | use Cycle\Database\Injection\ParameterInterface; |
||
25 | use Cycle\Database\Query\QueryBuilder; |
||
26 | |||
27 | class SQLServerDriver extends Driver |
||
28 | { |
||
29 | /** |
||
30 | * @var non-empty-string |
||
31 | */ |
||
32 | 2 | protected const DATETIME = 'Y-m-d\TH:i:s.000'; |
|
33 | |||
34 | 2 | /** |
|
35 | * @param SQLServerDriverConfig $config |
||
36 | * |
||
37 | * @throws DriverException |
||
38 | */ |
||
39 | public static function create(DriverConfig $config): static |
||
40 | { |
||
41 | $driver = new static( |
||
42 | $config, |
||
43 | new SQLServerHandler(), |
||
44 | new SQLServerCompiler('[]'), |
||
45 | 928 | new QueryBuilder( |
|
46 | new SQLServerSelectQuery(), |
||
47 | 928 | new SQLServerInsertQuery(), |
|
48 | new SQLServerUpdateQuery(), |
||
49 | 928 | new SQLServerDeleteQuery(), |
|
50 | 486 | ), |
|
51 | 4 | ); |
|
52 | |||
53 | 486 | if ((int) $driver->getPDO()->getAttribute(\PDO::ATTR_SERVER_VERSION) < 12) { |
|
54 | throw new DriverException('SQLServer driver supports only 12+ version of SQLServer'); |
||
55 | } |
||
56 | 486 | ||
57 | return $driver; |
||
58 | 486 | } |
|
59 | 96 | ||
60 | 96 | public function getType(): string |
|
61 | { |
||
62 | return 'SQLServer'; |
||
63 | 486 | } |
|
64 | 2 | ||
65 | /** |
||
66 | * Bind parameters into statement. SQLServer need encoding to be specified for binary parameters. |
||
67 | 486 | */ |
|
68 | protected function bindParameters( |
||
69 | \PDOStatement|PDOStatementInterface $statement, |
||
70 | iterable $parameters, |
||
71 | ): \PDOStatement|PDOStatementInterface { |
||
72 | $index = 0; |
||
73 | |||
74 | foreach ($parameters as $name => $parameter) { |
||
75 | if (\is_string($name)) { |
||
76 | $index = $name; |
||
77 | } else { |
||
78 | $index++; |
||
79 | } |
||
80 | |||
81 | $type = \PDO::PARAM_STR; |
||
82 | 486 | ||
83 | 486 | if ($parameter instanceof ParameterInterface) { |
|
84 | $type = $parameter->getType(); |
||
85 | $parameter = $parameter->getValue(); |
||
86 | 928 | } |
|
87 | |||
88 | /** @since PHP 8.1 */ |
||
89 | if ($parameter instanceof \BackedEnum) { |
||
0 ignored issues
–
show
|
|||
90 | $type = \PDO::PARAM_STR; |
||
91 | $parameter = $parameter->value; |
||
92 | } |
||
93 | |||
94 | if ($parameter instanceof \DateTimeInterface) { |
||
95 | $parameter = $this->formatDatetime($parameter); |
||
96 | } |
||
97 | 8 | ||
98 | if ($type === \PDO::PARAM_LOB) { |
||
99 | 8 | /** @psalm-suppress UndefinedConstant */ |
|
100 | $statement->bindParam( |
||
101 | 8 | $index, |
|
102 | 8 | $parameter, |
|
103 | $type, |
||
104 | 0, |
||
105 | \PDO::SQLSRV_ENCODING_BINARY, |
||
106 | ); |
||
107 | |||
108 | unset($parameter); |
||
109 | continue; |
||
110 | } |
||
111 | 2 | ||
112 | // numeric, @see http://php.net/manual/en/pdostatement.bindparam.php |
||
113 | 2 | $statement->bindValue($index, $parameter, $type); |
|
114 | unset($parameter); |
||
115 | } |
||
116 | 2 | ||
117 | return $statement; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Create nested transaction save point. |
||
122 | * |
||
123 | * @link http://en.wikipedia.org/wiki/Savepoint |
||
124 | * |
||
125 | 6 | * @param int $level Savepoint name/id, must not contain spaces and be valid database |
|
126 | * identifier. |
||
127 | 6 | */ |
|
128 | protected function createSavepoint(int $level): void |
||
129 | 6 | { |
|
130 | 6 | $this->logger?->info("Transaction: new savepoint 'SVP{$level}'"); |
|
131 | |||
132 | $this->execute('SAVE TRANSACTION ' . $this->identifier("SVP{$level}")); |
||
133 | } |
||
134 | |||
135 | 8 | /** |
|
136 | * Commit/release savepoint. |
||
137 | 8 | * |
|
138 | * @link http://en.wikipedia.org/wiki/Savepoint |
||
139 | * |
||
140 | * @param int $level Savepoint name/id, must not contain spaces and be valid database identifier. |
||
141 | 8 | */ |
|
142 | 8 | protected function releaseSavepoint(int $level): void |
|
143 | 8 | { |
|
144 | $this->logger?->info("Transaction: release savepoint 'SVP{$level}'"); |
||
145 | |||
146 | // SQLServer automatically commits nested transactions with parent transaction |
||
147 | } |
||
148 | 8 | ||
149 | 2 | /** |
|
150 | * Rollback savepoint. |
||
151 | * |
||
152 | 6 | * @link http://en.wikipedia.org/wiki/Savepoint |
|
153 | * |
||
154 | * @param int $level Savepoint name/id, must not contain spaces and be valid database identifier. |
||
155 | */ |
||
156 | protected function rollbackSavepoint(int $level): void |
||
157 | { |
||
158 | $this->logger?->info("Transaction: rollback savepoint 'SVP{$level}'"); |
||
159 | |||
160 | 4 | $this->execute('ROLLBACK TRANSACTION ' . $this->identifier("SVP{$level}")); |
|
161 | } |
||
162 | 4 | ||
163 | protected function mapException(\Throwable $exception, string $query): StatementException |
||
164 | 4 | { |
|
165 | 4 | $message = \strtolower($exception->getMessage()); |
|
166 | 4 | ||
167 | |||
168 | if ( |
||
169 | 4 | \str_contains($message, '0800') |
|
170 | || \str_contains($message, '080p') |
||
171 | || \str_contains($message, 'connection') |
||
172 | ) { |
||
173 | 4 | return new StatementException\ConnectionException($exception, $query); |
|
174 | } |
||
175 | |||
176 | if ((int) $exception->getCode() === 23000) { |
||
177 | return new StatementException\ConstrainException($exception, $query); |
||
178 | } |
||
179 | |||
180 | return new StatementException($exception, $query); |
||
181 | } |
||
182 | } |
||
183 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths