PostgresGrammar.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <?php
  2. namespace Illuminate\Database\Query\Grammars;
  3. use Illuminate\Support\Arr;
  4. use Illuminate\Support\Str;
  5. use Illuminate\Database\Query\Builder;
  6. class PostgresGrammar extends Grammar
  7. {
  8. /**
  9. * All of the available clause operators.
  10. *
  11. * @var array
  12. */
  13. protected $operators = [
  14. '=', '<', '>', '<=', '>=', '<>', '!=',
  15. 'like', 'not like', 'ilike',
  16. '&', '|', '#', '<<', '>>', '>>=', '=<<',
  17. '&&', '@>', '<@', '?', '?|', '?&', '||', '-', '-', '#-',
  18. ];
  19. /**
  20. * Compile a "where date" clause.
  21. *
  22. * @param \Illuminate\Database\Query\Builder $query
  23. * @param array $where
  24. * @return string
  25. */
  26. protected function whereDate(Builder $query, $where)
  27. {
  28. $value = $this->parameter($where['value']);
  29. return $this->wrap($where['column']).'::date '.$where['operator'].' '.$value;
  30. }
  31. /**
  32. * Compile a "where time" clause.
  33. *
  34. * @param \Illuminate\Database\Query\Builder $query
  35. * @param array $where
  36. * @return string
  37. */
  38. protected function whereTime(Builder $query, $where)
  39. {
  40. $value = $this->parameter($where['value']);
  41. return $this->wrap($where['column']).'::time '.$where['operator'].' '.$value;
  42. }
  43. /**
  44. * Compile a date based where clause.
  45. *
  46. * @param string $type
  47. * @param \Illuminate\Database\Query\Builder $query
  48. * @param array $where
  49. * @return string
  50. */
  51. protected function dateBasedWhere($type, Builder $query, $where)
  52. {
  53. $value = $this->parameter($where['value']);
  54. return 'extract('.$type.' from '.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
  55. }
  56. /**
  57. * Compile the lock into SQL.
  58. *
  59. * @param \Illuminate\Database\Query\Builder $query
  60. * @param bool|string $value
  61. * @return string
  62. */
  63. protected function compileLock(Builder $query, $value)
  64. {
  65. if (! is_string($value)) {
  66. return $value ? 'for update' : 'for share';
  67. }
  68. return $value;
  69. }
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function compileInsert(Builder $query, array $values)
  74. {
  75. $table = $this->wrapTable($query->from);
  76. return empty($values)
  77. ? "insert into {$table} DEFAULT VALUES"
  78. : parent::compileInsert($query, $values);
  79. }
  80. /**
  81. * Compile an insert and get ID statement into SQL.
  82. *
  83. * @param \Illuminate\Database\Query\Builder $query
  84. * @param array $values
  85. * @param string $sequence
  86. * @return string
  87. */
  88. public function compileInsertGetId(Builder $query, $values, $sequence)
  89. {
  90. if (is_null($sequence)) {
  91. $sequence = 'id';
  92. }
  93. return $this->compileInsert($query, $values).' returning '.$this->wrap($sequence);
  94. }
  95. /**
  96. * Compile an update statement into SQL.
  97. *
  98. * @param \Illuminate\Database\Query\Builder $query
  99. * @param array $values
  100. * @return string
  101. */
  102. public function compileUpdate(Builder $query, $values)
  103. {
  104. $table = $this->wrapTable($query->from);
  105. // Each one of the columns in the update statements needs to be wrapped in the
  106. // keyword identifiers, also a place-holder needs to be created for each of
  107. // the values in the list of bindings so we can make the sets statements.
  108. $columns = $this->compileUpdateColumns($values);
  109. $from = $this->compileUpdateFrom($query);
  110. $where = $this->compileUpdateWheres($query);
  111. return trim("update {$table} set {$columns}{$from} {$where}");
  112. }
  113. /**
  114. * Compile the columns for the update statement.
  115. *
  116. * @param array $values
  117. * @return string
  118. */
  119. protected function compileUpdateColumns($values)
  120. {
  121. // When gathering the columns for an update statement, we'll wrap each of the
  122. // columns and convert it to a parameter value. Then we will concatenate a
  123. // list of the columns that can be added into this update query clauses.
  124. return collect($values)->map(function ($value, $key) {
  125. return $this->wrap($key).' = '.$this->parameter($value);
  126. })->implode(', ');
  127. }
  128. /**
  129. * Compile the "from" clause for an update with a join.
  130. *
  131. * @param \Illuminate\Database\Query\Builder $query
  132. * @return string|null
  133. */
  134. protected function compileUpdateFrom(Builder $query)
  135. {
  136. if (! isset($query->joins)) {
  137. return '';
  138. }
  139. // When using Postgres, updates with joins list the joined tables in the from
  140. // clause, which is different than other systems like MySQL. Here, we will
  141. // compile out the tables that are joined and add them to a from clause.
  142. $froms = collect($query->joins)->map(function ($join) {
  143. return $this->wrapTable($join->table);
  144. })->all();
  145. if (count($froms) > 0) {
  146. return ' from '.implode(', ', $froms);
  147. }
  148. }
  149. /**
  150. * Compile the additional where clauses for updates with joins.
  151. *
  152. * @param \Illuminate\Database\Query\Builder $query
  153. * @return string
  154. */
  155. protected function compileUpdateWheres(Builder $query)
  156. {
  157. $baseWheres = $this->compileWheres($query);
  158. if (! isset($query->joins)) {
  159. return $baseWheres;
  160. }
  161. // Once we compile the join constraints, we will either use them as the where
  162. // clause or append them to the existing base where clauses. If we need to
  163. // strip the leading boolean we will do so when using as the only where.
  164. $joinWheres = $this->compileUpdateJoinWheres($query);
  165. if (trim($baseWheres) == '') {
  166. return 'where '.$this->removeLeadingBoolean($joinWheres);
  167. }
  168. return $baseWheres.' '.$joinWheres;
  169. }
  170. /**
  171. * Compile the "join" clause where clauses for an update.
  172. *
  173. * @param \Illuminate\Database\Query\Builder $query
  174. * @return string
  175. */
  176. protected function compileUpdateJoinWheres(Builder $query)
  177. {
  178. $joinWheres = [];
  179. // Here we will just loop through all of the join constraints and compile them
  180. // all out then implode them. This should give us "where" like syntax after
  181. // everything has been built and then we will join it to the real wheres.
  182. foreach ($query->joins as $join) {
  183. foreach ($join->wheres as $where) {
  184. $method = "where{$where['type']}";
  185. $joinWheres[] = $where['boolean'].' '.$this->$method($query, $where);
  186. }
  187. }
  188. return implode(' ', $joinWheres);
  189. }
  190. /**
  191. * Prepare the bindings for an update statement.
  192. *
  193. * @param array $bindings
  194. * @param array $values
  195. * @return array
  196. */
  197. public function prepareBindingsForUpdate(array $bindings, array $values)
  198. {
  199. // Update statements with "joins" in Postgres use an interesting syntax. We need to
  200. // take all of the bindings and put them on the end of this array since they are
  201. // added to the end of the "where" clause statements as typical where clauses.
  202. $bindingsWithoutJoin = Arr::except($bindings, 'join');
  203. return array_values(
  204. array_merge($values, $bindings['join'], Arr::flatten($bindingsWithoutJoin))
  205. );
  206. }
  207. /**
  208. * Compile a delete statement into SQL.
  209. *
  210. * @param \Illuminate\Database\Query\Builder $query
  211. * @return string
  212. */
  213. public function compileDelete(Builder $query)
  214. {
  215. $table = $this->wrapTable($query->from);
  216. return isset($query->joins)
  217. ? $this->compileDeleteWithJoins($query, $table)
  218. : parent::compileDelete($query);
  219. }
  220. /**
  221. * Compile a delete query that uses joins.
  222. *
  223. * @param \Illuminate\Database\Query\Builder $query
  224. * @param string $table
  225. * @param array $where
  226. * @return string
  227. */
  228. protected function compileDeleteWithJoins($query, $table)
  229. {
  230. $using = ' USING '.collect($query->joins)->map(function ($join) {
  231. return $this->wrapTable($join->table);
  232. })->implode(', ');
  233. $where = count($query->wheres) > 0 ? ' '.$this->compileUpdateWheres($query) : '';
  234. return trim("delete from {$table}{$using}{$where}");
  235. }
  236. /**
  237. * Compile a truncate table statement into SQL.
  238. *
  239. * @param \Illuminate\Database\Query\Builder $query
  240. * @return array
  241. */
  242. public function compileTruncate(Builder $query)
  243. {
  244. return ['truncate '.$this->wrapTable($query->from).' restart identity' => []];
  245. }
  246. /**
  247. * Wrap a single string in keyword identifiers.
  248. *
  249. * @param string $value
  250. * @return string
  251. */
  252. protected function wrapValue($value)
  253. {
  254. if ($value === '*') {
  255. return $value;
  256. }
  257. // If the given value is a JSON selector we will wrap it differently than a
  258. // traditional value. We will need to split this path and wrap each part
  259. // wrapped, etc. Otherwise, we will simply wrap the value as a string.
  260. if (Str::contains($value, '->')) {
  261. return $this->wrapJsonSelector($value);
  262. }
  263. return '"'.str_replace('"', '""', $value).'"';
  264. }
  265. /**
  266. * Wrap the given JSON selector.
  267. *
  268. * @param string $value
  269. * @return string
  270. */
  271. protected function wrapJsonSelector($value)
  272. {
  273. $path = explode('->', $value);
  274. $field = $this->wrapValue(array_shift($path));
  275. $wrappedPath = $this->wrapJsonPathAttributes($path);
  276. $attribute = array_pop($wrappedPath);
  277. if (! empty($wrappedPath)) {
  278. return $field.'->'.implode('->', $wrappedPath).'->>'.$attribute;
  279. }
  280. return $field.'->>'.$attribute;
  281. }
  282. /**
  283. * Wrap the attributes of the give JSON path.
  284. *
  285. * @param array $path
  286. * @return array
  287. */
  288. protected function wrapJsonPathAttributes($path)
  289. {
  290. return array_map(function ($attribute) {
  291. return "'$attribute'";
  292. }, $path);
  293. }
  294. }