Your IP : 13.58.41.115


Current Path : /var/www/www-root/data/webdav/www/monolith-realty.ru/bitrix/modules/main/lib/db/
Upload File :
Current File : /var/www/www-root/data/webdav/www/monolith-realty.ru/bitrix/modules/main/lib/db/pgsqlsqlhelper.php

<?php

namespace Bitrix\Main\DB;

use Bitrix\Main\Type;
use Bitrix\Main\ORM;
use Bitrix\Main\ORM\Fields\ScalarField;

class PgsqlSqlHelper extends SqlHelper
{
	/**
	 * @inheritdoc
	 */
	public function getLeftQuote()
	{
		return '"';
	}

	/**
	 * @inheritdoc
	 */
	public function getRightQuote()
	{
		return '"';
	}

	/**
	 * @inheritdoc
	 */
	public function getAliasLength()
	{
		return 63;
	}

	/**
	 * @inheritdoc
	 */
	public function quote($identifier)
	{
		return pg_escape_identifier($this->connection->getResource(), mb_strtolower($identifier));
	}

	/**
	 * @inheritdoc
	 */
	public function values($identifier)
	{
		return 'EXCLUDED.' . $this->quote($identifier);
	}

	/**
	 * @inheritdoc
	 */
	public function getQueryDelimiter()
	{
		return ';';
	}

	/**
	 * @inheritdoc
	 */
	public function forSql($value, $maxLength = 0)
	{
		if ($maxLength > 0)
		{
			$value = mb_substr($value, 0, $maxLength);
		}

		return pg_escape_string($this->connection->getResource(), $value);
	}

	/**
	 * @inheritdoc
	 */
	public function convertToDbBinary($value)
	{
		return "'" . pg_escape_bytea($value) . "'";
		//return "E'\\\\x".bin2hex($value) . "'";
		//return "decode('".bin2hex($value)."', 'hex')";
	}

	/**
	 * @inheritdoc
	 */
	public function getCurrentDateTimeFunction()
	{
		return 'now()';
	}

	/**
	 * @inheritdoc
	 */
	public function getCurrentDateFunction()
	{
		return 'current_date';
	}

	/**
	 * @inheritdoc
	 */
	public function addSecondsToDateTime($seconds, $from = null)
	{
		if ($from === null)
		{
			$from = static::getCurrentDateTimeFunction();
		}
		else
		{
			$from = $from . "::timestamp";
		}

		return $from . " + cast(" . $seconds . "||' second' as interval)";
	}

	/**
	 * @inheritdoc
	 */
	public function addDaysToDateTime($days, $from = null)
	{
		if ($from === null)
		{
			$from = static::getCurrentDateTimeFunction();
		}
		else
		{
			$from = $from . "::timestamp";
		}

		return '(' . $from . " + cast(" . $days . "||' day' as interval))";
	}

	/**
	 * @inheritdoc
	 */
	public function getDatetimeToDateFunction($value)
	{
		return 'cast('.$value.' as date)';
	}

	/**
	 * @inheritdoc
	 */
	public function formatDate($format, $field = null)
	{
		static $translation  = [
			'YYYY' => 'YYYY',
			'MMMM' => 'FMMonth',
			'MI' => 'MI',
			'HH' => 'HH24',
			'GG' => 'HH12',
			'TT' => 'PM',
			'M' => 'Mon',
			'H' => 'HH12',
			'G' => 'FMHH12',
			'T' => 'PM',
			'W' => 'D',
		];

		$dbFormat = '';
		foreach (preg_split('/(YYYY|MMMM|MM|MI|DD|HH|GG|SS|TT|M|H|G|T|W)/', $format, -1, PREG_SPLIT_DELIM_CAPTURE) as $part)
		{
			$dbFormat .= $translation[$part] ?? $part;
		}

		if ($field === null)
		{
			return $dbFormat;
		}
		else
		{
			return "to_char(".$field.", '".$dbFormat."')";
		}
	}

	/**
	 * @inheritdoc
	 */
	public function getRegexpOperator($field, $regexp)
	{
		return $field . ' ~ ' . $regexp;
	}

	/**
	 * @inheritdoc
	 */
	public function getConcatFunction()
	{
		return implode(" || ", func_get_args());
	}

	/**
	 * @inheritdoc
	 */
	public function getRandomFunction()
	{
		return 'random()';
	}

