3namespace Bitrix\Main\DB;
6use Bitrix\Main\ArgumentException;
7use Bitrix\Main\ORM\Fields\ScalarField;
8use Bitrix\Main\ORM\Fields\IntegerField;
32 if (($pos = strpos(
$host,
":")) !==
false)
34 $port = intval(substr(
$host, $pos + 1));
38 $connectionString =
" host='" . addslashes(
$host) .
"'";
41 $connectionString .=
" port='" . addslashes($port) .
"'";
43 $connectionString .=
" dbname='" . addslashes($this->database) .
"'";
44 $connectionString .=
" user='" . addslashes($this->login) .
"'";
45 $connectionString .=
" password='" . addslashes($this->password) .
"'";
47 if (isset($this->configuration[
'charset']))
49 $connectionString .=
" options='--client_encoding=" . $this->configuration[
'charset'] .
"'";
52 set_error_handler([$this,
'connectionErrorHandler']);
55 if ($this->isPersistent())
66 restore_error_handler();
72 'Pgsql connect error [' . $this->host .
']',
73 error_get_last()[
'message']
81 $this->afterConnected();
91 pg_close($this->resource);
92 $this->resource =
null;
114 $trackerQuery?->startQuery($sql, $binds);
117 set_error_handler(
function () {
121 $result = pg_query($this->resource, $sql);
123 restore_error_handler();
125 $trackerQuery?->finishQuery();
127 $this->lastQueryResult =
$result;
150 $insert = $this->getSqlHelper()->prepareInsert($tableName,
$data);
154 !isset(
$data[$identity])
159 $sql =
"INSERT INTO " . $tableName .
"(" . $insert[0] .
") VALUES (" . $insert[1] .
") RETURNING " . $identity;
160 $row = $this->query($sql)->fetch();
161 return intval(array_shift($row));
165 $sql =
"INSERT INTO " . $tableName .
"(" . $insert[0] .
") VALUES (" . $insert[1] .
")";
167 return $data[$identity] ??
null;
178 return (
int)$this->query(
'SELECT bx_lastval() as X')->fetch()[
'X'];
191 return pg_affected_rows($this->lastQueryResult);
202 WHERE schemaname = 'public'
203 AND tablename = '" . $this->getSqlHelper()->forSql($tableName) .
"'
206 return is_array($row);
214 return $this->
getIndexName($tableName, $columns) !==
null;
229 SELECT a.attnum, a.attname
231 LEFT JOIN pg_attribute a ON a.attrelid = t.oid
232 WHERE t.relname = '" . $this->getSqlHelper()->forSql($tableName) .
"'
234 while (
$a = $r->fetch())
236 if (
$a[
'ATTNUM'] > 0)
238 $tableColumns[
$a[
'ATTNUM']] =
$a[
'ATTNAME'];
243 SELECT relname, indkey, pg_get_expr(pg_index.indexprs, pg_index.indrelid) full_text
244 FROM pg_class, pg_index
245 WHERE pg_class.oid = pg_index.indexrelid
246 AND pg_class.oid IN (
248 FROM pg_index, pg_class
249 WHERE pg_class.relname = '" . $this->getSqlHelper()->forSql($tableName) .
"'
250 AND pg_class.oid = pg_index.indrelid
254 while (
$a = $r->fetch())
256 $indexes[
$a[
'RELNAME']] = [];
260 if (preg_match_all(
'/,\s*\(?([a-z0-9_]+)(?:\)::text)?/i',
$a[
'FULL_TEXT'], $match))
262 foreach ($match[1] as
$i => $colName)
264 $indexes[
$a[
'RELNAME']][
$i] = mb_strtoupper($colName);
270 foreach (explode(
' ',
$a[
'INDKEY']) as
$i => $indkey)
272 $indexes[
$a[
'RELNAME']][
$i] = mb_strtoupper($tableColumns[$indkey]);
277 return static::findIndex($indexes, $columns, $strict);
282 $columnsList = mb_strtolower(implode(
",", $columns));
284 foreach ($indexes as $indexName => $indexColumns)
286 ksort($indexColumns);
287 $indexColumnList = mb_strtolower(implode(
",", $indexColumns));
290 if ($indexColumnList === $columnsList)
297 if (str_starts_with($indexColumnList, $columnsList))
309 $sqlHelper = $this->getSqlHelper();
310 $fullTextColumns = [];
313 SELECT relname, indkey, pg_get_expr(pg_index.indexprs, pg_index.indrelid) full_text
314 FROM pg_class, pg_index
315 WHERE pg_class.oid = pg_index.indexrelid
316 AND pg_class.oid IN (
318 FROM pg_index, pg_class
319 WHERE pg_class.relname = '" . $sqlHelper->forSql(mb_strtolower($tableName)) .
"'
320 AND pg_class.oid = pg_index.indrelid
323 $res = $this->query($sql);
324 while ($row =
$res->fetch())
326 if ($row[
'FULL_TEXT'])
329 if (preg_match_all(
'/,\s*\(?([a-z0-9_]+)(?:\)::text)?/i', $row[
'FULL_TEXT'], $match))
331 foreach ($match[1] as
$i => $colName)
333 $fullTextColumns[mb_strtoupper($colName)] =
true;
339 return $fullTextColumns;
347 if (!isset($this->tableColumnsCache[$tableName]) || empty($this->tableColumnsCache[$tableName]))
351 $sqlHelper = $this->getSqlHelper();
359 character_maximum_length
361 information_schema.columns
363 table_catalog = '" . $sqlHelper->forSql($this->getDatabase()) .
"'
364 and table_schema = 'public'
365 and table_name = '" . $sqlHelper->forSql(mb_strtolower($tableName)) .
"'
370 $this->tableColumnsCache[$tableName] = [];
371 while ($fieldInfo =
$query->fetch())
373 $fieldName = mb_strtoupper($fieldInfo[
'COLUMN_NAME']);
374 $fieldType = $fieldInfo[
'DATA_TYPE'];
375 $field = $sqlHelper->getFieldByColumnType($fieldName, $fieldType);
376 if (is_a($field,
'\Bitrix\Main\ORM\Fields\StringField'))
378 if ($fieldInfo[
'CHARACTER_MAXIMUM_LENGTH'])
380 $field->configureSize($fieldInfo[
'CHARACTER_MAXIMUM_LENGTH']);
383 if (array_key_exists($fieldName, $fullTextColumns))
385 $field->configureFulltext(
true);
389 $this->tableColumnsCache[$tableName][$fieldName] = $field;
393 return $this->tableColumnsCache[$tableName];
401 $sql =
'CREATE TABLE IF NOT EXISTS ' . $this->getSqlHelper()->quote($tableName) .
' (';
404 foreach (
$fields as $columnName => $field)
409 'Field `%s` should be an Entity\ScalarField instance', $columnName
413 $realColumnName = $field->getColumnName();
415 if (in_array($columnName, $autoincrement,
true))
417 $type =
'INT GENERATED BY DEFAULT AS IDENTITY';
421 switch ($field->getSize())
424 $type =
'SMALLINT GENERATED BY DEFAULT AS IDENTITY';
427 $type =
'BIGINT GENERATED BY DEFAULT AS IDENTITY';
434 $type = $this->getSqlHelper()->getColumnTypeByField($field);
436 $sqlFields[] = $this->getSqlHelper()->quote($realColumnName)
438 . ($field->isNullable() ?
'' :
' NOT NULL');
441 $sql .= join(
', ', $sqlFields);
443 if (!empty($primary))
445 foreach ($primary as &$primaryColumn)
447 $realColumnName =
$fields[$primaryColumn]->getColumnName();
448 $primaryColumn = $this->getSqlHelper()->quote($realColumnName);
451 $sql .=
', PRIMARY KEY(' . join(
', ', $primary) .
')';
462 public function createIndex($tableName, $indexName, $columnNames, $columnLengths =
null, $indexType =
null)
464 if (!is_array($columnNames))
466 $columnNames = [$columnNames];
469 $sqlHelper = $this->getSqlHelper();
471 foreach ($columnNames as &$columnName)
473 $columnName = $sqlHelper->quote($columnName);
477 if ($indexType === static::INDEX_UNIQUE)
479 return $this->query(
'CREATE UNIQUE INDEX IF NOT EXISTS ' . $sqlHelper->quote($indexName) .
' ON ' . $sqlHelper->quote($tableName) .
'(' . implode(
',', $columnNames) .
')');
481 elseif ($indexType === static::INDEX_FULLTEXT)
483 return $this->query(
'CREATE INDEX IF NOT EXISTS ' . $sqlHelper->quote($indexName) .
' ON ' . $sqlHelper->quote($tableName) .
' USING GIN (to_tsvector(\'english\', ' . implode(
',', $columnNames) .
'))');
487 return $this->query(
'CREATE INDEX IF NOT EXISTS ' . $sqlHelper->quote($indexName) .
' ON ' . $sqlHelper->quote($tableName) .
'(' . implode(
',', $columnNames) .
')');
496 $this->query(
'ALTER TABLE ' . $this->getSqlHelper()->quote($currentName) .
' RENAME TO ' . $this->getSqlHelper()->quote($newName));
504 $this->query(
'DROP TABLE ' . $this->getSqlHelper()->quote($tableName));
512 if ($this->transactionLevel == 0)
514 $this->query(
"START TRANSACTION");
518 $this->query(
"SAVEPOINT TRANS{$this->transactionLevel}");
521 $this->transactionLevel++;
529 $this->transactionLevel--;
531 if ($this->transactionLevel < 0)
536 if ($this->transactionLevel == 0)
539 $this->query(
"COMMIT");
548 $this->transactionLevel--;
550 if ($this->transactionLevel < 0)
555 if ($this->transactionLevel == 0)
557 $this->query(
"ROLLBACK");
561 $this->query(
"ROLLBACK TO SAVEPOINT TRANS{$this->transactionLevel}");
574 $timeout = (int)$timeout;
577 $sql =
'SELECT bx_get_lock(' .
$name .
', ' . $timeout .
') as L';
578 $lock = $this->query($sql)->fetch();
580 return ($lock[
'L'] == 0);
590 $sql =
'SELECT bx_release_lock(' .
$name .
') as L';
591 $lock = $this->query($sql)->fetch();
593 return ($lock[
'L'] == 0);
598 $unique = \CMain::GetServerUniqID();
600 return crc32($unique .
'|' .
$name);
619 $version = trim(pg_version($this->resource)[
'server']);
621 preg_match(
"#^.*?([0-9]+\\.[0-9]+)#", $version,
$ar);
625 return [$this->version,
null];
633 return pg_last_error($this->resource);
650 pg_set_error_verbosity($this->resource, PGSQL_ERRORS_VERBOSE);
658 if (
$code ==
'23505')
isTableExists($tableName)
static findIndex(array $indexes, array $columns, $strict)
connectionErrorHandler($errno, $errstr, $errfile='', $errline=0, $errcontext=null)
getIndexName($tableName, array $columns, $strict=false)
getTableFields($tableName)
createResult($result, Diag\SqlTrackerQuery $trackerQuery=null)
queryInternal($sql, array $binds=null, Diag\SqlTrackerQuery $trackerQuery=null)
configureErrorVerbosity()
renameTable($currentName, $newName)
isIndexExists($tableName, array $columns)
createIndex($tableName, $indexName, $columnNames, $columnLengths=null, $indexType=null)
createQueryException($code='', $databaseMessage='', $query='')
add($tableName, array $data, $identity="ID")
getTableFullTextFields($tableName)
createTable($tableName, $fields, $primary=[], $autoincrement=[])
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)