SqlserverCompiler.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Database;
  17. use Cake\Database\Expression\FunctionExpression;
  18. /**
  19. * Responsible for compiling a Query object into its SQL representation
  20. * for SQL Server
  21. *
  22. * @internal
  23. */
  24. class SqlserverCompiler extends QueryCompiler
  25. {
  26. /**
  27. * SQLserver does not support ORDER BY in UNION queries.
  28. *
  29. * @var bool
  30. */
  31. protected $_orderedUnion = false;
  32. /**
  33. * @inheritDoc
  34. */
  35. protected $_templates = [
  36. 'delete' => 'DELETE',
  37. 'where' => ' WHERE %s',
  38. 'group' => ' GROUP BY %s ',
  39. 'order' => ' %s',
  40. 'offset' => ' OFFSET %s ROWS',
  41. 'epilog' => ' %s',
  42. ];
  43. /**
  44. * @inheritDoc
  45. */
  46. protected $_selectParts = [
  47. 'with', 'select', 'from', 'join', 'where', 'group', 'having', 'window', 'order',
  48. 'offset', 'limit', 'union', 'epilog',
  49. ];
  50. /**
  51. * Helper function used to build the string representation of a `WITH` clause,
  52. * it constructs the CTE definitions list without generating the `RECURSIVE`
  53. * keyword that is neither required nor valid.
  54. *
  55. * @param array $parts List of CTEs to be transformed to string
  56. * @param \Cake\Database\Query $query The query that is being compiled
  57. * @param \Cake\Database\ValueBinder $generator The placeholder generator to be used in expressions
  58. * @return string
  59. */
  60. protected function _buildWithPart(array $parts, Query $query, ValueBinder $generator): string
  61. {
  62. $expressions = [];
  63. foreach ($parts as $cte) {
  64. $expressions[] = $cte['cte']->sql($generator);
  65. }
  66. return sprintf('WITH %s ', implode(', ', $expressions));
  67. }
  68. /**
  69. * Generates the INSERT part of a SQL query
  70. *
  71. * To better handle concurrency and low transaction isolation levels,
  72. * we also include an OUTPUT clause so we can ensure we get the inserted
  73. * row's data back.
  74. *
  75. * @param array $parts The parts to build
  76. * @param \Cake\Database\Query $query The query that is being compiled
  77. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  78. * @return string
  79. */
  80. protected function _buildInsertPart(array $parts, Query $query, ValueBinder $generator): string
  81. {
  82. $table = $parts[0];
  83. $columns = $this->_stringifyExpressions($parts[1], $generator);
  84. $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator);
  85. return sprintf(
  86. 'INSERT%s INTO %s (%s) OUTPUT INSERTED.*',
  87. $modifiers,
  88. $table,
  89. implode(', ', $columns)
  90. );
  91. }
  92. /**
  93. * Generates the LIMIT part of a SQL query
  94. *
  95. * @param int $limit the limit clause
  96. * @param \Cake\Database\Query $query The query that is being compiled
  97. * @return string
  98. */
  99. protected function _buildLimitPart(int $limit, Query $query): string
  100. {
  101. if ($query->clause('offset') === null) {
  102. return '';
  103. }
  104. return sprintf(' FETCH FIRST %d ROWS ONLY', $limit);
  105. }
  106. /**
  107. * Helper function used to build the string representation of a HAVING clause,
  108. * it constructs the field list taking care of aliasing and
  109. * converting expression objects to string.
  110. *
  111. * @param array $parts list of fields to be transformed to string
  112. * @param \Cake\Database\Query $query The query that is being compiled
  113. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  114. * @return string
  115. */
  116. protected function _buildHavingPart($parts, $query, $generator)
  117. {
  118. $selectParts = $query->clause('select');
  119. foreach ($selectParts as $selectKey => $selectPart) {
  120. if (!$selectPart instanceof FunctionExpression) {
  121. continue;
  122. }
  123. foreach ($parts as $k => $p) {
  124. if (!is_string($p)) {
  125. continue;
  126. }
  127. preg_match_all(
  128. '/\b' . trim($selectKey, '[]') . '\b/i',
  129. $p,
  130. $matches
  131. );
  132. if (empty($matches[0])) {
  133. continue;
  134. }
  135. $parts[$k] = preg_replace(
  136. ['/\[|\]/', '/\b' . trim($selectKey, '[]') . '\b/i'],
  137. ['', $selectPart->sql($generator)],
  138. $p
  139. );
  140. }
  141. }
  142. return sprintf('HAVING %s', implode(', ', $parts));
  143. }
  144. }