	/**
	 * @inheritdoc
	 */
	public function getSha1Function($field)
	{
		return "encode(digest(replace(" . $field . ", '\\', '\\\\')::bytea, 'sha1'), 'hex')";
	}

	/**
	 * @inheritdoc
	 */
	public function getIsNullFunction($expression, $result)
	{
		return "COALESCE(".$expression.", ".$result.")";
	}

	/**
	 * @inheritdoc
	 */
	public function getLengthFunction($field)
	{
		return "OCTET_LENGTH(".$field.")";
	}

	public function getMatchFunction($field, $value)
	{
		return "to_tsvector('english'::regconfig, " . $field . ") @@ to_tsquery('english'::regconfig, " . $value . ")";
	}

	public function getMatchAndExpression($values, $prefixSearch = false)
	{
		if ($prefixSearch)
		{
			foreach ($values as $i => $searchTerm)
			{
				$values[$i] = $searchTerm . ':*';
			}
		}
		return implode(' & ', $values);
	}

	public function getMatchOrExpression($values, $prefixSearch = false)
	{
		if ($prefixSearch)
		{
			foreach ($values as $i => $searchTerm)
			{
				$values[$i] = $searchTerm . ':*';
			}
		}
		return implode(' | ', $values);
	}

	/**
	 * @inheritdoc
	 */
	public function getCharToDateFunction($value)
	{
		return "timestamp '".$value."'";
	}

	/**
	 * @inheritdoc
	 */
	public function getDateToCharFunction($fieldName)
	{
		return "TO_CHAR(".$fieldName.", 'YYYY-MM-DD HH24:MI:SS')";
	}

	/**
	 * @inheritdoc
	 */
	public function getConverter(ScalarField $field)
	{
		if($field instanceof ORM\Fields\DatetimeField)
		{
			return array($this, "convertFromDbDateTime");
		}
		elseif($field instanceof ORM\Fields\DateField)
		{
			return array($this, "convertFromDbDate");
		}
		else
		{
			return parent::getConverter($field);
		}
	}

	/**
	 * @inheritdoc
	 */
	public function convertFromDbDateTime($value)
	{
		if($value !== null && $value != '0000-00-00 00:00:00')
		{
			return new Type\DateTime($value, "Y-m-d H:i:s");
		}

		return null;
	}

	/**
	 * @inheritdoc
	 */
	public function convertFromDbDate($value)
	{
		if($value !== null && $value != '0000-00-00')
		{
			return new Type\Date($value, "Y-m-d");
		}

		return null;
	}

	/**
	 * @inheritdoc
	 */
	public function castToChar($fieldName)
	{
		return 'CAST('.$fieldName.' AS varchar)';
	}

	/**
	 * @inheritdoc
	 */
	public function softCastTextToChar($fieldName)
	{
		return $fieldName;
	}

	/**
	 * @inheritdoc
	 */
	public function getColumnTypeByField(ScalarField $field)
	{
		if ($field instanceof ORM\Fields\IntegerField)
		{
			switch ($field->getSize())
			{
				case 2:
					return 'smallint';
				case 4:
					return 'integer';
				case 8:
					return 'bigint';
			}
			return 'integer';
		}
		elseif ($field instanceof ORM\Fields\DecimalField)
		{
			$defaultPrecision = 18;
			$defaultScale = 2;

			$precision = $field->getPrecision() > 0 ? $field->getPrecision() : $defaultPrecision;
			$scale = $field->getScale() > 0 ? $field->getScale() : $defaultScale;

			if ($scale >= $precision)
			{
				$precision = $defaultPrecision;
				$scale = $defaultScale;
			}

			return "decimal($precision, $scale)";
		}
		elseif ($field instanceof ORM\Fields\FloatField)
		{
			return 'float8';
		}
		elseif ($field instanceof ORM\Fields\DatetimeField)
		{
			return 'timestamp';
		}
		elseif ($field instanceof ORM\Fields\DateField)
		{
			return 'date';
		}
		elseif ($field instanceof ORM\Fields\TextField)
		{
			return 'text';
		}
		elseif ($field instanceof ORM\Fields\BooleanField)
		{
			$values = $field->getValues();

			if (preg_match('/^[0-9]+$/', $values[0]) && preg_match('/^[0-9]+$/', $values[1]))
			{
				return 'int';
			}
			else
			{
				return 'varchar('.max(mb_strlen($values[0]), mb_strlen($values[1])).')';
			}
		}
		elseif ($field instanceof ORM\Fields\EnumField)
		{
			return 'varchar('.max(array_map('strlen', $field->getValues())).')';
		}
		else
		{
			// string by default
			$defaultLength = false;
			foreach ($field->getValidators() as $validator)
			{
				if ($validator instanceof ORM\Fields\Validators\LengthValidator)
				{
					if ($defaultLength === false || $defaultLength > $validator->getMax())
					{
						$defaultLength = $validator->getMax();
					}
				}
			}
			return 'varchar('.($defaultLength > 0? $defaultLength: 255).')';
		}
	}

