28 if (($pos = strpos($host,
":")) !==
false)
30 $port = intval(substr($host, $pos + 1));
31 $host = substr($host, 0, $pos);
34 $connectionString =
" host='" . addslashes($host) .
"'";
37 $connectionString .=
" port='" . addslashes($port) .
"'";
39 $connectionString .=
" dbname='" . addslashes($this->database) .
"'";
40 $connectionString .=
" user='" . addslashes($this->login) .
"'";
41 $connectionString .=
" password='" . addslashes($this->password) .
"'";
43 if (isset($this->configuration[
'charset']))
45 $connectionString .=
" options='--client_encoding=" . $this->configuration[
'charset'] .
"'";
48 set_error_handler([$this,
'connectionErrorHandler']);
49 if ($this->options & self::PERSISTENT)
51 $connection = @pg_pconnect($connectionString);
55 $connection = @pg_connect($connectionString);
57 restore_error_handler();
62 'Pgsql connect error ['.$this->host.
']',
63 error_get_last()[
'message']
67 $this->resource = $connection;
70 $this->afterConnected();
80 pg_close($this->resource);
81 $this->resource =
null;
103 $trackerQuery?->startQuery($sql, $binds);
105 $result = pg_query($this->resource, $sql);
107 $trackerQuery?->finishQuery();
109 $this->lastQueryResult = $result;
124 return new PgsqlResult($result, $this, $trackerQuery);
130 public function add($tableName, array $data, $identity =
"ID")
132 $insert = $this->getSqlHelper()->prepareInsert($tableName, $data);
136 !isset($data[$identity])
141 $sql =
"INSERT INTO ".$tableName.
"(".$insert[0].
") VALUES (".$insert[1].
") RETURNING ".$identity;
142 $row = $this->query($sql)->fetch();
143 return array_shift($row);
147 $sql =
"INSERT INTO ".$tableName.
"(".$insert[0].
") VALUES (".$insert[1].
")";
149 return $data[$identity];
160 return (
int)$this->query(
'SELECT bx_lastval() as X')->fetch()[
'X'];
173 return pg_affected_rows($this->lastQueryResult);
181 $result = $this->query(
"
184 WHERE schemaname = 'public'
185 AND tablename = '".$this->getSqlHelper()->forSql($tableName).
"'
187 $row = $result->fetch();
188 return is_array($row);
195 return $this->
getIndexName($tableName, $columns) !==
null;
200 public function getIndexName($tableName, array $columns, $strict =
false)
209 SELECT a.attnum, a.attname
211 LEFT JOIN pg_attribute a ON a.attrelid = t.oid
212 WHERE t.relname = '".$this->getSqlHelper()->forSql($tableName).
"'
214 while ($a = $r->fetch())
218 $tableColumns[$a[
'ATTNUM']] = $a[
'ATTNAME'];
223 SELECT relname, indkey, pg_get_expr(pg_index.indexprs, pg_index.indrelid) full_text
224 FROM pg_class, pg_index
225 WHERE pg_class.oid = pg_index.indexrelid
226 AND pg_class.oid IN (
228 FROM pg_index, pg_class
229 WHERE pg_class.relname = '".$this->getSqlHelper()->forSql($tableName).
"'
230 AND pg_class.oid = pg_index.indrelid
234 while ($a = $r->fetch())
236 $indexes[$a[
'RELNAME']] = [];
240 if (preg_match_all(
'/,\s*([a-z0-9_]+)/i', $a[
'FULL_TEXT'], $match))
242 foreach ($match[1] as $i => $colName)
244 $indexes[$a[
'RELNAME']][$i] = mb_strtoupper($colName);
250 foreach (explode(
' ', $a[
'INDKEY']) as $i => $indkey)
252 $indexes[$a[
'RELNAME']][$i] = mb_strtoupper($tableColumns[$indkey]);
257 return static::findIndex($indexes, $columns, $strict);
260 protected static function findIndex(array $indexes, array $columns, $strict)
262 $columnsList = mb_strtolower(implode(
",", $columns));
264 foreach ($indexes as $indexName => $indexColumns)
266 ksort($indexColumns);
267 $indexColumnList = mb_strtolower(implode(
",", $indexColumns));
270 if ($indexColumnList === $columnsList)
277 if (str_starts_with($indexColumnList, $columnsList))
292 if (!isset($this->tableColumnsCache[$tableName]) || empty($this->tableColumnsCache[$tableName]))
296 $sqlHelper = $this->getSqlHelper();
297 $query = $this->query(
"
301 character_maximum_length
303 information_schema.columns
305 table_catalog = '" . $sqlHelper->forSql($this->getDatabase()) .
"'
306 and table_schema = 'public'
307 and table_name = '" . $sqlHelper->forSql(mb_strtolower($tableName)) .
"'
312 $this->tableColumnsCache[$tableName] = [];
313 while ($fieldInfo = $query->fetch())
315 $fieldName = mb_strtoupper($fieldInfo[
'COLUMN_NAME']);
316 $fieldType = $fieldInfo[
'DATA_TYPE'];
317 $field = $sqlHelper->getFieldByColumnType($fieldName, $fieldType);
319 $fieldInfo[
'CHARACTER_MAXIMUM_LENGTH']
320 && is_a($field,
'\Bitrix\Main\ORM\Fields\StringField')
323 $field->configureSize($fieldInfo[
'CHARACTER_MAXIMUM_LENGTH']);
326 $this->tableColumnsCache[$tableName][$fieldName] = $field;
330 return $this->tableColumnsCache[$tableName];
336 public function createTable($tableName, $fields, $primary = array(), $autoincrement = array())
338 $sql =
'CREATE TABLE '.$this->getSqlHelper()->quote($tableName).
' (';
339 $sqlFields = array();
341 foreach ($fields as $columnName => $field)
346 'Field `%s` should be an Entity\ScalarField instance', $columnName
350 $realColumnName = $field->getColumnName();
352 if (in_array($columnName, $autoincrement,
true))
354 $type =
'INT GENERATED BY DEFAULT AS IDENTITY';
358 switch ($field->getSize())
361 $type =
'SMALLINT GENERATED BY DEFAULT AS IDENTITY';
364 $type =
'BIGINT GENERATED BY DEFAULT AS IDENTITY';
371 $type = $this->getSqlHelper()->getColumnTypeByField($field);
373 $sqlFields[] = $this->getSqlHelper()->quote($realColumnName)
375 . ($field->isNullable() ?
'' :
' NOT NULL')
379 $sql .= join(
', ', $sqlFields);
381 if (!empty($primary))
383 foreach ($primary as &$primaryColumn)
385 $realColumnName = $fields[$primaryColumn]->getColumnName();
386 $primaryColumn = $this->getSqlHelper()->quote($realColumnName);
389 $sql .=
', PRIMARY KEY('.join(
', ', $primary).
')';
401 public function createIndex($tableName, $indexName, $columnNames, $columnLengths =
null, $indexType =
null)
403 if (!is_array($columnNames))
405 $columnNames = array($columnNames);
408 $sqlHelper = $this->getSqlHelper();
410 foreach ($columnNames as &$columnName)
412 $columnName = $sqlHelper->quote($columnName);
416 if ($indexType === static::INDEX_UNIQUE)
418 return $this->query(
'CREATE UNIQUE INDEX ' . $sqlHelper->quote($indexName) .
' ON ' . $sqlHelper->quote($tableName) .
'(' . implode(
',', $columnNames) .
')');
420 elseif ($indexType === static::INDEX_FULLTEXT)
422 return $this->query(
'CREATE INDEX ' . $sqlHelper->quote($indexName) .
' ON ' . $sqlHelper->quote($tableName) .
' USING GIN (to_tsvector(\'english\', ' . implode(
',', $columnNames) .
'))');
426 return $this->query(
'CREATE INDEX ' . $sqlHelper->quote($indexName) .
' ON ' . $sqlHelper->quote($tableName) .
'(' . implode(
',', $columnNames) .
')');
435 $this->query(
'ALTER TABLE '.$this->getSqlHelper()->quote($currentName).
' RENAME TO '.$this->getSqlHelper()->quote($newName));
443 $this->query(
'DROP TABLE '.$this->getSqlHelper()->quote($tableName));
451 if ($this->transactionLevel == 0)
453 $this->query(
"START TRANSACTION");
457 $this->query(
"SAVEPOINT TRANS{$this->transactionLevel}");
460 $this->transactionLevel++;
468 $this->transactionLevel--;
470 if ($this->transactionLevel < 0)
475 if ($this->transactionLevel == 0)
478 $this->query(
"COMMIT");
487 $this->transactionLevel--;
489 if ($this->transactionLevel < 0)
494 if ($this->transactionLevel == 0)
496 $this->query(
"ROLLBACK");
500 $this->query(
"ROLLBACK TO SAVEPOINT TRANS{$this->transactionLevel}");
511 public function lock($name, $timeout = 0)
513 $timeout = (int)$timeout;
516 $sql =
'SELECT bx_get_lock(' . $name .
', ' . $timeout .
') as L';
517 $lock = $this->query($sql)->fetch();
519 return ($lock[
'L'] == 0);
529 $sql =
'SELECT bx_release_lock(' . $name .
') as L';
530 $lock = $this->query($sql)->fetch();
532 return ($lock[
'L'] == 0);
537 $unique = \CMain::GetServerUniqID();
539 return crc32($unique .
'|' . $name);
555 if ($this->version ==
null)
558 $version = trim(pg_version($this->resource)[
'server']);
560 preg_match(
"#^.*?([0-9]+\\.[0-9]+)#", $version, $ar);
561 $this->version = $ar[1];
564 return array($this->version,
null);
572 return pg_last_error($this->resource);
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)
renameTable($currentName, $newName)
isIndexExists($tableName, array $columns)
createIndex($tableName, $indexName, $columnNames, $columnLengths=null, $indexType=null)
add($tableName, array $data, $identity="ID")
createTable($tableName, $fields, $primary=array(), $autoincrement=array())