Expression
2 years ago
ForUpdate
2 years ago
ForUpdate.php
2 years ago
Limit.php
2 years ago
QueryBuilder.php
2 years ago
QueryException.php
2 years ago
SelectQuery.php
2 years ago
index.php
2 years ago
QueryBuilder.php
584 lines
| 1 | <?php |
| 2 | namespace MailPoetVendor\Doctrine\DBAL\Query; |
| 3 | if (!defined('ABSPATH')) exit; |
| 4 | use MailPoetVendor\Doctrine\DBAL\Cache\QueryCacheProfile; |
| 5 | use MailPoetVendor\Doctrine\DBAL\Connection; |
| 6 | use MailPoetVendor\Doctrine\DBAL\Exception; |
| 7 | use MailPoetVendor\Doctrine\DBAL\ParameterType; |
| 8 | use MailPoetVendor\Doctrine\DBAL\Query\Expression\CompositeExpression; |
| 9 | use MailPoetVendor\Doctrine\DBAL\Query\Expression\ExpressionBuilder; |
| 10 | use MailPoetVendor\Doctrine\DBAL\Query\ForUpdate\ConflictResolutionMode; |
| 11 | use MailPoetVendor\Doctrine\DBAL\Result; |
| 12 | use MailPoetVendor\Doctrine\DBAL\Statement; |
| 13 | use MailPoetVendor\Doctrine\DBAL\Types\Type; |
| 14 | use MailPoetVendor\Doctrine\Deprecations\Deprecation; |
| 15 | use function array_key_exists; |
| 16 | use function array_keys; |
| 17 | use function array_unshift; |
| 18 | use function count; |
| 19 | use function func_get_arg; |
| 20 | use function func_get_args; |
| 21 | use function func_num_args; |
| 22 | use function implode; |
| 23 | use function is_array; |
| 24 | use function is_object; |
| 25 | use function key; |
| 26 | use function method_exists; |
| 27 | use function strtoupper; |
| 28 | use function substr; |
| 29 | use function ucfirst; |
| 30 | class QueryBuilder |
| 31 | { |
| 32 | public const SELECT = 0; |
| 33 | public const DELETE = 1; |
| 34 | public const UPDATE = 2; |
| 35 | public const INSERT = 3; |
| 36 | public const STATE_DIRTY = 0; |
| 37 | public const STATE_CLEAN = 1; |
| 38 | private Connection $connection; |
| 39 | private const SQL_PARTS_DEFAULTS = ['select' => [], 'distinct' => \false, 'from' => [], 'join' => [], 'set' => [], 'where' => null, 'groupBy' => [], 'having' => null, 'orderBy' => [], 'values' => [], 'for_update' => null]; |
| 40 | private array $sqlParts = self::SQL_PARTS_DEFAULTS; |
| 41 | private ?string $sql = null; |
| 42 | private $params = []; |
| 43 | private array $paramTypes = []; |
| 44 | private int $type = self::SELECT; |
| 45 | private int $state = self::STATE_CLEAN; |
| 46 | private int $firstResult = 0; |
| 47 | private ?int $maxResults = null; |
| 48 | private int $boundCounter = 0; |
| 49 | private ?QueryCacheProfile $resultCacheProfile = null; |
| 50 | public function __construct(Connection $connection) |
| 51 | { |
| 52 | $this->connection = $connection; |
| 53 | } |
| 54 | public function expr() |
| 55 | { |
| 56 | return $this->connection->getExpressionBuilder(); |
| 57 | } |
| 58 | public function getType() |
| 59 | { |
| 60 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5551', 'Relying on the type of the query being built is deprecated.' . ' If necessary, track the type of the query being built outside of the builder.'); |
| 61 | return $this->type; |
| 62 | } |
| 63 | public function getConnection() |
| 64 | { |
| 65 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5780', '%s is deprecated. Use the connection used to instantiate the builder instead.', __METHOD__); |
| 66 | return $this->connection; |
| 67 | } |
| 68 | public function getState() |
| 69 | { |
| 70 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5551', 'Relying on the query builder state is deprecated as it is an internal concern.'); |
| 71 | return $this->state; |
| 72 | } |
| 73 | public function fetchAssociative() |
| 74 | { |
| 75 | return $this->executeQuery()->fetchAssociative(); |
| 76 | } |
| 77 | public function fetchNumeric() |
| 78 | { |
| 79 | return $this->executeQuery()->fetchNumeric(); |
| 80 | } |
| 81 | public function fetchOne() |
| 82 | { |
| 83 | return $this->executeQuery()->fetchOne(); |
| 84 | } |
| 85 | public function fetchAllNumeric() : array |
| 86 | { |
| 87 | return $this->executeQuery()->fetchAllNumeric(); |
| 88 | } |
| 89 | public function fetchAllAssociative() : array |
| 90 | { |
| 91 | return $this->executeQuery()->fetchAllAssociative(); |
| 92 | } |
| 93 | public function fetchAllKeyValue() : array |
| 94 | { |
| 95 | return $this->executeQuery()->fetchAllKeyValue(); |
| 96 | } |
| 97 | public function fetchAllAssociativeIndexed() : array |
| 98 | { |
| 99 | return $this->executeQuery()->fetchAllAssociativeIndexed(); |
| 100 | } |
| 101 | public function fetchFirstColumn() : array |
| 102 | { |
| 103 | return $this->executeQuery()->fetchFirstColumn(); |
| 104 | } |
| 105 | public function executeQuery() : Result |
| 106 | { |
| 107 | return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes, $this->resultCacheProfile); |
| 108 | } |
| 109 | public function executeStatement() : int |
| 110 | { |
| 111 | return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); |
| 112 | } |
| 113 | public function execute() |
| 114 | { |
| 115 | if ($this->type === self::SELECT) { |
| 116 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4578', 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.'); |
| 117 | return $this->executeQuery(); |
| 118 | } |
| 119 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4578', 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.'); |
| 120 | return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); |
| 121 | } |
| 122 | public function getSQL() |
| 123 | { |
| 124 | if ($this->sql !== null && $this->state === self::STATE_CLEAN) { |
| 125 | return $this->sql; |
| 126 | } |
| 127 | switch ($this->type) { |
| 128 | case self::INSERT: |
| 129 | $sql = $this->getSQLForInsert(); |
| 130 | break; |
| 131 | case self::DELETE: |
| 132 | $sql = $this->getSQLForDelete(); |
| 133 | break; |
| 134 | case self::UPDATE: |
| 135 | $sql = $this->getSQLForUpdate(); |
| 136 | break; |
| 137 | case self::SELECT: |
| 138 | $sql = $this->getSQLForSelect(); |
| 139 | break; |
| 140 | } |
| 141 | $this->state = self::STATE_CLEAN; |
| 142 | $this->sql = $sql; |
| 143 | return $sql; |
| 144 | } |
| 145 | public function setParameter($key, $value, $type = ParameterType::STRING) |
| 146 | { |
| 147 | if ($type !== null) { |
| 148 | $this->paramTypes[$key] = $type; |
| 149 | } else { |
| 150 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5550', 'Using NULL as prepared statement parameter type is deprecated.' . 'Omit or use ParameterType::STRING instead'); |
| 151 | } |
| 152 | $this->params[$key] = $value; |
| 153 | return $this; |
| 154 | } |
| 155 | public function setParameters(array $params, array $types = []) |
| 156 | { |
| 157 | $this->paramTypes = $types; |
| 158 | $this->params = $params; |
| 159 | return $this; |
| 160 | } |
| 161 | public function getParameters() |
| 162 | { |
| 163 | return $this->params; |
| 164 | } |
| 165 | public function getParameter($key) |
| 166 | { |
| 167 | return $this->params[$key] ?? null; |
| 168 | } |
| 169 | public function getParameterTypes() |
| 170 | { |
| 171 | return $this->paramTypes; |
| 172 | } |
| 173 | public function getParameterType($key) |
| 174 | { |
| 175 | return $this->paramTypes[$key] ?? ParameterType::STRING; |
| 176 | } |
| 177 | public function setFirstResult($firstResult) |
| 178 | { |
| 179 | $this->state = self::STATE_DIRTY; |
| 180 | $this->firstResult = $firstResult; |
| 181 | return $this; |
| 182 | } |
| 183 | public function getFirstResult() |
| 184 | { |
| 185 | return $this->firstResult; |
| 186 | } |
| 187 | public function setMaxResults($maxResults) |
| 188 | { |
| 189 | $this->state = self::STATE_DIRTY; |
| 190 | $this->maxResults = $maxResults; |
| 191 | return $this; |
| 192 | } |
| 193 | public function getMaxResults() |
| 194 | { |
| 195 | return $this->maxResults; |
| 196 | } |
| 197 | public function forUpdate(int $conflictResolutionMode = ConflictResolutionMode::ORDINARY) : self |
| 198 | { |
| 199 | $this->state = self::STATE_DIRTY; |
| 200 | $this->sqlParts['for_update'] = new ForUpdate($conflictResolutionMode); |
| 201 | return $this; |
| 202 | } |
| 203 | public function add($sqlPartName, $sqlPart, $append = \false) |
| 204 | { |
| 205 | $isArray = is_array($sqlPart); |
| 206 | $isMultiple = is_array($this->sqlParts[$sqlPartName]); |
| 207 | if ($isMultiple && !$isArray) { |
| 208 | $sqlPart = [$sqlPart]; |
| 209 | } |
| 210 | $this->state = self::STATE_DIRTY; |
| 211 | if ($append) { |
| 212 | if ($sqlPartName === 'orderBy' || $sqlPartName === 'groupBy' || $sqlPartName === 'select' || $sqlPartName === 'set') { |
| 213 | foreach ($sqlPart as $part) { |
| 214 | $this->sqlParts[$sqlPartName][] = $part; |
| 215 | } |
| 216 | } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) { |
| 217 | $key = key($sqlPart); |
| 218 | $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; |
| 219 | } elseif ($isMultiple) { |
| 220 | $this->sqlParts[$sqlPartName][] = $sqlPart; |
| 221 | } else { |
| 222 | $this->sqlParts[$sqlPartName] = $sqlPart; |
| 223 | } |
| 224 | return $this; |
| 225 | } |
| 226 | $this->sqlParts[$sqlPartName] = $sqlPart; |
| 227 | return $this; |
| 228 | } |
| 229 | public function select($select = null) |
| 230 | { |
| 231 | $this->type = self::SELECT; |
| 232 | if ($select === null) { |
| 233 | return $this; |
| 234 | } |
| 235 | if (is_array($select)) { |
| 236 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::select() is deprecated, ' . 'pass each value as an individual variadic argument instead.'); |
| 237 | } |
| 238 | $selects = is_array($select) ? $select : func_get_args(); |
| 239 | return $this->add('select', $selects); |
| 240 | } |
| 241 | public function distinct() : self |
| 242 | { |
| 243 | $this->sqlParts['distinct'] = func_num_args() < 1 || func_get_arg(0); |
| 244 | $this->state = self::STATE_DIRTY; |
| 245 | return $this; |
| 246 | } |
| 247 | public function addSelect($select = null) |
| 248 | { |
| 249 | $this->type = self::SELECT; |
| 250 | if ($select === null) { |
| 251 | return $this; |
| 252 | } |
| 253 | if (is_array($select)) { |
| 254 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::addSelect() is deprecated, ' . 'pass each value as an individual variadic argument instead.'); |
| 255 | } |
| 256 | $selects = is_array($select) ? $select : func_get_args(); |
| 257 | return $this->add('select', $selects, \true); |
| 258 | } |
| 259 | public function delete($delete = null, $alias = null) |
| 260 | { |
| 261 | $this->type = self::DELETE; |
| 262 | if ($delete === null) { |
| 263 | return $this; |
| 264 | } |
| 265 | return $this->add('from', ['table' => $delete, 'alias' => $alias]); |
| 266 | } |
| 267 | public function update($update = null, $alias = null) |
| 268 | { |
| 269 | $this->type = self::UPDATE; |
| 270 | if ($update === null) { |
| 271 | return $this; |
| 272 | } |
| 273 | return $this->add('from', ['table' => $update, 'alias' => $alias]); |
| 274 | } |
| 275 | public function insert($insert = null) |
| 276 | { |
| 277 | $this->type = self::INSERT; |
| 278 | if ($insert === null) { |
| 279 | return $this; |
| 280 | } |
| 281 | return $this->add('from', ['table' => $insert]); |
| 282 | } |
| 283 | public function from($from, $alias = null) |
| 284 | { |
| 285 | return $this->add('from', ['table' => $from, 'alias' => $alias], \true); |
| 286 | } |
| 287 | public function join($fromAlias, $join, $alias, $condition = null) |
| 288 | { |
| 289 | return $this->innerJoin($fromAlias, $join, $alias, $condition); |
| 290 | } |
| 291 | public function innerJoin($fromAlias, $join, $alias, $condition = null) |
| 292 | { |
| 293 | return $this->add('join', [$fromAlias => ['joinType' => 'inner', 'joinTable' => $join, 'joinAlias' => $alias, 'joinCondition' => $condition]], \true); |
| 294 | } |
| 295 | public function leftJoin($fromAlias, $join, $alias, $condition = null) |
| 296 | { |
| 297 | return $this->add('join', [$fromAlias => ['joinType' => 'left', 'joinTable' => $join, 'joinAlias' => $alias, 'joinCondition' => $condition]], \true); |
| 298 | } |
| 299 | public function rightJoin($fromAlias, $join, $alias, $condition = null) |
| 300 | { |
| 301 | return $this->add('join', [$fromAlias => ['joinType' => 'right', 'joinTable' => $join, 'joinAlias' => $alias, 'joinCondition' => $condition]], \true); |
| 302 | } |
| 303 | public function set($key, $value) |
| 304 | { |
| 305 | return $this->add('set', $key . ' = ' . $value, \true); |
| 306 | } |
| 307 | public function where($predicates) |
| 308 | { |
| 309 | if (!(func_num_args() === 1 && $predicates instanceof CompositeExpression)) { |
| 310 | $predicates = CompositeExpression::and(...func_get_args()); |
| 311 | } |
| 312 | return $this->add('where', $predicates); |
| 313 | } |
| 314 | public function andWhere($where) |
| 315 | { |
| 316 | $args = func_get_args(); |
| 317 | $where = $this->getQueryPart('where'); |
| 318 | if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { |
| 319 | $where = $where->with(...$args); |
| 320 | } else { |
| 321 | array_unshift($args, $where); |
| 322 | $where = CompositeExpression::and(...$args); |
| 323 | } |
| 324 | return $this->add('where', $where, \true); |
| 325 | } |
| 326 | public function orWhere($where) |
| 327 | { |
| 328 | $args = func_get_args(); |
| 329 | $where = $this->getQueryPart('where'); |
| 330 | if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { |
| 331 | $where = $where->with(...$args); |
| 332 | } else { |
| 333 | array_unshift($args, $where); |
| 334 | $where = CompositeExpression::or(...$args); |
| 335 | } |
| 336 | return $this->add('where', $where, \true); |
| 337 | } |
| 338 | public function groupBy($groupBy) |
| 339 | { |
| 340 | if (is_array($groupBy) && count($groupBy) === 0) { |
| 341 | return $this; |
| 342 | } |
| 343 | if (is_array($groupBy)) { |
| 344 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::groupBy() is deprecated, ' . 'pass each value as an individual variadic argument instead.'); |
| 345 | } |
| 346 | $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); |
| 347 | return $this->add('groupBy', $groupBy, \false); |
| 348 | } |
| 349 | public function addGroupBy($groupBy) |
| 350 | { |
| 351 | if (is_array($groupBy) && count($groupBy) === 0) { |
| 352 | return $this; |
| 353 | } |
| 354 | if (is_array($groupBy)) { |
| 355 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::addGroupBy() is deprecated, ' . 'pass each value as an individual variadic argument instead.'); |
| 356 | } |
| 357 | $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); |
| 358 | return $this->add('groupBy', $groupBy, \true); |
| 359 | } |
| 360 | public function setValue($column, $value) |
| 361 | { |
| 362 | $this->sqlParts['values'][$column] = $value; |
| 363 | return $this; |
| 364 | } |
| 365 | public function values(array $values) |
| 366 | { |
| 367 | return $this->add('values', $values); |
| 368 | } |
| 369 | public function having($having) |
| 370 | { |
| 371 | if (!(func_num_args() === 1 && $having instanceof CompositeExpression)) { |
| 372 | $having = CompositeExpression::and(...func_get_args()); |
| 373 | } |
| 374 | return $this->add('having', $having); |
| 375 | } |
| 376 | public function andHaving($having) |
| 377 | { |
| 378 | $args = func_get_args(); |
| 379 | $having = $this->getQueryPart('having'); |
| 380 | if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { |
| 381 | $having = $having->with(...$args); |
| 382 | } else { |
| 383 | array_unshift($args, $having); |
| 384 | $having = CompositeExpression::and(...$args); |
| 385 | } |
| 386 | return $this->add('having', $having); |
| 387 | } |
| 388 | public function orHaving($having) |
| 389 | { |
| 390 | $args = func_get_args(); |
| 391 | $having = $this->getQueryPart('having'); |
| 392 | if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { |
| 393 | $having = $having->with(...$args); |
| 394 | } else { |
| 395 | array_unshift($args, $having); |
| 396 | $having = CompositeExpression::or(...$args); |
| 397 | } |
| 398 | return $this->add('having', $having); |
| 399 | } |
| 400 | public function orderBy($sort, $order = null) |
| 401 | { |
| 402 | return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), \false); |
| 403 | } |
| 404 | public function addOrderBy($sort, $order = null) |
| 405 | { |
| 406 | return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), \true); |
| 407 | } |
| 408 | public function getQueryPart($queryPartName) |
| 409 | { |
| 410 | Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6179', 'Getting query parts is deprecated as they are implementation details.'); |
| 411 | return $this->sqlParts[$queryPartName]; |
| 412 | } |
| 413 | public function getQueryParts() |
| 414 | { |
| 415 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6179', 'Getting query parts is deprecated as they are implementation details.'); |
| 416 | return $this->sqlParts; |
| 417 | } |
| 418 | public function resetQueryParts($queryPartNames = null) |
| 419 | { |
| 420 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', '%s() is deprecated, instead use dedicated reset methods for the parts that shall be reset.', __METHOD__); |
| 421 | $queryPartNames ??= array_keys($this->sqlParts); |
| 422 | foreach ($queryPartNames as $queryPartName) { |
| 423 | $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName]; |
| 424 | } |
| 425 | $this->state = self::STATE_DIRTY; |
| 426 | return $this; |
| 427 | } |
| 428 | public function resetQueryPart($queryPartName) |
| 429 | { |
| 430 | if ($queryPartName === 'distinct') { |
| 431 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', 'Calling %s() with "distinct" is deprecated, call distinct(false) instead.', __METHOD__); |
| 432 | return $this->distinct(\false); |
| 433 | } |
| 434 | $newMethodName = 'reset' . ucfirst($queryPartName); |
| 435 | if (array_key_exists($queryPartName, self::SQL_PARTS_DEFAULTS) && method_exists($this, $newMethodName)) { |
| 436 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', 'Calling %s() with "%s" is deprecated, call %s() instead.', __METHOD__, $queryPartName, $newMethodName); |
| 437 | return $this->{$newMethodName}(); |
| 438 | } |
| 439 | Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', 'Calling %s() with "%s" is deprecated without replacement.', __METHOD__, $queryPartName, $newMethodName); |
| 440 | $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName]; |
| 441 | $this->state = self::STATE_DIRTY; |
| 442 | return $this; |
| 443 | } |
| 444 | public function resetWhere() : self |
| 445 | { |
| 446 | $this->sqlParts['where'] = self::SQL_PARTS_DEFAULTS['where']; |
| 447 | $this->state = self::STATE_DIRTY; |
| 448 | return $this; |
| 449 | } |
| 450 | public function resetGroupBy() : self |
| 451 | { |
| 452 | $this->sqlParts['groupBy'] = self::SQL_PARTS_DEFAULTS['groupBy']; |
| 453 | $this->state = self::STATE_DIRTY; |
| 454 | return $this; |
| 455 | } |
| 456 | public function resetHaving() : self |
| 457 | { |
| 458 | $this->sqlParts['having'] = self::SQL_PARTS_DEFAULTS['having']; |
| 459 | $this->state = self::STATE_DIRTY; |
| 460 | return $this; |
| 461 | } |
| 462 | public function resetOrderBy() : self |
| 463 | { |
| 464 | $this->sqlParts['orderBy'] = self::SQL_PARTS_DEFAULTS['orderBy']; |
| 465 | $this->state = self::STATE_DIRTY; |
| 466 | return $this; |
| 467 | } |
| 468 | private function getSQLForSelect() : string |
| 469 | { |
| 470 | return $this->connection->getDatabasePlatform()->createSelectSQLBuilder()->buildSQL(new SelectQuery($this->sqlParts['distinct'], $this->sqlParts['select'], $this->getFromClauses(), $this->sqlParts['where'], $this->sqlParts['groupBy'], $this->sqlParts['having'], $this->sqlParts['orderBy'], new Limit($this->maxResults, $this->firstResult), $this->sqlParts['for_update'])); |
| 471 | } |
| 472 | private function getFromClauses() : array |
| 473 | { |
| 474 | $fromClauses = []; |
| 475 | $knownAliases = []; |
| 476 | // Loop through all FROM clauses |
| 477 | foreach ($this->sqlParts['from'] as $from) { |
| 478 | if ($from['alias'] === null) { |
| 479 | $tableSql = $from['table']; |
| 480 | $tableReference = $from['table']; |
| 481 | } else { |
| 482 | $tableSql = $from['table'] . ' ' . $from['alias']; |
| 483 | $tableReference = $from['alias']; |
| 484 | } |
| 485 | $knownAliases[$tableReference] = \true; |
| 486 | $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases); |
| 487 | } |
| 488 | $this->verifyAllAliasesAreKnown($knownAliases); |
| 489 | return $fromClauses; |
| 490 | } |
| 491 | private function verifyAllAliasesAreKnown(array $knownAliases) : void |
| 492 | { |
| 493 | foreach ($this->sqlParts['join'] as $fromAlias => $joins) { |
| 494 | if (!isset($knownAliases[$fromAlias])) { |
| 495 | throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); |
| 496 | } |
| 497 | } |
| 498 | } |
| 499 | private function getSQLForInsert() : string |
| 500 | { |
| 501 | return 'INSERT INTO ' . $this->sqlParts['from']['table'] . ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' . ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')'; |
| 502 | } |
| 503 | private function getSQLForUpdate() : string |
| 504 | { |
| 505 | $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); |
| 506 | return 'UPDATE ' . $table . ' SET ' . implode(', ', $this->sqlParts['set']) . ($this->sqlParts['where'] !== null ? ' WHERE ' . (string) $this->sqlParts['where'] : ''); |
| 507 | } |
| 508 | private function getSQLForDelete() : string |
| 509 | { |
| 510 | $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); |
| 511 | return 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . (string) $this->sqlParts['where'] : ''); |
| 512 | } |
| 513 | public function __toString() |
| 514 | { |
| 515 | return $this->getSQL(); |
| 516 | } |
| 517 | public function createNamedParameter($value, $type = ParameterType::STRING, $placeHolder = null) |
| 518 | { |
| 519 | if ($placeHolder === null) { |
| 520 | $this->boundCounter++; |
| 521 | $placeHolder = ':dcValue' . $this->boundCounter; |
| 522 | } |
| 523 | $this->setParameter(substr($placeHolder, 1), $value, $type); |
| 524 | return $placeHolder; |
| 525 | } |
| 526 | public function createPositionalParameter($value, $type = ParameterType::STRING) |
| 527 | { |
| 528 | $this->setParameter($this->boundCounter, $value, $type); |
| 529 | $this->boundCounter++; |
| 530 | return '?'; |
| 531 | } |
| 532 | private function getSQLForJoins($fromAlias, array &$knownAliases) : string |
| 533 | { |
| 534 | $sql = ''; |
| 535 | if (isset($this->sqlParts['join'][$fromAlias])) { |
| 536 | foreach ($this->sqlParts['join'][$fromAlias] as $join) { |
| 537 | if (array_key_exists($join['joinAlias'], $knownAliases)) { |
| 538 | throw QueryException::nonUniqueAlias((string) $join['joinAlias'], array_keys($knownAliases)); |
| 539 | } |
| 540 | $sql .= ' ' . strtoupper($join['joinType']) . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias']; |
| 541 | if ($join['joinCondition'] !== null) { |
| 542 | $sql .= ' ON ' . $join['joinCondition']; |
| 543 | } |
| 544 | $knownAliases[$join['joinAlias']] = \true; |
| 545 | } |
| 546 | foreach ($this->sqlParts['join'][$fromAlias] as $join) { |
| 547 | $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases); |
| 548 | } |
| 549 | } |
| 550 | return $sql; |
| 551 | } |
| 552 | public function __clone() |
| 553 | { |
| 554 | foreach ($this->sqlParts as $part => $elements) { |
| 555 | if (is_array($this->sqlParts[$part])) { |
| 556 | foreach ($this->sqlParts[$part] as $idx => $element) { |
| 557 | if (!is_object($element)) { |
| 558 | continue; |
| 559 | } |
| 560 | $this->sqlParts[$part][$idx] = clone $element; |
| 561 | } |
| 562 | } elseif (is_object($elements)) { |
| 563 | $this->sqlParts[$part] = clone $elements; |
| 564 | } |
| 565 | } |
| 566 | foreach ($this->params as $name => $param) { |
| 567 | if (!is_object($param)) { |
| 568 | continue; |
| 569 | } |
| 570 | $this->params[$name] = clone $param; |
| 571 | } |
| 572 | } |
| 573 | public function enableResultCache(QueryCacheProfile $cacheProfile) : self |
| 574 | { |
| 575 | $this->resultCacheProfile = $cacheProfile; |
| 576 | return $this; |
| 577 | } |
| 578 | public function disableResultCache() : self |
| 579 | { |
| 580 | $this->resultCacheProfile = null; |
| 581 | return $this; |
| 582 | } |
| 583 | } |
| 584 |