	/**
	 * @inheritdoc
	 */
	public function getFieldByColumnType($name, $type, array $parameters = null)
	{
		switch ($type)
		{
			case 'bigint':
			case 'int8':
			case 'bigserial':
			case 'serial8':
				$field = (new ORM\Fields\IntegerField($name))->configureSize(8);
				break;
			case 'integer':
			case 'int':
			case 'int4':
			case 'serial':
			case 'serial4':
				$field = (new ORM\Fields\IntegerField($name))->configureSize(4);
				break;
			case 'smallint':
			case 'int2':
			case 'smallserial':
			case 'serial2':
				$field = (new ORM\Fields\IntegerField($name))->configureSize(2);
				break;
			case 'double precision':
			case 'float4':
			case 'float8':
			case 'numeric':
			case 'decimal':
			case 'real':
				$field = new ORM\Fields\FloatField($name);
				break;
			case 'timestamp':
			case 'timestamp without time zone':
			case 'timestamptz':
			case 'timestamp with time zone':
				$field = new ORM\Fields\DatetimeField($name);
				break;
			case 'date':
				$field = new ORM\Fields\DateField($name);
				break;
			case 'bytea':
				$field = new ORM\Fields\StringField($name, ['binary' => true]);
				break;
			default:
				$field = new ORM\Fields\StringField($name);
		}

		$field->setConnection($this->connection);

		return $field;
	}

	/**
	 * @inheritdoc
	 */
	public function getTopSql($sql, $limit, $offset = 0)
	{
		$offset = intval($offset);
		$limit = intval($limit);

		if ($offset > 0 && $limit <= 0)
		{
			throw new \Bitrix\Main\ArgumentException("Limit must be set if offset is set");
		}

		if ($limit > 0)
		{
			$sql .= "\nLIMIT ".$limit;
		}

		if ($offset > 0)
		{
			$sql .= " OFFSET ".$offset;
		}

		$sql .= "\n";

		return $sql;
	}

	/**
	 * @inheritdoc
	 */
	public function getInsertIgnore($tableName, $fields, $sql)
	{
		return 'INSERT INTO ' . $tableName . $fields . $sql . ' ON CONFLICT DO NOTHING';
	}

	/**
	 * @inheritdoc
	 */
	public function getAscendingOrder()
	{
		return 'ASC NULLS FIRST';
	}

	/**
	 * @inheritdoc
	 */
	public function getDescendingOrder()
	{
		return 'DESC NULLS LAST';
	}

	/**
	 * @inheritdoc
	 */
	public function prepareMerge($tableName, array $primaryFields, array $insertFields, array $updateFields)
	{
		$insert = $this->prepareInsert($tableName, $insertFields);
		$update = $this->prepareUpdate($tableName, $updateFields);

		if (
			$insert && $insert[0] != "" && $insert[1] != ""
			&& $update
			&& $primaryFields
		)
		{
			$sql = 'INSERT INTO ' . $this->quote($tableName) . ' ('.$insert[0].')
				VALUES (' . $insert[1] . ')
				ON CONFLICT (' . implode(',', array_map([$this, 'quote'], $primaryFields)) . ')
				DO UPDATE SET ' . $update[0];
		}
		else
		{
			$sql = "";
		}

		return array(
			$sql
		);

	}

