Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
sqlhelper.php
1<?php
2
3namespace Bitrix\Main\DB;
4
8
9abstract class SqlHelper
10{
12 protected $connection;
13
14 protected $idCache;
15
20 {
21 $this->connection = $connection;
22 }
23
29 public function getLeftQuote()
30 {
31 return '';
32 }
33
39 public function getRightQuote()
40 {
41 return '';
42 }
43
49 abstract public function getAliasLength();
50
65 public function quote($identifier)
66 {
67 if (empty($this->idCache[$identifier]))
68 {
69 // security unshielding
70 $quotedIdentifier = str_replace([$this->getLeftQuote(), $this->getRightQuote()], '', $identifier);
71
72 // shield [[database.]tablename.]columnname
73 if (str_contains($quotedIdentifier, '.'))
74 {
75 $quotedIdentifier = str_replace('.', $this->getRightQuote() . '.' . $this->getLeftQuote(), $quotedIdentifier);
76 }
77
78 // shield general borders
79 $this->idCache[$identifier] = $this->getLeftQuote() . $quotedIdentifier . $this->getRightQuote();
80 }
81
82 return $this->idCache[$identifier];
83 }
84
90 abstract public function getQueryDelimiter();
91
100 abstract public function forSql($value, $maxLength = 0);
101
107 abstract public function getCurrentDateTimeFunction();
108
114 abstract public function getCurrentDateFunction();
115
128 abstract public function addSecondsToDateTime($seconds, $from = null);
129
143 public function addDaysToDateTime($days, $from = null)
144 {
145 throw new Main\NotImplementedException('Method should be implemented in a child class.');
146 }
147
157 abstract public function getDatetimeToDateFunction($value);
158
185 abstract public function formatDate($format, $field = null);
186
201 public function getSubstrFunction($str, $from, $length = null)
202 {
203 $sql = 'SUBSTR('.$str.', '.$from;
204
205 if (!is_null($length))
206 $sql .= ', '.$length;
207
208 return $sql.')';
209 }
210
220 abstract public function getConcatFunction();
221
233 abstract public function getIsNullFunction($expression, $result);
234
244 abstract public function getLengthFunction($field);
245
257 abstract public function getCharToDateFunction($value);
258
271 abstract public function getDateToCharFunction($fieldName);
272
280 abstract public function castToChar($fieldName);
281
291 abstract public function softCastTextToChar($fieldName);
292
305 abstract public function getTopSql($sql, $limit, $offset = 0);
306
317 public function prepareInsert($tableName, array $fields, $returnAsArray = false)
318 {
319 $columns = array();
320 $values = array();
321
322 $tableFields = $this->connection->getTableFields($tableName);
323
324 // one registry
325 $tableFields = array_change_key_case($tableFields, CASE_UPPER);
326 $fields = array_change_key_case($fields, CASE_UPPER);
327
328 foreach ($fields as $columnName => $value)
329 {
330 if (isset($tableFields[$columnName]))
331 {
332 $columns[] = $this->quote($columnName);
333 $values[] = $this->convertToDb($value, $tableFields[$columnName]);
334 }
335 else
336 {
337 trigger_error("Column `{$columnName}` is not found in the `{$tableName}` table", E_USER_WARNING);
338 }
339 }
340
341 $binds = $this->prepareBinds($tableFields, $fields);
342
343 return array(
344 $returnAsArray ? $columns : implode(", ", $columns),
345 $returnAsArray ? $values : implode(", ", $values),
346 $binds
347 );
348 }
349
358 public function prepareUpdate($tableName, array $fields)
359 {
360 $update = array();
361
362 $tableFields = $this->connection->getTableFields($tableName);
363
364 // one registry
365 $tableFields = array_change_key_case($tableFields, CASE_UPPER);
366 $fields = array_change_key_case($fields, CASE_UPPER);
367
368 foreach ($fields as $columnName => $value)
369 {
370 if (isset($tableFields[$columnName]))
371 {
372 $update[] = $this->quote($columnName).' = '.$this->convertToDb($value, $tableFields[$columnName]);
373 }
374 else
375 {
376 trigger_error("Column `{$columnName}` is not found in the `{$tableName}` table", E_USER_WARNING);
377 }
378 }
379
380 $binds = $this->prepareBinds($tableFields, $fields);
381
382 return array(
383 implode(", ", $update),
384 $binds
385 );
386 }
387
398 abstract public function prepareMerge($tableName, array $primaryFields, array $insertFields, array $updateFields);
399
408 protected function prepareBinds(array $tableFields, array $fields)
409 {
410 return array();
411 }
412
422 public function prepareAssignment($tableName, $columnName, $value)
423 {
424 $tableField = $this->connection->getTableField($tableName, $columnName);
425
426 return $this->quote($columnName).' = '.$this->convertToDb($value, $tableField);
427 }
428
437 public function convertToDb($value, ORM\Fields\IReadable $field = null)
438 {
439 if ($value === null)
440 {
441 return "NULL";
442 }
443
444 if ($value instanceof SqlExpression)
445 {
446 return $value->compile();
447 }
448
449 if (is_a($field, '\Bitrix\Main\ORM\Fields\StringField'))
450 {
451 $size = $field->getSize();
452 if ($size)
453 {
454 $value = mb_substr($value, 0, $size);
455 }
456 }
457
458 if($field instanceof ORM\Fields\IReadable)
459 {
460 $result = $field->convertValueToDb($value);
461 }
462 else
463 {
464 $result = $this->convertToDbString($value);
465 }
466
467 return $result;
468 }
469
480 public function convertFromDb($value, ORM\Fields\IReadable $field)
481 {
482 return $field->convertValueFromDb($value);
483 }
484
492 public function convertToDbInteger($value)
493 {
494 return intval($value);
495 }
496
502 public function convertFromDbInteger($value)
503 {
504 return intval($value);
505 }
506
515 public function convertToDbFloat($value, $scale = null)
516 {
517 $value = doubleval($value);
518 if(!is_finite($value))
519 {
520 $value = 0;
521 }
522
523 return $scale !== null ? "'".round($value, $scale)."'" : "'".$value."'";
524 }
525
532 public function convertFromDbFloat($value, $scale = null)
533 {
534 $value = doubleval($value);
535
536 return $scale !== null ? round($value, $scale) : $value;
537 }
538
547 public function convertToDbString($value, $length = null)
548 {
549 return "'".$this->forSql($value, $length)."'";
550 }
551
558 public function convertFromDbString($value, $length = null)
559 {
560 if ($length > 0)
561 {
562 $value = mb_substr($value, 0, $length);
563 }
564
565 return strval($value);
566 }
567
575 public function convertToDbText($value)
576 {
577 return $this->convertToDbString($value);
578 }
579
585 public function convertFromDbText($value)
586 {
587 return $this->convertFromDbString($value);
588 }
589
598 public function convertToDbDate($value)
599 {
600 if (empty($value))
601 {
602 return "NULL";
603 }
604 elseif($value instanceof Type\Date)
605 {
606 return $this->getCharToDateFunction($value->format("Y-m-d"));
607 }
608 else
609 {
610 throw new Main\ArgumentTypeException('value', '\Bitrix\Main\Type\Date');
611 }
612 }
613
620 public function convertFromDbDate($value)
621 {
622 return new Type\Date($value);
623 }
624
633 public function convertToDbDateTime($value)
634 {
635 if (empty($value))
636 {
637 return "NULL";
638 }
639 elseif($value instanceof Type\Date)
640 {
641 if($value instanceof Type\DateTime)
642 {
643 $value = clone($value);
644 $value->setDefaultTimeZone();
645 }
646 return $this->getCharToDateFunction($value->format("Y-m-d H:i:s"));
647 }
648 else
649 {
650 throw new Main\ArgumentTypeException('value', '\Bitrix\Main\Type\Date');
651 }
652 }
653
660 public function convertFromDbDateTime($value)
661 {
662 return new Type\DateTime($value);
663 }
664
676 public function convertDatetimeField($value)
677 {
678 return $this->convertFromDbDateTime($value);
679 }
680
692 public function convertDateField($value)
693 {
694 return $this->convertFromDbDate($value);
695 }
696
705 public function getConverter(ORM\Fields\ScalarField $field)
706 {
707 return false;
708 }
709
717 abstract public function getColumnTypeByField(ORM\Fields\ScalarField $field);
718
729 abstract public function getFieldByColumnType($name, $type, array $parameters = null);
730
736 public function getAscendingOrder()
737 {
738 return 'ASC';
739 }
740
746 public function getDescendingOrder()
747 {
748 return 'DESC';
749 }
750
756 public function getConditionalAssignment(string $field, string $value): string
757 {
758 $field = $this->quote($field);
759 $hash = $this->convertToDbString(sha1($value));
760 $value = $this->convertToDbString($value);
761
762 return 'case when ' . $this->getSha1Function($field) . ' = ' . $hash . ' then ' . $field . ' else ' . $value . ' end';
763 }
764
775 public function getInsertIgnore($tableName, $fields, $sql)
776 {
777 throw new Main\NotImplementedException('Method should be implemented in a child class.');
778 }
779
785 public function getRandomFunction()
786 {
787 return 'rand()';
788 }
789
799 public function getSha1Function($field)
800 {
801 return 'sha1(' . $field . ')';
802 }
803
815 public function getRegexpOperator($field, $regexp)
816 {
817 throw new Main\NotImplementedException('Method should be implemented in a child class.');
818 }
819
829 public function values($identifier)
830 {
831 throw new Main\NotImplementedException('Method should be implemented in a child class.');
832 }
833
837 public function getMatchFunction($field, $value)
838 {
839 throw new Main\NotImplementedException('Method should be implemented in a child class.');
840 }
841
845 public function getMatchAndExpression($values, $prefixSearch = false)
846 {
847 throw new Main\NotImplementedException('Method should be implemented in a child class.');
848 }
849
853 public function getMatchOrExpression($values, $prefixSearch = false)
854 {
855 throw new Main\NotImplementedException('Method should be implemented in a child class.');
856 }
857
868 public function prepareMergeMultiple($tableName, array $primaryFields, array $insertRows)
869 {
870 throw new Main\NotImplementedException('Method should be implemented in a child class.');
871 }
872
884 public function prepareMergeSelect($tableName, array $primaryFields, array $selectFields, $select, $updateFields)
885 {
886 throw new Main\NotImplementedException('Method should be implemented in a child class.');
887 }
888
901 public function prepareDeleteLimit($tableName, array $primaryFields, $where, array $order, $limit)
902 {
903 throw new Main\NotImplementedException('Method should be implemented in a child class.');
904 }
905
909 public function initRowNumber($variableName)
910 {
911 throw new Main\NotImplementedException('Method should be implemented in a child class.');
912 }
913
917 public function getRowNumber($variableName)
918 {
919 throw new Main\NotImplementedException('Method should be implemented in a child class.');
920 }
921
934 public function prepareCorrelatedUpdate($tableName, $tableAlias, $fields, $from, $where)
935 {
936 throw new Main\NotImplementedException('Method should be implemented in a child class.');
937 }
938
950 public function prepareMergeValues(string $tableName, array $primaryFields, array $insertRows, array $updateFields = []): string
951 {
952 $insertColumns = array_keys($insertRows[array_key_first($insertRows)] ?? []);
953 $insertValuesStrings = [];
954 foreach ($insertRows as $row)
955 {
956 [, $rowValues] = $this->prepareInsert($tableName, $row);
957 $insertValuesStrings[] = $rowValues;
958 }
959
960 if (empty($updateFields))
961 {
962 $notPrimaryFields = array_diff($insertColumns, $primaryFields);
963 if (empty($notPrimaryFields))
964 {
965 trigger_error("Only primary fields to update, use getInsertIgnore() or specify fields", E_USER_WARNING);
966 }
967 $updateFields = $notPrimaryFields;
968 }
969
970 $compatibleUpdateFields = [];
971
972 foreach ($updateFields as $key => $value)
973 {
974 if (is_numeric($key) && is_string($value))
975 {
976 $compatibleUpdateFields[$value] = new SqlExpression('?v', $value);
977 }
978 else
979 {
980 $compatibleUpdateFields[$key] = $value;
981 }
982 }
983
984 $insertValueString = 'values (' . implode('),(', $insertValuesStrings) . ')';
985
986 return $this->prepareMergeSelect($tableName, $primaryFields, $insertColumns, $insertValueString, $compatibleUpdateFields);
987 }
988
996 public function getOrderByStringField(string $field, array $values, bool $quote = true): string
997 {
998 return $this->getOrderByField($field, $values, [$this, 'convertToDbString'], $quote);
999 }
1000
1008 public function getOrderByIntField(string $field, array $values, bool $quote = true): string
1009 {
1010 return $this->getOrderByField($field, $values, [$this, 'convertFromDbInteger'], $quote);
1011 }
1012
1021 protected function getOrderByField(string $field, array $values, callable $callback, bool $quote = true): string
1022 {
1023 return $quote ? $this->quote($field) : $field;
1024 }
1025}
softCastTextToChar($fieldName)
getOrderByField(string $field, array $values, callable $callback, bool $quote=true)
prepareMergeMultiple($tableName, array $primaryFields, array $insertRows)
getMatchFunction($field, $value)
getOrderByStringField(string $field, array $values, bool $quote=true)
__construct(Connection $connection)
Definition sqlhelper.php:19
getTopSql($sql, $limit, $offset=0)
convertToDbString($value, $length=null)
getConverter(ORM\Fields\ScalarField $field)
getIsNullFunction($expression, $result)
getDateToCharFunction($fieldName)
formatDate($format, $field=null)
getRegexpOperator($field, $regexp)
getConditionalAssignment(string $field, string $value)
prepareDeleteLimit($tableName, array $primaryFields, $where, array $order, $limit)
prepareUpdate($tableName, array $fields)
convertToDbFloat($value, $scale=null)
getColumnTypeByField(ORM\Fields\ScalarField $field)
convertFromDbString($value, $length=null)
addDaysToDateTime($days, $from=null)
getFieldByColumnType($name, $type, array $parameters=null)
convertFromDb($value, ORM\Fields\IReadable $field)
prepareAssignment($tableName, $columnName, $value)
initRowNumber($variableName)
prepareInsert($tableName, array $fields, $returnAsArray=false)
convertFromDbFloat($value, $scale=null)
getSubstrFunction($str, $from, $length=null)
prepareCorrelatedUpdate($tableName, $tableAlias, $fields, $from, $where)
getMatchAndExpression($values, $prefixSearch=false)
convertToDb($value, ORM\Fields\IReadable $field=null)
getMatchOrExpression($values, $prefixSearch=false)
forSql($value, $maxLength=0)
addSecondsToDateTime($seconds, $from=null)
prepareMergeSelect($tableName, array $primaryFields, array $selectFields, $select, $updateFields)
prepareMergeValues(string $tableName, array $primaryFields, array $insertRows, array $updateFields=[])
prepareBinds(array $tableFields, array $fields)
getInsertIgnore($tableName, $fields, $sql)
prepareMerge($tableName, array $primaryFields, array $insertFields, array $updateFields)
getOrderByIntField(string $field, array $values, bool $quote=true)
getRowNumber($variableName)