PostgresDialectTrait.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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\Dialect;
  16. use Cake\Database\Expression\FunctionExpression;
  17. use Cake\Database\Schema\PostgresSchema;
  18. use Cake\Database\SqlDialectTrait;
  19. /**
  20. * Contains functions that encapsulates the SQL dialect used by Postgres,
  21. * including query translators and schema introspection.
  22. *
  23. * @internal
  24. */
  25. trait PostgresDialectTrait
  26. {
  27. use SqlDialectTrait;
  28. /**
  29. * String used to start a database identifier quoting to make it safe
  30. *
  31. * @var string
  32. */
  33. protected $_startQuote = '"';
  34. /**
  35. * String used to end a database identifier quoting to make it safe
  36. *
  37. * @var string
  38. */
  39. protected $_endQuote = '"';
  40. /**
  41. * The schema dialect class for this driver
  42. *
  43. * @var \Cake\Database\Schema\PostgresSchema
  44. */
  45. protected $_schemaDialect;
  46. /**
  47. * Distinct clause needs no transformation
  48. *
  49. * @param \Cake\Database\Query $query The query to be transformed
  50. * @return \Cake\Database\Query
  51. */
  52. protected function _transformDistinct($query)
  53. {
  54. return $query;
  55. }
  56. /**
  57. * Modifies the original insert query to append a "RETURNING *" epilogue
  58. * so that the latest insert id can be retrieved
  59. *
  60. * @param \Cake\Database\Query $query The query to translate.
  61. * @return \Cake\Database\Query
  62. */
  63. protected function _insertQueryTranslator($query)
  64. {
  65. if (!$query->clause('epilog')) {
  66. $query->epilog('RETURNING *');
  67. }
  68. return $query;
  69. }
  70. /**
  71. * Returns a dictionary of expressions to be transformed when compiling a Query
  72. * to SQL. Array keys are method names to be called in this class
  73. *
  74. * @return array
  75. */
  76. protected function _expressionTranslators()
  77. {
  78. $namespace = 'Cake\Database\Expression';
  79. return [
  80. $namespace . '\FunctionExpression' => '_transformFunctionExpression'
  81. ];
  82. }
  83. /**
  84. * Receives a FunctionExpression and changes it so that it conforms to this
  85. * SQL dialect.
  86. *
  87. * @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert
  88. * to postgres SQL.
  89. * @return void
  90. */
  91. protected function _transformFunctionExpression(FunctionExpression $expression)
  92. {
  93. switch ($expression->getName()) {
  94. case 'CONCAT':
  95. // CONCAT function is expressed as exp1 || exp2
  96. $expression->setName('')->setConjunction(' ||');
  97. break;
  98. case 'DATEDIFF':
  99. $expression
  100. ->setName('')
  101. ->setConjunction('-')
  102. ->iterateParts(function ($p) {
  103. if (is_string($p)) {
  104. $p = ['value' => [$p => 'literal'], 'type' => null];
  105. } else {
  106. $p['value'] = [$p['value']];
  107. }
  108. return new FunctionExpression('DATE', $p['value'], [$p['type']]);
  109. });
  110. break;
  111. case 'CURRENT_DATE':
  112. $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']);
  113. $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'date' => 'literal']);
  114. break;
  115. case 'CURRENT_TIME':
  116. $time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']);
  117. $expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'time' => 'literal']);
  118. break;
  119. case 'NOW':
  120. $expression->setName('LOCALTIMESTAMP')->add([' 0 ' => 'literal']);
  121. break;
  122. case 'RAND':
  123. $expression->setName('RANDOM');
  124. break;
  125. case 'DATE_ADD':
  126. $expression
  127. ->setName('')
  128. ->setConjunction(' + INTERVAL')
  129. ->iterateParts(function ($p, $key) {
  130. if ($key === 1) {
  131. $p = sprintf("'%s'", $p);
  132. }
  133. return $p;
  134. });
  135. break;
  136. case 'DAYOFWEEK':
  137. $expression
  138. ->setName('EXTRACT')
  139. ->setConjunction(' ')
  140. ->add(['DOW FROM' => 'literal'], [], true)
  141. ->add([') + (1' => 'literal']); // Postgres starts on index 0 but Sunday should be 1
  142. break;
  143. }
  144. }
  145. /**
  146. * Get the schema dialect.
  147. *
  148. * Used by Cake\Database\Schema package to reflect schema and
  149. * generate schema.
  150. *
  151. * @return \Cake\Database\Schema\PostgresSchema
  152. */
  153. public function schemaDialect()
  154. {
  155. if (!$this->_schemaDialect) {
  156. $this->_schemaDialect = new PostgresSchema($this);
  157. }
  158. return $this->_schemaDialect;
  159. }
  160. /**
  161. * {@inheritDoc}
  162. */
  163. public function disableForeignKeySQL()
  164. {
  165. return 'SET CONSTRAINTS ALL DEFERRED';
  166. }
  167. /**
  168. * {@inheritDoc}
  169. */
  170. public function enableForeignKeySQL()
  171. {
  172. return 'SET CONSTRAINTS ALL IMMEDIATE';
  173. }
  174. }