	/**
	 * @inheritdoc
	 */
	public function prepareMergeMultiple($tableName, array $primaryFields, array $insertRows)
	{
		$result = [];
		$head = '';
		$tail = '';
		$maxBodySize = 1024*1024; //1 Mb
		$body = [];
		$bodySize = 0;
		foreach ($insertRows as $insertFields)
		{
			$insert = $this->prepareInsert($tableName, $insertFields, true);
			if (!$head && $insert && $insert[0])
			{
				$head = 'INSERT INTO ' . $this->quote($tableName) . ' (' . implode(', ', $insert[0]) . ') VALUES ';
				$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])) . ')';
			}
			if ($insert && $insert[1])
			{
				$values = '(' . implode(', ', $insert[1]) . ')';
				$bodySize += mb_strlen($values) + 4;
				$body[] = $values;
				if ($bodySize > $maxBodySize)
				{
					$result[] = $head.implode(', ', $body).$tail;
					$body = [];
					$bodySize = 0;
				}
			}
		}
		if ($body)
		{
			$result[] = $head.implode(', ', $body).$tail;
		}

		return $result;
	}

	/**
	 * @inheritdoc
	 */
	public function prepareMergeSelect($tableName, array $primaryFields, array $selectFields, $select, $updateFields)
	{
		$updateColumns = [];
		$updateValues = [];

		$tableFields = $this->connection->getTableFields($tableName);
		// one registry
		$tableFields = array_change_key_case($tableFields, CASE_UPPER);
		$updateFields = array_change_key_case($updateFields, CASE_UPPER);
		foreach ($updateFields as $columnName => $value)
		{
			if (isset($tableFields[$columnName]))
			{
				$updateColumns[] = $this->quote($columnName);
				$updateValues[] = $this->convertToDb($value, $tableFields[$columnName]);
			}
			else
			{
				trigger_error("Column `{$columnName}` is not found in the `{$tableName}` table", E_USER_WARNING);
			}
		}

		$sql = 'INSERT INTO ' . $this->quote($tableName) . ' (' . implode(',', array_map([$this, 'quote'], $selectFields)) . ') ';
		$sql .= $select;
		$sql .= ' ON CONFLICT (' . implode(',', array_map([$this, 'quote'], $primaryFields)) . ') DO UPDATE SET ';
		if (count($updateColumns) === 1)
		{
			$sql .=  $updateColumns[0] . ' = ' . $updateValues[0];
		}
		else
		{
			$sql .= ' (' . implode(', ', $updateColumns) . ') = (' . implode(', ', $updateValues) . ')';
		}

		return $sql;
	}

	/**
	 * @inheritdoc
	 */
	public function prepareDeleteLimit($tableName, array $primaryFields, $where, array $order, $limit)
	{
		$primaryColumns = [];
		foreach ($primaryFields as $columnName)
		{
			$primaryColumns[] = $this->quote($columnName);
		}
		$sqlPrimary = implode(', ', $primaryColumns);

		$orderColumns = [];
		foreach ($order as $columnName => $sort)
		{
			$orderColumns[] = $this->quote($columnName) . ' ' . $sort;
		}
		$sqlOrder = $orderColumns ? ' ORDER BY ' . implode(', ', $orderColumns) : '';
		return 'DELETE FROM ' . $this->quote($tableName) . ' WHERE (' . $sqlPrimary . ') IN (SELECT ' . $sqlPrimary . ' FROM ' . $this->quote($tableName) . ' WHERE ' . $where . $sqlOrder . ' LIMIT ' . intval($limit) . ')';
	}

	public function initRowNumber($variableName)
	{
		return '';
	}

	public function getRowNumber($variableName)
	{
		return 'row_number() over()';
	}

	/**
	 * @inheritdoc
	 */
	public function prepareCorrelatedUpdate($tableName, $tableAlias, $fields, $from, $where)
	{
		$dml = "UPDATE " . $tableName . ' AS ' . $tableAlias . " SET\n";

		$set = '';
		foreach ($fields as $fieldName => $fieldValue)
		{
			$set .= ($set ? ',' : '') . $fieldName . ' = ' .$fieldValue . "\n";
		}
		$dml .= $set;
		$dml .= 'FROM ' . $from . "\n";
		$dml .= 'WHERE ' . $where . "\n";

		return $dml;
	}

	protected function getOrderByField(string $field, array $values, callable $callback, bool $quote = true): string
	{
		$field = $quote ? $this->quote($field) : $field;
		$values = implode(',', array_map($callback, $values));

		return "array_position(ARRAY[{$values}], {$field})";
	}
}