Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
pgsqlsqlhelper.php
1<?php
2
3namespace Bitrix\Main\DB;
4
8
10{
14 public function getLeftQuote()
15 {
16 return '"';
17 }
18
22 public function getRightQuote()
23 {
24 return '"';
25 }
26
30 public function getAliasLength()
31 {
32 return 63;
33 }
34
38 public function quote($identifier)
39 {
40 return pg_escape_identifier($this->connection->getResource(), mb_strtolower($identifier));
41 }
42
46 public function values($identifier)
47 {
48 return 'EXCLUDED.' . $this->quote($identifier);
49 }
50
54 public function getQueryDelimiter()
55 {
56 return ';';
57 }
58
62 public function forSql($value, $maxLength = 0)
63 {
64 if ($maxLength > 0)
65 {
66 $value = mb_substr($value, 0, $maxLength);
67 }
68
69 return pg_escape_string($this->connection->getResource(), $value);
70 }
71
76 {
77 return 'now()';
78 }
79
83 public function getCurrentDateFunction()
84 {
85 return 'current_date';
86 }
87
91 public function addSecondsToDateTime($seconds, $from = null)
92 {
93 if ($from === null)
94 {
95 $from = static::getCurrentDateTimeFunction();
96 }
97 else
98 {
99 $from = $from . "::timestamp";
100 }
101
102 return $from . " + cast(" . $seconds . "||' second' as interval)";
103 }
104
108 public function addDaysToDateTime($days, $from = null)
109 {
110 if ($from === null)
111 {
112 $from = static::getCurrentDateTimeFunction();
113 }
114 else
115 {
116 $from = $from . "::timestamp";
117 }
118
119 return '(' . $from . " + cast(" . $days . "||' day' as interval))";
120 }
121
125 public function getDatetimeToDateFunction($value)
126 {
127 return 'cast('.$value.' as date)';
128 }
129
133 public function formatDate($format, $field = null)
134 {
135 static $search = array(
136 "MMMM",
137 "HH",
138 "GG",
139 "G",
140 "TT",
141 "T",
142 "W",
143 );
144 static $replace = array(
145 "FMMonth",
146 "HH24",
147 "HH12",
148 "FMHH12",
149 "PM",
150 "PM",
151 "D",
152 );
153
154 $format = str_replace($search, $replace, $format);
155
156 if ($field === null)
157 {
158 return $format;
159 }
160 else
161 {
162 return "to_char(".$field.", '".$format."')";
163 }
164 }
165
169 public function getRegexpOperator($field, $regexp)
170 {
171 return $field . ' ~ ' . $regexp;
172 }
173
177 public function getConcatFunction()
178 {
179 return implode(" || ", func_get_args());
180 }
181
185 public function getRandomFunction()
186 {
187 return 'random()';
188 }
189
193 public function getSha1Function($field)
194 {
195 return "encode(digest(replace(" . $field . ", '\\', '\\\\')::bytea, 'sha1'), 'hex')";
196 }
197
201 public function getIsNullFunction($expression, $result)
202 {
203 return "COALESCE(".$expression.", ".$result.")";
204 }
205
209 public function getLengthFunction($field)
210 {
211 return "OCTET_LENGTH(".$field.")";
212 }
213
214 public function getMatchFunction($field, $value)
215 {
216 return "to_tsvector('english'::regconfig, " . $field . ") @@ to_tsquery('english'::regconfig, " . $value . ")";
217 }
218
219 public function getMatchAndExpression($values, $prefixSearch = false)
220 {
221 if ($prefixSearch)
222 {
223 foreach ($values as $i => $searchTerm)
224 {
225 $values[$i] = $searchTerm . ':*';
226 }
227 }
228 return implode(' & ', $values);
229 }
230
231 public function getMatchOrExpression($values, $prefixSearch = false)
232 {
233 if ($prefixSearch)
234 {
235 foreach ($values as $i => $searchTerm)
236 {
237 $values[$i] = $searchTerm . ':*';
238 }
239 }
240 return implode(' | ', $values);
241 }
242
246 public function getCharToDateFunction($value)
247 {
248 return "timestamp '".$value."'";
249 }
250
254 public function getDateToCharFunction($fieldName)
255 {
256 return "TO_CHAR(".$fieldName.", 'YYYY-MM-DD HH24:MI:SS')";
257 }
258
262 public function getConverter(ScalarField $field)
263 {
264 if($field instanceof ORM\Fields\DatetimeField)
265 {
266 return array($this, "convertFromDbDateTime");
267 }
268 elseif($field instanceof ORM\Fields\DateField)
269 {
270 return array($this, "convertFromDbDate");
271 }
272 else
273 {
274 return parent::getConverter($field);
275 }
276 }
277
281 public function convertFromDbDateTime($value)
282 {
283 if($value !== null && $value != '0000-00-00 00:00:00')
284 {
285 return new Type\DateTime($value, "Y-m-d H:i:s");
286 }
287
288 return null;
289 }
290
294 public function convertFromDbDate($value)
295 {
296 if($value !== null && $value != '0000-00-00')
297 {
298 return new Type\Date($value, "Y-m-d");
299 }
300
301 return null;
302 }
303
307 public function castToChar($fieldName)
308 {
309 return 'CAST('.$fieldName.' AS char)';
310 }
311
315 public function softCastTextToChar($fieldName)
316 {
317 return $fieldName;
318 }
319
323 public function getColumnTypeByField(ScalarField $field)
324 {
325 if ($field instanceof ORM\Fields\IntegerField)
326 {
327 switch ($field->getSize())
328 {
329 case 2:
330 return 'smallint';
331 case 4:
332 return 'integer';
333 case 8:
334 return 'bigint';
335 }
336 return 'integer';
337 }
338 elseif ($field instanceof ORM\Fields\DecimalField)
339 {
340 $defaultPrecision = 18;
341 $defaultScale = 2;
342
343 $precision = $field->getPrecision() > 0 ? $field->getPrecision() : $defaultPrecision;
344 $scale = $field->getScale() > 0 ? $field->getScale() : $defaultScale;
345
346 if ($scale >= $precision)
347 {
348 $precision = $defaultPrecision;
349 $scale = $defaultScale;
350 }
351
352 return "decimal($precision, $scale)";
353 }
354 elseif ($field instanceof ORM\Fields\FloatField)
355 {
356 return 'float8';
357 }
358 elseif ($field instanceof ORM\Fields\DatetimeField)
359 {
360 return 'timestamp';
361 }
362 elseif ($field instanceof ORM\Fields\DateField)
363 {
364 return 'date';
365 }
366 elseif ($field instanceof ORM\Fields\TextField)
367 {
368 return 'text';
369 }
370 elseif ($field instanceof ORM\Fields\BooleanField)
371 {
372 $values = $field->getValues();
373
374 if (preg_match('/^[0-9]+$/', $values[0]) && preg_match('/^[0-9]+$/', $values[1]))
375 {
376 return 'int';
377 }
378 else
379 {
380 return 'varchar('.max(mb_strlen($values[0]), mb_strlen($values[1])).')';
381 }
382 }
383 elseif ($field instanceof ORM\Fields\EnumField)
384 {
385 return 'varchar('.max(array_map('strlen', $field->getValues())).')';
386 }
387 else
388 {
389 // string by default
390 $defaultLength = false;
391 foreach ($field->getValidators() as $validator)
392 {
393 if ($validator instanceof ORM\Fields\Validators\LengthValidator)
394 {
395 if ($defaultLength === false || $defaultLength > $validator->getMax())
396 {
397 $defaultLength = $validator->getMax();
398 }
399 }
400 }
401 return 'varchar('.($defaultLength > 0? $defaultLength: 255).')';
402 }
403 }
404
408 public function getFieldByColumnType($name, $type, array $parameters = null)
409 {
410 switch ($type)
411 {
412 case 'bigint':
413 case 'int8':
414 case 'bigserial':
415 case 'serial8':
416 return (new ORM\Fields\IntegerField($name))->configureSize(8);
417 case 'integer':
418 case 'int':
419 case 'int4':
420 case 'serial':
421 case 'serial4':
422 return (new ORM\Fields\IntegerField($name))->configureSize(4);
423 case 'smallint':
424 case 'int2':
425 case 'smallserial':
426 case 'serial2':
427 return (new ORM\Fields\IntegerField($name))->configureSize(2);
428 case 'double precision':
429 case 'float4':
430 case 'float8':
431 case 'numeric':
432 case 'decimal':
433 case 'real':
434 return new ORM\Fields\FloatField($name);
435 case 'timestamp':
436 case 'timestamp without time zone':
437 case 'timestamptz':
438 case 'timestamp with time zone':
439 return new ORM\Fields\DatetimeField($name);
440 case 'date':
441 return new ORM\Fields\DateField($name);
442 }
443 return new ORM\Fields\StringField($name);
444 }
445
449 public function getTopSql($sql, $limit, $offset = 0)
450 {
451 $offset = intval($offset);
452 $limit = intval($limit);
453
454 if ($offset > 0 && $limit <= 0)
455 {
456 throw new \Bitrix\Main\ArgumentException("Limit must be set if offset is set");
457 }
458
459 if ($limit > 0)
460 {
461 $sql .= "\nLIMIT ".$limit;
462 }
463
464 if ($offset > 0)
465 {
466 $sql .= " OFFSET ".$offset;
467 }
468
469 $sql .= "\n";
470
471 return $sql;
472 }
473
477 public function getInsertIgnore($tableName, $fields, $sql)
478 {
479 return 'INSERT INTO ' . $tableName . $fields . $sql . ' ON CONFLICT DO NOTHING';
480 }
481
485 public function getAscendingOrder()
486 {
487 return 'ASC NULLS FIRST';
488 }
489
493 public function getDescendingOrder()
494 {
495 return 'DESC NULLS LAST';
496 }
497
501 public function prepareMerge($tableName, array $primaryFields, array $insertFields, array $updateFields)
502 {
503 $insert = $this->prepareInsert($tableName, $insertFields);
504 $update = $this->prepareUpdate($tableName, $updateFields);
505
506 if (
507 $insert && $insert[0] != "" && $insert[1] != ""
508 && $update
509 && $primaryFields
510 )
511 {
512 $sql = 'INSERT INTO ' . $this->quote($tableName) . ' ('.$insert[0].')
513 VALUES (' . $insert[1] . ')
514 ON CONFLICT (' . implode(',', array_map([$this, 'quote'], $primaryFields)) . ')
515 DO UPDATE SET ' . $update[0];
516 }
517 else
518 {
519 $sql = "";
520 }
521
522 return array(
523 $sql
524 );
525
526 }
527
531 public function prepareMergeMultiple($tableName, array $primaryFields, array $insertRows)
532 {
533 $result = [];
534 $head = '';
535 $tail = '';
536 $maxBodySize = 1024*1024; //1 Mb
537 $body = [];
538 $bodySize = 0;
539 foreach ($insertRows as $insertFields)
540 {
541 $insert = $this->prepareInsert($tableName, $insertFields, true);
542 if (!$head && $insert && $insert[0])
543 {
544 $head = 'INSERT INTO ' . $this->quote($tableName) . ' (' . implode(', ', $insert[0]) . ') VALUES ';
545 $tail = ' ON CONFLICT (' . implode(',', array_map([$this, 'quote'], $primaryFields)) . ') DO UPDATE SET (' . implode(', ', $insert[0]) . ') = (' . implode(', ', array_map(function($f){return 'EXCLUDED.'.$f;}, $insert[0])) . ')';
546 }
547 if ($insert && $insert[1])
548 {
549 $values = '(' . implode(', ', $insert[1]) . ')';
550 $bodySize += mb_strlen($values) + 4;
551 $body[] = $values;
552 if ($bodySize > $maxBodySize)
553 {
554 $result[] = $head.implode(', ', $body).$tail;
555 $body = [];
556 $bodySize = 0;
557 }
558 }
559 }
560 if ($body)
561 {
562 $result[] = $head.implode(', ', $body).$tail;
563 }
564
565 return $result;
566 }
567
571 public function prepareMergeSelect($tableName, array $primaryFields, array $selectFields, $select, $updateFields)
572 {
573 $updateColumns = [];
574 $updateValues = [];
575
576 $tableFields = $this->connection->getTableFields($tableName);
577 // one registry
578 $tableFields = array_change_key_case($tableFields, CASE_UPPER);
579 $updateFields = array_change_key_case($updateFields, CASE_UPPER);
580 foreach ($updateFields as $columnName => $value)
581 {
582 if (isset($tableFields[$columnName]))
583 {
584 $updateColumns[] = $this->quote($columnName);
585 $updateValues[] = $this->convertToDb($value, $tableFields[$columnName]);
586 }
587 else
588 {
589 trigger_error("Column `{$columnName}` is not found in the `{$tableName}` table", E_USER_WARNING);
590 }
591 }
592
593 $sql = 'INSERT INTO ' . $this->quote($tableName) . ' (' . implode(',', array_map([$this, 'quote'], $selectFields)) . ') ';
594 $sql .= $select;
595 $sql .= ' ON CONFLICT (' . implode(',', array_map([$this, 'quote'], $primaryFields)) . ') DO UPDATE SET ';
596 if (count($updateColumns) === 1)
597 {
598 $sql .= $updateColumns[0] . ' = ' . $updateValues[0];
599 }
600 else
601 {
602 $sql .= ' (' . implode(', ', $updateColumns) . ') = (' . implode(', ', $updateValues) . ')';
603 }
604
605 return $sql;
606 }
607
611 public function prepareDeleteLimit($tableName, array $primaryFields, $where, array $order, $limit)
612 {
613 $primaryColumns = [];
614 foreach ($primaryFields as $columnName)
615 {
616 $primaryColumns[] = $this->quote($columnName);
617 }
618 $sqlPrimary = implode(', ', $primaryColumns);
619
620 $orderColumns = [];
621 foreach ($order as $columnName => $sort)
622 {
623 $orderColumns[] = $this->quote($columnName) . ' ' . $sort;
624 }
625 $sqlOrder = $orderColumns ? ' ORDER BY ' . implode(', ', $orderColumns) : '';
626 return 'DELETE FROM ' . $this->quote($tableName) . ' WHERE (' . $sqlPrimary . ') IN (SELECT ' . $sqlPrimary . ' FROM ' . $this->quote($tableName) . ' WHERE ' . $where . $sqlOrder . ' LIMIT ' . intval($limit) . ')';
627 }
628
629 public function initRowNumber($variableName)
630 {
631 return '';
632 }
633
634 public function getRowNumber($variableName)
635 {
636 return 'row_number() over()';
637 }
638
642 public function prepareCorrelatedUpdate($tableName, $tableAlias, $fields, $from, $where)
643 {
644 $dml = "UPDATE " . $tableName . ' AS ' . $tableAlias . " SET\n";
645
646 $set = '';
647 foreach ($fields as $fieldName => $fieldValue)
648 {
649 $set .= ($set ? ',' : '') . $fieldName . ' = ' .$fieldValue . "\n";
650 }
651 $dml .= $set;
652 $dml .= 'FROM ' . $from . "\n";
653 $dml .= 'WHERE ' . $where . "\n";
654
655 return $dml;
656 }
657
658 protected function getOrderByField(string $field, array $values, callable $callback, bool $quote = true): string
659 {
660 $field = $quote ? $this->quote($field) : $field;
661 $values = implode(',', array_map($callback, $values));
662
663 return "array_position(ARRAY[{$values}], {$field})";
664 }
665}
getOrderByField(string $field, array $values, callable $callback, bool $quote=true)
prepareMergeMultiple($tableName, array $primaryFields, array $insertRows)
getTopSql($sql, $limit, $offset=0)
getIsNullFunction($expression, $result)
formatDate($format, $field=null)
prepareDeleteLimit($tableName, array $primaryFields, $where, array $order, $limit)
addDaysToDateTime($days, $from=null)
getFieldByColumnType($name, $type, array $parameters=null)
getColumnTypeByField(ScalarField $field)
getConverter(ScalarField $field)
prepareCorrelatedUpdate($tableName, $tableAlias, $fields, $from, $where)
getMatchAndExpression($values, $prefixSearch=false)
getMatchOrExpression($values, $prefixSearch=false)
forSql($value, $maxLength=0)
addSecondsToDateTime($seconds, $from=null)
prepareMergeSelect($tableName, array $primaryFields, array $selectFields, $select, $updateFields)
getInsertIgnore($tableName, $fields, $sql)
prepareMerge($tableName, array $primaryFields, array $insertFields, array $updateFields)
prepareUpdate($tableName, array $fields)
prepareInsert($tableName, array $fields, $returnAsArray=false)
convertToDb($value, ORM\Fields\IReadable $field=null)