QueryCompiler.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Database;
  16. use Cake\Database\Expression\QueryExpression;
  17. /**
  18. * Responsible for compiling a Query object into its SQL representation
  19. *
  20. * @internal
  21. */
  22. class QueryCompiler
  23. {
  24. /**
  25. * List of sprintf templates that will be used for compiling the SQL for
  26. * this query. There are some clauses that can be built as just as the
  27. * direct concatenation of the internal parts, those are listed here.
  28. *
  29. * @var array
  30. */
  31. protected $_templates = [
  32. 'delete' => 'DELETE',
  33. 'where' => ' WHERE %s',
  34. 'group' => ' GROUP BY %s ',
  35. 'having' => ' HAVING %s ',
  36. 'order' => ' %s',
  37. 'limit' => ' LIMIT %s',
  38. 'offset' => ' OFFSET %s',
  39. 'epilog' => ' %s'
  40. ];
  41. /**
  42. * The list of query clauses to traverse for generating a SELECT statement
  43. *
  44. * @var array
  45. */
  46. protected $_selectParts = [
  47. 'select', 'from', 'join', 'where', 'group', 'having', 'order', 'limit',
  48. 'offset', 'union', 'epilog'
  49. ];
  50. /**
  51. * The list of query clauses to traverse for generating an UPDATE statement
  52. *
  53. * @var array
  54. */
  55. protected $_updateParts = ['update', 'set', 'where', 'epilog'];
  56. /**
  57. * The list of query clauses to traverse for generating a DELETE statement
  58. *
  59. * @var array
  60. */
  61. protected $_deleteParts = ['delete', 'modifier', 'from', 'where', 'epilog'];
  62. /**
  63. * The list of query clauses to traverse for generating an INSERT statement
  64. *
  65. * @var array
  66. */
  67. protected $_insertParts = ['insert', 'values', 'epilog'];
  68. /**
  69. * Indicate whether or not this query dialect supports ordered unions.
  70. *
  71. * Overridden in subclasses.
  72. *
  73. * @var bool
  74. */
  75. protected $_orderedUnion = true;
  76. /**
  77. * Returns the SQL representation of the provided query after generating
  78. * the placeholders for the bound values using the provided generator
  79. *
  80. * @param \Cake\Database\Query $query The query that is being compiled
  81. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  82. * @return \Closure
  83. */
  84. public function compile(Query $query, ValueBinder $generator)
  85. {
  86. $sql = '';
  87. $type = $query->type();
  88. $query->traverse(
  89. $this->_sqlCompiler($sql, $query, $generator),
  90. $this->{'_' . $type . 'Parts'}
  91. );
  92. // Propagate bound parameters from sub-queries if the
  93. // placeholders can be found in the SQL statement.
  94. if ($query->getValueBinder() !== $generator) {
  95. foreach ($query->getValueBinder()->bindings() as $binding) {
  96. $placeholder = ':' . $binding['placeholder'];
  97. if (preg_match('/' . $placeholder . '(?:\W|$)/', $sql) > 0) {
  98. $generator->bind($placeholder, $binding['value'], $binding['type']);
  99. }
  100. }
  101. }
  102. return $sql;
  103. }
  104. /**
  105. * Returns a callable object that can be used to compile a SQL string representation
  106. * of this query.
  107. *
  108. * @param string $sql initial sql string to append to
  109. * @param \Cake\Database\Query $query The query that is being compiled
  110. * @param \Cake\Database\ValueBinder $generator The placeholder and value binder object
  111. * @return \Closure
  112. */
  113. protected function _sqlCompiler(&$sql, $query, $generator)
  114. {
  115. return function ($parts, $name) use (&$sql, $query, $generator) {
  116. if (!isset($parts) ||
  117. ((is_array($parts) || $parts instanceof \Countable) && !count($parts))
  118. ) {
  119. return;
  120. }
  121. if ($parts instanceof ExpressionInterface) {
  122. $parts = [$parts->sql($generator)];
  123. }
  124. if (isset($this->_templates[$name])) {
  125. $parts = $this->_stringifyExpressions((array)$parts, $generator);
  126. return $sql .= sprintf($this->_templates[$name], implode(', ', $parts));
  127. }
  128. return $sql .= $this->{'_build' . ucfirst($name) . 'Part'}($parts, $query, $generator);
  129. };
  130. }
  131. /**
  132. * Helper function used to build the string representation of a SELECT clause,
  133. * it constructs the field list taking care of aliasing and
  134. * converting expression objects to string. This function also constructs the
  135. * DISTINCT clause for the query.
  136. *
  137. * @param array $parts list of fields to be transformed to string
  138. * @param \Cake\Database\Query $query The query that is being compiled
  139. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  140. * @return string
  141. */
  142. protected function _buildSelectPart($parts, $query, $generator)
  143. {
  144. $driver = $query->getConnection()->getDriver();
  145. $select = 'SELECT%s %s%s';
  146. if ($this->_orderedUnion && $query->clause('union')) {
  147. $select = '(SELECT%s %s%s';
  148. }
  149. $distinct = $query->clause('distinct');
  150. $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator);
  151. $normalized = [];
  152. $parts = $this->_stringifyExpressions($parts, $generator);
  153. foreach ($parts as $k => $p) {
  154. if (!is_numeric($k)) {
  155. $p = $p . ' AS ' . $driver->quoteIdentifier($k);
  156. }
  157. $normalized[] = $p;
  158. }
  159. if ($distinct === true) {
  160. $distinct = 'DISTINCT ';
  161. }
  162. if (is_array($distinct)) {
  163. $distinct = $this->_stringifyExpressions($distinct, $generator);
  164. $distinct = sprintf('DISTINCT ON (%s) ', implode(', ', $distinct));
  165. }
  166. return sprintf($select, $modifiers, $distinct, implode(', ', $normalized));
  167. }
  168. /**
  169. * Helper function used to build the string representation of a FROM clause,
  170. * it constructs the tables list taking care of aliasing and
  171. * converting expression objects to string.
  172. *
  173. * @param array $parts list of tables to be transformed to string
  174. * @param \Cake\Database\Query $query The query that is being compiled
  175. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  176. * @return string
  177. */
  178. protected function _buildFromPart($parts, $query, $generator)
  179. {
  180. $select = ' FROM %s';
  181. $normalized = [];
  182. $parts = $this->_stringifyExpressions($parts, $generator);
  183. foreach ($parts as $k => $p) {
  184. if (!is_numeric($k)) {
  185. $p = $p . ' ' . $k;
  186. }
  187. $normalized[] = $p;
  188. }
  189. return sprintf($select, implode(', ', $normalized));
  190. }
  191. /**
  192. * Helper function used to build the string representation of multiple JOIN clauses,
  193. * it constructs the joins list taking care of aliasing and converting
  194. * expression objects to string in both the table to be joined and the conditions
  195. * to be used.
  196. *
  197. * @param array $parts list of joins to be transformed to string
  198. * @param \Cake\Database\Query $query The query that is being compiled
  199. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  200. * @return string
  201. */
  202. protected function _buildJoinPart($parts, $query, $generator)
  203. {
  204. $joins = '';
  205. foreach ($parts as $join) {
  206. $subquery = $join['table'] instanceof Query || $join['table'] instanceof QueryExpression;
  207. if ($join['table'] instanceof ExpressionInterface) {
  208. $join['table'] = $join['table']->sql($generator);
  209. }
  210. if ($subquery) {
  211. $join['table'] = '(' . $join['table'] . ')';
  212. }
  213. $joins .= sprintf(' %s JOIN %s %s', $join['type'], $join['table'], $join['alias']);
  214. $condition = '';
  215. if (isset($join['conditions']) && $join['conditions'] instanceof ExpressionInterface) {
  216. $condition = $join['conditions']->sql($generator);
  217. }
  218. if (strlen($condition)) {
  219. $joins .= " ON {$condition}";
  220. } else {
  221. $joins .= ' ON 1 = 1';
  222. }
  223. }
  224. return $joins;
  225. }
  226. /**
  227. * Helper function to generate SQL for SET expressions.
  228. *
  229. * @param array $parts List of keys & values to set.
  230. * @param \Cake\Database\Query $query The query that is being compiled
  231. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  232. * @return string
  233. */
  234. protected function _buildSetPart($parts, $query, $generator)
  235. {
  236. $set = [];
  237. foreach ($parts as $part) {
  238. if ($part instanceof ExpressionInterface) {
  239. $part = $part->sql($generator);
  240. }
  241. if ($part[0] === '(') {
  242. $part = substr($part, 1, -1);
  243. }
  244. $set[] = $part;
  245. }
  246. return ' SET ' . implode('', $set);
  247. }
  248. /**
  249. * Builds the SQL string for all the UNION clauses in this query, when dealing
  250. * with query objects it will also transform them using their configured SQL
  251. * dialect.
  252. *
  253. * @param array $parts list of queries to be operated with UNION
  254. * @param \Cake\Database\Query $query The query that is being compiled
  255. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  256. * @return string
  257. */
  258. protected function _buildUnionPart($parts, $query, $generator)
  259. {
  260. $parts = array_map(function ($p) use ($generator) {
  261. $p['query'] = $p['query']->sql($generator);
  262. $p['query'] = $p['query'][0] === '(' ? trim($p['query'], '()') : $p['query'];
  263. $prefix = $p['all'] ? 'ALL ' : '';
  264. if ($this->_orderedUnion) {
  265. return "{$prefix}({$p['query']})";
  266. }
  267. return $prefix . $p['query'];
  268. }, $parts);
  269. if ($this->_orderedUnion) {
  270. return sprintf(")\nUNION %s", implode("\nUNION ", $parts));
  271. }
  272. return sprintf("\nUNION %s", implode("\nUNION ", $parts));
  273. }
  274. /**
  275. * Builds the SQL fragment for INSERT INTO.
  276. *
  277. * @param array $parts The insert parts.
  278. * @param \Cake\Database\Query $query The query that is being compiled
  279. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  280. * @return string SQL fragment.
  281. */
  282. protected function _buildInsertPart($parts, $query, $generator)
  283. {
  284. $table = $parts[0];
  285. $columns = $this->_stringifyExpressions($parts[1], $generator);
  286. $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator);
  287. return sprintf('INSERT%s INTO %s (%s)', $modifiers, $table, implode(', ', $columns));
  288. }
  289. /**
  290. * Builds the SQL fragment for INSERT INTO.
  291. *
  292. * @param array $parts The values parts.
  293. * @param \Cake\Database\Query $query The query that is being compiled
  294. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  295. * @return string SQL fragment.
  296. */
  297. protected function _buildValuesPart($parts, $query, $generator)
  298. {
  299. return implode('', $this->_stringifyExpressions($parts, $generator));
  300. }
  301. /**
  302. * Builds the SQL fragment for UPDATE.
  303. *
  304. * @param array $parts The update parts.
  305. * @param \Cake\Database\Query $query The query that is being compiled
  306. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  307. * @return string SQL fragment.
  308. */
  309. protected function _buildUpdatePart($parts, $query, $generator)
  310. {
  311. $table = $this->_stringifyExpressions($parts, $generator);
  312. $modifiers = $this->_buildModifierPart($query->clause('modifier'), $query, $generator);
  313. return sprintf('UPDATE%s %s', $modifiers, implode(',', $table));
  314. }
  315. /**
  316. * Builds the SQL modifier fragment
  317. *
  318. * @param array $parts The query modifier parts
  319. * @param \Cake\Database\Query $query The query that is being compiled
  320. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  321. * @return string SQL fragment.
  322. */
  323. protected function _buildModifierPart($parts, $query, $generator)
  324. {
  325. if ($parts === []) {
  326. return '';
  327. }
  328. return ' ' . implode(' ', $this->_stringifyExpressions($parts, $generator, false));
  329. }
  330. /**
  331. * Helper function used to covert ExpressionInterface objects inside an array
  332. * into their string representation.
  333. *
  334. * @param array $expressions list of strings and ExpressionInterface objects
  335. * @param \Cake\Database\ValueBinder $generator the placeholder generator to be used in expressions
  336. * @param bool $wrap Whether to wrap each expression object with parenthesis
  337. * @return array
  338. */
  339. protected function _stringifyExpressions($expressions, $generator, $wrap = true)
  340. {
  341. $result = [];
  342. foreach ($expressions as $k => $expression) {
  343. if ($expression instanceof ExpressionInterface) {
  344. $value = $expression->sql($generator);
  345. $expression = $wrap ? '(' . $value . ')' : $value;
  346. }
  347. $result[$k] = $expression;
  348. }
  349. return $result;
  350. }
  351. }