PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.15.4
Independent Analytics – WordPress Analytics Plugin v2.15.4
2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / vendor / illuminate / database / Query / Grammars / PostgresGrammar.php
independent-analytics / vendor / illuminate / database / Query / Grammars Last commit date
Grammar.php 3 weeks ago MySqlGrammar.php 3 weeks ago PostgresGrammar.php 3 weeks ago SQLiteGrammar.php 3 weeks ago SqlServerGrammar.php 3 weeks ago
PostgresGrammar.php
538 lines
1 <?php
2
3 namespace IAWPSCOPED\Illuminate\Database\Query\Grammars;
4
5 use IAWPSCOPED\Illuminate\Database\Query\Builder;
6 use IAWPSCOPED\Illuminate\Support\Arr;
7 use IAWPSCOPED\Illuminate\Support\Str;
8 /** @internal */
9 class PostgresGrammar extends Grammar
10 {
11 /**
12 * All of the available clause operators.
13 *
14 * @var string[]
15 */
16 protected $operators = ['=', '<', '>', '<=', '>=', '<>', '!=', 'like', 'not like', 'between', 'ilike', 'not ilike', '~', '&', '|', '#', '<<', '>>', '<<=', '>>=', '&&', '@>', '<@', '?', '?|', '?&', '||', '-', '@?', '@@', '#-', 'is distinct from', 'is not distinct from'];
17 /**
18 * The grammar specific bitwise operators.
19 *
20 * @var array
21 */
22 protected $bitwiseOperators = ['~', '&', '|', '#', '<<', '>>', '<<=', '>>='];
23 /**
24 * Compile a basic where clause.
25 *
26 * @param \Illuminate\Database\Query\Builder $query
27 * @param array $where
28 * @return string
29 */
30 protected function whereBasic(Builder $query, $where)
31 {
32 if (\str_contains(\strtolower($where['operator']), 'like')) {
33 return \sprintf('%s::text %s %s', $this->wrap($where['column']), $where['operator'], $this->parameter($where['value']));
34 }
35 return parent::whereBasic($query, $where);
36 }
37 /**
38 * Compile a bitwise operator where clause.
39 *
40 * @param \Illuminate\Database\Query\Builder $query
41 * @param array $where
42 * @return string
43 */
44 protected function whereBitwise(Builder $query, $where)
45 {
46 $value = $this->parameter($where['value']);
47 $operator = \str_replace('?', '??', $where['operator']);
48 return '(' . $this->wrap($where['column']) . ' ' . $operator . ' ' . $value . ')::bool';
49 }
50 /**
51 * Compile a "where date" clause.
52 *
53 * @param \Illuminate\Database\Query\Builder $query
54 * @param array $where
55 * @return string
56 */
57 protected function whereDate(Builder $query, $where)
58 {
59 $value = $this->parameter($where['value']);
60 return $this->wrap($where['column']) . '::date ' . $where['operator'] . ' ' . $value;
61 }
62 /**
63 * Compile a "where time" clause.
64 *
65 * @param \Illuminate\Database\Query\Builder $query
66 * @param array $where
67 * @return string
68 */
69 protected function whereTime(Builder $query, $where)
70 {
71 $value = $this->parameter($where['value']);
72 return $this->wrap($where['column']) . '::time ' . $where['operator'] . ' ' . $value;
73 }
74 /**
75 * Compile a date based where clause.
76 *
77 * @param string $type
78 * @param \Illuminate\Database\Query\Builder $query
79 * @param array $where
80 * @return string
81 */
82 protected function dateBasedWhere($type, Builder $query, $where)
83 {
84 $value = $this->parameter($where['value']);
85 return 'extract(' . $type . ' from ' . $this->wrap($where['column']) . ') ' . $where['operator'] . ' ' . $value;
86 }
87 /**
88 * Compile a "where fulltext" clause.
89 *
90 * @param \Illuminate\Database\Query\Builder $query
91 * @param array $where
92 * @return string
93 */
94 public function whereFullText(Builder $query, $where)
95 {
96 $language = $where['options']['language'] ?? 'english';
97 if (!\in_array($language, $this->validFullTextLanguages())) {
98 $language = 'english';
99 }
100 $columns = \IAWPSCOPED\collect($where['columns'])->map(function ($column) use($language) {
101 return "to_tsvector('{$language}', {$this->wrap($column)})";
102 })->implode(' || ');
103 $mode = 'plainto_tsquery';
104 if (($where['options']['mode'] ?? []) === 'phrase') {
105 $mode = 'phraseto_tsquery';
106 }
107 if (($where['options']['mode'] ?? []) === 'websearch') {
108 $mode = 'websearch_to_tsquery';
109 }
110 return "({$columns}) @@ {$mode}('{$language}', {$this->parameter($where['value'])})";
111 }
112 /**
113 * Get an array of valid full text languages.
114 *
115 * @return array
116 */
117 protected function validFullTextLanguages()
118 {
119 return ['simple', 'arabic', 'danish', 'dutch', 'english', 'finnish', 'french', 'german', 'hungarian', 'indonesian', 'irish', 'italian', 'lithuanian', 'nepali', 'norwegian', 'portuguese', 'romanian', 'russian', 'spanish', 'swedish', 'tamil', 'turkish'];
120 }
121 /**
122 * Compile the "select *" portion of the query.
123 *
124 * @param \Illuminate\Database\Query\Builder $query
125 * @param array $columns
126 * @return string|null
127 */
128 protected function compileColumns(Builder $query, $columns)
129 {
130 // If the query is actually performing an aggregating select, we will let that
131 // compiler handle the building of the select clauses, as it will need some
132 // more syntax that is best handled by that function to keep things neat.
133 if (!\is_null($query->aggregate)) {
134 return;
135 }
136 if (\is_array($query->distinct)) {
137 $select = 'select distinct on (' . $this->columnize($query->distinct) . ') ';
138 } elseif ($query->distinct) {
139 $select = 'select distinct ';
140 } else {
141 $select = 'select ';
142 }
143 return $select . $this->columnize($columns);
144 }
145 /**
146 * Compile a "JSON contains" statement into SQL.
147 *
148 * @param string $column
149 * @param string $value
150 * @return string
151 */
152 protected function compileJsonContains($column, $value)
153 {
154 $column = \str_replace('->>', '->', $this->wrap($column));
155 return '(' . $column . ')::jsonb @> ' . $value;
156 }
157 /**
158 * Compile a "JSON contains key" statement into SQL.
159 *
160 * @param string $column
161 * @return string
162 */
163 protected function compileJsonContainsKey($column)
164 {
165 $segments = \explode('->', $column);
166 $lastSegment = \array_pop($segments);
167 if (\filter_var($lastSegment, \FILTER_VALIDATE_INT) !== \false) {
168 $i = $lastSegment;
169 } elseif (\preg_match('/\\[(-?[0-9]+)\\]$/', $lastSegment, $matches)) {
170 $segments[] = Str::beforeLast($lastSegment, $matches[0]);
171 $i = $matches[1];
172 }
173 $column = \str_replace('->>', '->', $this->wrap(\implode('->', $segments)));
174 if (isset($i)) {
175 return \vsprintf('case when %s then %s else false end', ['jsonb_typeof((' . $column . ")::jsonb) = 'array'", 'jsonb_array_length((' . $column . ')::jsonb) >= ' . ($i < 0 ? \abs($i) : $i + 1)]);
176 }
177 $key = "'" . \str_replace("'", "''", $lastSegment) . "'";
178 return 'coalesce((' . $column . ')::jsonb ?? ' . $key . ', false)';
179 }
180 /**
181 * Compile a "JSON length" statement into SQL.
182 *
183 * @param string $column
184 * @param string $operator
185 * @param string $value
186 * @return string
187 */
188 protected function compileJsonLength($column, $operator, $value)
189 {
190 $column = \str_replace('->>', '->', $this->wrap($column));
191 return 'jsonb_array_length((' . $column . ')::jsonb) ' . $operator . ' ' . $value;
192 }
193 /**
194 * Compile a single having clause.
195 *
196 * @param array $having
197 * @return string
198 */
199 protected function compileHaving(array $having)
200 {
201 if ($having['type'] === 'Bitwise') {
202 return $this->compileHavingBitwise($having);
203 }
204 return parent::compileHaving($having);
205 }
206 /**
207 * Compile a having clause involving a bitwise operator.
208 *
209 * @param array $having
210 * @return string
211 */
212 protected function compileHavingBitwise($having)
213 {
214 $column = $this->wrap($having['column']);
215 $parameter = $this->parameter($having['value']);
216 return '(' . $column . ' ' . $having['operator'] . ' ' . $parameter . ')::bool';
217 }
218 /**
219 * Compile the lock into SQL.
220 *
221 * @param \Illuminate\Database\Query\Builder $query
222 * @param bool|string $value
223 * @return string
224 */
225 protected function compileLock(Builder $query, $value)
226 {
227 if (!\is_string($value)) {
228 return $value ? 'for update' : 'for share';
229 }
230 return $value;
231 }
232 /**
233 * Compile an insert ignore statement into SQL.
234 *
235 * @param \Illuminate\Database\Query\Builder $query
236 * @param array $values
237 * @return string
238 */
239 public function compileInsertOrIgnore(Builder $query, array $values)
240 {
241 return $this->compileInsert($query, $values) . ' on conflict do nothing';
242 }
243 /**
244 * Compile an insert and get ID statement into SQL.
245 *
246 * @param \Illuminate\Database\Query\Builder $query
247 * @param array $values
248 * @param string $sequence
249 * @return string
250 */
251 public function compileInsertGetId(Builder $query, $values, $sequence)
252 {
253 return $this->compileInsert($query, $values) . ' returning ' . $this->wrap($sequence ?: 'id');
254 }
255 /**
256 * Compile an update statement into SQL.
257 *
258 * @param \Illuminate\Database\Query\Builder $query
259 * @param array $values
260 * @return string
261 */
262 public function compileUpdate(Builder $query, array $values)
263 {
264 if (isset($query->joins) || isset($query->limit)) {
265 return $this->compileUpdateWithJoinsOrLimit($query, $values);
266 }
267 return parent::compileUpdate($query, $values);
268 }
269 /**
270 * Compile the columns for an update statement.
271 *
272 * @param \Illuminate\Database\Query\Builder $query
273 * @param array $values
274 * @return string
275 */
276 protected function compileUpdateColumns(Builder $query, array $values)
277 {
278 return \IAWPSCOPED\collect($values)->map(function ($value, $key) {
279 $column = \IAWPSCOPED\last(\explode('.', $key));
280 if ($this->isJsonSelector($key)) {
281 return $this->compileJsonUpdateColumn($column, $value);
282 }
283 return $this->wrap($column) . ' = ' . $this->parameter($value);
284 })->implode(', ');
285 }
286 /**
287 * Compile an "upsert" statement into SQL.
288 *
289 * @param \Illuminate\Database\Query\Builder $query
290 * @param array $values
291 * @param array $uniqueBy
292 * @param array $update
293 * @return string
294 */
295 public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
296 {
297 $sql = $this->compileInsert($query, $values);
298 $sql .= ' on conflict (' . $this->columnize($uniqueBy) . ') do update set ';
299 $columns = \IAWPSCOPED\collect($update)->map(function ($value, $key) {
300 return \is_numeric($key) ? $this->wrap($value) . ' = ' . $this->wrapValue('excluded') . '.' . $this->wrap($value) : $this->wrap($key) . ' = ' . $this->parameter($value);
301 })->implode(', ');
302 return $sql . $columns;
303 }
304 /**
305 * Prepares a JSON column being updated using the JSONB_SET function.
306 *
307 * @param string $key
308 * @param mixed $value
309 * @return string
310 */
311 protected function compileJsonUpdateColumn($key, $value)
312 {
313 $segments = \explode('->', $key);
314 $field = $this->wrap(\array_shift($segments));
315 $path = "'{" . \implode(',', $this->wrapJsonPathAttributes($segments, '"')) . "}'";
316 return "{$field} = jsonb_set({$field}::jsonb, {$path}, {$this->parameter($value)})";
317 }
318 /**
319 * Compile an update from statement into SQL.
320 *
321 * @param \Illuminate\Database\Query\Builder $query
322 * @param array $values
323 * @return string
324 */
325 public function compileUpdateFrom(Builder $query, $values)
326 {
327 $table = $this->wrapTable($query->from);
328 // Each one of the columns in the update statements needs to be wrapped in the
329 // keyword identifiers, also a place-holder needs to be created for each of
330 // the values in the list of bindings so we can make the sets statements.
331 $columns = $this->compileUpdateColumns($query, $values);
332 $from = '';
333 if (isset($query->joins)) {
334 // When using Postgres, updates with joins list the joined tables in the from
335 // clause, which is different than other systems like MySQL. Here, we will
336 // compile out the tables that are joined and add them to a from clause.
337 $froms = \IAWPSCOPED\collect($query->joins)->map(function ($join) {
338 return $this->wrapTable($join->table);
339 })->all();
340 if (\count($froms) > 0) {
341 $from = ' from ' . \implode(', ', $froms);
342 }
343 }
344 $where = $this->compileUpdateWheres($query);
345 return \trim("update {$table} set {$columns}{$from} {$where}");
346 }
347 /**
348 * Compile the additional where clauses for updates with joins.
349 *
350 * @param \Illuminate\Database\Query\Builder $query
351 * @return string
352 */
353 protected function compileUpdateWheres(Builder $query)
354 {
355 $baseWheres = $this->compileWheres($query);
356 if (!isset($query->joins)) {
357 return $baseWheres;
358 }
359 // Once we compile the join constraints, we will either use them as the where
360 // clause or append them to the existing base where clauses. If we need to
361 // strip the leading boolean we will do so when using as the only where.
362 $joinWheres = $this->compileUpdateJoinWheres($query);
363 if (\trim($baseWheres) == '') {
364 return 'where ' . $this->removeLeadingBoolean($joinWheres);
365 }
366 return $baseWheres . ' ' . $joinWheres;
367 }
368 /**
369 * Compile the "join" clause where clauses for an update.
370 *
371 * @param \Illuminate\Database\Query\Builder $query
372 * @return string
373 */
374 protected function compileUpdateJoinWheres(Builder $query)
375 {
376 $joinWheres = [];
377 // Here we will just loop through all of the join constraints and compile them
378 // all out then implode them. This should give us "where" like syntax after
379 // everything has been built and then we will join it to the real wheres.
380 foreach ($query->joins as $join) {
381 foreach ($join->wheres as $where) {
382 $method = "where{$where['type']}";
383 $joinWheres[] = $where['boolean'] . ' ' . $this->{$method}($query, $where);
384 }
385 }
386 return \implode(' ', $joinWheres);
387 }
388 /**
389 * Prepare the bindings for an update statement.
390 *
391 * @param array $bindings
392 * @param array $values
393 * @return array
394 */
395 public function prepareBindingsForUpdateFrom(array $bindings, array $values)
396 {
397 $values = \IAWPSCOPED\collect($values)->map(function ($value, $column) {
398 return \is_array($value) || $this->isJsonSelector($column) && !$this->isExpression($value) ? \json_encode($value) : $value;
399 })->all();
400 $bindingsWithoutWhere = Arr::except($bindings, ['select', 'where']);
401 return \array_values(\array_merge($values, $bindings['where'], Arr::flatten($bindingsWithoutWhere)));
402 }
403 /**
404 * Compile an update statement with joins or limit into SQL.
405 *
406 * @param \Illuminate\Database\Query\Builder $query
407 * @param array $values
408 * @return string
409 */
410 protected function compileUpdateWithJoinsOrLimit(Builder $query, array $values)
411 {
412 $table = $this->wrapTable($query->from);
413 $columns = $this->compileUpdateColumns($query, $values);
414 $alias = \IAWPSCOPED\last(\preg_split('/\\s+as\\s+/i', $query->from));
415 $selectSql = $this->compileSelect($query->select($alias . '.ctid'));
416 return "update {$table} set {$columns} where {$this->wrap('ctid')} in ({$selectSql})";
417 }
418 /**
419 * Prepare the bindings for an update statement.
420 *
421 * @param array $bindings
422 * @param array $values
423 * @return array
424 */
425 public function prepareBindingsForUpdate(array $bindings, array $values)
426 {
427 $values = \IAWPSCOPED\collect($values)->map(function ($value, $column) {
428 return \is_array($value) || $this->isJsonSelector($column) && !$this->isExpression($value) ? \json_encode($value) : $value;
429 })->all();
430 $cleanBindings = Arr::except($bindings, 'select');
431 return \array_values(\array_merge($values, Arr::flatten($cleanBindings)));
432 }
433 /**
434 * Compile a delete statement into SQL.
435 *
436 * @param \Illuminate\Database\Query\Builder $query
437 * @return string
438 */
439 public function compileDelete(Builder $query)
440 {
441 if (isset($query->joins) || isset($query->limit)) {
442 return $this->compileDeleteWithJoinsOrLimit($query);
443 }
444 return parent::compileDelete($query);
445 }
446 /**
447 * Compile a delete statement with joins or limit into SQL.
448 *
449 * @param \Illuminate\Database\Query\Builder $query
450 * @return string
451 */
452 protected function compileDeleteWithJoinsOrLimit(Builder $query)
453 {
454 $table = $this->wrapTable($query->from);
455 $alias = \IAWPSCOPED\last(\preg_split('/\\s+as\\s+/i', $query->from));
456 $selectSql = $this->compileSelect($query->select($alias . '.ctid'));
457 return "delete from {$table} where {$this->wrap('ctid')} in ({$selectSql})";
458 }
459 /**
460 * Compile a truncate table statement into SQL.
461 *
462 * @param \Illuminate\Database\Query\Builder $query
463 * @return array
464 */
465 public function compileTruncate(Builder $query)
466 {
467 return ['truncate ' . $this->wrapTable($query->from) . ' restart identity cascade' => []];
468 }
469 /**
470 * Wrap the given JSON selector.
471 *
472 * @param string $value
473 * @return string
474 */
475 protected function wrapJsonSelector($value)
476 {
477 $path = \explode('->', $value);
478 $field = $this->wrapSegments(\explode('.', \array_shift($path)));
479 $wrappedPath = $this->wrapJsonPathAttributes($path);
480 $attribute = \array_pop($wrappedPath);
481 if (!empty($wrappedPath)) {
482 return $field . '->' . \implode('->', $wrappedPath) . '->>' . $attribute;
483 }
484 return $field . '->>' . $attribute;
485 }
486 /**
487 * Wrap the given JSON selector for boolean values.
488 *
489 * @param string $value
490 * @return string
491 */
492 protected function wrapJsonBooleanSelector($value)
493 {
494 $selector = \str_replace('->>', '->', $this->wrapJsonSelector($value));
495 return '(' . $selector . ')::jsonb';
496 }
497 /**
498 * Wrap the given JSON boolean value.
499 *
500 * @param string $value
501 * @return string
502 */
503 protected function wrapJsonBooleanValue($value)
504 {
505 return "'" . $value . "'::jsonb";
506 }
507 /**
508 * Wrap the attributes of the given JSON path.
509 *
510 * @param array $path
511 * @return array
512 */
513 protected function wrapJsonPathAttributes($path)
514 {
515 $quote = \func_num_args() === 2 ? \func_get_arg(1) : "'";
516 return \IAWPSCOPED\collect($path)->map(function ($attribute) {
517 return $this->parseJsonPathArrayKeys($attribute);
518 })->collapse()->map(function ($attribute) use($quote) {
519 return \filter_var($attribute, \FILTER_VALIDATE_INT) !== \false ? $attribute : $quote . $attribute . $quote;
520 })->all();
521 }
522 /**
523 * Parse the given JSON path attribute for array keys.
524 *
525 * @param string $attribute
526 * @return array
527 */
528 protected function parseJsonPathArrayKeys($attribute)
529 {
530 if (\preg_match('/(\\[[^\\]]+\\])+$/', $attribute, $parts)) {
531 $key = Str::beforeLast($attribute, $parts[0]);
532 \preg_match_all('/\\[([^\\]]+)\\]/', $parts[0], $keys);
533 return \IAWPSCOPED\collect([$key])->merge($keys[1])->diff('')->values()->all();
534 }
535 return [$attribute];
536 }
537 }
538