Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 128 · С нуля → реальные задачи → полный справочник

PostgreSQL в PHP: от первой таблицы до полного справочника ext/pgsql

Полная официальная ветка ext/pgsql: установка и подключение, CRUD, параметры, результаты, транзакции, COPY, асинхронная работа, ошибки, уведомления и large objects — с 98 функциями и выполняемыми примерами.

PostgreSQL для PHP: зачем база данных и какой результат получит новичок

Интернет-магазину нужно помнить товары и заказы после завершения PHP-скрипта. Массив живёт только во время запроса, а PostgreSQL хранит строки между запросами, проверяет ограничения и согласует одновременную работу нескольких пользователей. PHP принимает HTTP-запрос и ведёт сценарий; PostgreSQL отвечает за долговременные данные; расширение ext/pgsql передаёт команды и результаты между ними.

Первый запрос без скрытых шагов
  1. pg_connect('') просит libpq взять PGHOST/PGPORT/PGDATABASE/PGUSER/PGPASSWORD из окружения и возвращает $db
  2. проверка $db === false останавливает пример с кодом 1, если соединение не создано
  3. PHP-код вызывает pg_query и передаёт явный объект соединения $db
  4. ext/pgsql через клиентскую библиотеку libpq отправляет текст SELECT серверу
  5. PostgreSQL разбирает запрос, вычисляет 2 + 3 и формирует строку total = 5
  6. pg_query возвращает PgSql\Result либо false; вторая проверка останавливает пример на SQL-ошибке
  7. pg_fetch_assoc превращает строку ответа в PHP-массив; echo показывает значение

Первые две строки листинга открывают PHP-код и включают строгую передачу scalar-типов в этом файле; на сам SQL это не влияет. pg_connect('') — вызов функции: пустая connection string не содержит секрет, поэтому libpq читает заранее заданные переменные окружения. $db = сохраняет результат. === false отличает именно отказ; фигурные скобки ограничивают действие if, а exit(1) завершает CLI-пример с ненулевым кодом. Затем $result = ...; сохраняет ответ запроса. У pg_query тоже есть договор PgSql\Result|false, поэтому код проверяет второй отказ до pg_fetch_assoc; exit(2) отличает его от отказа подключения. Запятая разделяет аргументы, точка с запятой завершает инструкцию, а ['total'] читает элемент массива по имени столбца. Подробные основы: переменная и присваивание, условие if, вызов и return, ключи массива.

Получить один видимый результат из PostgreSQLПример для указанного окружения
<?php
declare(strict_types=1);

$db = pg_connect('');
if ($db === false) {
    echo "Нет соединения\n";
    exit(1);
}
$result = pg_query($db, 'SELECT 2 + 3 AS total');
if ($result === false) {
    echo "Запрос не выполнен\n";
    exit(2);
}
$row = pg_fetch_assoc($result);
echo 'Итого: ', $row['total'], "\n";
Ожидаемый результат
Итого: 5

PHP открыл соединение по PGHOST/PGPORT/PGDATABASE/PGUSER/PGPASSWORD из окружения, отправил SQL, получил PgSql\Result, взял поле total и напечатал его. Пример реально выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql; секретов в листинге нет.

Если нужноНачните здесь
Установить и проверить слой PostgreSQLТребования → установка → настройка подключения
Сделать первую таблицуБазовый маршрут CRUD ниже
Безопасно принять значение формыpg_query_params()
Найти конкретную функциюКарта задач и полное дерево в конце главы

Требования: PHP, ext/pgsql, libpq и сервер — четыре разных слоя

Сообщение «Call to undefined function pg_connect» означает отсутствие/неактивность расширения в том SAPI, где выполняется код. «Connection refused» означает, что функция уже есть, но сервер не принимает соединение по указанному адресу. Ошибка аутентификации приходит ещё позже: сеть и сервер доступны, реквизиты не приняты. Эти отказы исправляются в разных местах.

СлойЧто делаетКак проверить
PHP/SAPIЗапускает файл: CLI, FPM, Apache могут читать разные ini.PHP_VERSION, PHP_SAPI, php --ini.
ext/pgsqlДаёт функции pg_* и классы PgSql\Connection/Result/Lob.extension_loaded('pgsql'), php --ri pgsql.
libpqКлиентская C-библиотека, через которую расширение говорит с сервером.PGSQL_LIBPQ_VERSION; часть API зависит от версии сборки.
PostgreSQL serverХранит таблицы, исполняет SQL и транзакции.Успешное соединение и pg_version($db)['server'].

Официальная ветка PHP требует libpq 9.1+ для PHP 8.0–8.3 и libpq 10+ начиная с PHP 8.4. Наличие нового PostgreSQL-сервера не обновляет libpq внутри уже собранной DLL. Поэтому, например, pg_set_chunked_rows_size() может отсутствовать при PHP той же версии.

Проверить четыре слоя без раскрытия пароляПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
echo 'PHP: ', PHP_VERSION, "\n";
echo 'ext/pgsql: ', extension_loaded('pgsql') ? 'да' : 'нет', "\n";
echo 'libpq >= 10: ', version_compare(PGSQL_LIBPQ_VERSION, '10.0', '>=') ? 'да' : 'нет', "\n";
$version = pg_version($db);
echo 'сервер отвечает: ', isset($version['server']) ? 'да' : 'нет', "\n";
Ожидаемый результат
PHP: 8.5.11
ext/pgsql: да
libpq >= 10: да
сервер отвечает: да

Проверка различает версию PHP, наличие расширения, клиентскую библиотеку и реальный ответ сервера. Она не печатает connection string и пароль. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Установка ext/pgsql и проверка именно нужного SAPI

При сборке PHP из исходников PostgreSQL включают параметром --with-pgsql[=DIR]. В пакетных системах обычно устанавливают pgsql-модуль той же версии PHP; в Windows активируют совместимую php_pgsql.dll строкой extension=pgsql в том ini, который читает нужный SAPI. Конкретное имя пакета зависит от ОС и репозитория, поэтому его сверяют с документацией дистрибутива.

После изменения ini перезапускают долгоживущий FPM/Apache worker. Проверка php -m относится к CLI и не доказывает, что модуль появился в FPM. Для web-процесса используйте временную закрытую диагностическую точку с extension_loaded, затем удалите её. dl() из официального описания не является обычным способом развёртывания: функция ограничена поддерживаемыми SAPI, сборкой и настройкой enable_dl; в штатной установке расширение подключают через ini и перезапускают процесс.

Команды диагностики, а не runtime-тест базы:

php --ini
php --ri pgsql
php -r "var_dump(extension_loaded('pgsql'));"
Проверить регистрацию расширения в текущем PHPСамостоятельный пример
<?php
declare(strict_types=1);

echo extension_loaded('pgsql') ? "Расширение включено\n" : "Расширение не включено\n";
echo function_exists('pg_connect') ? "pg_connect доступна\n" : "pg_connect отсутствует\n";
Ожидаемый результат
Расширение включено
pg_connect доступна

Обе строки проверяют только регистрацию ext/pgsql в текущем PHP-процессе. Реальное соединение отдельно проверено в разделе требований: оно добавляет сеть, сервер, базу, роль и пароль.

Настроить соединение: безопасные реквизиты, отдельная роль и явный объект $db

Connection string содержит host, port, dbname и user; пароль libpq может взять из переменной окружения PGPASSWORD или защищённого password-file. В учебном стенде bootstrap читает PGHOST, PGPORT, PGDATABASE, PGUSER, а пароль остаётся в окружении процесса. В приложении секрет лучше выдаёт secret manager платформы. Его не помещают в Git, URL, HTML, exception для посетителя или отладочный дамп.

Подготовка до первого SQL
  1. Администратор создаёт отдельную базу и роль с минимальными правами
  2. Среда запуска передаёт PHP host, port, dbname, user и секрет
  3. Код проверяет наличие настроек, но не печатает их значения
  4. pg_connect возвращает явный PgSql\Connection или код обрабатывает отказ
  5. Каждая следующая pg_* функция получает этот $db первым аргументом
Остановиться с понятной ошибкой, если настройка не переданаСамостоятельный пример
<?php
declare(strict_types=1);

putenv('PHP_MANUAL_DEMO_MISSING');
function requiredSetting(string $name): string {
    $value = getenv($name);
    if ($value === false || $value === '') {
        throw new RuntimeException("Не задана настройка {$name}");
    }
    return $value;
}
try { requiredSetting('PHP_MANUAL_DEMO_MISSING'); }
catch (RuntimeException $error) { echo $error->getMessage(), "\n"; }
Ожидаемый результат
Не задана настройка PHP_MANUAL_DEMO_MISSING

Пример не маскирует отсутствие значения fallback-паролем. В реальном сервисе текст пишут в закрытый deploy-log, а посетителю возвращают общий ответ.

Проверить базу и роль без вывода секретаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT current_database() AS db, current_user AS role');
$row = pg_fetch_assoc($result);
echo $row['db'], " / ", $row['role'], "\n";
Ожидаемый результат
php_manual / php_manual_user

Имя базы и роли допустимы в локальной диагностике; пароль не входит в SELECT и не печатается. На production даже эти сведения не отдают публичному посетителю. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

У расширения исторически есть неявное «последнее открытое» соединение, но его использование deprecated с PHP 8.1. Явный $db показывает, к какой базе относится запрос, и не меняет смысл при появлении второй интеграции.

Runtime Configuration: все шесть директив pgsql

Эти настройки управляют расширением в PHP-процессе. Они не заменяют postgresql.conf, pg_hba.conf, лимиты пула и права роли.

ДирективаDefault / уровеньПрактический договор
pgsql.allow_persistent1 / INI_SYSTEMРазрешает persistent connections; отключение не закрывает уже созданную архитектурную потребность в пуле.
pgsql.max_persistent-1 / INI_SYSTEMМаксимум persistent-соединений на PHP-процесс; -1 снимает лимит расширения.
pgsql.max_links-1 / INI_SYSTEMВсе соединения на PHP-процесс, включая persistent; серверный max_connections остаётся отдельным.
pgsql.auto_reset_persistent0 / INI_SYSTEMПроверять broken persistent link при pg_pconnect ценой дополнительной работы. Не очищает прикладное session-state целиком.
pgsql.ignore_notice0 / INI_ALLИгнорировать backend notices. Не путать notice с SQL-ошибкой результата.
pgsql.log_notice0 / INI_ALLПисать notices в PHP-log; работает только когда ignore_notice выключен.
Прочитать настройки расширения и увидеть их уровеньПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$names = ['pgsql.allow_persistent', 'pgsql.max_persistent', 'pgsql.max_links', 'pgsql.auto_reset_persistent', 'pgsql.ignore_notice', 'pgsql.log_notice'];
foreach ($names as $name) {
    $value = ini_get($name);
    echo $name, ': ', $value === false ? 'нет' : $value, "\n";
}
Ожидаемый результат
pgsql.allow_persistent: 1
pgsql.max_persistent: -1
pgsql.max_links: -1
pgsql.auto_reset_persistent: 0
pgsql.ignore_notice: 0
pgsql.log_notice: 0

Все шесть имён относятся к ext/pgsql. Значения приходят строками; -1 означает отсутствие числового лимита расширения, а не гарантию бесконечного лимита сервера или ОС. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Типы ресурсов до PHP 8.1 и opaque-классы сейчас

До PHP 8.1 расширение возвращало resource для соединения, результата и открытого large object. Теперь PgSql\Connection, PgSql\Result и PgSql\Lob — final opaque-классы: их создаёт расширение, пользователь не вызывает new, не добавляет свойства и не наследуется. Они являются дескрипторами состояния в libpq/сервере, а не ORM-моделями товара.

Увидеть современные типы connection и resultПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 42 AS answer');
echo get_debug_type($db), "\n";
echo get_debug_type($result), "\n";
echo is_resource($db) ? "старый resource\n" : "не resource\n";
Ожидаемый результат
PgSql\Connection
PgSql\Result
не resource

С PHP 8.1 эти значения — opaque-объекты. Старый is_resource($db) теперь ложен, поэтому миграционный код проверяет instanceof или результат функции. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обычное завершение скрипта освобождает непостоянное соединение и результаты, но явные pg_free_result() и pg_close() полезны в длинном worker после последнего использования. Закрытие PHP-дескриптора не откатывает уже подтверждённый COMMIT и не удаляет строки из таблицы.

Все предопределённые константы ext/pgsql по назначению

Константа — именованное готовое значение без знака $. Её передают только функции, которая ожидает эту группу. Например, PGSQL_ASSOC не является статусом соединения, хотя обе константы имеют тип int. Часть новых констант отсутствует, если ext/pgsql собран со старой libpq; переносимый код сначала проверяет defined().

ГруппаВсе константыЧто различают
Версия клиентской libpqPGSQL_LIBPQ_VERSION · PGSQL_LIBPQ_VERSION_STRКороткая версия libpq; *_STR с PHP 8.0 совпадает с ней и deprecated.
Форма строки результатаPGSQL_ASSOC · PGSQL_NUM · PGSQL_BOTHАссоциативные ключи, числовые индексы либо обе формы в pg_fetch_array.
Открытие соединенияPGSQL_CONNECT_FORCE_NEW · PGSQL_CONNECT_ASYNCСоздать отдельное соединение либо начать неблокирующее подключение.
Этап асинхронного подключенияPGSQL_CONNECTION_STARTED · PGSQL_CONNECTION_MADE · PGSQL_CONNECTION_AWAITING_RESPONSE · PGSQL_CONNECTION_AUTH_OK · PGSQL_CONNECTION_SETENV · PGSQL_CONNECTION_SSL_STARTUPПромежуточные состояния pg_connect_poll; это не итоговая оценка пригодности сессии.
Итог состояния соединенияPGSQL_CONNECTION_OK · PGSQL_CONNECTION_BADИсправное либо неисправное соединение по pg_connection_status.
Отсчёт позиции large objectPGSQL_SEEK_SET · PGSQL_SEEK_CUR · PGSQL_SEEK_ENDНачало объекта, текущая позиция или конец для pg_lo_seek.
Статус результатаPGSQL_EMPTY_QUERY · PGSQL_COMMAND_OK · PGSQL_TUPLES_OK · PGSQL_TUPLES_CHUNK · PGSQL_COPY_OUT · PGSQL_COPY_IN · PGSQL_BAD_RESPONSE · PGSQL_NONFATAL_ERROR · PGSQL_FATAL_ERRORПустая команда, успешная команда без/со строками, chunk, COPY или разные классы отказа. TUPLES_CHUNK требует PHP 8.4+ и libpq 17+.
Состояние транзакцииPGSQL_TRANSACTION_IDLE · PGSQL_TRANSACTION_ACTIVE · PGSQL_TRANSACTION_INTRANS · PGSQL_TRANSACTION_INERROR · PGSQL_TRANSACTION_UNKNOWNВне транзакции, выполняется команда, нормальная/сломанная транзакция либо неизвестное состояние.
Структурированные поля ошибкиPGSQL_DIAG_SEVERITY · PGSQL_DIAG_SEVERITY_NONLOCALIZED · PGSQL_DIAG_SQLSTATE · PGSQL_DIAG_MESSAGE_PRIMARY · PGSQL_DIAG_MESSAGE_DETAIL · PGSQL_DIAG_MESSAGE_HINT · PGSQL_DIAG_STATEMENT_POSITION · PGSQL_DIAG_INTERNAL_POSITION · PGSQL_DIAG_INTERNAL_QUERY · PGSQL_DIAG_CONTEXT · PGSQL_DIAG_SOURCE_FILE · PGSQL_DIAG_SOURCE_LINE · PGSQL_DIAG_SOURCE_FUNCTION · PGSQL_DIAG_SCHEMA_NAME · PGSQL_DIAG_TABLE_NAME · PGSQL_DIAG_COLUMN_NAME · PGSQL_DIAG_DATATYPE_NAME · PGSQL_DIAG_CONSTRAINT_NAMEОтдельные поля pg_result_error_field. Для логики выбирают стабильный SQLSTATE, а не переводимый текст. NONLOCALIZED требует PostgreSQL 9.6+ и PHP 7.3+.
Подробность сообщенийPGSQL_ERRORS_TERSE · PGSQL_ERRORS_DEFAULT · PGSQL_ERRORS_VERBOSE · PGSQL_ERRORS_SQLSTATEКраткое, обычное, подробное либо только severity + SQLSTATE. До PostgreSQL 11.1 режим SQLSTATE совпадает с TERSE; без кода также используется TERSE.
Работа с noticesPGSQL_NOTICE_LAST · PGSQL_NOTICE_ALL · PGSQL_NOTICE_CLEARПоследнее notice, весь список или очистка накопленных notices.
Форма возврата статусаPGSQL_STATUS_LONG · PGSQL_STATUS_STRINGЧисловая константа статуса либо строка командного статуса.
Преобразование значенийPGSQL_CONV_IGNORE_DEFAULT · PGSQL_CONV_FORCE_NULL · PGSQL_CONV_IGNORE_NOT_NULLIGNORE_DEFAULT не учитывает default таблицы при преобразовании; FORCE_NULL превращает пустую строку в SQL NULL; IGNORE_NOT_NULL разрешает не отклонять NULL для NOT NULL на этапе преобразования. Ограничение БД всё равно действует при запросе.
DML-помощникиPGSQL_DML_NO_CONV · PGSQL_DML_EXEC · PGSQL_DML_ASYNC · PGSQL_DML_STRING · PGSQL_DML_ESCAPENO_CONV передаёт всё как есть и требует ручного escaping внешних данных; EXEC исполняет, ASYNC отправляет async, STRING возвращает SQL. ESCAPE экранирует все значения вместо pg_convert и пропускает lookup метаданных. Для новых внешних значений обычно яснее pg_query_params.
Polling подключенияPGSQL_POLLING_FAILED · PGSQL_POLLING_READING · PGSQL_POLLING_WRITING · PGSQL_POLLING_OK · PGSQL_POLLING_ACTIVEОтказ, ожидание чтения/записи, готовность или активный этап pg_connect_poll.
Видимость server contextPGSQL_SHOW_CONTEXT_NEVER · PGSQL_SHOW_CONTEXT_ERRORS · PGSQL_SHOW_CONTEXT_ALWAYSС PHP 8.3: никогда, только errors (default) либо errors/notices/warnings в pg_last_error и pg_result_error.
Трассировка libpqPGSQL_TRACE_SUPPRESS_TIMESTAMPS · PGSQL_TRACE_REGRESS_MODESUPPRESS_TIMESTAMPS (PHP 8.4.20+) убирает timestamps; REGRESS_MODE (PHP 8.3+) добавляет, например, OID для регрессионного trace.
Выбрать форму строки и проверить статус результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query_params($db, 'SELECT 7 AS id, $1::text AS title', ['Чай']);
$row = pg_fetch_array($result, 0, PGSQL_ASSOC);
echo $row['id'], ': ', $row['title'], "\n";
echo pg_result_status($result) === PGSQL_TUPLES_OK ? "строки получены\n" : "другой статус\n";
Ожидаемый результат
7: Чай
строки получены

PGSQL_ASSOC — аргумент режима, PGSQL_TUPLES_OK — код успешного результата со строками. Число константы в коде не пишут: имя сохраняет смысл. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Примеры: один понятный маршрут вместо несвязанных фрагментов

Дальше одна история развивается постепенно: список задач магазина. Сначала создаём структуру, затем вставляем значения параметрами, читаем результат, изменяем одну строку, удаляем другую и объединяем связанные изменения транзакцией. Каждый шаг показывает stdout и не требует угадать, что осталось в базе от другого примера.

Путь данных списка задач
  1. Миграция задаёт колонки, типы, PRIMARY KEY и NOT NULL
  2. PHP получает проверенное название задачи и передаёт его отдельно от SQL
  3. PostgreSQL сохраняет строку и возвращает id
  4. SELECT возвращает PgSql\Result; fetch превращает строки в PHP-массивы
  5. UPDATE/DELETE проверяют число затронутых строк; транзакция держит связанные изменения вместе
Создать временную таблицу и увидеть сохранённые строкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE lesson_notes (id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY, title text NOT NULL)');
pg_query_params($db, 'INSERT INTO lesson_notes (title) VALUES ($1), ($2)', ['Позвонить', 'Отправить счёт']);
$result = pg_query($db, 'SELECT id, title FROM lesson_notes ORDER BY id');
foreach (pg_fetch_all($result) as $row) { echo $row['id'], '. ', $row['title'], "\n"; }
Ожидаемый результат
1. Позвонить
2. Отправить счёт

TEMP-таблица живёт только в учебном соединении. В рабочей схеме CREATE TABLE выполняет миграция с версионированием, а web-запросы делают DML. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Пройдите следующий раздел сверху вниз, затем откройте карту задач. Опытный читатель может сразу перейти к полному дереву 98 функций.

Базовое использование: таблица, CRUD и перебор результата

CREATE TABLE задаёт структуру. INSERT создаёт строки, SELECT читает, UPDATE изменяет, DELETE удаляет. Это SQL-команды PostgreSQL; функции PHP только доставляют их и читают ответ. В рабочем проекте структуру меняют миграциями, а роль приложения получает лишь необходимые права.

CREATE → INSERT → SELECT → UPDATE → DELETEПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE lesson_products (id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY, sku text UNIQUE NOT NULL, title text NOT NULL, price integer CHECK (price >= 0), active boolean NOT NULL DEFAULT true)');
$insert = pg_query_params($db, 'INSERT INTO lesson_products (sku, title, price) VALUES ($1, $2, $3) RETURNING id', ['TEA-01', 'Чай', 350]);
$id = (int) pg_fetch_result($insert, 0, 'id');
echo "Создан товар {$id}\n";
$select = pg_query_params($db, 'SELECT sku, title, price FROM lesson_products WHERE id = $1', [$id]);
$row = pg_fetch_assoc($select);
echo $row['sku'], ': ', $row['title'], ' — ', $row['price'], "\n";
$update = pg_query_params($db, 'UPDATE lesson_products SET price = $1 WHERE id = $2', [390, $id]);
echo 'Изменено: ', pg_affected_rows($update), "\n";
$delete = pg_query_params($db, 'DELETE FROM lesson_products WHERE id = $1', [$id]);
echo 'Удалено: ', pg_affected_rows($delete), "\n";
Ожидаемый результат
Создан товар 1
TEA-01: Чай — 350
Изменено: 1
Удалено: 1

RETURNING отдаёт id той строки, которую реально создал сервер. UPDATE и DELETE ограничены WHERE id = $1, а affected_rows подтверждает ровно одну строку. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Перебрать несколько строк без загрузки всей выборкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE lesson_stock (sku text PRIMARY KEY, quantity integer NOT NULL)');
pg_query_params($db, 'INSERT INTO lesson_stock VALUES ($1, $2), ($3, $4)', ['BOOK-01', 4, 'PEN-02', 12]);
$result = pg_query($db, 'SELECT sku, quantity FROM lesson_stock ORDER BY sku');
while ($row = pg_fetch_assoc($result)) {
    echo $row['sku'], ': ', $row['quantity'], "\n";
}
Ожидаемый результат
BOOK-01: 4
PEN-02: 12

while повторяет тело, пока pg_fetch_assoc возвращает следующую строку; после последней функция вернёт false. Для огромной выборки всё равно нужны страницы/streaming-стратегия, иначе серверный result уже может быть большим. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

ФрагментЧто означает
$1, $2Позиции значений в pg_query_params; это не PHP-переменные внутри строки SQL.
RETURNING idPostgreSQL возвращает созданный ключ без отдельного SELECT «последнего id».
WHERE id = $1Граница изменения. Без WHERE UPDATE/DELETE затронет все строки.
pg_affected_rowsФактическое число изменённых/удалённых строк данного результата.
while + fetchБерёт одну следующую строку, выполняет тело и останавливается на false.

Следующий шаг: добавить форму в главе HTML-форма и $_POST, проверить данные до запроса и связать изменения транзакцией.

Запросы в реальном сервисе: параметры, ошибки, SAVEPOINT и конкуренция

Базовый запрос становится прикладным, когда учитывает недоверенные значения, отказ SQL и другого пользователя, который меняет те же данные. Значения всегда отделяем от структуры SQL. Ошибку проверяем у конкретного PgSql\Result и по SQLSTATE. После ошибки внутри транзакции выполняем ROLLBACK либо ROLLBACK TO SAVEPOINT до следующих команд.

Передать строку с SQL-похожим текстом как обычное значениеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE lesson_messages (body text NOT NULL)');
$message = "'); DELETE FROM lesson_messages; --";
pg_query_params($db, 'INSERT INTO lesson_messages (body) VALUES ($1)', [$message]);
$result = pg_query($db, 'SELECT body, count(*) OVER () AS total FROM lesson_messages');
$row = pg_fetch_assoc($result);
echo $row['body'], "\nстрок: ", $row['total'], "\n";
Ожидаемый результат
'); DELETE FROM lesson_messages; --
строк: 1

Опасно выглядящие символы дошли до text-колонки как данные и не стали SQL. Параметры защищают значения; имя таблицы/колонки нельзя заменить $1. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сохранить заказ, но отменить необязательный бонусПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE lesson_events (name text UNIQUE NOT NULL)');
pg_query($db, 'BEGIN');
try {
    if (pg_query_params($db, 'INSERT INTO lesson_events VALUES ($1)', ['order-created']) === false) {
        throw new RuntimeException('Не создано основное событие');
    }
    pg_query($db, 'SAVEPOINT optional_bonus');
    $bonus = @pg_query_params($db, 'INSERT INTO lesson_events VALUES ($1)', ['order-created']);
    if ($bonus === false) {
        pg_query($db, 'ROLLBACK TO SAVEPOINT optional_bonus');
    }
    pg_query($db, 'RELEASE SAVEPOINT optional_bonus');
    pg_query($db, 'COMMIT');
} catch (Throwable $error) {
    pg_query($db, 'ROLLBACK');
    throw $error;
}
$result = pg_query($db, 'SELECT name FROM lesson_events');
echo pg_fetch_result($result, 0, 0), "\n";
Ожидаемый результат
order-created

Нарушение UNIQUE переводит транзакцию в INERROR; обычный запрос до ROLLBACK/ROLLBACK TO был бы отвергнут. Знак @ здесь только убирает ожидаемый warning из учебного stdout: код всё равно проверяет false, а рабочий сервис сохраняет SQLSTATE и детали в закрытый журнал. SAVEPOINT сохраняет обязательный шаг, но это не независимая вложенная транзакция. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Два соединения конкурируют за последний товарПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s connect_timeout=5', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$other = pg_connect($dsn, PGSQL_CONNECT_FORCE_NEW);
if ($other === false) { throw new RuntimeException('Не открыто второе соединение'); }
$table = 'lesson_inventory_' . pg_get_pid($db) . '_' . bin2hex(random_bytes(6));
$identifier = pg_escape_identifier($db, $table);
$created = false;
try {
    if (pg_query($db, "CREATE TABLE {$identifier} (sku text PRIMARY KEY, quantity integer NOT NULL CHECK (quantity >= 0))") === false) {
        throw new RuntimeException('Учебная таблица не создана');
    }
    $created = true;
    if (pg_query_params($db, "INSERT INTO {$identifier} VALUES ($1, $2)", ['ONE-01', 1]) === false) {
        throw new RuntimeException('Учебный остаток не создан');
    }
    pg_query($db, 'BEGIN');
    $first = pg_query_params($db, "UPDATE {$identifier} SET quantity = quantity - 1 WHERE sku = $1 AND quantity > 0 RETURNING quantity", ['ONE-01']);
    if (!pg_send_query_params($other, "UPDATE {$identifier} SET quantity = quantity - 1 WHERE sku = $1 AND quantity > 0 RETURNING quantity", ['ONE-01'])) {
        throw new RuntimeException('Второй UPDATE не отправлен');
    }
    $socket = pg_socket($other);
    $deadline = microtime(true) + 3.0;
    while (true) {
        $flush = pg_flush($other);
        if ($flush === false) { throw new RuntimeException('Не отправлены данные запроса'); }
        if ($flush === true) { break; }
        $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут отправки второго UPDATE'); }
        $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания записи'); }
        if ($ready === 0 && microtime(true) < $deadline) { usleep(10_000); }
    }
    pg_consume_input($other);
    echo 'второй ждёт: ', pg_connection_busy($other) ? 'да' : 'нет', "\n";
    pg_query($db, 'COMMIT');
    while (pg_connection_busy($other)) {
        if (!pg_consume_input($other)) { throw new RuntimeException('Ошибка чтения результата'); }
        if (!pg_connection_busy($other)) { break; }
        $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут результата второго UPDATE'); }
        $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания чтения'); }
        if ($ready === 0 && microtime(true) < $deadline) { usleep(10_000); }
    }
    $second = pg_get_result($other);
    echo 'первый: ', pg_num_rows($first), "\nвторой: ", pg_num_rows($second), "\n";
} finally {
    $status = pg_transaction_status($db);
    if ($status === PGSQL_TRANSACTION_INTRANS || $status === PGSQL_TRANSACTION_INERROR) { @pg_query($db, 'ROLLBACK'); }
    if ($created && pg_query($db, "DROP TABLE {$identifier}") === false) {
        throw new RuntimeException('Своя учебная таблица не удалена');
    }
    pg_close($other);
}
Ожидаемый результат
второй ждёт: да
первый: 1
второй: 0

Первое соединение изменило строку внутри незавершённой транзакции и удерживает row lock. Второе соединение действительно отправило тот же UPDATE и осталось busy до COMMIT. Отправка и чтение используют один общий deadline microtime(true) + 3.0; poll различает timeout и ошибку. После освобождения блокировки PostgreSQL заново проверил quantity > 0, поэтому второй результат содержит ноль строк. PID плюс случайный суффикс предотвращают столкновение параллельных опытов; try начинается до CREATE, а created-флаг разрешает DROP только для таблицы этого запуска. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Граница конкурентности. При стандартном READ COMMITTED, если два соединения действительно выполняют этот UPDATE одновременно, PostgreSQL сериализует изменение одной строки: второй запрос дождётся row lock, заново проверит quantity > 0 и вернёт ноль строк. На более строгой изоляции либо при нескольких строках/таблицах возможны serialization failure или deadlock; повторяют всю короткую транзакцию ограниченное число раз и только при идемпотентном договоре. Нельзя держать блокировку, пока код ждёт банк, HTTP API или ввод человека.

UPDATE и DELETE. Сначала формулируйте границу WHERE, затем передавайте значения параметрами, проверяйте pg_affected_rows() и определяйте бизнес-смысл нуля: «не найдено», «уже изменено» или конфликт. Пустое условие нельзя превращать в массовую операцию по умолчанию.

Когда PDO. Выбирайте PDO, когда проекту нужен единый объектный интерфейс и возможность поддерживать несколько СУБД. Выбирайте ext/pgsql, когда полезны PostgreSQL-специфичные COPY, LISTEN/NOTIFY, large objects, libpq async и детальная диагностика. PDO не делает SQL переносимым автоматически; ext/pgsql не отменяет архитектуру репозитория и тесты.

PgSql\Connection: дескриптор одной серверной сессии

PgSql\Connection появился в PHP 8.1 вместо connection resource. Это final opaque-класс без пользовательских методов: функции pg_* принимают объект аргументом. Один объект относится к одной сессии PostgreSQL, где живут текущая транзакция, временные таблицы, prepared statements и параметры session.

Получить Connection только через расширениеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$reflection = new ReflectionClass(PgSql\Connection::class);
echo get_debug_type($db), "\n";
echo $reflection->isFinal() ? "final\n" : "не final\n";
try { new PgSql\Connection(); echo "создан через new\n"; }
catch (Error $error) { echo "прямой new запрещён\n"; }
Ожидаемый результат
PgSql\Connection
final
прямой new запрещён

Объект создаёт pg_connect/pg_pconnect. Он закрывает внутренний указатель libpq, поэтому его нельзя сконструировать, унаследовать или сериализовать как настройки. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть две независимые серверные сессииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s connect_timeout=5', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$other = pg_connect($dsn, PGSQL_CONNECT_FORCE_NEW);
echo pg_get_pid($db) === pg_get_pid($other) ? "одна сессия\n" : "две сессии\n";
pg_close($other);
Ожидаемый результат
две сессии

Каждое соединение имеет собственный backend PID, транзакцию, prepared statements и session-настройки. Force new нужен осознанно: лишние соединения расходуют лимит сервера. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не храните Connection в PHP-session/кеше и не передавайте между процессами. Создайте его в bootstrap приложения, передайте зависимому сервису и завершите транзакцию до возврата ответа.

PgSql\Result: строки, статус и ошибка одного SQL-запроса

PgSql\Result — final opaque-класс с PHP 8.1. У него нет пользовательских методов. Fetch-функции двигают внутреннюю позицию и возвращают строки, field-функции читают схему результата, status/error-функции описывают выполнение. Result не является соединением и не выполняет новый SQL.

Отделить контейнер ответа от строки данныхПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'A-01' AS sku, 2 AS quantity");
echo get_debug_type($result), "\n";
$row = pg_fetch_assoc($result);
echo get_debug_type($row), "\n";
echo $row['sku'], ': ', $row['quantity'], "\n";
Ожидаемый результат
PgSql\Result
array
A-01: 2

Result хранит статус, строки и метаданные ответа. Только fetch создаёт обычный PHP-массив строки; числовое значение PostgreSQL здесь приходит строкой "2". Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить SQLSTATE у конкретного неудачного результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_send_query($db, 'SELECT * FROM lesson_table_that_does_not_exist');
$result = pg_get_result($db);
$state = pg_result_error_field($result, PGSQL_DIAG_SQLSTATE);
echo $state, "\n";
echo pg_result_status($result) === PGSQL_FATAL_ERROR ? "запрос отклонён\n" : "неожиданный статус\n";
Ожидаемый результат
42P01
запрос отклонён

Асинхронная пара использована, чтобы получить PgSql\Result даже для SQL-ошибки без warning от синхронного pg_query. SQLSTATE 42P01 стабильно означает undefined_table; подробный текст зависит от locale. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Большой результат занимает память в libpq и PHP. Не считайте pg_fetch_all() обязательным: построчное чтение уменьшает PHP-копию, а pagination/COPY/chunk mode меняют сам объём ответа. После последнего использования длинный worker может вызвать pg_free_result().

PgSql\Lob: открытый large object, а не загруженный файл целиком

PgSql\Lob заменил large-object resource в PHP 8.1. Его возвращает pg_lo_open() внутри транзакции. Отдельный OID идентифицирует серверный объект, а Lob содержит текущую позицию и режим открытого потока. Это другой механизм, чем колонка bytea, файл на диске PHP или обычный HTTP upload.

Записать и прочитать large object внутри транзакцииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$transaction = false;
$lob = null;
try {
    if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('BEGIN не выполнен'); }
    $transaction = true;
    $oid = pg_lo_create($db);
    if ($oid === false) { throw new RuntimeException('Large object не создан'); }
    $lob = pg_lo_open($db, $oid, 'w+');
    if ($lob === false) { throw new RuntimeException('Large object не открыт'); }
    if (pg_lo_write($lob, 'document-body') !== 13) { throw new RuntimeException('Запись неполна'); }
    if (!pg_lo_seek($lob, 0, PGSQL_SEEK_SET)) { throw new RuntimeException('Позиция не изменена'); }
    echo get_debug_type($lob), "\n";
    $bytes = pg_lo_read($lob, 13);
    if ($bytes === false) { throw new RuntimeException('Large object не прочитан'); }
    echo $bytes, "\n";
    if (!pg_lo_close($lob)) { throw new RuntimeException('Дескриптор не закрыт'); }
    $lob = null;
    if (!pg_lo_unlink($db, $oid)) { throw new RuntimeException('Large object не удалён'); }
    if (pg_query($db, 'COMMIT') === false) { throw new RuntimeException('COMMIT не выполнен'); }
    $transaction = false;
} finally {
    if ($lob instanceof PgSql\Lob) { @pg_lo_close($lob); }
    if ($transaction) { @pg_query($db, 'ROLLBACK'); }
}
Ожидаемый результат
PgSql\Lob
document-body

Large object API требует транзакцию. OID — ссылка в БД, PgSql\Lob — открытая позиция чтения/записи. finally закрывает оставшийся descriptor и откатывает незавершённую транзакцию; успешный путь отдельно удаляет объект и фиксирует COMMIT. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить свойства opaque-класса LobПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$reflection = new ReflectionClass(PgSql\Lob::class);
echo $reflection->isFinal() ? "final\n" : "не final\n";
try { new PgSql\Lob(); echo "создан через new\n"; }
catch (Error $error) { echo "прямой new запрещён\n"; }
echo count($reflection->getProperties()), " свойств\n";
Ожидаемый результат
final
прямой new запрещён
0 свойств

Lob не хранит имя файла, MIME-type или владельца. Эти прикладные метаданные проект хранит в обычной таблице рядом с OID либо использует объектное хранилище. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Large objects полезны для частичного чтения/записи внутри PostgreSQL, но требуют явного lifecycle: права, транзакция, закрытие, unlink и уборка сирот. Для раздачи больших медиа через CDN чаще подходит объектное хранилище, а в БД остаются ключ и метаданные.

Карта по задаче и полное дерево всех 98 функций ext/pgsql

Карта отвечает на вопрос «что мне сделать сейчас?». Полное дерево ниже отвечает на вопрос «какие инструменты вообще есть в официальной ветке?». Это независимые входы в одни и те же подробные карточки; ни один из них не подменяет другой.

ЗадачаИнструментЧто обязательно проверить
Проверить доступность базыpg_connect() → pg_connection_status() → маленький SELECTОтличить сеть, auth и SQL; не показывать DSN посетителю.
Принять значение формыpg_query_params()Сначала тип/бизнес-проверка; параметр защищает значение, не имя таблицы.
Добавить и получить idpg_query_params() с INSERT ... RETURNING idПроверить result/status и прочитать id из этой команды.
Вывести списокpg_fetch_assoc() или pg_fetch_all()Построчно для больших результатов; преобразовать типы после проверки.
Изменить/удалить одну записьpg_query_params() → pg_affected_rows()Явный WHERE; ноль строк получает бизнес-смысл.
Согласовать несколько измененийBEGIN / COMMIT / ROLLBACK / SAVEPOINTКороткая транзакция; после ошибки сначала rollback.
Импортировать большой пакетpg_copy_from() / pg_put_copy_data()Формат COPY, NULL, encoding, транзакция и отчёт по плохой строке.
Получать события БДLISTEN + NOTIFY → pg_get_notify()Уведомление не является долговечной очередью.
Хранить двоичные данныеbytea: pg_escape_bytea(); LO: pg_lo_open()Выбрать по размеру, streaming, backup, CDN и lifecycle.
Запустить несколько запросов без последовательного ожиданияpg_send_query_params() → pg_get_result()Дочитать все результаты; async не делает SQL быстрее сам по себе.
Разобрать серверную ошибкуpg_result_error_field() с PGSQL_DIAG_SQLSTATEЛогировать детали закрыто; пользователю вернуть безопасный текст.
Поддержать несколько СУБДPDO и драйверыУнифицированный API не делает диалекты SQL одинаковыми.

Полное дерево функций. Все 98 официальных refentry распределены по назначению; каждое имя ведёт к сигнатуре, параметрам, возврату, отказу и двум практическим сценариям.

КатегорияВсе функции
Подключение, сессия и сведения о сервереpg_connect() · pg_pconnect() · pg_connect_poll() · pg_connection_status() · pg_connection_reset() · pg_ping() · pg_close() · pg_host() · pg_port() · pg_dbname() · pg_options() · pg_tty() · pg_client_encoding() · pg_set_client_encoding() · pg_parameter_status() · pg_version() · pg_get_pid() · pg_socket() · pg_socket_poll() · pg_cancel_query() · pg_change_password()
Обычные запросы, параметры и DML-помощникиpg_query() · pg_query_params() · pg_prepare() · pg_execute() · pg_close_stmt() · pg_escape_identifier() · pg_escape_literal() · pg_escape_string() · pg_convert() · pg_meta_data() · pg_insert() · pg_select() · pg_update() · pg_delete()
Строки результата и метаданные полейpg_affected_rows() · pg_fetch_all() · pg_fetch_all_columns() · pg_fetch_array() · pg_fetch_assoc() · pg_fetch_object() · pg_fetch_result() · pg_fetch_row() · pg_field_is_null() · pg_field_name() · pg_field_num() · pg_field_prtlen() · pg_field_size() · pg_field_table() · pg_field_type() · pg_field_type_oid() · pg_num_fields() · pg_num_rows() · pg_result_seek()
Статус, ошибки, транзакция и память результатаpg_result_status() · pg_result_error() · pg_result_error_field() · pg_last_error() · pg_last_notice() · pg_free_result() · pg_result_memory_size() · pg_jit() · pg_transaction_status() · pg_last_oid() · pg_set_error_context_visibility() · pg_set_error_verbosity()
Асинхронные запросы и управление потокомpg_connection_busy() · pg_consume_input() · pg_flush() · pg_get_result() · pg_send_execute() · pg_send_prepare() · pg_send_query() · pg_send_query_params() · pg_set_chunked_rows_size()
Пакетный обмен COPYpg_copy_from() · pg_copy_to() · pg_put_line() · pg_end_copy() · pg_put_copy_data() · pg_put_copy_end()
Уведомления и трассировка клиентаpg_get_notify() · pg_trace() · pg_untrace()
Двоичные значения byteapg_escape_bytea() · pg_unescape_bytea()
Большие объекты PostgreSQLpg_lo_create() · pg_lo_open() · pg_lo_write() · pg_lo_read() · pg_lo_read_all() · pg_lo_seek() · pg_lo_tell() · pg_lo_truncate() · pg_lo_close() · pg_lo_unlink() · pg_lo_import() · pg_lo_export()

Идентификаторы PostgreSQL без кавычек приводятся к нижнему регистру. Внешнее имя таблицы нельзя передать как $1: выберите его из allowlist и примените pg_escape_identifier(). Сведения о схеме получают из information_schema и каталогов PostgreSQL, а не склеиванием произвольного пользовательского SQL.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

pg_connectОткрыть обычное соединение с PostgreSQL и получить объект, через который выполняются запросы.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_connect(string $connection_string, int $flags = 0): PgSql\Connection|false

Открыть обычное соединение с PostgreSQL и получить объект, через который выполняются запросы.

Запись pg_connect(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
string $connection_stringСтрока параметров libpq. Не собирайте её из произвольного пользовательского ввода; задайте connect_timeout и понятный application_name.
int $flags (по умолчанию 0)0, PGSQL_CONNECT_FORCE_NEW и/или PGSQL_CONNECT_ASYNC. ASYNC меняет способ завершения подключения, но не делает SQL автоматически асинхронным.

Возврат и договор. Возвращает PgSql\Connection либо false. Строка соединения понимается libpq: host, port, dbname, user, connect_timeout, application_name и другие параметры. Пароль лучше передавать через защищённый secret/env, а не хранить в исходнике.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Приложение читает доверенную конфигурацию
  2. libpq выбирает Unix socket или TCP и при настройке TLS, затем аутентифицируется
  3. PostgreSQL создаёт backend-сессию
  4. PHP получает PgSql\Connection или false
Подключиться и увидеть выбранную базуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf(
    'host=%s port=%s dbname=%s user=%s connect_timeout=5 application_name=php_manual_visible_connect',
    getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER')
);
$connection = pg_connect($dsn, PGSQL_CONNECT_FORCE_NEW);
if ($connection === false) { throw new RuntimeException('Не удалось подключиться'); }
echo pg_dbname($connection), "\n";
var_dump($connection instanceof PgSql\Connection);
pg_close($connection);
Ожидаемый результат
php_manual
bool(true)

Строка подключения собрана из окружения без вывода пароля. Вызов pg_connect виден прямо в примере: объект подтверждает успешный вход, а имя базы помогает не выполнить учебный SQL не там. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать недоступный адрес до SQLПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$failed = @pg_connect('host=127.0.0.1 port=1 dbname=missing connect_timeout=1');
echo $failed === false ? "Соединения нет\n" : "Неожиданное соединение\n";
Ожидаемый результат
Соединения нет

Порт 1 выбран как заведомо отдельная локальная точка. При false таблицы и запросы ещё не существуют: приложение отвечает безопасно либо повторяет подключение по своей политике. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_pconnect(), pg_connection_status(), pg_ping(), pg_close().

pg_pconnectПолучить persistent-соединение, которое PHP-worker может повторно использовать после окончания одного запроса.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_pconnect(string $connection_string, int $flags = 0): PgSql\Connection|false

Получить persistent-соединение, которое PHP-worker может повторно использовать после окончания одного запроса.

Запись pg_pconnect(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
string $connection_stringДоверенная строка libpq. Даже небольшое отличие application_name/options создаёт другой ключ persistent-соединения.
int $flags (по умолчанию 0)Флаги соединения. FORCE_NEW заставляет отдельное подключение; необходимость измеряют, а не включают автоматически.

Возврат и договор. Возвращает PgSql\Connection либо false. При повторной выдаче ext/pgsql выполняет RESET ALL, но это не удаляет все объекты/подписки backend-сессии. Экономия зависит от SAPI и нагрузки; функция не заменяет внешний pooler.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Worker ищет persistent-соединение по строке
  2. При совпадении получает прежнюю backend-сессию и RESET ALL
  3. Temp-объекты и часть состояния всё ещё существуют
  4. Код завершает/очищает собственные ресурсы
Увидеть одну persistent-сессию внутри процессаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s application_name=php_manual_persistent', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$first = pg_pconnect($dsn);
$second = pg_pconnect($dsn);
if ($first === false || $second === false) { throw new RuntimeException('Нет persistent-соединения'); }
var_dump(pg_get_pid($first) === pg_get_pid($second));
Ожидаемый результат
bool(true)

В одном CLI-процессе одинаковая строка подключения возвращает тот же persistent backend. В FPM жизненный цикл относится к worker, а не ко всем посетителям и не гарантирует один PID навсегда. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Понять риск сохранённого состояния сессииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s application_name=php_manual_persistent_state', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$first = pg_pconnect($dsn);
pg_query($first, 'CREATE TEMP TABLE persistent_probe (id integer)');
pg_query($first, 'INSERT INTO persistent_probe VALUES (1)');
pg_query($first, "SET search_path TO pg_catalog");
$second = pg_pconnect($dsn);
$path = pg_fetch_result(pg_query($second, 'SHOW search_path'), 0, 0);
$count = pg_fetch_result(pg_query($second, 'SELECT count(*) FROM pg_temp.persistent_probe'), 0, 0);
echo $path, "\n", $count, "\n";
Ожидаемый результат
"$user", public
1

При повторной выдаче persistent-соединения ext/pgsql выполняет RESET ALL: search_path вернулся к default. Но временная таблица той же backend-сессии осталась. Очистка GUC не равна полной изоляции состояния; транзакции, temp-объекты, LISTEN и другие сессионные ресурсы требуют политики. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connect(), pg_connection_reset(), pg_transaction_status().

pg_connect_pollПродвигать подключение, начатое pg_connect с PGSQL_CONNECT_ASYNC, и узнать его текущее состояние.

PHP 5.6+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_connect_poll(PgSql\Connection $connection): int

Продвигать подключение, начатое pg_connect с PGSQL_CONNECT_ASYNC, и узнать его текущее состояние.

Запись pg_connect_poll(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает одну из PGSQL_POLLING_READING, WRITING, ACTIVE, OK, FAILED. Только OK означает готовое соединение; FAILED — окончательный отказ. Повторный вызов делают после готовности сокета, иначе получится активное ожидание CPU.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. pg_connect ASYNC создаёт незавершённое соединение
  2. pg_connect_poll сообщает, чтения или записи ждёт libpq
  3. event loop ждёт сокет
  4. PGSQL_POLLING_OK разрешает выполнять SQL
Довести асинхронное подключение до готовностиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s application_name=php_manual_poll', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$async = pg_connect($dsn, PGSQL_CONNECT_ASYNC | PGSQL_CONNECT_FORCE_NEW);
if ($async === false) { throw new RuntimeException('Не удалось начать подключение'); }
try {
    $socket = pg_socket($async);
    if ($socket === false) { throw new RuntimeException('Нет сокета подключения'); }
    $deadline = hrtime(true) + 5_000_000_000;
    do {
        $state = pg_connect_poll($async);
        if ($state === PGSQL_POLLING_FAILED) { throw new RuntimeException('Подключение отклонено'); }
        if ($state === PGSQL_POLLING_OK) { break; }

        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Истёк таймаут подключения'); }
        if ($state === PGSQL_POLLING_READING || $state === PGSQL_POLLING_WRITING) {
            $ready = pg_socket_poll(
                $socket,
                $state === PGSQL_POLLING_READING ? 1 : 0,
                $state === PGSQL_POLLING_WRITING ? 1 : 0,
                $remainingMs,
            );
            if ($ready < 0) { throw new RuntimeException('Ошибка ожидания сокета'); }
            if ($ready === 0) { usleep(10_000); }
        } else {
            usleep(1_000);
        }
    } while (true);
    echo pg_dbname($async), "\n";
} finally {
    pg_close($async);
}
Ожидаемый результат
php_manual

ASYNC возвращает управление до завершения установления соединения. Один монотонный deadline ограничивает весь процесс. -1 от pg_socket_poll завершает опыт ошибкой, а ранний 0 повторяется с короткой паузой до deadline; незавершённое соединение закрывается в finally. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не считать промежуточное состояние готовым соединениемПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s application_name=php_manual_poll_state', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$async = pg_connect($dsn, PGSQL_CONNECT_ASYNC | PGSQL_CONNECT_FORCE_NEW);
if ($async === false) { throw new RuntimeException('Не удалось начать подключение'); }
$seen = [];
try {
    $socket = pg_socket($async);
    if ($socket === false) { throw new RuntimeException('Нет сокета подключения'); }
    $deadline = hrtime(true) + 5_000_000_000;
    do {
        $state = pg_connect_poll($async);
        $seen[$state] = true;
        if ($state === PGSQL_POLLING_FAILED) { throw new RuntimeException('Подключение отклонено'); }
        if ($state === PGSQL_POLLING_OK) { break; }

        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Истёк таймаут подключения'); }
        if ($state === PGSQL_POLLING_READING || $state === PGSQL_POLLING_WRITING) {
            $ready = pg_socket_poll(
                $socket,
                $state === PGSQL_POLLING_READING ? 1 : 0,
                $state === PGSQL_POLLING_WRITING ? 1 : 0,
                $remainingMs,
            );
            if ($ready < 0) { throw new RuntimeException('Ошибка ожидания сокета'); }
            if ($ready === 0) { usleep(10_000); }
        } else {
            usleep(1_000);
        }
    } while (true);
    echo isset($seen[PGSQL_POLLING_OK]) ? "Готово\n" : "Не готово\n";
} finally {
    pg_close($async);
}
Ожидаемый результат
Готово

Набор промежуточных состояний зависит от сети и аутентификации, поэтому пример проверяет только достижение OK/FAILED. Монотонный deadline общий для всех стадий; -1 и ранний 0 различаются, а finally закрывает как готовое, так и незавершённое учебное соединение. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connect(), pg_socket(), pg_socket_poll().

pg_connection_statusУзнать, считает ли libpq соединение рабочим или потерянным.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_connection_status(PgSql\Connection $connection): int

Узнать, считает ли libpq соединение рабочим или потерянным.

Запись pg_connection_status(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает PGSQL_CONNECTION_OK либо PGSQL_CONNECTION_BAD. Это быстрый снимок; сразу после проверки сеть всё равно может оборваться, поэтому результат каждой операции остаётся главным доказательством.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PHP передаёт объект соединения
  2. libpq сообщает OK/BAD
  3. Код выбирает восстановление либо работу
  4. SQL всё равно проверяется отдельно
Проверить открытое соединениеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
echo pg_connection_status($db) === PGSQL_CONNECTION_OK ? "OK\n" : "BAD\n";
Ожидаемый результат
OK

Статус описывает транспортное состояние объекта. Он не доказывает, что конкретная таблица существует или что текущая транзакция пригодна к продолжению. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отличить живое соединение от ошибочной транзакцииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'BEGIN');
@pg_query($db, 'SELECT * FROM manual_missing_table');
echo pg_connection_status($db) === PGSQL_CONNECTION_OK ? "Связь жива\n" : "Связь потеряна\n";
echo pg_transaction_status($db) === PGSQL_TRANSACTION_INERROR ? "Транзакция сломана\n" : "Транзакция пригодна\n";
pg_query($db, 'ROLLBACK');
Ожидаемый результат
Связь жива
Транзакция сломана

После SQL-ошибки TCP-соединение остаётся OK, но транзакция принимает только ROLLBACK/ROLLBACK TO. Для этих вопросов нужны две разные функции. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_ping(), pg_connection_reset(), pg_transaction_status().

pg_connection_resetПереподключить существующий объект по его исходной строке соединения.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_connection_reset(PgSql\Connection $connection): bool

Переподключить существующий объект по его исходной строке соединения.

Запись pg_connection_reset(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает true при успешном новом подключении и false при отказе. Транзакции, prepared statements, временные таблицы, LISTEN и session settings прежней сессии не являются сохранённым контрактом.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Приложение обнаруживает BAD или загрязнённую сессию
  2. libpq закрывает/заменяет backend
  3. Аутентификация выполняется снова
  4. Объект PHP указывает на новую сессию
Сбросить изменённую сессионную настройкуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, "SET application_name TO 'temporary_job'");
var_dump(pg_connection_reset($db));
$result = pg_query($db, "SELECT current_setting('application_name')");
echo pg_fetch_result($result, 0, 0), "\n";
Ожидаемый результат
bool(true)
php_manual_example

Reset переподключает объект с исходной строкой; временная настройка исчезает, application_name из bootstrap возвращается. Временные таблицы и незавершённое состояние тоже не следует считать сохранёнными. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть новую backend-сессию после resetПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$before = pg_get_pid($db);
if (!pg_connection_reset($db)) { throw new RuntimeException('Reset не удался'); }
$after = pg_get_pid($db);
var_dump($before !== $after);
Ожидаемый результат
bool(true)

Новый PID показывает, что reset не «очистил пару переменных» внутри прежнего backend, а установил новую сессию. PID позднее может переиспользоваться, поэтому это диагностический признак, не бизнес-ID. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_ping(), pg_connection_status(), pg_get_pid().

pg_pingПроверить доступность сервера через текущее соединение и при необходимости попытаться переподключиться.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_ping(?PgSql\Connection $connection = null): bool

Проверить доступность сервера через текущее соединение и при необходимости попытаться переподключиться.

Запись pg_ping(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает bool. Реализация может вызвать reset для потерянного соединения, поэтому сессионное состояние после восстановления нельзя считать прежним. Частый ping перед каждым SQL добавляет запросы и не устраняет гонку между ping и операцией.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код передаёт соединение
  2. libpq проверяет статус
  3. При BAD пытается reset
  4. PHP получает true/false
Проверить учебный сервер перед заданиемПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
var_dump(pg_ping($db));
Ожидаемый результат
bool(true)

Успешный ping подтверждает, что libpq может связаться с сервером сейчас. Это не повод пропустить проверку следующего запроса. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не путать ping с проверкой схемыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$missing = @pg_query($db, 'SELECT * FROM manual_missing_table');
var_dump($missing === false);
var_dump(pg_ping($db));
Ожидаемый результат
bool(true)
bool(true)

Ошибка таблицы не означает падение сервера: SQL отказал, а соединение осталось доступным. Диагностика должна сообщать правильную причину. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connection_status(), pg_connection_reset().

pg_closeЯвно закрыть соединение PostgreSQL, когда оно больше не нужно текущему процессу.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_close(?PgSql\Connection $connection = null): true

Явно закрыть соединение PostgreSQL, когда оно больше не нужно текущему процессу.

Запись pg_close(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает true. Закрытие завершает backend-сессию; незавершённая транзакция откатывается сервером. Объект становится закрытым и его последующее использование вызывает Error.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код завершил последнюю операцию
  2. pg_close закрывает libpq connection
  3. Сервер завершает сессию и откатывает незавершённое
  4. Закрытый объект больше не используется
Закрыть отдельное соединениеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s application_name=php_manual_close', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$extra = pg_connect($dsn, PGSQL_CONNECT_FORCE_NEW);
var_dump(pg_close($extra));
Ожидаемый результат
bool(true)

Закрывается специально созданное соединение, а bootstrap-соединение остаётся для остальных действий примера. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть, что закрытый объект больше не рабочийПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$dsn = sprintf('host=%s port=%s dbname=%s user=%s application_name=php_manual_closed', getenv('PGHOST'), getenv('PGPORT'), getenv('PGDATABASE'), getenv('PGUSER'));
$extra = pg_connect($dsn, PGSQL_CONNECT_FORCE_NEW);
pg_close($extra);
try { pg_dbname($extra); } catch (Error $error) { echo $error::class, "\n"; }
Ожидаемый результат
Error

После закрытия тот же PgSql\Connection нельзя передавать функциям. В обычном коротком CLI PHP сам закрывает непостоянные соединения, но явное закрытие полезно при долгом процессе и большом числе независимых БД. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connect(), pg_pconnect().

pg_hostПолучить host, связанный с текущим соединением.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_host(?PgSql\Connection $connection = null): string

Получить host, связанный с текущим соединением.

Запись pg_host(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает строку адреса либо socket directory в соответствующем окружении. Не раскрывайте внутренние адреса в публичной ошибке.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Соединение хранит параметры libpq
  2. pg_host читает host
  3. Журнал добавляет окружение
  4. Пользовательский ответ остаётся безопасным
Показать адрес выбранного соединенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
echo pg_host($db), "\n";
Ожидаемый результат
127.0.0.1

Адрес полезен в диагностике нескольких окружений; он не заменяет проверку имени базы и пользователя. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сверить host с параметрами соединенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$version = pg_version($db);
echo pg_host($db) === getenv('PGHOST') ? "Совпало\n" : "Проверьте socket/host\n";
Ожидаемый результат
Совпало

При Unix socket или DNS текст может отличаться от ожидания, поэтому это диагностическая подсказка, а не проверка подлинности сервера. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_port(), pg_dbname(), pg_options().

pg_portПолучить TCP-порт текущего соединения.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_port(?PgSql\Connection $connection = null): string

Получить TCP-порт текущего соединения.

Запись pg_port(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает строку с номером порта. Для Unix socket сервер всё равно имеет настроенный port, хотя транспорт отличается.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. libpq хранит эффективный endpoint
  2. pg_port читает port
  3. Код показывает/сопоставляет окружение
  4. SQL не выполняется
Показать нестандартный учебный портПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
echo pg_port($db), "\n";
Ожидаемый результат
55432

Проектный сервер намеренно не занимает системный 5432. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Преобразовать диагностическую строку в числоПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$port = (int) pg_port($db);
var_dump($port > 0 && $port <= 65535);
Ожидаемый результат
bool(true)

Функция возвращает строку. Числовое преобразование допустимо для сравнения диапазона, но исходное значение удобно хранить как часть DSN/журнала. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_host(), pg_dbname().

pg_dbnameПолучить имя базы, к которой относится соединение.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_dbname(?PgSql\Connection $connection = null): string

Получить имя базы, к которой относится соединение.

Запись pg_dbname(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает строку. Это имя текущего подключения, а не список баз и не схема search_path.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Соединение выбрало database
  2. pg_dbname возвращает имя
  3. Guard сравнивает ожидаемое окружение
  4. Только после этого выполняется учебный DDL
Убедиться, что миграция направлена в учебную базуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
echo pg_dbname($db), "\n";
Ожидаемый результат
php_manual

Проверка особенно полезна перед DDL в ручном инструменте; автоматический deploy дополнительно разделяет реквизиты и права. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Остановиться при неожиданной базеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
if (pg_dbname($db) !== 'php_manual') { throw new RuntimeException('Неверная база'); }
echo "Можно продолжать\n";
Ожидаемый результат
Можно продолжать

Guard защищает учебный скрипт от ошибки конфигурации, но имя нельзя считать единственной защитой — роль должна иметь минимальные права. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_host(), pg_port(), pg_parameter_status().

pg_optionsПолучить startup options, с которыми работает backend текущего соединения.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_options(?PgSql\Connection $connection = null): string

Получить startup options, с которыми работает backend текущего соединения.

Запись pg_options(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает строку, часто пустую. Не используйте её как полный снимок конфигурации PostgreSQL.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. libpq открыл сессию с options
  2. Backend сохранил startup options
  3. pg_options возвращает строку
  4. Текущие GUC читаются отдельно
Прочитать startup-options соединенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$options = pg_options($db);
echo $options === '' ? "Без startup-options\n" : $options . "\n";
Ожидаемый результат
Без startup-options

SET внутри сессии не обязан появляться здесь: функция показывает параметры командной строки backend/startup options, а не все текущие GUC. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отличить options от текущей настройкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, "SET statement_timeout TO '1500ms'");
$timeout = pg_fetch_result(pg_query($db, "SELECT current_setting('statement_timeout')"), 0, 0);
echo pg_options($db) === '' ? "options пусты\n" : pg_options($db) . "\n";
echo $timeout, "\n";
pg_query($db, 'RESET statement_timeout');
Ожидаемый результат
options пусты
1500ms

Текущие GUC читают SHOW/current_setting/pg_parameter_status, если сервер их сообщает. pg_options отвечает на другой вопрос. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_parameter_status(), pg_version().

pg_ttyПолучить исторический tty-параметр соединения PostgreSQL.

PHP 4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_tty(?PgSql\Connection $connection = null): string

Получить исторический tty-параметр соединения PostgreSQL.

Запись pg_tty(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает строку, обычно пустую в современных окружениях. Не используйте пустоту как признак отказа.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Соединение хранит legacy tty field
  2. pg_tty читает его
  3. Пустая строка допустима
  4. Статус проверяется через pg_connection_status
Проверить исторический tty-параметрПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$tty = pg_tty($db);
echo $tty === '' ? "TTY не задан\n" : $tty . "\n";
Ожидаемый результат
TTY не задан

Современное TCP-соединение обычно возвращает пустую строку; функция сохраняется для совместимости и редко определяет решение приложения. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не путать пустой tty с ошибкой соединенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
printf("tty=%s status=%s\n", pg_tty($db) === '' ? '<empty>' : pg_tty($db), pg_connection_status($db) === PGSQL_CONNECTION_OK ? 'OK' : 'BAD');
Ожидаемый результат
tty=<empty> status=OK

Пустой tty нормален: транспортное состояние проверяется отдельной функцией. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connection_status(), pg_host().

pg_client_encodingУзнать активную клиентскую кодировку соединения.

PHP 4.0.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_client_encoding(?PgSql\Connection $connection = null): string

Узнать активную клиентскую кодировку соединения.

Запись pg_client_encoding(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает имя кодировки строкой. Она относится к текущей сессии и может отличаться после reset или SET client_encoding.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PHP передаёт текстовые байты
  2. client_encoding задаёт их интерпретацию
  3. pg_client_encoding возвращает имя
  4. Код сравнивает ожидаемый UTF8
Увидеть клиентскую кодировкуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
echo pg_client_encoding($db), "\n";
Ожидаемый результат
UTF8

Кодировка описывает преобразование между клиентом и сервером. UTF8 позволяет передавать русский текст без смены режима на каждом запросе. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сменить и вернуть кодировку явноПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$before = pg_client_encoding($db);
var_dump(pg_set_client_encoding($db, 'UTF8') === 0);
echo pg_client_encoding($db), "\n";
pg_set_client_encoding($db, $before);
Ожидаемый результат
bool(true)
UTF8

Нулевой код — успех. Пример не выбирает LATIN1 ради демонстрации: неверная смена способна исказить реальные данные. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_set_client_encoding(), pg_parameter_status().

pg_set_client_encodingИзменить client_encoding текущей сессии, когда источник действительно требует другой кодировки.

PHP 4.0.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_set_client_encoding(PgSql\Connection $connection, string $encoding): int

Изменить client_encoding текущей сессии, когда источник действительно требует другой кодировки.

Запись pg_set_client_encoding(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $encodingИмя клиентской кодировки PostgreSQL, например UTF8; менять её без необходимости не следует.

Возврат и договор. Возвращает 0 при успехе и -1 при отказе. Это изменение состояния соединения; в persistent worker верните исходное значение либо не меняйте его вообще.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код проверяет договор источника
  2. SET client_encoding отправляется серверу
  3. 0 подтверждает принятие
  4. pg_client_encoding проверяет результат
Сменить и вернуть кодировку явноПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$before = pg_client_encoding($db);
var_dump(pg_set_client_encoding($db, 'UTF8') === 0);
echo pg_client_encoding($db), "\n";
pg_set_client_encoding($db, $before);
Ожидаемый результат
bool(true)
UTF8

Нулевой код — успех. Пример не выбирает LATIN1 ради демонстрации: неверная смена способна исказить реальные данные. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать неизвестное имя кодировкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$before = pg_client_encoding($db);
$code = @pg_set_client_encoding($db, 'NO_SUCH_ENCODING');
var_dump($code === -1);
echo pg_client_encoding($db), "\n";
var_dump(pg_client_encoding($db) === $before);
Ожидаемый результат
bool(true)
UTF8
bool(true)

При отказе функция возвращает -1, а рабочая кодировка остаётся прежней. Не продолжайте импорт, если его контракт требует конкретную кодировку. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_client_encoding(), pg_parameter_status().

pg_parameter_statusПолучить значение session parameter, которое сервер сообщил клиенту через протокол.

PHP 5+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_parameter_status(PgSql\Connection $connection, string $name): string|false

Получить значение session parameter, которое сервер сообщил клиенту через протокол.

Запись pg_parameter_status(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $nameАргумент $name; допустимые значения и единицы указаны ниже в договоре функции.

Возврат и договор. Возвращает строку либо false, если такого status-параметра нет. Типичные имена: server_version, server_encoding, client_encoding, application_name, is_superuser, session_authorization, standard_conforming_strings.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Сервер отправляет ParameterStatus
  2. libpq хранит последнее значение
  3. pg_parameter_status ищет имя
  4. PHP получает строку или false
Прочитать параметр, присланный серверомПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$version = pg_parameter_status($db, 'server_version');
echo is_string($version) && $version !== '' ? "Версия получена\n" : "Нет параметра\n";
Ожидаемый результат
Версия получена

Значение подходит для диагностики возможностей, но ветвление лучше строить по требуемой версии/feature check, а не по строковому сравнению. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отличить отсутствующий параметр от пустой строкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$value = pg_parameter_status($db, 'manual.no_such_parameter');
var_dump($value === false);
Ожидаемый результат
bool(true)

false означает, что сервер не сообщил parameter status с таким именем. Не все GUC автоматически доступны этим API; для произвольного значения используют SHOW/current_setting при наличии права. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_version(), pg_client_encoding(), pg_options().

pg_versionПолучить версии libpq, сервера и связанные сведения текущего соединения.

PHP 5+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_version(?PgSql\Connection $connection = null): array

Получить версии libpq, сервера и связанные сведения текущего соединения.

Запись pg_version(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает ассоциативный массив. Ключ client существует и без соединения в некоторых формах API; server/protocol требуют активного соединения. Не смешивайте версию PHP, ext/pgsql, libpq и PostgreSQL server.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PHP вызывает ext/pgsql
  2. Расширение знает версию libpq client
  3. Соединение знает версию server/protocol
  4. Массив позволяет диагностировать несовместимость
Разделить версию клиента и сервераПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$version = pg_version($db);
echo 'client=', $version['client'], "\n";
echo 'server=', $version['server'], "\n";
Ожидаемый результат
client=16.15
server=18.6

Клиент — libpq внутри php_pgsql.dll, сервер — отдельный процесс PostgreSQL. Новая версия сервера не добавляет функции, отсутствующие в старой клиентской DLL. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить наличие протокольной информацииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$version = pg_version($db);
var_dump(isset($version['protocol']));
var_dump(version_compare($version['server'], '14', '>='));
Ожидаемый результат
bool(true)
bool(true)

Конкретный набор ключей зависит от libpq. Требование проекта формулируют как минимальную серверную версию/возможность и проверяют один раз при старте. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_parameter_status(), pg_jit().

pg_get_pidПолучить PID backend-процесса PostgreSQL для текущего соединения.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_get_pid(PgSql\Connection $connection): int

Получить PID backend-процесса PostgreSQL для текущего соединения.

Запись pg_get_pid(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает положительное целое. Это диагностическая привязка, не PID PHP и не постоянный идентификатор пользователя/задания.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Соединение обслуживает backend
  2. pg_get_pid читает его PID
  3. Журнал связывает запрос с pg_stat_activity
  4. После reconnect значение обновляется
Записать backend PID для диагностикиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$pid = pg_get_pid($db);
var_dump($pid > 0);
Ожидаемый результат
bool(true)

PID относится к процессу PostgreSQL на сервере. Его можно сопоставить с pg_stat_activity при расследовании блокировки. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сверить PID с серверной функциейПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$fromPhp = pg_get_pid($db);
$fromSql = (int) pg_fetch_result(pg_query($db, 'SELECT pg_backend_pid()'), 0, 0);
var_dump($fromPhp === $fromSql);
Ожидаемый результат
bool(true)

Два пути описывают одну backend-сессию. После reset/reconnect номер меняется; со временем ОС может переиспользовать PID. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_version(), pg_connection_reset().

pg_socketПолучить PHP stream underlying-сокета соединения для интеграции с event loop/stream_select.

PHP 5.6+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_socket(PgSql\Connection $connection): resource|false

Получить PHP stream underlying-сокета соединения для интеграции с event loop/stream_select.

Запись pg_socket(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает resource stream либо false. Владельцем остаётся PgSql\Connection: не вызывайте fclose и не читайте сырые байты протокола.

Отказ. false означает, что ext/pgsql не смогла получить сокет соединения. Закрытый PgSql\Connection вызывает Error; полученный stream нельзя закрывать отдельно.

Что происходит
  1. pg_send_query запускает команду
  2. pg_socket даёт наблюдаемый stream
  3. stream_select ждёт readable
  4. pg_consume_input и pg_get_result завершают цикл
Получить поток сокета без его закрытияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
echo get_resource_type($socket), "\n";
Ожидаемый результат
stream

Поток принадлежит соединению. Его используют в stream_select/event loop, но не читают протокол вручную и не закрывают отдельно. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Ждать асинхронный ответ через stream_selectПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$clean = false;
try {
    $socket = pg_socket($db);
    if ($socket === false) { throw new RuntimeException('Нет сокета'); }
    $deadline = hrtime(true) + 3_000_000_000;
    if (!pg_send_query($db, 'SELECT 42 AS answer')) { throw new RuntimeException('Запрос не принят'); }
    $flushed = pg_flush($db);
    if ($flushed === false) { throw new RuntimeException('Ошибка начала отправки'); }

    while ($flushed === 0) {
        $remainingNs = $deadline - hrtime(true);
        if ($remainingNs <= 0) { throw new RuntimeException('Таймаут отправки'); }
        $read = null;
        $write = [$socket];
        $except = null;
        $ready = stream_select($read, $write, $except, 0, (int) min(200_000, ceil($remainingNs / 1_000)));
        if ($ready === false) { throw new RuntimeException('Ошибка ожидания записи'); }
        if ($ready === 0) { continue; }
        $flushed = pg_flush($db);
        if ($flushed === false) { throw new RuntimeException('Ошибка досылки'); }
    }

    while (pg_connection_busy($db)) {
        $remainingNs = $deadline - hrtime(true);
        if ($remainingNs <= 0) { throw new RuntimeException('Таймаут ответа'); }
        $read = [$socket];
        $write = null;
        $except = null;
        $ready = stream_select($read, $write, $except, 0, (int) min(200_000, ceil($remainingNs / 1_000)));
        if ($ready === false) { throw new RuntimeException('Ошибка ожидания чтения'); }
        if ($ready === 0) { continue; }
        if (!pg_consume_input($db)) { throw new RuntimeException('Ошибка чтения libpq'); }
    }

    $result = pg_get_result($db);
    if (!$result instanceof PgSql\Result || pg_result_status($result) !== PGSQL_TUPLES_OK) {
        throw new RuntimeException('Нет успешного результата');
    }
    $answer = pg_fetch_result($result, 0, 'answer');
    while (pg_get_result($db) instanceof PgSql\Result) {}
    $clean = true;
    echo $answer, "\n";
} finally {
    if (!$clean) { @pg_close($db); }
}
Ожидаемый результат
42

stream_select обслуживает и возможную досылку, и чтение ответа в пределах одного монотонного deadline. Каждый результат send/select/consume проверяется; очередь результатов дочитывается. Если синхронизация не подтверждена, uncertain connection закрывается, а не возвращается в pool. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_socket_poll(), pg_consume_input(), pg_get_result().

pg_socket_pollЖдать готовность PostgreSQL-сокета на чтение/запись с таймаутом.

PHP 8.4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_socket_poll(resource $socket, int $read, int $write, int $timeout = -1): int

Ждать готовность PostgreSQL-сокета на чтение/запись с таймаутом.

Запись pg_socket_poll(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
resource $socketStream от pg_socket; не закрывайте его отдельно.
int $read1 — ждать чтение, 0 — не проверять.
int $write1 — ждать запись, 0 — не проверять.
int $timeout (по умолчанию -1)Миллисекунды; -1 ждать без ограничения, 0 проверить немедленно.

Возврат и договор. Возвращает >0 при готовности, 0 при timeout, -1 при ошибке. Timeout задаётся одним целым числом миллисекунд. Это низкоуровневый строитель event loop; обычному синхронному веб-запросу чаще достаточно pg_query_params.

Отказ. -1 означает ошибку системного poll; 0 — штатный таймаут. Не-resource вместо stream от pg_socket отвергается TypeError, а закрывать stream вручную нельзя.

Что происходит
  1. Код выбирает read/write interest числами 1/0
  2. ОС ждёт timeout миллисекунд
  3. Функция возвращает readiness/timeout/error
  4. libpq отдельно потребляет данные
Получить timeout без готовых данныхПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
$ready = pg_socket_poll($socket, 1, 0, 1);
var_dump($ready === 0 || $ready > 0);
Ожидаемый результат
bool(true)

На свободном соединении результат может зависеть от фоновых протокольных данных, поэтому договор — 0 timeout либо положительная readiness, а не фиксированный ответ. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Дождаться готовности результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$clean = false;
try {
    $socket = pg_socket($db);
    if ($socket === false) { throw new RuntimeException('Нет сокета'); }
    $deadline = hrtime(true) + 3_000_000_000;
    if (!pg_send_query($db, 'SELECT 7 * 6 AS answer')) { throw new RuntimeException('Запрос не принят'); }
    $flushed = pg_flush($db);
    if ($flushed === false) { throw new RuntimeException('Ошибка начала отправки'); }

    while ($flushed === 0) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут отправки'); }
        $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания записи'); }
        if ($ready === 0) { usleep(10_000); continue; }
        $flushed = pg_flush($db);
        if ($flushed === false) { throw new RuntimeException('Ошибка досылки'); }
    }

    while (pg_connection_busy($db)) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут ответа'); }
        $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания чтения'); }
        if ($ready === 0) { usleep(10_000); continue; }
        if (!pg_consume_input($db)) { throw new RuntimeException('Ошибка чтения libpq'); }
    }

    $result = pg_get_result($db);
    if (!$result instanceof PgSql\Result || pg_result_status($result) !== PGSQL_TUPLES_OK) {
        throw new RuntimeException('Нет успешного результата');
    }
    $answer = pg_fetch_result($result, 0, 'answer');
    while (pg_get_result($db) instanceof PgSql\Result) {}
    $clean = true;
    echo $answer, "\n";
} finally {
    if (!$clean) { @pg_close($db); }
}
Ожидаемый результат
42

Poll только сообщает readiness; после положительного ответа libpq явно потребляет input, busy проверяется повторно, а результаты дочитываются. На Windows/libpq ранний 0 повторяется до общего монотонного deadline. При ошибке или таймауте uncertain connection закрывается. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_socket(), pg_connect_poll(), pg_consume_input().

pg_cancel_queryПопросить сервер отменить выполняющуюся команду текущего backend.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_cancel_query(PgSql\Connection $connection): bool

Попросить сервер отменить выполняющуюся команду текущего backend.

Запись pg_cancel_query(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает bool успешности отправки cancel request. PHP 8.5 ext/pgsql после запроса отмены очищает оставшиеся результаты этого соединения. Отмена не закрывает соединение и не гарантирует откат уже зафиксированных действий; внутри транзакции проверьте статус и выполните нужный ROLLBACK.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Долгий SQL выполняется backend
  2. pg_cancel_query отправляет отдельный cancel packet
  3. Сервер прерывает текущую команду
  4. ext/pgsql очищает её результаты, код проверяет дальнейшее состояние
Отменить долгий запрос с текущего соединенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_send_query($db, 'SELECT pg_sleep(5)');
usleep(50_000);
var_dump(pg_cancel_query($db));
$result = pg_query($db, 'SELECT 1 AS ready');
echo pg_fetch_result($result, 0, 'ready'), "\n";
Ожидаемый результат
bool(true)
1

Cancel посылает серверу запрос отмены. В PHP 8.5 ext/pgsql pg_cancel_query затем забирает и очищает оставшиеся PGresult, поэтому пример проверяет готовность следующим запросом, а не вызывает pg_get_result. true означает отправку cancel request, не бизнес-откат уже зафиксированных действий. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не использовать cancel как проверку наличия задачиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
var_dump(pg_cancel_query($db));
echo pg_connection_status($db) === PGSQL_CONNECTION_OK ? "Соединение доступно\n" : "Соединение потеряно\n";
Ожидаемый результат
bool(true)
Соединение доступно

На idle-соединении cancel-запрос может успешно отправиться, но отменять нечего. Состояние своей задачи приложение отслеживает отдельно. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_send_query(), pg_transaction_status().

pg_change_passwordИзменить пароль роли PostgreSQL через защищённый протокольный вызов вместо ручной сборки ALTER ROLE.

PHP 8.4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_change_password(PgSql\Connection $connection, string $user, #[\SensitiveParameter] string $password): bool

Изменить пароль роли PostgreSQL через защищённый протокольный вызов вместо ручной сборки ALTER ROLE.

Запись pg_change_password(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $userАргумент $user; допустимые значения и единицы указаны ниже в договоре функции.
#[\SensitiveParameter] string $passwordАргумент $password; допустимые значения и единицы указаны ниже в договоре функции.

Возврат и договор. Возвращает bool. Нужны права менять указанную роль; функция не обновляет secret store приложения и не переключает уже открытые сессии. Параметр password помечен в PHP как SensitiveParameter, поэтому обычная трассировка исключения не должна раскрывать его значение; это не заменяет безопасное хранение и журналирование.

Отказ. false означает отказ сервера, например недостаток прав или неизвестную роль. Не выводите pg_last_error вместе с секретом и не записывайте password в свой журнал.

Что происходит
  1. Код получает новый секрет из защищённого источника
  2. pg_change_password просит сервер изменить роль
  3. Сервер проверяет полномочия
  4. Secret store и новые соединения переключаются по плану
Проверить вызов без смены учебного секретаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$password = getenv('PGPASSWORD');
if ($password === false) { throw new RuntimeException('Нет PGPASSWORD'); }
var_dump(pg_change_password($db, getenv('PGUSER'), $password));
Ожидаемый результат
bool(true)

Учебный пользователь меняет собственный пароль на то же значение, поэтому стенд остаётся доступным. В приложении новый секрет сначала безопасно доставляют потребителям и предусматривают ротацию/откат. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть отказ при попытке изменить чужую рольПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$changed = @pg_change_password($db, 'postgres', 'not-used-value');
var_dump($changed === false);
echo pg_connection_status($db) === PGSQL_CONNECTION_OK ? "Связь жива\n" : "Связь потеряна\n";
Ожидаемый результат
bool(true)
Связь жива

Непривилегированная роль не меняет пароль postgres. Ошибка прав не означает разрыв соединения; секрет не попадает в журнал примера. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connect(), pg_parameter_status().

pg_queryОтправить синхронный SQL и дождаться его результата.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_query(PgSql\Connection $connection, string $query): PgSql\Result|false

Отправить синхронный SQL и дождаться его результата.

Запись pg_query(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $querySQL, структура которого известна программе. Несколько команд усложняют обработку статусов и транзакций; для учебных/боевых операций отправляйте осмысленные шаги отдельно.

Возврат и договор. Возвращает PgSql\Result либо false. Пока сервер выполняет запрос, текущий PHP-поток ждёт. Функция допускает SQL-текст, но внешние значения нельзя склеивать с ним: используйте pg_query_params/pg_prepare.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Программа формирует постоянный SQL
  2. libpq отправляет команду
  3. PHP ждёт завершение
  4. PgSql\Result даёт строки или командный статус
Выполнить постоянный SQL без внешних значенийПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 2 + 3 AS total');
if ($result === false) { throw new RuntimeException(pg_last_error($db)); }
echo pg_fetch_result($result, 0, 'total'), "\n";
Ожидаемый результат
5

SQL полностью задан программой; результат проверяется до чтения. Для значений формы, URL или API нужен pg_query_params. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Создать временную таблицу и увидеть командный статусПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$created = pg_query($db, 'CREATE TEMP TABLE task_queue (id integer PRIMARY KEY, title text NOT NULL)');
if ($created === false) { throw new RuntimeException(pg_last_error($db)); }
echo pg_result_status($created, PGSQL_STATUS_STRING), "\n";
Ожидаемый результат
CREATE TABLE

DDL возвращает PgSql\Result без строк. Статус показывает выполненную команду; временная таблица исчезнет вместе с соединением. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_send_query(), pg_result_status().

pg_query_paramsВыполнить один SQL-запрос, передав значения отдельно через $1, $2 и далее.

PHP 5.1+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_query_params(PgSql\Connection $connection, string $query, array $params): PgSql\Result|false

Выполнить один SQL-запрос, передав значения отдельно через $1, $2 и далее.

Запись pg_query_params(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $queryРовно одна SQL-команда с placeholders $1, $2... Значения не заключают в SQL-кавычки.
array $paramsИндексный массив значений. null передаёт SQL NULL; boolean/числа/массивы нормализуйте под ожидаемый PostgreSQL type.

Возврат и договор. Возвращает PgSql\Result либо false. Число параметров должно совпасть с placeholders. null становится SQL NULL; остальные значения передаются в текстовом формате, а PostgreSQL выводит тип из контекста или явного cast. PHP-массив сам по себе не становится PostgreSQL array.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. SQL содержит $1/$2, но не пользовательский текст
  2. PHP передаёт отдельный список значений
  3. PostgreSQL выводит/проверяет типы
  4. Результат читается только после проверки false
Сохранить кавычку и SQL-подобный текст как данныеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE products (name text NOT NULL)');
$name = "Ручка'); DROP TABLE products; --";
$insert = pg_query_params($db, 'INSERT INTO products (name) VALUES ($1)', [$name]);
if ($insert === false) { throw new RuntimeException(pg_last_error($db)); }
$result = pg_query($db, 'SELECT name FROM products');
echo pg_fetch_result($result, 0, 'name'), "\n";
Ожидаемый результат
Ручка'); DROP TABLE products; --

Строка не становится частью SQL-синтаксиса: сервер получает команду и значение отдельно. Параметры защищают границу значений, а не разрешения пользователя или имена таблиц. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Передать NULL, boolean и число с явными типами SQLПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query_params($db, 'SELECT $1::integer AS qty, $2::boolean AS enabled, $3::text AS note', ['12', 'true', null]);
if ($result === false) { throw new RuntimeException(pg_last_error($db)); }
$row = pg_fetch_assoc($result);
echo $row['qty'], '|', $row['enabled'], '|', ($row['note'] === null ? 'NULL' : $row['note']), "\n";
Ожидаемый результат
12|t|NULL

ext/pgsql передаёт значения как строки/NULL без массива OID типов; явный cast делает договор понятным. PHP false превращается в пустую строку и часто не соответствует ожидаемой SQL-логике, поэтому boolean нормализуют явно. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_prepare(), pg_execute(), pg_escape_identifier().

pg_prepareСоздать именованную либо безымянную prepared statement в текущей PostgreSQL-сессии.

PHP 5.1+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_prepare(PgSql\Connection $connection, string $statement_name, string $query): PgSql\Result|false

Создать именованную либо безымянную prepared statement в текущей PostgreSQL-сессии.

Запись pg_prepare(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $statement_nameНепустое уникальное имя либо пустая строка для заменяемой безымянной statement в текущей сессии.
string $queryОдна SQL-команда с $1/$2; значения будут переданы при execute.

Возврат и договор. Возвращает PgSql\Result со статусом подготовки либо false. Выполнение происходит позже через pg_execute/pg_send_execute. Непустое имя должно быть уникально в сессии; пустая строка выбирает единственную безымянную statement, которую следующий вызов с пустым именем заменяет. Планирование может быть custom или generic; «prepare всегда быстрее» без измерения неверно.

Отказ. Повтор уже занятого непустого имени возвращает false и warning. Пустое имя само по себе не ошибка: оно намеренно заменяет прежнюю безымянную statement.

Что происходит
  1. Программа даёт SQL с $1
  2. Сервер разбирает и запоминает statement_name
  3. PgSql\Result подтверждает подготовку
  4. pg_execute позже передаёт значения
Подготовить вставку один разПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE jobs (id integer PRIMARY KEY, title text NOT NULL)');
$prepared = pg_prepare($db, 'add_job', 'INSERT INTO jobs (id, title) VALUES ($1::integer, $2::text)');
if ($prepared === false) { throw new RuntimeException(pg_last_error($db)); }
echo pg_result_status($prepared) === PGSQL_COMMAND_OK ? "Подготовлено\n" : "Неожиданный статус\n";
Ожидаемый результат
Подготовлено

Подготовка разбирает SQL в текущей сессии, но ещё не вставляет строку. У результата подготовки строковый command tag пуст, поэтому проверяется числовой статус PGSQL_COMMAND_OK. Имя add_job существует только на этом соединении. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать повтор имени prepared statementПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$first = pg_prepare($db, 'manual_duplicate', 'SELECT $1::integer');
$second = @pg_prepare($db, 'manual_duplicate', 'SELECT $1::text');
var_dump($first instanceof PgSql\Result);
var_dump($second === false);
Ожидаемый результат
bool(true)
bool(true)

Одно имя нельзя молча переопределить другой командой. Выберите уникальные имена, DEALLOCATE/pg_close_stmt при доступности либо держите каталог prepared statements в одном месте. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Осознанно заменить безымянную prepared statementПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$first = pg_prepare($db, '', 'SELECT 1 AS value');
$second = pg_prepare($db, '', 'SELECT 2 AS value');
if ($first === false || $second === false) { throw new RuntimeException(pg_last_error($db)); }
$result = pg_execute($db, '', []);
if ($result === false) { throw new RuntimeException(pg_last_error($db)); }
echo pg_fetch_result($result, 0, 'value'), "\n";
Ожидаемый результат
2

Пустая строка — допустимое имя специальной безымянной statement. Следующий pg_prepare с пустым именем заменяет предыдущую; это отличается от конфликта двух одинаковых непустых имён. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_execute(), pg_close_stmt(), pg_query_params().

pg_executeВыполнить ранее подготовленную команду с конкретным набором значений.

PHP 5.1+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_execute(PgSql\Connection $connection, string $statement_name, array $params): PgSql\Result|false

Выполнить ранее подготовленную команду с конкретным набором значений.

Запись pg_execute(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $statement_nameИмя prepared statement, созданной на этом же соединении.
array $paramsИндексный список значений для всех placeholders в порядке $1, $2...

Возврат и договор. Возвращает PgSql\Result либо false. Statement должна существовать на том же соединении. Значения передаются как текст/NULL; число и типы проверяет PostgreSQL по подготовленной команде.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. pg_prepare создал add_job
  2. pg_execute связывает список значений
  3. Сервер выполняет prepared statement
  4. PgSql\Result показывает строки/affected rows или false
Исполнить одну подготовленную вставку для двух заданийПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE jobs (id integer PRIMARY KEY, title text NOT NULL)');
pg_prepare($db, 'add_job_twice', 'INSERT INTO jobs (id, title) VALUES ($1::integer, $2::text)');
foreach ([[1, 'Импорт'], [2, 'Отчёт']] as $params) {
    $result = pg_execute($db, 'add_job_twice', $params);
    if ($result === false) { throw new RuntimeException(pg_last_error($db)); }
}
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM jobs'), 0, 0), "\n";
Ожидаемый результат
2

SQL подготовлен один раз, данные меняются. Это удобно внутри одного соединения/worker; между разными соединениями statement нужно подготовить отдельно. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить контролируемый отказ несовместимого значенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_prepare($db, 'need_integer', 'SELECT $1::integer AS number');
$failed = @pg_execute($db, 'need_integer', ['twelve']);
var_dump($failed === false);
echo pg_connection_status($db) === PGSQL_CONNECTION_OK ? "Связь жива\n" : "Связь потеряна\n";
Ожидаемый результат
bool(true)
Связь жива

Сервер отверг значение типа integer, но соединение сохранилось. Если вызов был внутри BEGIN, транзакция станет INERROR и потребует ROLLBACK. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_prepare(), pg_send_execute(), pg_close_stmt().

pg_close_stmtОсвободить именованную prepared statement на сервере через libpq 17+.

PHP 8.5+; требует libpq 17+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_close_stmt(PgSql\Connection $connection, string $statement_name): PgSql\Result|false

Освободить именованную prepared statement на сервере через libpq 17+.

Запись pg_close_stmt(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $statement_nameИмя подготовленной команды в пределах текущего соединения; это идентификатор протокола, а не SQL-текст.

Возврат и договор. Возвращает PgSql\Result либо false. Функция существует только если ext/pgsql собрана с PQclosePrepared из libpq 17+. Имя после успешного закрытия можно подготовить заново.

Отказ. Пустое имя и строка с NUL отвергаются ValueError. false означает, что сервер не закрыл statement; отсутствие самой функции означает сборку ext/pgsql без PQclosePrepared, а не слишком старый PostgreSQL server.

Граница текущего стенда. Requires a build with libpq 17+ PQclosePrepared support; the local DLL reports libpq 16.15. Карточка сохраняется, потому что функция есть в официальной ветке PHP 8.5.11; пример для неё проверяется по исходнику/синтаксису и явно не выдаётся за выполненный на этой DLL.

Что происходит
  1. Код проверяет function_exists/build capability
  2. pg_close_stmt отправляет close prepared
  3. Сервер освобождает statement resources
  4. Имя можно использовать повторно
Закрыть statement, если сборка поддерживает libpq 17 APIПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
if (!function_exists('pg_close_stmt')) {
    echo "Нужна PHP-сборка с libpq 17+\n";
    return;
}
pg_prepare($db, 'temporary_supported', 'SELECT 1');
$closed = pg_close_stmt($db, 'temporary_supported');
echo $closed instanceof PgSql\Result ? "Statement закрыта\n" : "Закрыть не удалось\n";
Ожидаемый результат
Нужна PHP-сборка с libpq 17+

На текущем стенде php_pgsql.dll собрана с libpq 16.15, поэтому честно выполняется capability-ветка. На поддерживаемой сборке тот же пример вызывает pg_close_stmt и проверяет реальный PgSql\Result. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

После close повторно использовать то же имяПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
if (!function_exists('pg_close_stmt')) {
    echo "Нужна PHP-сборка с libpq 17+\n";
    return;
}
pg_prepare($db, 'reusable_stmt', 'SELECT 1 AS value');
$closed = pg_close_stmt($db, 'reusable_stmt');
if (!$closed instanceof PgSql\Result) { throw new RuntimeException(pg_last_error($db)); }
pg_prepare($db, 'reusable_stmt', 'SELECT 2 AS value');
$result = pg_execute($db, 'reusable_stmt', []);
echo pg_fetch_result($result, 0, 'value'), "\n";
Ожидаемый результат
Нужна PHP-сборка с libpq 17+

Текущая сборка показывает честную границу capability. При libpq 17+ пример выполняет саму функцию, затем доказывает документированный эффект: освобождённое имя можно подготовить заново. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Освободить имя совместимым SQL DEALLOCATEПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
if (pg_prepare($db, 'temporary_calc', 'SELECT $1::integer * 2') === false) {
    throw new RuntimeException('Команда не подготовлена');
}
try {
    if (function_exists('pg_close_stmt')) {
        $closed = pg_close_stmt($db, 'temporary_calc');
        if (!$closed instanceof PgSql\Result) { throw new RuntimeException('Не закрыто через API'); }
        echo "Закрыто API\n";
    } else {
        $closed = pg_query($db, 'DEALLOCATE temporary_calc');
        if (!$closed instanceof PgSql\Result) { throw new RuntimeException('Не закрыто через SQL'); }
        echo "Закрыто SQL\n";
    }

    if (pg_prepare($db, 'temporary_calc', 'SELECT $1::integer * 3') === false) {
        throw new RuntimeException('Имя не освободилось');
    }
    $result = pg_execute($db, 'temporary_calc', [7]);
    if (!$result instanceof PgSql\Result) { throw new RuntimeException('Повторная команда не выполнена'); }
    echo 'Повторное имя: ', pg_fetch_result($result, 0, 0), "\n";
} finally {
    @pg_query($db, 'DEALLOCATE temporary_calc');
}
Ожидаемый результат
Закрыто SQL
Повторное имя: 21

И pg_close_stmt, и совместимый SQL DEALLOCATE освобождают server-side prepared statement в текущей сессии. Пример доказывает это повторной подготовкой и выполнением команды под тем же именем; финальный DEALLOCATE очищает учебное состояние. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_prepare(), pg_execute().

pg_escape_identifierЗаключить одну часть SQL-идентификатора в двойные кавычки по правилам текущего соединения.

PHP 5.4.4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_escape_identifier(PgSql\Connection $connection, string $string): string|false

Заключить одну часть SQL-идентификатора в двойные кавычки по правилам текущего соединения.

Запись pg_escape_identifier(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $stringАргумент $string; допустимые значения и единицы указаны ниже в договоре функции.

Возврат и договор. Возвращает quoted identifier. Функция не проверяет существование и полномочия; внешнее имя сначала ограничивают allowlist. Для значений используйте параметры, не identifiers.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Пользователь выбирает ключ из allowlist
  2. pg_escape_identifier цитирует одну часть
  3. Программа собирает SQL-структуру
  4. Значения остаются $1-параметрами
Выбрать разрешённую колонку сортировкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$allowed = ['name', 'price_kopecks'];
$requested = 'name';
if (!in_array($requested, $allowed, true)) { throw new InvalidArgumentException('Неизвестная колонка'); }
$sql = 'SELECT ' . pg_escape_identifier($db, $requested) . " FROM (VALUES ('Ручка')) AS p(name)";
echo pg_fetch_result(pg_query($db, $sql), 0, 0), "\n";
Ожидаемый результат
Ручка

Allowlist решает, разрешена ли колонка, quoting — как записать уже разрешённое имя. Параметр $1 не может заменить identifier. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Цитировать части qualified name отдельноПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$schema = pg_escape_identifier($db, 'pg_temp');
$table = pg_escape_identifier($db, 'Order Items');
echo $schema, '.', $table, "\n";
Ожидаемый результат
"pg_temp"."Order Items"

Если передать строку pg_temp.Order Items целиком, точка станет частью одного identifier. Структуру schema.table задаёт программа, каждую часть цитирует отдельно. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_escape_literal().

pg_escape_literalСоздать полный quoted SQL literal для редких мест, где параметр нельзя использовать.

PHP 5.4.4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_escape_literal(PgSql\Connection $connection, string $string): string|false

Создать полный quoted SQL literal для редких мест, где параметр нельзя использовать.

Запись pg_escape_literal(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $stringАргумент $string; допустимые значения и единицы указаны ниже в договоре функции.

Возврат и договор. Возвращает строку вместе с внешними одинарными кавычками либо false при ошибке соединения/памяти. Это не общий заменитель pg_query_params.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Текст остаётся значением
  2. pg_escape_literal добавляет кавычки/escaping
  3. Программа вставляет literal только в разрешённое место SQL
  4. Сервер получает одну структуру
Увидеть безопасную SQL-запись литералаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$literal = pg_escape_literal($db, "O'Reilly");
echo $literal, "\n";
Ожидаемый результат
'O''Reilly'

Возвращаемая строка уже содержит внешние одинарные кавычки. Для обычного запроса pg_query_params понятнее; literal нужен главным образом при построении ограниченной SQL-структуры, где параметры невозможны. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Задать комментарий DDL из доверенной конфигурацииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE manual_literal (id integer)');
$comment = pg_escape_literal($db, "Учебная таблица O'Reilly");
$result = pg_query($db, 'COMMENT ON TABLE manual_literal IS ' . $comment);
echo pg_result_status($result, PGSQL_STATUS_STRING), "\n";
Ожидаемый результат
COMMENT

COMMENT ON не принимает $1 в utility-команде этого вида. Значение берётся из доверенной конфигурации и цитируется как literal; имя таблицы остаётся программной константой. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_escape_string(), pg_escape_identifier().

pg_escape_stringЭкранировать содержимое SQL-строки без добавления внешних кавычек.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_escape_string(PgSql\Connection $connection, string $string): string

Экранировать содержимое SQL-строки без добавления внешних кавычек.

Запись pg_escape_string(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $stringАргумент $string; допустимые значения и единицы указаны ниже в договоре функции.

Возврат и договор. Возвращает escaped string. Современный основной путь для данных — pg_query_params/pg_execute. Функция нужна при работе с legacy-конструкторами или специальными форматами после анализа контекста.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Строка содержит апостроф
  2. pg_escape_string удваивает/экранирует по соединению
  3. Код сам обязан добавить корректный SQL-контекст
  4. Ошибка контекста остаётся ответственностью автора
Увидеть отличие от полного literalПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$escaped = pg_escape_string($db, "O'Reilly");
echo $escaped, "\n";
echo pg_escape_literal($db, "O'Reilly"), "\n";
Ожидаемый результат
O''Reilly
'O''Reilly'

pg_escape_string возвращает только внутреннее содержимое. Если вручную забыть внешние кавычки или применить строку в другом контексте, SQL сломается; поэтому параметризация предпочтительнее. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сравнить ручную сборку с параметромПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$value = "A'B";
$manual = "SELECT '" . pg_escape_string($db, $value) . "' AS value";
$first = pg_fetch_result(pg_query($db, $manual), 0, 0);
$second = pg_fetch_result(pg_query_params($db, 'SELECT $1::text AS value', [$value]), 0, 0);
var_dump($first === $second);
Ожидаемый результат
bool(true)

Оба дают один результат для простого text, но второй вариант отделяет структуру и значение и не требует помнить кавычки. Escape не решает identifiers, LIKE wildcards, JSON-path или regex semantics. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_escape_literal(), pg_query_params().

pg_convertПреобразовать ассоциативные значения в SQL-представления с учётом типов существующей таблицы.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_convert(PgSql\Connection $connection, string $table_name, array $values, int $flags = 0): array|false

Преобразовать ассоциативные значения в SQL-представления с учётом типов существующей таблицы.

Запись pg_convert(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя таблицы. Параметры запросов не подставляют идентификаторы; внешнее имя сначала проверяют по allowlist.
array $valuesАссоциативный массив «колонка => значение» для DML-помощника.
int $flags (по умолчанию 0)PGSQL_CONV_IGNORE_DEFAULT, PGSQL_CONV_FORCE_NULL и/или PGSQL_CONV_IGNORE_NOT_NULL. Они меняют техническое преобразование, но не правила предметной области.

Возврат и договор. Возвращает преобразованный массив либо false; с PHP 8.3 несовместимые типы/значения могут выбросить TypeError/ValueError. Требует доступа к метаданным таблицы и не заменяет бизнес-валидацию.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Ключи массива сопоставляются колонкам
  2. pg_convert читает типы таблицы
  3. Значения получают SQL-представление
  4. DML helper либо автор SQL использует результат осознанно
Преобразовать значения по реальной схеме таблицыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$table = 'manual_convert_items_' . pg_get_pid($db) . '_' . bin2hex(random_bytes(6));
$identifier = pg_escape_identifier($db, $table);
$created = false;
try {
    if (pg_query($db, "CREATE TABLE {$identifier} (qty integer NOT NULL, enabled boolean, note text)") === false) {
        throw new RuntimeException('Учебная таблица не создана');
    }
    $created = true;
    $converted = pg_convert($db, $table, ['qty' => 12, 'enabled' => true, 'note' => null]);
    if ($converted === false) { throw new RuntimeException('Не преобразовано'); }
    echo $converted['"qty"'], '|', $converted['"enabled"'], '|', $converted['"note"'], "\n";
} finally {
    if ($created && pg_query($db, "DROP TABLE {$identifier}") === false) {
        throw new RuntimeException('Своя учебная таблица не удалена');
    }
}
Ожидаемый результат
12|'t'|NULL

Функция читает каталог обычной таблицы и возвращает SQL-представления, а не «очищенный бизнес-массив». Имя постоянно только в пределах опыта, но получает PID и случайный суффикс; чужая таблица заранее не удаляется, а finally удаляет объект лишь после собственного успешного CREATE. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить TypeError до отправки неправильного integerПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$table = 'manual_convert_numbers_' . pg_get_pid($db) . '_' . bin2hex(random_bytes(6));
$identifier = pg_escape_identifier($db, $table);
$created = false;
try {
    if (pg_query($db, "CREATE TABLE {$identifier} (qty integer NOT NULL)") === false) {
        throw new RuntimeException('Учебная таблица не создана');
    }
    $created = true;
    try {
        pg_convert($db, $table, ['qty' => ['not', 'scalar']]);
    } catch (TypeError|ValueError $error) {
        echo $error::class, "\n";
    }
} finally {
    if ($created && pg_query($db, "DROP TABLE {$identifier}") === false) {
        throw new RuntimeException('Своя учебная таблица не удалена');
    }
}
Ожидаемый результат
TypeError

С PHP 8.3 несовместимое значение вызывает TypeError/ValueError. Случайное имя исключает столкновение параллельных запусков; флаг created не позволяет cleanup удалить таблицу, которую этот запуск не создал. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_meta_data(), pg_insert(), pg_update().

pg_meta_dataПолучить описание колонок существующей таблицы.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_meta_data(PgSql\Connection $connection, string $table_name, bool $extended = false): array|false

Получить описание колонок существующей таблицы.

Запись pg_meta_data(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя таблицы. Параметры запросов не подставляют идентификаторы; внешнее имя сначала проверяют по allowlist.
bool $extended (по умолчанию false)false для базового набора, true для расширенного. Проверяйте наличие конкретных ключей перед чтением.

Возврат и договор. Возвращает массив по именам колонок либо false. Базовые элементы включают num, type, len, not null, has default; extended добавляет сведения. Требуются видимость/права каталога.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Функция находит table OID
  2. Читает системные каталоги
  3. Строит карту колонка → свойства
  4. Код использует только ожидаемые поля
Увидеть тип и обязательность колонкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$table = 'manual_metadata_items_' . pg_get_pid($db) . '_' . bin2hex(random_bytes(6));
$identifier = pg_escape_identifier($db, $table);
$created = false;
try {
    if (pg_query($db, "CREATE TABLE {$identifier} (id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, title text NOT NULL, note text)") === false) {
        throw new RuntimeException('Учебная таблица не создана');
    }
    $created = true;
    $meta = pg_meta_data($db, $table);
    if ($meta === false) { throw new RuntimeException('Метаданные недоступны'); }
    echo $meta['title']['type'], '|', ($meta['title']['not null'] ? 'required' : 'optional'), "\n";
} finally {
    if ($created && pg_query($db, "DROP TABLE {$identifier}") === false) {
        throw new RuntimeException('Своя учебная таблица не удалена');
    }
}
Ожидаемый результат
text|required

Метаданные помогают универсальному импортеру, но не превращают любую колонку в разрешённое поле формы. PID и случайный суффикс изолируют параллельные запуски; cleanup удаляет только таблицу, успешно созданную этим запуском. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Найти default/identity в расширенных метаданныхПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$table = 'manual_metadata_extended_' . pg_get_pid($db) . '_' . bin2hex(random_bytes(6));
$identifier = pg_escape_identifier($db, $table);
$created = false;
try {
    if (pg_query($db, "CREATE TABLE {$identifier} (id bigint GENERATED ALWAYS AS IDENTITY, created_at timestamptz NOT NULL DEFAULT now())") === false) {
        throw new RuntimeException('Учебная таблица не создана');
    }
    $created = true;
    $meta = pg_meta_data($db, $table, true);
    if ($meta === false) { throw new RuntimeException('Метаданные недоступны'); }
    var_dump($meta['created_at']['has default']);
    echo $meta['created_at']['type'], "\n";
} finally {
    if ($created && pg_query($db, "DROP TABLE {$identifier}") === false) {
        throw new RuntimeException('Своя учебная таблица не удалена');
    }
}
Ожидаемый результат
bool(true)
timestamptz

Набор полей extended зависит от версии; код проверяет нужный ключ. Случайное имя не пересекается с чужими объектами, а created-флаг ограничивает DROP объектом этого запуска. Runtime-introspection не заменяет миграции. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_convert(), pg_field_type(), pg_field_table().

pg_insertВставить одну запись из ассоциативного массива либо получить построенный SQL.

PHP 4.3+; ValueError/TypeError для части неверных входов с PHP 8.3 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_insert(PgSql\Connection $connection, string $table_name, array $values, int $flags = PGSQL_DML_EXEC): PgSql\Result|string|bool

Вставить одну запись из ассоциативного массива либо получить построенный SQL.

Запись pg_insert(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя таблицы. Параметры запросов не подставляют идентификаторы; внешнее имя сначала проверяют по allowlist.
array $valuesТолько ожидаемые ключи колонок. Не передавайте $_POST целиком: сначала allowlist, типы и бизнес-правила.
int $flags (по умолчанию PGSQL_DML_EXEC)Обычно PGSQL_DML_ESCAPE|PGSQL_DML_EXEC либо PGSQL_DML_STRING для осознанного preview. NO_CONV/raw требуют отдельного доказательства безопасности.

Возврат и договор. В зависимости от flags возвращает PgSql\Result/bool, SQL-строку либо false. Официальный default работает с raw values: без PGSQL_DML_ESCAPE данные должны быть заранее корректно подготовлены. Для внешних данных предпочтительнее явный pg_query_params.

Отказ. Неизвестная таблица/колонка, несовместимое значение или неверная комбинация флагов дают false либо, начиная с PHP 8.3, ValueError/TypeError для части ошибок входа. Проверяйте конкретный возвращаемый вариант до чтения результата.

Что происходит
  1. Ключи массива становятся колонками
  2. Флаги решают conversion/escape/execute/string
  3. Сервер выполняет один INSERT либо PHP получает SQL
  4. Код проверяет affected rows/ошибку
Добавить проверенную запись DML-помощникомПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE helper_products (sku text PRIMARY KEY, price integer NOT NULL)');
$result = pg_insert($db, 'helper_products', ['sku' => 'PEN-01', 'price' => 120], PGSQL_DML_ESCAPE | PGSQL_DML_EXEC);
if (!$result instanceof PgSql\Result) { throw new RuntimeException('INSERT не выполнен'); }
echo pg_affected_rows($result), "\n";
Ожидаемый результат
1

Флаг ESCAPE обязателен для этого варианта с raw values. Для динамического приложения pg_query_params обычно яснее: виден SQL, типы, RETURNING и обработка конфликта. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Посмотреть SQL вместо выполненияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE helper_preview (name text NOT NULL)');
$sql = pg_insert($db, 'helper_preview', ['name' => "O'Reilly"], PGSQL_DML_ESCAPE | PGSQL_DML_STRING);
echo str_starts_with((string) $sql, 'INSERT INTO'), "\n";
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM helper_preview'), 0, 0), "\n";
Ожидаемый результат
1
0

STRING возвращает текст и не меняет таблицу. Не публикуйте SQL с персональными значениями в браузер/обычный лог; preview нужен для диагностики/миграции и требует тех же проверок. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_convert(), pg_select().

pg_selectПолучить записи таблицы по набору простых равенств «колонка => значение».

PHP 4.3+; пустой conditions разрешён с PHP 8.4 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_select(PgSql\Connection $connection, string $table_name, array $conditions = [], int $flags = PGSQL_DML_EXEC, int $mode = PGSQL_ASSOC): array|string|false

Получить записи таблицы по набору простых равенств «колонка => значение».

Запись pg_select(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя таблицы. Параметры запросов не подставляют идентификаторы; внешнее имя сначала проверяют по allowlist.
array $conditions (по умолчанию [])Только ожидаемые колонки и значения равенства. С PHP 8.4 пустой массив выбирает все строки.
int $flags (по умолчанию PGSQL_DML_EXEC)PGSQL_DML_ESCAPE|EXEC для выполнения либо STRING для preview; conversion-флаги меняют обработку значений.
int $mode (по умолчанию PGSQL_ASSOC)Режим, задаваемый PGSQL_* константой; допустимые значения перечислены в договоре функции.

Возврат и договор. При PGSQL_DML_EXEC возвращает массив строк либо false; при PGSQL_DML_STRING — SQL string. Это ограниченный helper без произвольных JOIN/range/sort. Начиная с PHP 8.4 пустой conditions разрешён и выбирает все строки, поэтому массовое чтение должно быть явной политикой приложения.

Отказ. false сообщает об ошибке метаданных/SQL. До PHP 8.4 пустой conditions отвергался; в PHP 8.4+ он допустим и способен вернуть всю таблицу — проверяйте бизнес-намерение до вызова.

Что происходит
  1. Allowlist формирует conditions
  2. Helper преобразует имена и значения
  3. SQL выполняется или возвращается строкой
  4. Массив строк проверяется перед выдачей
Выбрать строки по простому равенствуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE helper_orders (id integer PRIMARY KEY, status text NOT NULL)');
pg_query($db, "INSERT INTO helper_orders VALUES (1, 'new'), (2, 'paid')");
$rows = pg_select($db, 'helper_orders', ['status' => 'paid'], PGSQL_DML_ESCAPE | PGSQL_DML_EXEC);
echo count($rows), '|', $rows[0]['id'], "\n";
Ожидаемый результат
1|2

Помощник строит только равенства по переданным колонкам. Для диапазона, JOIN, ORDER BY и LIMIT нужен явный параметризованный SELECT. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не допустить случайный полный экспортПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE helper_customers (id integer PRIMARY KEY)');
pg_query($db, 'INSERT INTO helper_customers VALUES (1), (2)');
$conditions = [];
if ($conditions === []) { echo "Фильтр обязателен\n"; }
else { pg_select($db, 'helper_customers', $conditions); }
Ожидаемый результат
Фильтр обязателен

Пустой conditions может означать все строки. Решение — явная политика приложения, а не надежда на helper. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_insert(), pg_update().

pg_updateОбновить колонки строк, подходящих под простые условия равенства.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_update(PgSql\Connection $connection, string $table_name, array $values, array $conditions, int $flags = PGSQL_DML_EXEC): string|bool

Обновить колонки строк, подходящих под простые условия равенства.

Запись pg_update(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя таблицы. Параметры запросов не подставляют идентификаторы; внешнее имя сначала проверяют по allowlist.
array $valuesРазрешённые изменяемые колонки; не весь входной массив.
array $conditionsОбязательные в обычном сценарии условия равенства; массовый UPDATE оформляют отдельной операцией.
int $flags (по умолчанию PGSQL_DML_EXEC)Режим conversion/escape и EXEC/STRING.

Возврат и договор. Возвращает true при успехе, false при отказе либо SQL string с PGSQL_DML_STRING. Сам helper не возвращает affected rows. Пустые/слишком широкие conditions способны изменить много строк; сложные и конкурентные изменения пишите явным параметризованным SQL с RETURNING.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Проверенные values задают новые поля
  2. Проверенные conditions задают равенства
  3. Helper строит UPDATE
  4. Код проверяет bool и при необходимости отдельно читает результат
Изменить одну найденную записьПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE helper_stock (sku text PRIMARY KEY, qty integer NOT NULL)');
pg_query($db, "INSERT INTO helper_stock VALUES ('PEN-01', 5)");
$updated = pg_update($db, 'helper_stock', ['qty' => 4], ['sku' => 'PEN-01'], PGSQL_DML_ESCAPE | PGSQL_DML_EXEC);
var_dump($updated);
echo pg_fetch_result(pg_query($db, "SELECT qty FROM helper_stock WHERE sku='PEN-01'"), 0, 0), "\n";
Ожидаемый результат
bool(true)
4

pg_update возвращает true при успешном выполнении, а не PgSql\Result, поэтому число изменённых строк из него получить нельзя. Для атомарного списания и точной проверки масштаба лучше явный UPDATE ... WHERE qty >= $1 RETURNING. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Остановить UPDATE без условийПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$conditions = [];
if ($conditions === []) { echo "UPDATE без WHERE запрещён\n"; } else { pg_update($db, 'some_table', ['status' => 'done'], $conditions); }
Ожидаемый результат
UPDATE без WHERE запрещён

Код делает политику видимой до обращения к БД. Даже если helper разрешает форму, массовое изменение требует отдельного намерения/права. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_select(), pg_delete().

pg_deleteУдалить строки по простым условиям либо получить построенный DELETE.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_delete(PgSql\Connection $connection, string $table_name, array $conditions, int $flags = PGSQL_DML_EXEC): string|bool

Удалить строки по простым условиям либо получить построенный DELETE.

Запись pg_delete(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя таблицы. Параметры запросов не подставляют идентификаторы; внешнее имя сначала проверяют по allowlist.
array $conditionsКлючи/значения, однозначно ограничивающие удаление. Пустой массив в обычном API запрещайте до helper.
int $flags (по умолчанию PGSQL_DML_EXEC)PGSQL_DML_ESCAPE и EXEC для выполнения либо STRING для осознанного preview.

Возврат и договор. Возвращает true при успехе, false при отказе либо SQL string с PGSQL_DML_STRING. Сам helper не сообщает affected rows. Удаление необратимо после commit; проверяйте условия, право и политику аудита/soft delete, а для точного результата используйте явный DELETE ... RETURNING.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Приложение проверяет право удаления
  2. Conditions ограничивают строки
  3. Helper строит/исполняет DELETE
  4. Код проверяет bool либо использует RETURNING в явном SQL
Удалить конкретную учебную записьПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE helper_tokens (id integer PRIMARY KEY, label text NOT NULL)');
pg_query($db, "INSERT INTO helper_tokens VALUES (1, 'expired'), (2, 'active')");
$deleted = pg_delete($db, 'helper_tokens', ['id' => 1], PGSQL_DML_ESCAPE | PGSQL_DML_EXEC);
var_dump($deleted);
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM helper_tokens'), 0, 0), "\n";
Ожидаемый результат
bool(true)
1

Удаление ограничено первичным ключом; true подтверждает выполнение helper, но не сообщает affected rows. В рабочем сервисе проверяют владельца/право и решают, требуется soft delete/audit. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить DELETE как строку без выполненияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE helper_cleanup (id integer PRIMARY KEY)');
pg_query($db, 'INSERT INTO helper_cleanup VALUES (1)');
$sql = pg_delete($db, 'helper_cleanup', ['id' => 1], PGSQL_DML_ESCAPE | PGSQL_DML_STRING);
echo str_starts_with((string) $sql, 'DELETE FROM'), "\n";
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM helper_cleanup'), 0, 0), "\n";
Ожидаемый результат
1
1

STRING помогает увидеть действие, но лог с полными значениями может утечь. Preview не является разрешением на последующее выполнение. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_select(), pg_update().

pg_affected_rowsУзнать, сколько строк затронул INSERT, UPDATE или DELETE; PostgreSQL также сообщает число строк SELECT.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_affected_rows(PgSql\Result $result): int

Узнать, сколько строк затронул INSERT, UPDATE или DELETE; PostgreSQL также сообщает число строк SELECT.

Запись pg_affected_rows(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.

Возврат и договор. Возвращает целое число. Ноль означает, что команда не затронула ни одной строки, и сам по себе не является PHP-ошибкой. Счётчик относится только к переданному PgSql\Result.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. SQL-команда создаёт PgSql\Result
  2. libpq сохраняет command-tuples
  3. pg_affected_rows читает это число
  4. Код сравнивает его с ожидаемым масштабом
Подтвердить, что добавлены две строкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE affected_items (sku text PRIMARY KEY)');
$result = pg_query_params($db, 'INSERT INTO affected_items (sku) VALUES ($1), ($2)', ['PEN-01', 'BOOK-02']);
echo pg_affected_rows($result), "\n";
Ожидаемый результат
2

Счётчик относится к конкретному INSERT-result. Он позволяет проверить ожидаемый масштаб изменения до перехода к следующему действию. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть особенность SELECT в этой функцииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT n FROM generate_series(1, 3) AS n');
printf("affected=%d, rows=%d\n", pg_affected_rows($result), pg_num_rows($result));
Ожидаемый результат
affected=3, rows=3

Несмотря на название, libpq сообщает число выбранных строк и для SELECT. Для читаемости прикладного кода у SELECT обычно используют pg_num_rows, а у INSERT/UPDATE/DELETE — pg_affected_rows. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_num_rows(), pg_result_status(), pg_query_params().

pg_fetch_allСкопировать все строки небольшого результата в один PHP-массив.

PHP 4.3+; mode с PHP 7.1; [] для пустого результата с PHP 8.0 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_fetch_all(PgSql\Result $result, int $mode = PGSQL_ASSOC): array

Скопировать все строки небольшого результата в один PHP-массив.

Запись pg_fetch_all(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $mode (по умолчанию PGSQL_ASSOC)PGSQL_ASSOC по умолчанию, PGSQL_NUM для числовых индексов или PGSQL_BOTH для обоих наборов ключей. BOTH дублирует ссылки на значения и обычно не нужен.

Возврат и договор. Возвращает массив строк; SQL NULL становится PHP null. mode выбирает PGSQL_ASSOC, PGSQL_NUM или PGSQL_BOTH. С PHP 8.0 результат без строк — пустой массив. Вся выборка размещается в памяти PHP, поэтому большие экспорты читают построчно или страницами.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PgSql\Result уже содержит ответ сервера
  2. Функция проходит все строки
  3. Каждая строка превращается в массив выбранного mode
  4. Внешний массив передаётся шаблону или сервису
Получить весь маленький список товаровПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT * FROM (VALUES ('PEN-01', 120), ('BOOK-02', 450)) AS p(sku, price) ORDER BY sku");
$rows = pg_fetch_all($result);
foreach ($rows as $row) { echo $row['sku'], '=', $row['price'], "\n"; }
Ожидаемый результат
BOOK-02=450
PEN-01=120

Каждая строка стала ассоциативным массивом. Числа PostgreSQL здесь приходят строками, поэтому бизнес-код явно преобразует их после проверки, если ему нужна арифметика. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать выборку без совпаденийПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 1 AS id WHERE false');
$rows = pg_fetch_all($result);
printf("count=%d, empty=%s\n", count($rows), $rows === [] ? 'yes' : 'no');
Ожидаемый результат
count=0, empty=yes

Начиная с PHP 8.0 пустая выборка возвращает [], а не false. Ошибка запроса проверяется раньше по результату pg_query; пустой список — штатный бизнес-результат. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_assoc(), pg_fetch_row(), pg_num_rows(), pg_free_result().

pg_fetch_all_columnsПолучить значения одной колонки всех строк как плоский массив.

PHP 5.1+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_fetch_all_columns(PgSql\Result $result, int $field = 0): array

Получить значения одной колонки всех строк как плоский массив.

Запись pg_fetch_all_columns(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $field (по умолчанию 0)Номер колонки от 0 до pg_num_fields($result)-1. Это позиция в SELECT, а не имя таблицы.

Возврат и договор. Возвращает array; поле задаётся номером от нуля, по умолчанию 0. SQL NULL становится null, пустая выборка — []. Отрицательный или слишком большой номер вызывает ValueError.

Отказ. Неверный номер поля вызывает ValueError. Ошибку SQL нужно обработать до вызова, потому что false от pg_query нельзя передать как PgSql\Result.

Что происходит
  1. Код выбирает номер колонки
  2. Функция проходит каждую строку PgSql\Result
  3. Берёт одно значение либо null
  4. Получается плоский список
Собрать только артикулы из второй колонкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT * FROM (VALUES (10, 'PEN-01'), (20, 'BOOK-02')) AS p(id, sku) ORDER BY id");
$skus = pg_fetch_all_columns($result, 1);
echo implode(',', $skus), "\n";
Ожидаемый результат
PEN-01,BOOK-02

Номер поля начинается с нуля: 1 означает вторую колонку SELECT. Это удобно для плоского списка ID/кодов, когда остальные поля не нужны PHP-коду. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сохранить NULL и поймать неверный номер поляПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT * FROM (VALUES (NULL::text), ('ready')) AS x(note)");
$notes = pg_fetch_all_columns($result);
echo $notes[0] === null ? "NULL сохранён\n" : "Не NULL\n";
try { pg_fetch_all_columns($result, 2); } catch (ValueError $error) { echo "Нет такой колонки\n"; }
Ожидаемый результат
NULL сохранён
Нет такой колонки

SQL NULL не превращается в пустую строку. Номер за пределами результата вызывает ValueError, поэтому для динамического результата сначала находят колонку через pg_field_num и проверяют -1. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_all(), pg_field_num(), pg_num_fields().

pg_fetch_arrayПрочитать одну строку результата с числовыми, именованными либо обоими видами ключей.

PHP 4+; nullable row с PHP 8.3 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_fetch_array(PgSql\Result $result, ?int $row = null, int $mode = PGSQL_BOTH): array|false

Прочитать одну строку результата с числовыми, именованными либо обоими видами ключей.

Запись pg_fetch_array(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
?int $row (по умолчанию null)null для следующей строки или её номер от 0. Номер вне диапазона возвращает false.
int $mode (по умолчанию PGSQL_BOTH)PGSQL_BOTH по умолчанию; обычно явно выбирают PGSQL_ASSOC либо PGSQL_NUM.

Возврат и договор. Возвращает array или false. Значения PostgreSQL представлены строками, SQL NULL — null. row=null читает следующую строку; явный номер читает строку от нуля. mode: PGSQL_ASSOC, PGSQL_NUM или PGSQL_BOTH.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PgSql\Result хранит строки и текущую позицию
  2. row выбирает позицию либо next
  3. mode формирует ключи массива
  4. Курсор продвигается, а конец даёт false
Увидеть оба набора ключей PGSQL_BOTHПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'PEN-01'::text AS sku, 3::integer AS qty");
$row = pg_fetch_array($result, 0);
printf("%s|%s|keys=%d\n", $row[0], $row['sku'], count($row));
Ожидаемый результат
PEN-01|PEN-01|keys=4

По умолчанию два поля доступны и как 0/1, и как sku/qty, поэтому у массива четыре ключа. Обычно выбирают один режим, чтобы не путать чтение и сериализацию. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Читать строки по одной и заметить конецПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT * FROM (VALUES (1, 'new'), (2, 'paid')) AS o(id, status) ORDER BY id");
while (($row = pg_fetch_array($result, null, PGSQL_ASSOC)) !== false) { echo $row['id'], ':', $row['status'], "\n"; }
var_dump(pg_fetch_array($result, null, PGSQL_ASSOC));
Ожидаемый результат
1:new
2:paid
bool(false)

null просит следующую строку внутреннего курсора. false после последней строки — нормальный сигнал окончания, а не обязательно ошибка БД. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_assoc(), pg_fetch_row(), pg_result_seek().

pg_fetch_assocПрочитать одну строку как ассоциативный массив «имя колонки => значение».

PHP 4.3+; nullable row с PHP 8.3 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_fetch_assoc(PgSql\Result $result, ?int $row = null): array|false

Прочитать одну строку как ассоциативный массив «имя колонки => значение».

Запись pg_fetch_assoc(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
?int $row (по умолчанию null)Номер строки результата с нуля; null означает следующую строку текущего курсора там, где функция это поддерживает.

Возврат и договор. Эквивалентно pg_fetch_array(..., PGSQL_ASSOC). Возвращает array либо false в конце/при ошибке; значения — строки, SQL NULL — null. Одинаковые имена колонок конфликтуют, поэтому задавайте уникальные SQL aliases.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. SELECT задаёт имена/aliases
  2. Функция берёт следующую либо указанную строку
  3. Имена становятся ключами
  4. Код обращается к значениям по смыслу
Прочитать строку по понятным именамПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'A-17'::text AS order_no, 990::integer AS total");
$row = pg_fetch_assoc($result);
echo $row['order_no'], '|', $row['total'], "\n";
Ожидаемый результат
A-17|990

Имена берутся из SELECT aliases. Это делает код устойчивее к перестановке колонок, если сами alias сохраняются. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не потерять поле JOIN из-за одинаковых имёнПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$bad = pg_fetch_assoc(pg_query($db, 'SELECT 1 AS id, 2 AS id'));
$good = pg_fetch_assoc(pg_query($db, 'SELECT 1 AS product_id, 2 AS order_id'));
echo $bad['id'], '|', $good['product_id'], '|', $good['order_id'], "\n";
Ожидаемый результат
2|1|2

В ассоциативном массиве одинаковый ключ id не может хранить два значения: последнее перезаписало первое. В JOIN каждой одноимённой колонке дают уникальный alias. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_array(), pg_fetch_row(), pg_fetch_all().

pg_fetch_objectПрочитать строку как объект stdClass или экземпляр указанного класса.

PHP 4+; ValueError для лишних constructor_args с PHP 8.3 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_fetch_object(PgSql\Result $result, ?int $row = null, string $class = 'stdClass', array $constructor_args = []): object|false

Прочитать строку как объект stdClass или экземпляр указанного класса.

Запись pg_fetch_object(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
?int $row (по умолчанию null)null для следующей строки или номер от 0.
string $class (по умолчанию 'stdClass')Имя загруженного класса; по умолчанию stdClass.
array $constructor_args (по умолчанию [])Аргументы его конструктора. Они не являются значениями колонок.

Возврат и договор. Возвращает object либо false. Имена колонок становятся свойствами, SQL NULL — null. Для пользовательского class свойства устанавливаются до конструктора; constructor_args передаются конструктору. Непустые аргументы при отсутствии конструктора вызывают ValueError с PHP 8.3.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Функция выбирает строку
  2. Создаёт объект указанного class
  3. Записывает свойства из колонок
  4. Затем вызывает конструктор с constructor_args
Получить простую stdClass-строкуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'BOOK-02'::text AS sku, 450::integer AS price");
$product = pg_fetch_object($result);
echo $product->sku, '=', $product->price, "\n";
Ожидаемый результат
BOOK-02=450

stdClass лишь меняет синтаксис доступа с массива на свойства. Значение price всё равно строка и ещё не стало проверенным объектом предметной области. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть порядок заполнения пользовательского классаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
class ProductRow {
    public string $sku;
    public string $qty;
    public string $seenByConstructor = '';
    public function __construct(public string $source) { $this->seenByConstructor = $this->sku ?? '<unset>'; }
}
$result = pg_query($db, "SELECT 'PEN-01'::text AS sku, 4::integer AS qty");
$row = pg_fetch_object($result, 0, ProductRow::class, ['database']);
printf("%s|%s|%s|%s\n", $row->sku, $row->qty, $row->source, $row->seenByConstructor);
Ожидаемый результат
PEN-01|4|database|PEN-01

ext/pgsql присваивает свойства до вызова конструктора. Такое неявное hydration может нарушить типы/инварианты сложной модели; для доменного объекта безопаснее получить assoc-row, проверить данные и вызвать именованный factory. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_assoc(), pg_fetch_array().

pg_fetch_resultПолучить одну ячейку по строке и имени/номеру поля без создания массива всей строки.

PHP 4.2+; row nullable с PHP 8.3; двухаргументная форма deprecated с PHP 8.4 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_fetch_result(PgSql\Result $result, ?int $row, string|int $field): string|false|null

Получить одну ячейку по строке и имени/номеру поля без создания массива всей строки.

Запись pg_fetch_result(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
?int $rowНомер строки от 0 или null для текущей/следующей позиции. Используйте трёхаргументную форму.
string|int $fieldНомер поля от 0 либо точное имя/alias.

Возврат и договор. Возвращает string, null для SQL NULL или false при неверной позиции/ошибке. Boolean приходит как "t"/"f", массивы и другие типы — в текстовом формате PostgreSQL. Двухаргументная форма deprecated с PHP 8.4; используйте row=null явно для текущей строки.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код указывает строку и поле
  2. libpq находит одну ячейку
  3. SQL NULL становится null, остальные значения — text
  4. Приложение проверяет и преобразует тип
Получить одно значение COUNT(*)Пример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT count(*) AS total FROM generate_series(1, 4)');
$total = pg_fetch_result($result, 0, 'total');
printf("value=%s, php-type=%s\n", $total, gettype($total));
Ожидаемый результат
value=4, php-type=string

Даже count(*) приходит строкой. После проверки диапазона приложение может привести значение к int; сама функция не знает бизнес-типа. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Различить PostgreSQL boolean и NULLПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT true AS enabled, NULL::text AS note');
$enabled = pg_fetch_result($result, 0, 'enabled');
$note = pg_fetch_result($result, 0, 'note');
echo $enabled, '|', $note === null ? 'NULL' : $note, "\n";
Ожидаемый результат
t|NULL

PostgreSQL bool возвращается текстом t/f, а SQL NULL — PHP null. Проверка if ($enabled) была бы ошибочной для строки "f", потому что непустая строка truthy; значение преобразуют явно. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_assoc(), pg_field_is_null(), pg_field_type().

pg_fetch_rowПрочитать одну строку как массив с индексами 0, 1, 2 в порядке SELECT.

PHP 4+; nullable row с PHP 8.3 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_fetch_row(PgSql\Result $result, ?int $row = null, int $mode = PGSQL_NUM): array|false

Прочитать одну строку как массив с индексами 0, 1, 2 в порядке SELECT.

Запись pg_fetch_row(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
?int $row (по умолчанию null)Номер строки результата с нуля; null означает следующую строку текущего курсора там, где функция это поддерживает.
int $mode (по умолчанию PGSQL_NUM)Режим, задаваемый PGSQL_* константой; допустимые значения перечислены в договоре функции.

Возврат и договор. Возвращает array либо false после последней строки/при ошибке. По умолчанию mode=PGSQL_NUM; современная сигнатура также принимает другие PGSQL_* modes, но для именованных ключей яснее pg_fetch_assoc. Значения — строки, SQL NULL — null.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. SELECT определяет порядок полей
  2. Функция берёт строку текущего курсора
  3. Поля получают индексы с нуля
  4. Следующий вызов читает следующую строку
Распаковать строку по позициямПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'A-17'::text AS order_no, 990::integer AS total");
[$orderNo, $total] = pg_fetch_row($result);
echo $orderNo, '|', $total, "\n";
Ожидаемый результат
A-17|990

Числовые индексы повторяют порядок SELECT. Такой код короток, когда контракт колонок фиксирован и виден рядом. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Пройти поток строк и заметить зависимость от порядкаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT * FROM (VALUES (1, 'new'), (2, 'paid')) AS o(id, status) ORDER BY id");
while (($row = pg_fetch_row($result)) !== false) { echo $row[0], ':', $row[1], "\n"; }
Ожидаемый результат
1:new
2:paid

Для экспорта без смысловых ключей числовой массив удобен. Если SELECT переставит колонки, индексы изменятся молча; прикладному коду чаще понятнее pg_fetch_assoc с aliases. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_assoc(), pg_fetch_array(), pg_result_seek().

pg_field_is_nullПроверить, является ли конкретная ячейка именно SQL NULL.

PHP 4.2+; row nullable с PHP 8.3; двухаргументная форма deprecated с PHP 8.4 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_is_null(PgSql\Result $result, ?int $row, string|int $field): int|false

Проверить, является ли конкретная ячейка именно SQL NULL.

Запись pg_field_is_null(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
?int $rowНомер строки от 0 либо null в современной трёхаргументной форме.
string|int $fieldИмя/alias или номер поля.

Возврат и договор. Возвращает 1 для NULL, 0 для любого не-NULL значения и false при неверной позиции/ошибке. Двухаргументная форма deprecated с PHP 8.4; передавайте row явно, в том числе null.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код выбирает строку и поле
  2. libpq проверяет флаг NULL без чтения текста
  3. PHP получает 1 или 0
  4. Бизнес-логика не смешивает NULL, пустую строку и ноль
Отличить SQL NULL от пустой строкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT NULL::text AS missing, ''::text AS empty");
printf("missing=%d, empty=%d\n", pg_field_is_null($result, 0, 'missing'), pg_field_is_null($result, 0, 'empty'));
Ожидаемый результат
missing=1, empty=0

Обе величины могут выглядеть пустыми в HTML, но имеют разный смысл: NULL — значение отсутствует, пустая строка — значение задано и имеет нулевую длину. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сохранить различие «скидка не задана» и «скидка ноль»Пример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT NULL::integer AS discount UNION ALL SELECT 0 ORDER BY discount NULLS FIRST');
for ($row = 0; $row < 2; $row++) { echo pg_field_is_null($result, $row, 'discount') === 1 ? "not-set\n" : pg_fetch_result($result, $row, 'discount') . "\n"; }
Ожидаемый результат
not-set
0

Проверка не подменяет значение оператором empty(): 0 — реальная скидка, NULL — отсутствие настройки. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_result(), pg_fetch_assoc().

pg_field_nameПолучить имя выходной колонки по её номеру в результате.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_name(PgSql\Result $result, int $field): string

Получить имя выходной колонки по её номеру в результате.

Запись pg_field_name(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $fieldНомер поля с нуля либо имя столбца — в зависимости от сигнатуры конкретной функции.

Возврат и договор. Возвращает string. Номер начинается с 0; отрицательный или номер >= pg_num_fields вызывает ValueError в современной реализации. Имя — alias результата, оно не доказывает происхождение из конкретной таблицы.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. SELECT создаёт список выходных колонок
  2. Код передаёт позицию
  3. libpq возвращает label/alias
  4. Код строит заголовок или карту полей
Построить заголовок выгрузки из результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 7 AS id, 'PEN-01'::text AS sku, 120::integer AS price");
$names = [];
for ($field = 0; $field < pg_num_fields($result); $field++) { $names[] = pg_field_name($result, $field); }
echo implode(',', $names), "\n";
Ожидаемый результат
id,sku,price

Имена описывают конкретный SELECT, а не всю таблицу. Для пользовательского CSV заголовки обычно переводят по allowlist, а не показывают внутренние имена напрямую. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обнаружить конфликт имён до assoc-fetchПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 1 AS id, 2 AS id');
echo pg_field_name($result, 0), '|', pg_field_name($result, 1), "\n";
echo pg_field_name(pg_query($db, 'SELECT 1 AS product_id, 2 AS order_id'), 1), "\n";
Ожидаемый результат
id|id
order_id

PostgreSQL допускает одинаковые подписи выходных колонок. pg_fetch_assoc потеряет одно значение, поэтому JOIN/вычисления получают уникальные aliases. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_field_num(), pg_num_fields(), pg_field_table().

pg_field_numНайти номер выходного поля по имени.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_num(PgSql\Result $result, string $field): int

Найти номер выходного поля по имени.

Запись pg_field_num(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
string $fieldИмя/alias результата. Предпочитайте простые lowercase snake_case aliases.

Возврат и договор. Возвращает номер с 0 либо -1, если поле не найдено. Имя трактуется как SQL identifier: без двойных кавычек downcase; quoted mixed-case нужно передать вместе с двойными кавычками.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Результат хранит labels колонок
  2. Имя нормализуется как identifier
  3. libpq ищет позицию
  4. Код проверяет -1 до использования
Найти позицию колонки по aliasПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 10 AS id, 20 AS qty, 30 AS price');
echo pg_field_num($result, 'price'), "\n";
Ожидаемый результат
2

Нумерация начинается с нуля. Результат можно проверить на -1, а затем передать в pg_fetch_all_columns или функции метаданных. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Учесть регистр quoted aliasПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 1 AS "OrderID"');
printf("plain=%d, quoted=%d\n", pg_field_num($result, 'OrderID'), pg_field_num($result, '"OrderID"'));
Ожидаемый результат
plain=-1, quoted=0

Имя аргумента разбирается как SQL identifier: без двойных кавычек оно приводится к нижнему регистру. Практичнее давать aliases в snake_case без кавычек. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_field_name(), pg_fetch_all_columns().

pg_field_prtlenПолучить длину текстового представления конкретной ячейки результата в байтах.

PHP 4.2+; row nullable с PHP 8.3; двухаргументная форма deprecated с PHP 8.4 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_prtlen(PgSql\Result $result, ?int $row, string|int $field): int|false

Получить длину текстового представления конкретной ячейки результата в байтах.

Запись pg_field_prtlen(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
?int $rowНомер строки или null; используйте современную трёхаргументную форму.
string|int $fieldНомер поля с нуля либо имя столбца — в зависимости от сигнатуры конкретной функции.

Возврат и договор. Возвращает int либо false. Реализация использует libpq PQgetlength, поэтому многобайтный UTF-8 текст не равен числу видимых символов. Двухаргументная форма deprecated с PHP 8.4; используйте row=null/номер и поле третьим аргументом.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код выбирает ячейку
  2. libpq хранит её text/binary bytes
  3. PQgetlength сообщает число байт
  4. Код не выдаёт байты за символы или размер SQL-типа
Измерить переданные байты одной ячейкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'ABC'::text AS code");
echo pg_field_prtlen($result, 0, 'code'), " bytes\n";
Ожидаемый результат
3 bytes

Функция читает PQgetlength для конкретной ячейки результата. Это размер её текстового представления в байтах, а не объявленная ширина SQL-типа. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не принять UTF-8 байты за число буквПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'Привет'::text AS greeting");
$value = pg_fetch_result($result, 0, 'greeting');
printf("bytes=%d, text=%s\n", pg_field_prtlen($result, 0, 'greeting'), $value);
Ожидаемый результат
bytes=12, text=Привет

В UTF-8 шесть русских букв занимают 12 байт. Для лимита интерфейса по символам нужна Unicode-aware функция, а pg_field_prtlen полезна для оценки конкретного wire-value. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_field_size(), pg_fetch_result().

pg_field_sizeУзнать объявленную внутреннюю ширину базового типа выходного поля.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_size(PgSql\Result $result, int $field): int

Узнать объявленную внутреннюю ширину базового типа выходного поля.

Запись pg_field_size(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $fieldНомер поля с нуля либо имя столбца — в зависимости от сигнатуры конкретной функции.

Возврат и договор. Возвращает число байт для fixed-width типа или -1 для variable-width. Это свойство типа результата, не фактическая длина значения, не размер строки на диске и не память PHP.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Выходное поле имеет PostgreSQL type OID
  2. libpq сообщает typlen
  3. Функция возвращает bytes либо -1
  4. Код использует это только как метаданные
Сравнить fixed-width integer и variable-width textПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 1::integer AS qty, 'A'::text AS note");
printf("qty=%d, note=%d\n", pg_field_size($result, 0), pg_field_size($result, 1));
Ожидаемый результат
qty=4, note=-1

4 — внутренняя ширина типа int4; -1 означает переменную ширину text. Это не размер текущих значений 1 и A и не расход PHP-массива. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Построить краткую карту физических типов результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 1::bigint AS id, true::boolean AS enabled, current_date AS day');
for ($i = 0; $i < pg_num_fields($result); $i++) { printf("%s:%d\n", pg_field_name($result, $i), pg_field_size($result, $i)); }
Ожидаемый результат
id:8
enabled:1
day:4

Карта помогает диагностировать неожиданный cast запроса. Она не заменяет миграции и не годится для оценки размера строки на диске: там есть заголовки, NULL bitmap, TOAST и выравнивание. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_field_prtlen(), pg_field_type(), pg_field_type_oid().

pg_field_tableУзнать таблицу, из которой происходит прямое выходное поле, либо её OID.

PHP 5.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_table(PgSql\Result $result, int $field, bool $oid_only = false): string|int|false

Узнать таблицу, из которой происходит прямое выходное поле, либо её OID.

Запись pg_field_table(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $fieldНомер поля от 0.
bool $oid_only (по умолчанию false)false для имени; true для более дешёвого OID.

Возврат и договор. Возвращает имя таблицы, OID при oid_only=true или false, если у поля нет прямой таблицы/произошёл отказ. Получение имени требует lookup системного каталога и медленнее возврата OID. Вычисленные поля обычно дают false.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. libpq хранит table OID у выходного поля
  2. oid_only сразу возвращает OID
  3. Для имени ext/pgsql обращается к pg_class и кэширует
  4. Выражение без table OID даёт false
Узнать таблицу исходной колонкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE result_products (id integer PRIMARY KEY, title text)');
$result = pg_query($db, 'SELECT id, title FROM result_products');
echo pg_field_table($result, 0), "\n";
var_dump(is_int(pg_field_table($result, 0, true)));
Ожидаемый результат
result_products
bool(true)

В режиме oid_only возвращается OID без дополнительного поиска имени в каталоге. OID полезен внутри текущей БД, но не является переносимым ID между базами. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть отсутствие таблицы у вычисленного поляПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 2 + 3 AS total');
var_dump(pg_field_table($result, 0));
Ожидаемый результат
bool(false)

Выражение total не является прямой колонкой таблицы, поэтому происхождение false. То же возможно для некоторых views/выражений: не стройте авторизацию по этому runtime-метаданному. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_field_name(), pg_field_type_oid(), pg_meta_data().

pg_field_typeПолучить имя базового PostgreSQL-типа выходного поля.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_type(PgSql\Result $result, int $field): string

Получить имя базового PostgreSQL-типа выходного поля.

Запись pg_field_type(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $fieldНомер поля с нуля либо имя столбца — в зависимости от сигнатуры конкретной функции.

Возврат и договор. Возвращает string вроде int4, text, numeric, bool. Для domain возвращается underlying base type, а не имя domain. Тип результата не означает, что pg_fetch_* вернёт соответствующий PHP scalar: text protocol обычно даёт string.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Поле имеет type OID
  2. ext/pgsql сопоставляет OID с pg_type
  3. Возвращает имя базового типа
  4. Приложение отдельно преобразует значение
Увидеть базовые PostgreSQL-типы колонокПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 7::integer AS qty, 12.50::numeric AS price, 'ok'::text AS note");
for ($i = 0; $i < 3; $i++) { echo pg_field_type($result, $i), $i === 2 ? "\n" : '|'; }
Ожидаемый результат
int4|numeric|text

Функция возвращает внутренние имена базовых типов PostgreSQL. Они помогают диагностике/универсальному импорту, но схема ожидаемых полей всё равно задаётся приложением. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не спутать тип БД с PHP-типом полученного значенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 42::integer AS qty, true::boolean AS enabled');
$row = pg_fetch_assoc($result);
printf("db=%s/%s, php=%s/%s, values=%s/%s\n", pg_field_type($result, 0), pg_field_type($result, 1), gettype($row['qty']), gettype($row['enabled']), $row['qty'], $row['enabled']);
Ожидаемый результат
db=int4/bool, php=string/string, values=42/t

Метаданные говорят int4/bool, но text protocol отдаёт строки. Приведение в int/bool выполняют явно после проверки, а строку "f" нельзя передавать напрямую в if. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_field_type_oid(), pg_field_size(), pg_fetch_assoc().

pg_field_type_oidПолучить числовой OID базового типа выходного поля.

PHP 5.1+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_field_type_oid(PgSql\Result $result, int $field): string|int

Получить числовой OID базового типа выходного поля.

Запись pg_field_type_oid(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $fieldНомер поля с нуля либо имя столбца — в зависимости от сигнатуры конкретной функции.

Возврат и договор. Возвращает int либо string в зависимости от размера OID. Для domain возвращается OID underlying base type. Встроенные OID стабильны в PostgreSQL, а OID пользовательских объектов зависят от конкретной базы.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. libpq сообщает type OID поля
  2. Функция возвращает его без имени
  3. При необходимости серверный каталог объясняет OID
  4. Код не переносит пользовательский OID между базами
Получить стабильные OID встроенных типов в этой базеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 1::integer AS id, 'x'::text AS note");
echo pg_field_type_oid($result, 0), '|', pg_field_type_oid($result, 1), "\n";
Ожидаемый результат
23|25

23 — OID int4, 25 — OID text в PostgreSQL. Для пользовательских типов OID относятся к конкретной базе и не должны сохраняться как глобальные константы приложения. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Попросить сервер отформатировать найденный типПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 1::integer AS qty');
$oid = pg_field_type_oid($result, 0);
$nameResult = pg_query_params($db, 'SELECT format_type($1::oid, NULL) AS name', [$oid]);
echo pg_fetch_result($nameResult, 0, 'name'), "\n";
Ожидаемый результат
integer

OID передан параметром и расшифрован самим сервером. Для схемных инструментов можно читать pg_type, но бизнес-код обычно знает ожидаемый контракт без runtime-introspection. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_field_type(), pg_field_table().

pg_num_fieldsУзнать число выходных колонок в PgSql\Result.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_num_fields(PgSql\Result $result): int

Узнать число выходных колонок в PgSql\Result.

Запись pg_num_fields(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.

Возврат и договор. Возвращает int; официальный договор упоминает -1 при ошибке, но закрытый/неверный объект в современных версиях обычно вызывает Error/TypeError раньше. Для INSERT без RETURNING число полей равно 0.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PgSql\Result хранит описание колонок
  2. Функция читает PQnfields
  3. Код получает верхнюю границу
  4. Цикл обращается только к существующим позициям
Посчитать выходные колонки SELECTПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 1 AS id, 2 AS qty, 3 AS price');
echo pg_num_fields($result), "\n";
Ожидаемый результат
3

Считаются именно поля результата после aliases/выражений, а не количество колонок исходной таблицы. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Безопасно пройти все метаданные результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT 'A-17'::text AS order_no, 990::integer AS total");
for ($i = 0, $count = pg_num_fields($result); $i < $count; $i++) { printf("%d:%s\n", $i, pg_field_name($result, $i)); }
Ожидаемый результат
0:order_no
1:total

Граница i < count не передаёт несуществующий номер в pg_field_name. Такая схема нужна генератору CSV/таблицы, когда SELECT известен только во время выполнения. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_num_rows(), pg_field_name().

pg_num_rowsУзнать число строк, реально находящихся в данном результате.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_num_rows(PgSql\Result $result): int

Узнать число строк, реально находящихся в данном результате.

Запись pg_num_rows(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.

Возврат и договор. Возвращает int. Для SELECT это размер возвращённого result set; после LIMIT — размер страницы, не общее число совпадений. Результат уже буферизован libpq, поэтому функция не помогает стримить огромную выборку.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. SQL формирует набор строк
  2. libpq получает PgSql\Result
  3. pg_num_rows читает PQntuples
  4. Код различает размер результата и общий count
Посчитать возвращённые строкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT * FROM (VALUES ('new'), ('paid')) AS o(status)");
echo pg_num_rows($result), "\n";
Ожидаемый результат
2

Число относится к уже полученному PgSql\Result и не выполняет отдельный SELECT count(*). Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не спутать размер страницы с общим количествомПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT n, count(*) OVER () AS total FROM generate_series(1, 5) AS n ORDER BY n LIMIT 2');
$first = pg_fetch_assoc($result, 0);
printf("page=%d, total=%s\n", pg_num_rows($result), $first['total']);
Ожидаемый результат
page=2, total=5

pg_num_rows видит две строки после LIMIT, а window count сообщает пять подходящих строк. Для интерфейса пагинации эти числа отвечают на разные вопросы. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_affected_rows(), pg_fetch_all(), pg_set_chunked_rows_size().

pg_result_seekПередвинуть внутреннюю позицию чтения буферизованного результата на строку с заданным номером.

PHP 4.3+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_result_seek(PgSql\Result $result, int $row): bool

Передвинуть внутреннюю позицию чтения буферизованного результата на строку с заданным номером.

Запись pg_result_seek(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $rowНомер от 0 до pg_num_rows($result)-1.

Возврат и договор. Возвращает true для позиции 0..pg_num_rows-1 и false вне диапазона. Функция не выполняет SQL заново и не перемещает серверный cursor; весь обычный PgSql\Result уже находится у libpq.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Результат содержит N строк и current row
  2. Код задаёт индекс от 0
  3. Функция меняет current row
  4. Следующий fetch читает с новой позиции
Перейти к третьей строке перед чтениемПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT * FROM (VALUES ('A'), ('B'), ('C')) AS x(code)");
var_dump(pg_result_seek($result, 2));
echo pg_fetch_result($result, null, 'code'), "\n";
Ожидаемый результат
bool(true)
C

Номер 2 — третья строка. Следующий fetch без явного номера читает с новой внутренней позиции. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Перечитать результат с начала и проверить границуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT n FROM generate_series(1, 2) AS n ORDER BY n');
pg_fetch_row($result);
pg_fetch_row($result);
pg_result_seek($result, 0);
echo pg_fetch_result($result, null, 0), "\n";
var_dump(pg_result_seek($result, pg_num_rows($result)));
Ожидаемый результат
1
bool(false)

Seek можно применить только к уже буферизованным строкам этого результата. Позиция, равная count, находится за последней строкой и возвращает false. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_fetch_row(), pg_fetch_assoc(), pg_num_rows().

pg_result_statusУзнать протокольный статус результата либо command completion tag PostgreSQL.

PHP 4.2+; PGSQL_TUPLES_CHUNK появляется при поддержке chunked rows · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_result_status(PgSql\Result $result, int $mode = PGSQL_STATUS_LONG): string|int

Узнать протокольный статус результата либо command completion tag PostgreSQL.

Запись pg_result_status(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $mode (по умолчанию PGSQL_STATUS_LONG)PGSQL_STATUS_LONG по умолчанию для числовой константы; PGSQL_STATUS_STRING для command tag.

Возврат и договор. PGSQL_STATUS_LONG возвращает одну из PGSQL_EMPTY_QUERY, COMMAND_OK, TUPLES_OK, TUPLES_CHUNK, COPY_OUT, COPY_IN, BAD_RESPONSE, NONFATAL_ERROR, FATAL_ERROR. PGSQL_STATUS_STRING возвращает tag вроде SELECT 2 или UPDATE 1. Другой mode вызывает ValueError.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PostgreSQL завершает команду status/tag
  2. PgSql\Result сохраняет оба значения
  3. mode выбирает константу или строку
  4. Код отдельно проверяет строки/affected rows
Разделить машинный статус и command tag SELECTПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT n FROM generate_series(1, 2) AS n');
$status = pg_result_status($result);
$tag = pg_result_status($result, PGSQL_STATUS_STRING);
echo $status === PGSQL_TUPLES_OK ? 'TUPLES' : 'OTHER', '|', $tag, "\n";
Ожидаемый результат
TUPLES|SELECT 2

LONG возвращает константу для ветвления, STRING — командный tag сервера. Строка SELECT 2 содержит тип команды и число строк, но для количества в коде яснее pg_num_rows. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить UPDATE и увидеть число в command tagПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE status_jobs (id integer PRIMARY KEY, done boolean)');
pg_query($db, 'INSERT INTO status_jobs VALUES (1, false), (2, false)');
$result = pg_query($db, 'UPDATE status_jobs SET done=true WHERE id=2');
printf("%s|%s|affected=%d\n", pg_result_status($result) === PGSQL_COMMAND_OK ? 'COMMAND' : 'OTHER', pg_result_status($result, PGSQL_STATUS_STRING), pg_affected_rows($result));
Ожидаемый результат
COMMAND|UPDATE 1|affected=1

COMMAND_OK подтверждает класс успеха, а affected rows — ожидаемый масштаб. Успешный SQL ещё не доказывает бизнес-право или правильность выбранного WHERE. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_affected_rows(), pg_num_rows(), pg_result_error().

pg_result_errorПолучить текст ошибки, привязанный к конкретному PgSql\Result.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_result_error(PgSql\Result $result): string|false

Получить текст ошибки, привязанный к конкретному PgSql\Result.

Запись pg_result_error(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.

Возврат и договор. Возвращает сообщение ошибки строкой, пустую строку для успешного результата или false при невозможности чтения. Для failed SQL объект результата получают через асинхронный pg_send_query/pg_get_result, потому что синхронный pg_query возвращает false.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Запрос отправляется асинхронно
  2. pg_get_result сохраняет даже FATAL_ERROR result
  3. pg_result_error читает сообщение именно этого result
  4. Закрытый журнал получает детали, пользователь — безопасный ответ
Увидеть отсутствие ошибки у успешного результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT 42 AS answer');
var_dump(pg_result_error($result) === '');
Ожидаемый результат
bool(true)

Пустая строка означает, что с этим PgSql\Result не связано сообщение ошибки. Это не то же самое, что false от функции при невозможности прочитать результат. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сохранить сообщение именно неудачного запросаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$clean = false;
try {
    $socket = pg_socket($db);
    if ($socket === false) { throw new RuntimeException('Нет сокета'); }
    $deadline = hrtime(true) + 3_000_000_000;
    if (!pg_send_query($db, 'SELECT 1 / 0')) { throw new RuntimeException('Запрос не принят'); }
    $flushed = pg_flush($db);
    if ($flushed === false) { throw new RuntimeException('Ошибка начала отправки'); }
    while ($flushed === 0) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут отправки'); }
        $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания записи'); }
        if ($ready === 0) { usleep(10_000); continue; }
        $flushed = pg_flush($db);
        if ($flushed === false) { throw new RuntimeException('Ошибка досылки'); }
    }
    while (pg_connection_busy($db)) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут ответа'); }
        $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания чтения'); }
        if ($ready === 0) { usleep(10_000); continue; }
        if (!pg_consume_input($db)) { throw new RuntimeException('Ошибка чтения libpq'); }
    }
    $result = pg_get_result($db);
    if (!$result instanceof PgSql\Result || pg_result_status($result) !== PGSQL_FATAL_ERROR) {
        throw new RuntimeException('Не получен failed result');
    }
    while (pg_get_result($db) instanceof PgSql\Result) {}
    $clean = true;
    $message = pg_result_error($result);
    $state = pg_result_error_field($result, PGSQL_DIAG_SQLSTATE);
    echo is_string($message) && $message !== '' && $state === '22012' ? "Ошибка деления сохранена\n" : "Нет сообщения\n";
} finally {
    if (!$clean) { @pg_close($db); }
}
Ожидаемый результат
Ошибка деления сохранена

Синхронный pg_query при SQL-ошибке возвращает false и уничтожает PGresult, поэтому для привязанной диагностики нужен pg_send_query + pg_get_result. Отправка и чтение имеют общий монотонный deadline; ранний 0 poll повторяется, -1 прерывает ожидание, очередь результатов дочитывается, а uncertain connection закрывается. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_result_error_field(), pg_last_error(), pg_send_query(), pg_get_result().

pg_result_error_fieldПолучить структурированное поле отчёта PostgreSQL: SQLSTATE, основное сообщение, detail, hint, позицию или server context.

PHP 5.1+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_result_error_field(PgSql\Result $result, int $field_code): string|false|null

Получить структурированное поле отчёта PostgreSQL: SQLSTATE, основное сообщение, detail, hint, позицию или server context.

Запись pg_result_error_field(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.
int $field_codeОдна из PGSQL_DIAG_SEVERITY, SQLSTATE, MESSAGE_PRIMARY, MESSAGE_DETAIL, MESSAGE_HINT, STATEMENT_POSITION, INTERNAL_POSITION, INTERNAL_QUERY, CONTEXT, SOURCE_FILE, SOURCE_LINE, SOURCE_FUNCTION и поддерживаемых libpq diagnostic constants.

Возврат и договор. Возвращает string, null если выбранного поля нет, либо false при отказе. Наиболее устойчивое поле для ветвления — PGSQL_DIAG_SQLSTATE. Подробный failed-result получают через pg_send_query/pg_get_result.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Failed PGresult содержит diagnostic fields
  2. field_code выбирает одно поле
  3. SQLSTATE классифицирует причину
  4. Приложение отделяет решение, журнал и безопасный ответ
Получить стабильный SQLSTATE деления на нольПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$clean = false;
try {
    $socket = pg_socket($db);
    if ($socket === false) { throw new RuntimeException('Нет сокета'); }
    $deadline = hrtime(true) + 3_000_000_000;
    if (!pg_send_query($db, 'SELECT 1 / 0')) { throw new RuntimeException('Запрос не принят'); }
    $flushed = pg_flush($db);
    if ($flushed === false) { throw new RuntimeException('Ошибка начала отправки'); }
    while ($flushed === 0) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут отправки'); }
        $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания записи'); }
        if ($ready === 0) { usleep(10_000); continue; }
        $flushed = pg_flush($db);
        if ($flushed === false) { throw new RuntimeException('Ошибка досылки'); }
    }
    while (pg_connection_busy($db)) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут ответа'); }
        $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания чтения'); }
        if ($ready === 0) { usleep(10_000); continue; }
        if (!pg_consume_input($db)) { throw new RuntimeException('Ошибка чтения libpq'); }
    }
    $result = pg_get_result($db);
    if (!$result instanceof PgSql\Result || pg_result_status($result) !== PGSQL_FATAL_ERROR) {
        throw new RuntimeException('Не получен failed result');
    }
    while (pg_get_result($db) instanceof PgSql\Result) {}
    $clean = true;
    $state = pg_result_error_field($result, PGSQL_DIAG_SQLSTATE);
    $message = pg_result_error_field($result, PGSQL_DIAG_MESSAGE_PRIMARY);
    echo $state, '|message=', is_string($message) && $message !== '' ? 'yes' : 'no', "\n";
} finally {
    if (!$clean) { @pg_close($db); }
}
Ожидаемый результат
22012|message=yes

SQLSTATE 22012 предназначен для программного решения. Основное сообщение только проверяется на наличие. Один монотонный deadline ограничивает send/read; очередь полностью снимается, а соединение с неподтверждённой синхронизацией закрывается. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Распознать конфликт уникальности без разбора текстаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE diag_products (sku text PRIMARY KEY)');
pg_query($db, "INSERT INTO diag_products VALUES ('PEN-01')");
$clean = false;
try {
    $socket = pg_socket($db);
    if ($socket === false) { throw new RuntimeException('Нет сокета'); }
    $deadline = hrtime(true) + 3_000_000_000;
    if (!pg_send_query($db, "INSERT INTO diag_products VALUES ('PEN-01')")) { throw new RuntimeException('Запрос не принят'); }
    $flushed = pg_flush($db);
    if ($flushed === false) { throw new RuntimeException('Ошибка начала отправки'); }
    while ($flushed === 0) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут отправки'); }
        $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания записи'); }
        if ($ready === 0) { usleep(10_000); continue; }
        $flushed = pg_flush($db);
        if ($flushed === false) { throw new RuntimeException('Ошибка досылки'); }
    }
    while (pg_connection_busy($db)) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Таймаут ответа'); }
        $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания чтения'); }
        if ($ready === 0) { usleep(10_000); continue; }
        if (!pg_consume_input($db)) { throw new RuntimeException('Ошибка чтения libpq'); }
    }
    $result = pg_get_result($db);
    if (!$result instanceof PgSql\Result || pg_result_status($result) !== PGSQL_FATAL_ERROR) {
        throw new RuntimeException('Не получен failed result');
    }
    while (pg_get_result($db) instanceof PgSql\Result) {}
    $clean = true;
    $state = pg_result_error_field($result, PGSQL_DIAG_SQLSTATE);
    $detail = pg_result_error_field($result, PGSQL_DIAG_MESSAGE_DETAIL);
    printf("state=%s, detail=%s\n", $state, is_string($detail) && $detail !== '' ? 'yes' : 'no');
} finally {
    if (!$clean) { @pg_close($db); }
}
Ожидаемый результат
state=23505, detail=yes

23505 стабильно позволяет вернуть ответ «артикул уже существует». DETAIL лишь проверяется на наличие. Общий deadline и полный drain сохраняют протокольную границу; если это не удалось, соединение закрывается вместо повторного использования. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_result_error(), pg_last_error().

pg_last_errorПолучить последнее сообщение libpq для соединения, когда синхронный вызов вернул false.

PHP 4.2+; default connection deprecated с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_last_error(?PgSql\Connection $connection = null): string

Получить последнее сообщение libpq для соединения, когда синхронный вызов вернул false.

Запись pg_last_error(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явный PgSql\Connection. null/default connection оставлен для совместимости и deprecated с PHP 8.1.

Возврат и договор. Возвращает string, в том числе пустую. Сообщение может быть перезаписано следующим внутренним libpq-вызовом и хуже привязано к операции, чем pg_result_error/field. Передавайте connection явно: default connection deprecated с PHP 8.1.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Синхронная операция записывает ошибку в PGconn
  2. Код сразу вызывает pg_last_error($db)
  3. Сохраняет детали в закрытый журнал
  4. Следующий libpq-вызов может изменить снимок
Забрать последнюю ошибку явного соединенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
@pg_query($db, 'SELECT * FROM manual_missing_results_table');
$message = pg_last_error($db);
echo $message !== '' ? "Сообщение получено\n" : "Сообщения нет\n";
Ожидаемый результат
Сообщение получено

Функция полезна после синхронного pg_query=false, когда объекта результата нет. В лог добавляют контекст операции, но не показывают сырой SQL/ошибку посетителю. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сохранить ошибку до следующего libpq-вызоваПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
@pg_query($db, 'SELECT * FROM manual_missing_results_table');
$saved = pg_last_error($db);
pg_query($db, 'SELECT 1');
$now = pg_last_error($db);
printf("saved=%s, now=%s\n", $saved !== '' ? 'yes' : 'no', $now === '' ? 'empty' : 'present');
Ожидаемый результат
saved=yes, now=empty

Успешный следующий запрос очистил последнее сообщение. Поэтому pg_last_error — изменяемый снимок соединения; сохраните его сразу или предпочитайте diagnostics конкретного result. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_result_error(), pg_result_error_field(), pg_connection_status().

pg_last_noticeПрочитать последнее/все NOTICE-сообщения сервера или очистить их очередь на соединении.

PHP 4.0.6+; mode с PHP 7.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_last_notice(PgSql\Connection $connection, int $mode = PGSQL_NOTICE_LAST): array|string|bool

Прочитать последнее/все NOTICE-сообщения сервера или очистить их очередь на соединении.

Запись pg_last_notice(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
int $mode (по умолчанию PGSQL_NOTICE_LAST)PGSQL_NOTICE_LAST по умолчанию, PGSQL_NOTICE_ALL для копии очереди, PGSQL_NOTICE_CLEAR для очистки.

Возврат и договор. PGSQL_NOTICE_LAST возвращает string (пустую, если notices нет), PGSQL_NOTICE_ALL — array, PGSQL_NOTICE_CLEAR — bool. Сбор зависит от php.ini pgsql.ignore_notice, логирование — от pgsql.log_notice. NOTICE обычно предупреждение/информация, не failed-result.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PostgreSQL посылает NOTICE
  2. ext/pgsql сохраняет его в очереди connection
  3. mode выбирает last/all/clear
  4. Worker читает и очищает очередь по политике
Получить последнее серверное NOTICEПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_last_notice($db, PGSQL_NOTICE_CLEAR);
pg_query($db, "DO $$ BEGIN RAISE NOTICE 'manual-ready'; END $$;");
$notice = pg_last_notice($db);
echo str_contains($notice, 'manual-ready') ? "NOTICE получен\n" : "NOTICE потерян\n";
Ожидаемый результат
NOTICE получен

NOTICE не является SQL-ошибкой: команда DO завершилась. Его можно записать для диагностики миграции, но не считать автоматическим отказом операции. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Прочитать и очистить очередь notices долгого workerПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_last_notice($db, PGSQL_NOTICE_CLEAR);
pg_query($db, "DO $$ BEGIN RAISE NOTICE 'one'; RAISE NOTICE 'two'; END $$;");
$all = pg_last_notice($db, PGSQL_NOTICE_ALL);
$cleared = pg_last_notice($db, PGSQL_NOTICE_CLEAR);
$after = pg_last_notice($db, PGSQL_NOTICE_ALL);
printf("before=%d, cleared=%s, after=%d\n", count($all), $cleared ? 'yes' : 'no', count($after));
Ожидаемый результат
before=2, cleared=yes, after=0

В долгом процессе очередь иначе накапливается. Доступность сообщений зависит от pgsql.ignore_notice; приложение не должно строить обязательную бизнес-логику только на NOTICE. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_last_error(), pg_result_error().

pg_free_resultЯвно освободить libpq-память PgSql\Result до конца PHP-процесса.

PHP 4.2+; PgSql\Result вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_free_result(PgSql\Result $result): bool

Явно освободить libpq-память PgSql\Result до конца PHP-процесса.

Запись pg_free_result(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.

Возврат и договор. Возвращает true при успехе. После вызова объект закрыт; дальнейшее чтение вызывает Error. Обычно сборщик освобождает результат автоматически, но явный вызов полезен в долгих workers/циклах или после большого результата.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код закончил читать result
  2. pg_free_result вызывает PQclear
  3. Буфер libpq освобождается
  4. Объект остаётся закрытой оболочкой и больше не используется
Освободить результат и увидеть границу объектаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT n FROM generate_series(1, 3) AS n');
echo pg_num_rows($result), " rows\n";
var_dump(pg_free_result($result));
try { pg_num_rows($result); } catch (Error $error) { echo "Результат уже закрыт\n"; }
Ожидаемый результат
3 rows
bool(true)
Результат уже закрыт

После освобождения тот же объект нельзя читать. В коротком запросе PHP освободит его автоматически; явный free нужен, когда большой результат больше не нужен, а процесс продолжает жить. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Освобождать результаты в повторяющейся задачеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$freed = 0;
for ($batch = 1; $batch <= 3; $batch++) {
    $result = pg_query_params($db, 'SELECT repeat($1, 1000)', [(string) $batch]);
    pg_fetch_result($result, 0, 0);
    if (pg_free_result($result)) { $freed++; }
}
echo $freed, " results freed\n";
Ожидаемый результат
3 results freed

В queue worker результат каждого batch освобождается до следующего. Это не отменяет необходимости ограничивать размер SQL-выборки и измерять память процесса. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_result_memory_size(), pg_fetch_all().

pg_result_memory_sizeУзнать объём памяти в байтах, выделенный libpq для конкретного результата — если сборка поддерживает PQresultMemorySize.

PHP 8.4+; доступность зависит от сборки libpq/ext/pgsql · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_result_memory_size(PgSql\Result $result): int

Узнать объём памяти в байтах, выделенный libpq для конкретного результата — если сборка поддерживает PQresultMemorySize.

Запись pg_result_memory_size(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.

Возврат и договор. Возвращает int и ровно тот объём, который освободит pg_free_result. Функция добавлена в PHP 8.4, но компилируется условно; текущая Windows DLL её не экспортирует. Всегда проверяйте function_exists при поддержке разных сборок.

Отказ. Если символ отсутствует в сборке, прямой вызов завершится Error как вызов неопределённой функции. Проверяйте function_exists и не подменяйте эту метрику memory_get_usage.

Граница текущего стенда. Conditional ext/pgsql capability HAVE_PG_RESULT_MEMORY_SIZE; absent from this PHP 8.5.11 Windows DLL even though its reported libpq is 16.15. Карточка сохраняется, потому что функция есть в официальной ветке PHP 8.5.11; пример для неё проверяется по исходнику/синтаксису и явно не выдаётся за выполненный на этой DLL.

Что происходит
  1. Build ext/pgsql либо экспортирует capability, либо нет
  2. PgSql\Result владеет PGresult
  3. Функция измеряет память этого PGresult
  4. pg_free_result освобождает тот же буфер
Проверить capability конкретной PHP-сборкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, 'SELECT n FROM generate_series(1, 10) AS n');
if (!function_exists('pg_result_memory_size')) { echo "pg_result_memory_size недоступна в этой DLL\n"; }
else { echo pg_result_memory_size($result), " bytes\n"; }
Ожидаемый результат
pg_result_memory_size недоступна в этой DLL

Стенд использует PHP 8.5.11 и libpq 16.15, но Windows php_pgsql.dll собрана без HAVE_PG_RESULT_MEMORY_SIZE. Версия PHP/сервера сама по себе не гарантирует capability. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Освободить result и без функции измеренияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT repeat('x', 1000) FROM generate_series(1, 5)");
if (function_exists('pg_result_memory_size')) { echo 'measured=', pg_result_memory_size($result), "\n"; }
else { echo "measurement unavailable\n"; }
pg_free_result($result);
echo "result freed\n";
Ожидаемый результат
measurement unavailable
result freed

Отсутствие метрики не мешает управлять жизненным циклом. memory_get_usage не является эквивалентом: часть PGresult выделена libpq вне обычного учёта Zend memory manager. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_free_result(), pg_fetch_all().

pg_jitПолучить server settings jit_provider и jit как небольшой диагностический массив.

PHP 8.4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_jit(?PgSql\Connection $connection = null): array

Получить server settings jit_provider и jit как небольшой диагностический массив.

Запись pg_jit(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явное соединение. Default connection deprecated; передавайте $db.

Возврат и договор. Возвращает array с ключами jit_provider и jit; значение может быть null, если SHOW не поддерживается/отказывает. Функция сама выполняет SHOW-запросы и сообщает настройки, а не факт/время JIT конкретного SQL. На сборке без jit_provider не вызывайте её посреди важной транзакции: неудачный SHOW может сделать блок INERROR.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. ext/pgsql выполняет SHOW jit_provider
  2. Выполняет SHOW jit
  3. Собирает два значения в массив
  4. Код интерпретирует их как настройки сервера
Прочитать настройки JIT сервераПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$jit = pg_jit($db);
printf("provider=%s, jit=%s\n", $jit['jit_provider'] ?? 'none', $jit['jit'] ?? 'unknown');
Ожидаемый результат
provider=none, jit=on

Локальный PostgreSQL сообщает jit=on, но jit_provider=null: бинарная сборка не предоставляет provider. Значение on — настройка, а не доказательство, что конкретный запрос компилировался JIT. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть сессионное изменение JIT и вернуть настройкуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$before = pg_jit($db)['jit'] ?? 'on';
pg_query($db, 'SET jit TO off');
$jit = pg_jit($db);
echo $jit['jit'], "\n";
pg_query($db, 'SET jit TO ' . ($before === 'off' ? 'off' : 'on'));
Ожидаемый результат
off

Пример возвращает прежнее значение после проверки. Не вызываем pg_jit внутри открытой транзакции на этой сборке: внутренний SHOW jit_provider не поддержан и способен перевести transaction block в INERROR. Отключать JIT глобально по одному примеру нельзя: решение принимают по EXPLAIN и нагрузочным измерениям. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_version(), pg_query().

pg_transaction_statusУзнать состояние текущей серверной транзакции соединения.

PHP 5.1+; PgSql\Connection вместо resource с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_transaction_status(PgSql\Connection $connection): int

Узнать состояние текущей серверной транзакции соединения.

Запись pg_transaction_status(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает PGSQL_TRANSACTION_IDLE, ACTIVE, INTRANS, INERROR или UNKNOWN. ACTIVE виден при отправленном и ещё не завершённом async-query. INERROR требует rollback/rollback to savepoint; UNKNOWN означает плохое/неизвестное соединение.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Соединение имеет protocol transaction state
  2. Функция читает PQtransactionStatus
  3. Код различает normal, active и failed block
  4. Cleanup гарантированно выполняет COMMIT/ROLLBACK
Различить idle и открытую транзакциюПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
echo pg_transaction_status($db) === PGSQL_TRANSACTION_IDLE ? "IDLE\n" : "OTHER\n";
pg_query($db, 'BEGIN');
echo pg_transaction_status($db) === PGSQL_TRANSACTION_INTRANS ? "INTRANS\n" : "OTHER\n";
pg_query($db, 'ROLLBACK');
Ожидаемый результат
IDLE
INTRANS

После BEGIN соединение находится в исправном transaction block. В конце примера rollback возвращает bootstrap-соединение в чистое состояние. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

После SQL-ошибки выполнить обязательный rollbackПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'BEGIN');
@pg_query($db, 'SELECT * FROM manual_missing_tx_table');
echo pg_transaction_status($db) === PGSQL_TRANSACTION_INERROR ? "INERROR\n" : "OTHER\n";
pg_query($db, 'ROLLBACK');
echo pg_transaction_status($db) === PGSQL_TRANSACTION_IDLE ? "IDLE\n" : "OTHER\n";
Ожидаемый результат
INERROR
IDLE

В INERROR обычные команды будут отклоняться до ROLLBACK или ROLLBACK TO SAVEPOINT. Живое PGSQL_CONNECTION_OK не означает пригодную транзакцию. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connection_status(), pg_query(), pg_send_query().

pg_last_oidПрочитать исторический row OID из результата INSERT; для современных identity/serial обычно использовать INSERT ... RETURNING.

PHP 4.2+; современным PostgreSQL почти всегда нужен RETURNING · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_last_oid(PgSql\Result $result): string|int|false

Прочитать исторический row OID из результата INSERT; для современных identity/serial обычно использовать INSERT ... RETURNING.

Запись pg_last_oid(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Result $resultОбъект PgSql\Result, который вернул запрос; он хранит статус, строки и метаданные именно этого результата.

Возврат и договор. Возвращает OID как int/string либо false, если row OID отсутствует. Пользовательские таблицы не получают OID по умолчанию много лет, а WITH OIDS удалён из PostgreSQL 12. Функция не возвращает SERIAL/IDENTITY ID.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. INSERT создаёт command result
  2. Старый table row мог иметь OID
  3. pg_last_oid читает только этот OID
  4. Современный INSERT RETURNING возвращает нужный ключ явно
Увидеть отсутствие row OID у современной таблицыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE oid_orders (id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY)');
$result = pg_query($db, 'INSERT INTO oid_orders DEFAULT VALUES');
var_dump(pg_last_oid($result));
echo pg_result_status($result, PGSQL_STATUS_STRING), "\n";
Ожидаемый результат
bool(false)
INSERT 0 1

Современная таблица не имеет WITH OIDS, поэтому false ожидаем. Command tag подтверждает вставку, но не сообщает identity id. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить созданный identity через RETURNINGПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE returning_orders (id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, label text)');
$result = pg_query_params($db, 'INSERT INTO returning_orders (label) VALUES ($1) RETURNING id', ['first']);
var_dump(pg_last_oid($result));
$id = pg_fetch_result($result, 0, 'id');
echo "id={$id}\n";
Ожидаемый результат
bool(false)
id=1

Тот же result на современной identity-таблице даёт false из pg_last_oid, но содержит id через RETURNING. RETURNING связан именно с этой вставленной строкой, не зависит от имени sequence и служит современной заменой row OID. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_result_status(), pg_fetch_result(), pg_query_params().

pg_connection_busyПроверить, выполняется ли ещё ранее отправленный асинхронный запрос.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_connection_busy(PgSql\Connection $connection): bool

Проверить, выполняется ли ещё ранее отправленный асинхронный запрос.

Запись pg_connection_busy(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает true, пока соединение занято, и false, когда можно получать результат. В текущем php-src сама pg_connection_busy сначала вызывает PQconsumeInput и переносит уже доступные байты в libpq. Если ответ ещё не готов, event loop ждёт readable-сокет перед следующей проверкой; цикл usleep + чтение без readiness способен зря грузить CPU или блокироваться.

Отказ. false означает готовность к чтению результата, а не SQL-успех. Ошибка самого соединения проверяется pg_connection_status и результатом pg_get_result; закрытый объект вызывает Error.

Что происходит
  1. pg_send_* отправляет запрос
  2. pg_connection_busy потребляет уже доступный input и сообщает состояние
  3. event loop ждёт readable, если ответ ещё не готов
  4. pg_get_result забирает готовый PgSql\Result
Увидеть занятое и снова свободное соединениеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT pg_sleep(0.05), 42 AS answer');
if ($sent === false) { throw new RuntimeException('Запрос не принят libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
echo pg_connection_busy($db) ? "Занято\n" : "Уже готово\n";
while (pg_connection_busy($db)) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); }
}
$result = pg_get_result($db);
if (!$result instanceof PgSql\Result) { throw new RuntimeException('Нет результата'); }
echo pg_connection_busy($db) ? "Занято\n" : "Свободно\n";
echo pg_fetch_result($result, 0, 'answer'), "\n";
Ожидаемый результат
Занято
Свободно
42

Состояние busy относится к одному соединению: пока сервер считает предыдущий запрос, новое действие на этом же $db отправлять нельзя. На проверенной Windows/libpq-связке pg_socket_poll способен немедленно вернуть 0 ещё до готовности, поэтому 0 повторяет ожидание с короткой паузой, а таймаут определяет один общий deadline. При таймауте или ошибке poll соединение закрывается: оставлять в пуле сессию с недочитанным протоколом нельзя. На очень быстром запросе первый статус может уже оказаться «готово», поэтому для демонстрации используется короткий pg_sleep. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не отправлять вторую команду поверх незавершённойПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT pg_sleep(0.03)');
if ($sent === false) { throw new RuntimeException('Запрос не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
if (pg_connection_busy($db)) { echo "Сначала дочитаем первый результат\n"; }
while (pg_connection_busy($db)) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); }
}
while (pg_get_result($db) instanceof PgSql\Result) {}
Ожидаемый результат
Сначала дочитаем первый результат

Проверка не ускоряет запрос, а защищает протокол. Если отправить новый запрос в занятое соединение, ext/pgsql может ждать предыдущий и отбросить его непрочитанные результаты. Нулевой pg_socket_poll на Windows/libpq не считается окончательным таймаутом: цикл повторяет его до общего deadline; -1 остаётся немедленной ошибкой. При ошибке ожидания или исчерпании deadline пример закрывает сессию с неопределённым состоянием протокола. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_consume_input(), pg_get_result(), pg_socket().

pg_consume_inputПередать ожидающие в сокете данные PostgreSQL во внутренние буферы libpq.

PHP 5.6+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_consume_input(PgSql\Connection $connection): bool

Передать ожидающие в сокете данные PostgreSQL во внутренние буферы libpq.

Запись pg_consume_input(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает true, если ошибки чтения не было, и false при ошибке. true не говорит, что данные были, что запрос завершён или что SQL успешен.

Отказ. false означает ошибку чтения libpq. На blocking-соединении не вызывайте функцию вслепую: сначала дождитесь readable через pg_socket/pg_socket_poll или используйте event loop.

Что происходит
  1. Сокет становится читаемым
  2. pg_consume_input читает доступные байты без ожидания полного запроса
  3. pg_connection_busy проверяет завершение
  4. pg_get_result отдаёт готовые результаты
Читать вход, пока сервер считает результатПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT pg_sleep(0.03), 7 AS value');
if ($sent === false) { throw new RuntimeException('Запрос не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
while (true) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    if (!pg_consume_input($db)) { pg_close($db); throw new RuntimeException('Соединение не прочитано; соединение закрыто'); }
    if (!pg_connection_busy($db)) { break; }
}
$result = pg_get_result($db);
echo pg_fetch_result($result, 0, 'value'), "\n";
Ожидаемый результат
7

true от pg_consume_input означает отсутствие ошибки чтения, а не наличие полного результата. Готовность определяет pg_connection_busy, а сам результат возвращает pg_get_result. Проверенная Windows/libpq-связка иногда возвращает из pg_socket_poll ранний 0; код повторяет ожидание с паузой и завершает его только по общему deadline. При таймауте или ошибке чтения соединение закрывается, чтобы недочитанный ответ не попал в следующий запрос. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не принимать true за готовый SQL-результатПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT 9 AS value');
if ($sent === false) { throw new RuntimeException('Запрос не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
while (true) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
    if ($ready > 0) { break; }
    usleep(10_000);
}
$ok = pg_consume_input($db);
var_dump($ok);
while (pg_connection_busy($db)) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    if (!pg_consume_input($db)) { pg_close($db); throw new RuntimeException('Не удалось дочитать ответ; соединение закрыто'); }
}
$result = pg_get_result($db);
echo pg_fetch_result($result, 0, 'value'), "\n";
Ожидаемый результат
bool(true)
9

true подтверждает только отсутствие ошибки чтения уже доступных байтов. Пример отдельно проверяет busy, дочитывает ответ и лишь затем получает значение из PgSql\Result. Ранний 0 от pg_socket_poll на Windows/libpq повторяется до общего deadline; только -1 немедленно означает ошибку ожидания. При таймауте или ошибке сессия закрывается, потому что её очередь уже нельзя считать чистой. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_connection_busy(), pg_socket_poll(), pg_get_result().

pg_flushПопытаться без блокировки отправить накопленные исходящие данные соединения.

PHP 5.6+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_flush(PgSql\Connection $connection): int|bool

Попытаться без блокировки отправить накопленные исходящие данные соединения.

Запись pg_flush(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает true, когда всё отправлено или очередь пуста; строгое целое 0, если часть ещё осталась; false при ошибке. После flush запрос всё ещё может выполняться на сервере.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. pg_send_* формирует протокольное сообщение
  2. libpq может оставить байты в исходящем буфере
  3. pg_flush досылает доступную часть
  4. busy/consume/get_result обслуживают ответ
Корректно дослать большую асинхронную командуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$payload = str_repeat('x', 2_000_000);
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 5.0;
$sent = pg_send_query_params($db, 'SELECT length($1::text) AS bytes', [$payload]);
if ($sent === false) { throw new RuntimeException('Не принято libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
while (pg_connection_busy($db)) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); }
}
$result = pg_get_result($db);
echo pg_fetch_result($result, 0, 'bytes'), "\n";
Ожидаемый результат
2000000

На loopback весь буфер часто отправляется сразу, но цикл учитывает и 0: часть данных ушла, часть ещё ждёт. Значение 0 сравнивается строго, потому что оно ложно в обычном if. На Windows/libpq сам pg_socket_poll также способен вернуть ранний 0; он повторяется с паузой, а предел всех стадий задаёт один deadline. Таймаут или ошибка закрывают соединение с частично отправленным либо недочитанным протоколом. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить flush без ожидающих исходящих данныхПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$flushed = pg_flush($db);
var_dump($flushed);
Ожидаемый результат
bool(true)

true также означает, что отправлять было нечего. Это не результат SQL и не число строк; SQL-результат всё равно получают через pg_get_result. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_send_query_params(), pg_connection_busy(), pg_get_result().

pg_get_resultПолучить следующий результат ранее отправленной асинхронной команды.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_get_result(PgSql\Connection $connection): PgSql\Result|false

Получить следующий результат ранее отправленной асинхронной команды.

Запись pg_get_result(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.

Возврат и договор. Возвращает PgSql\Result для каждого результата по очереди и false, когда результатов больше нет. Сам объект может описывать успешный SELECT, команду без строк или SQL-ошибку — проверяйте pg_result_status.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Асинхронный запрос уже отправлен
  2. При необходимости вызов ждёт готовый результат
  3. Каждый вызов снимает один PgSql\Result
  4. false завершает обязательный цикл чтения
Забрать результаты нескольких постоянных команд по порядкуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT 10 AS value; SELECT 20 AS value');
if ($sent === false) { throw new RuntimeException('Не принято libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
$values = [];
while (($result = pg_get_result($db)) instanceof PgSql\Result) {
    if (pg_result_status($result) !== PGSQL_TUPLES_OK) { throw new RuntimeException(pg_result_error($result)); }
    $values[] = pg_fetch_result($result, 0, 'value');
}
echo implode(',', $values), "\n";
Ожидаемый результат
10,20

pg_send_query допускает несколько постоянных команд, а pg_get_result возвращает их результаты по одному. Цикл до false обязателен: непрочитанный результат ломает следующую операцию на соединении. На Windows/libpq ранний 0 от pg_socket_poll означает «ещё не готово»: пример повторяет ожидание с короткой паузой до одного общего deadline, отдельно обрабатывает -1 и закрывает соединение, если его состояние стало неопределённым. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отличить отсутствие результата от SQL-ошибкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT missing_column FROM missing_table');
if ($sent === false) { throw new RuntimeException('Не принято libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
$result = pg_get_result($db);
if ($result instanceof PgSql\Result) {
    echo pg_result_status($result) === PGSQL_FATAL_ERROR ? "SQL отклонён\n" : "Другой статус\n";
}
var_dump(pg_get_result($db));
Ожидаемый результат
SQL отклонён
bool(false)

SQL-ошибка приходит как объект PgSql\Result со статусом PGSQL_FATAL_ERROR. false означает только, что очередь результатов закончилась; путать эти состояния нельзя. Ранний 0 от pg_socket_poll на Windows/libpq повторяет ожидание до общего deadline; -1 немедленно считается ошибкой. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_send_query(), pg_result_status(), pg_result_error().

pg_send_queryОтправить один или несколько постоянных SQL-текстов, не ожидая их выполнения.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_send_query(PgSql\Connection $connection, string $query): int|bool

Отправить один или несколько постоянных SQL-текстов, не ожидая их выполнения.

Запись pg_send_query(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $queryОдин SQL-текст. Значения посетителя передают отдельно через параметры; структуру SQL формирует программа.

Возврат и договор. Возвращает bool: true означает, что libpq приняла команду для асинхронной отправки, false — локальный отказ. true не гарантирует, что исходящий буфер уже пуст: сразу проверьте pg_flush. Именно pg_flush возвращает 0, пока остались байты; после writable его вызывают повторно. SQL-успех подтверждает только каждый PgSql\Result из pg_get_result.

Отказ. pg_send_query возвращает только bool: false означает локальный отказ. После true отдельно проверяйте pg_flush: false — ошибка, 0 — остались байты и нужно ждать writable, успешное ненулевое значение — исходящая очередь пуста.

Что происходит
  1. Проверяется, что соединение свободно
  2. SQL отправляется libpq
  3. PHP продолжает независимую работу
  4. Все результаты считываются до false
Сделать независимую работу PHP, пока сервер считаетПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT pg_sleep(0.03), 6 * 7 AS answer');
if ($sent === false) { throw new RuntimeException('Не принято libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
$label = strtoupper('report');
while (pg_connection_busy($db)) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); }
}
$result = pg_get_result($db);
echo $label, ':', pg_fetch_result($result, 0, 'answer'), "\n";
Ожидаемый результат
REPORT:42

Асинхронность полезна, только если PHP действительно делает независимую работу или обслуживает другие события. Последовательный busy-loop сам по себе не ускоряет запрос. На Windows/libpq ранний 0 от poll означает «пока не готово», поэтому ожидание повторяется до общего deadline, а -1 обрабатывается отдельно как ошибка. При таймауте или ошибке ожидания пример закрывает соединение, чтобы незавершённый ответ не загрязнил следующую операцию. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить два результата одной постоянной пакетной командыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT 1 AS step; SELECT 2 AS step');
if ($sent === false) { throw new RuntimeException('Не принято libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
$steps = [];
while (($result = pg_get_result($db)) instanceof PgSql\Result) {
    $steps[] = pg_fetch_result($result, 0, 'step');
}
echo implode(' -> ', $steps), "\n";
Ожидаемый результат
1 -> 2

Несколько команд допустимы здесь только для заранее заданного программой SQL. Для внешних значений используйте pg_send_query_params, который принимает одну команду и параметры отдельно. На Windows/libpq ранний 0 от poll повторяет ожидание до общего deadline; -1 означает ошибку ожидания. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query(), pg_send_query_params(), pg_get_result().

pg_send_query_paramsАсинхронно отправить одну параметризованную SQL-команду.

PHP 5.1+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_send_query_params(PgSql\Connection $connection, string $query, array $params): int|bool

Асинхронно отправить одну параметризованную SQL-команду.

Запись pg_send_query_params(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $queryОдна параметризованная команда. Несколько команд через точку с запятой запрещены.
array $paramsВсе значения для $1, $2... в точном количестве; null становится SQL NULL, остальные передаются в текстовом формате.

Возврат и договор. Возвращает bool: true означает, что libpq приняла параметризованную команду, false — локальный отказ. Полноту очистки исходящего буфера отдельно проверяют pg_flush: его 0 требует ожидания writable и повторного flush. SQL допускает только одну команду; серверный успех или ошибка становятся известны из pg_get_result.

Отказ. false от pg_send_query_params — локальная ошибка отправки; эта функция не возвращает 0. После true значение 0 возможно у pg_flush и означает, что нужно ждать writable. Ошибка типов SQL приходит позднее как PgSql\Result.

Что происходит
  1. SQL хранит $1/$2 вместо значений
  2. Массив params передаётся отдельно
  3. PHP не ждёт сервер
  4. pg_get_result и pg_result_status подтверждают итог
Передать SQL-похожий текст как значениеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$value = "x'); SELECT pg_sleep(10); --";
$sent = pg_send_query_params($db, 'SELECT $1::text AS value', [$value]);
if ($sent === false) { throw new RuntimeException('Не принято libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
$result = pg_get_result($db);
echo pg_fetch_result($result, 0, 'value'), "\n";
while (pg_get_result($db) instanceof PgSql\Result) {}
Ожидаемый результат
x'); SELECT pg_sleep(10); --

Сервер получает одну SQL-команду и отдельное значение; вложенный текст не исполняется. Асинхронный вариант сохраняет ту же границу параметров, что pg_query_params. Ранний 0 от pg_socket_poll на Windows/libpq означает «пока не готово», поэтому код повторяет ожидание до общего deadline. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть серверную ошибку только при чтении результатаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query_params($db, 'SELECT $1::integer AS qty', ['twelve']);
if ($sent === false) { throw new RuntimeException('Не принято libpq'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
var_dump($sent === true);
$result = pg_get_result($db);
echo pg_result_status($result) === PGSQL_FATAL_ERROR ? "Значение отклонено сервером\n" : "Неожиданный статус\n";
Ожидаемый результат
bool(true)
Значение отклонено сервером

Успешная отправка не гарантирует успешный SQL. Несовместимый integer обнаруживается сервером позже и виден в PgSql\Result. Windows/libpq может рано вернуть 0 из pg_socket_poll; это повтор ожидания до общего deadline, тогда как -1 — немедленная ошибка. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_query_params(), pg_send_query(), pg_get_result().

pg_send_prepareОтправить создание prepared statement, не ожидая ответа сервера.

PHP 5.1+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_send_prepare(PgSql\Connection $connection, string $statement_name, string $query): int|bool

Отправить создание prepared statement, не ожидая ответа сервера.

Запись pg_send_prepare(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $statement_nameИмя подготовленной команды в пределах текущего соединения; это идентификатор протокола, а не SQL-текст.
string $queryОдин SQL-текст. Значения посетителя передают отдельно через параметры; структуру SQL формирует программа.

Возврат и договор. Возвращает bool: true означает, что libpq приняла запрос подготовки, false — локальный отказ. Состояние исходящего буфера затем проверяют pg_flush; только его 0 означает необходимость ждать writable и повторить flush. Серверный результат подготовки обязательно читают pg_get_result; одно непустое имя уникально, пустое имя заменяет предыдущую unnamed statement.

Отказ. pg_send_prepare возвращает только bool: false — локальный отказ. После true false/0 от pg_flush означают соответственно ошибку либо незавершённую досылку. Конфликт непустого имени является серверной ошибкой и приходит позже в PgSql\Result.

Что происходит
  1. Клиент задаёт имя и SQL с $1
  2. Запрос подготовки отправляется
  3. PHP может продолжить независимую работу
  4. pg_get_result подтверждает COMMAND_OK либо ошибку
Асинхронно подготовить именованную командуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_prepare($db, 'async_double', 'SELECT $1::integer * 2 AS value');
if ($sent === false) { throw new RuntimeException('Prepare не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
$result = pg_get_result($db);
echo pg_result_status($result) === PGSQL_COMMAND_OK ? "Подготовлено\n" : "Ошибка\n";
Ожидаемый результат
Подготовлено

Возвращаемое значение отправки подтверждает только протокол; PGSQL_COMMAND_OK в полученном результате подтверждает, что сервер создал statement. Ранний 0 от pg_socket_poll на Windows/libpq повторяется до общего deadline; -1 немедленно завершает ожидание ошибкой. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить ошибку повторного имени через очередь результатовПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_prepare($db, 'async_duplicate', 'SELECT 1');
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_prepare($db, 'async_duplicate', 'SELECT 2');
if ($sent === false) { throw new RuntimeException('Prepare не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
var_dump($sent === true);
$result = pg_get_result($db);
echo pg_result_status($result) === PGSQL_FATAL_ERROR ? "Имя уже занято\n" : "Неожиданный статус\n";
Ожидаемый результат
bool(true)
Имя уже занято

Отправка прошла, но сервер отклонил повтор statement_name. Это наглядно разделяет транспортный успех и результат операции. Ранний 0 от poll на Windows/libpq — повод повторить ожидание до общего deadline, а не объявлять таймаут. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_prepare(), pg_send_execute(), pg_get_result().

pg_send_executeАсинхронно выполнить ранее подготовленную statement с новым набором значений.

PHP 5.1+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_send_execute(PgSql\Connection $connection, string $statement_name, array $params): int|bool

Асинхронно выполнить ранее подготовленную statement с новым набором значений.

Запись pg_send_execute(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $statement_nameИмя подготовленной команды в пределах текущего соединения; это идентификатор протокола, а не SQL-текст.
array $paramsСписок значений для $1, $2 и далее. PHP передаёт их libpq как значения, а тип обычно выводит PostgreSQL из контекста/явного cast.

Возврат и договор. Возвращает bool: true означает, что libpq приняла execute, false — локальный отказ. После true отдельно вызывают pg_flush: 0 у pg_flush означает недосланные байты и требует ожидания writable. Statement должна существовать на том же соединении; результат или SQL-ошибка приходят через pg_get_result.

Отказ. pg_send_execute возвращает только bool: false — локальный отказ. После true значение 0 возможно у pg_flush и означает, что исходящий буфер ещё не пуст. Неизвестное statement_name — серверная ошибка в PgSql\Result, а не false отправки.

Что происходит
  1. pg_prepare/pg_send_prepare создал statement
  2. params связываются с $1/$2
  3. execute отправляется без ожидания
  4. результат читается и проверяется
Выполнить подготовленный расчёт без ожиданияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_prepare($db, 'async_total', 'SELECT $1::integer + $2::integer AS total');
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_execute($db, 'async_total', [20, 22]);
if ($sent === false) { throw new RuntimeException('Execute не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
$result = pg_get_result($db);
echo pg_fetch_result($result, 0, 'total'), "\n";
Ожидаемый результат
42

Statement подготовлена на том же соединении. Значения меняются при каждом execute; SQL повторно не передаётся. На Windows/libpq ранний 0 от pg_socket_poll повторяется с паузой до общего deadline; -1 считается ошибкой. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать неизвестное имя statementПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_execute($db, 'not_prepared_here', [1]);
if ($sent === false) { throw new RuntimeException('Execute не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
var_dump($sent === true);
$result = pg_get_result($db);
echo pg_result_status($result) === PGSQL_FATAL_ERROR ? "Statement не найдена\n" : "Неожиданный статус\n";
Ожидаемый результат
bool(true)
Statement не найдена

И здесь dispatch может пройти успешно, а ошибка обнаружится сервером. Prepared statements живут в конкретной сессии и не появляются автоматически в другом соединении. Ранний 0 от poll на Windows/libpq повторяет ожидание до общего deadline; -1 завершает его немедленно. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_execute(), pg_send_prepare(), pg_get_result().

pg_set_chunked_rows_sizeЗадать максимальное число строк в одном chunk результата асинхронного запроса.

PHP 8.4+; только libpq 17+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_set_chunked_rows_size(PgSql\Connection $connection, int $size): bool

Задать максимальное число строк в одном chunk результата асинхронного запроса.

Запись pg_set_chunked_rows_size(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
int $sizeЦелое от 1 до INT_MAX: максимум строк в одном chunk. Это не размер в байтах и не SQL LIMIT.

Возврат и договор. Возвращает true при включении и false при отказе. size должен быть от 1 до INT_MAX и означает число строк, не байты. Вызывать нужно после успешной отправки pg_send_query и до первого pg_get_result; после начала чтения возвращается false. Доступна только при сборке ext/pgsql с libpq 17+, которой нет на текущем стенде.

Отказ. size < 1 или больше INT_MAX даёт ValueError. false означает неверный момент/состояние или отказ libpq. Проверяйте function_exists: функция может отсутствовать в DLL той же версии PHP, если ext/pgsql собрана с libpq до 17.

Граница текущего стенда. Requires a build with libpq 17+ PQsetChunkedRowsMode support; the local DLL reports libpq 16.15. Карточка сохраняется, потому что функция есть в официальной ветке PHP 8.5.11; пример для неё проверяется по исходнику/синтаксису и явно не выдаётся за выполненный на этой DLL.

Что происходит
  1. pg_send_query запускает большой SELECT
  2. до чтения задаётся size >= 1
  3. каждый pg_get_result получает chunk до size строк
  4. цикл дочитывает все chunks до false
Получить пять строк chunks по две строкиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
if (!function_exists('pg_set_chunked_rows_size')) {
    echo "Нужна PHP-сборка с libpq 17+\n";
    return;
}
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT generate_series(1, 5) AS id');
if ($sent === false) { throw new RuntimeException('Запрос не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
if (!pg_set_chunked_rows_size($db, 2)) {
    pg_close($db);
    throw new RuntimeException('Chunk mode не включён; соединение закрыто');
}
$sizes = [];
$ids = [];
$finalReceived = false;
$error = null;
while (true) {
    while (pg_connection_busy($db)) {
        $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
        if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
        $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
        if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
        if ($ready === 0) { usleep(10_000); }
    }
    $chunk = pg_get_result($db);
    if (!$chunk instanceof PgSql\Result) { break; }
    $status = pg_result_status($chunk);
    if ($status === PGSQL_TUPLES_CHUNK) {
        $sizes[] = pg_num_rows($chunk);
        foreach (pg_fetch_all($chunk) ?: [] as $row) { $ids[] = $row['id']; }
    } elseif ($status === PGSQL_TUPLES_OK && pg_num_rows($chunk) === 0) {
        $finalReceived = true;
    } elseif ($status === PGSQL_FATAL_ERROR) {
        $error = pg_result_error($chunk) ?: 'PostgreSQL отклонил запрос';
    } else {
        $error = 'Неожиданный статус chunk-результата';
    }
}
if ($error !== null) { throw new RuntimeException($error); }
if (!$finalReceived) { throw new RuntimeException('Нет финального пустого PGSQL_TUPLES_OK'); }
echo implode(',', $sizes), "\n";
Ожидаемый результат
Нужна PHP-сборка с libpq 17+

На текущем стенде честно выполняется capability-ветка: php_pgsql.dll связана с libpq 16.15. На libpq 17+ код включает режим строго после pg_send_query и до первого pg_get_result. Прикладные строки принимаются только из PGSQL_TUPLES_CHUNK; финальный пустой PGSQL_TUPLES_OK подтверждает полное завершение. Ошибка запоминается, вся очередь дочитывается, и лишь затем код публикует размеры 2,2,1. Ранний 0 от pg_socket_poll на Windows/libpq повторяется до общего deadline; -1 немедленно считается ошибкой. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть, что после первого result менять размер поздноПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
if (!function_exists('pg_set_chunked_rows_size')) {
    echo "Нужна PHP-сборка с libpq 17+\n";
    return;
}
$socket = pg_socket($db);
if ($socket === false) { throw new RuntimeException('Нет сокета'); }
$deadline = microtime(true) + 3.0;
$sent = pg_send_query($db, 'SELECT generate_series(1, 3) AS id');
if ($sent === false) { throw new RuntimeException('Запрос не принят'); }
$flushed = pg_flush($db);
if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка начальной отправки; соединение закрыто'); }
while ($flushed === 0) {
    $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
    if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут записи; соединение закрыто'); }
    $ready = pg_socket_poll($socket, 0, 1, $remainingMs);
    if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания записи; соединение закрыто'); }
    if ($ready === 0) { usleep(10_000); continue; }
    $flushed = pg_flush($db);
    if ($flushed === false) { pg_close($db); throw new RuntimeException('Ошибка досылки; соединение закрыто'); }
}
if (!pg_set_chunked_rows_size($db, 1)) {
    pg_close($db);
    throw new RuntimeException('Chunk mode не включён; соединение закрыто');
}
$firstSize = null;
$lateChange = null;
$finalReceived = false;
$error = null;
while (true) {
    while (pg_connection_busy($db)) {
        $remainingMs = (int) ceil(($deadline - microtime(true)) * 1000);
        if ($remainingMs <= 0) { pg_close($db); throw new RuntimeException('Таймаут ответа; соединение закрыто'); }
        $ready = pg_socket_poll($socket, 1, 0, $remainingMs);
        if ($ready < 0) { pg_close($db); throw new RuntimeException('Ошибка ожидания чтения; соединение закрыто'); }
        if ($ready === 0) { usleep(10_000); }
    }
    $chunk = pg_get_result($db);
    if (!$chunk instanceof PgSql\Result) { break; }
    $status = pg_result_status($chunk);
    if ($status === PGSQL_TUPLES_CHUNK) {
        if ($firstSize === null) {
            $firstSize = pg_num_rows($chunk);
            $lateChange = pg_set_chunked_rows_size($db, 10);
        }
    } elseif ($status === PGSQL_TUPLES_OK && pg_num_rows($chunk) === 0) {
        $finalReceived = true;
    } elseif ($status === PGSQL_FATAL_ERROR) {
        $error = pg_result_error($chunk) ?: 'PostgreSQL отклонил запрос';
    } else {
        $error = 'Неожиданный статус chunk-результата';
    }
}
if ($error !== null) { throw new RuntimeException($error); }
if ($firstSize === null || !$finalReceived) { throw new RuntimeException('Неполная последовательность chunk-результатов'); }
if ($lateChange !== false) { throw new RuntimeException('Позднее изменение неожиданно принято'); }
echo 'rows=', $firstSize, "\n";
var_dump($lateChange);
Ожидаемый результат
Нужна PHP-сборка с libpq 17+

На поддерживаемой сборке первый PGSQL_TUPLES_CHUNK содержит одну строку, а поздняя попытка изменить size возвращает false: режим задаётся только между отправкой запроса и первым pg_get_result. Код всё равно дочитывает очередь, требует финальный пустой PGSQL_TUPLES_OK и только после этого показывает результат. PGSQL_FATAL_ERROR также запоминается и сообщается после drain. На текущей DLL capability-ветка честно сообщает недоступность. Ранний 0 от poll на Windows/libpq повторяется до общего deadline. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_send_query(), pg_get_result(), pg_result_memory_size().

pg_copy_fromМассово загрузить строки в таблицу через PostgreSQL COPY FROM.

PHP 4.2+; с PHP 8.5 rows принимает iterable · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_copy_from(PgSql\Connection $connection, string $table_name, Traversable|array $rows, string $separator = '	', string $null_as = '\\\\N'): bool

Массово загрузить строки в таблицу через PostgreSQL COPY FROM.

Запись pg_copy_from(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя таблицы. Параметры запросов не подставляют идентификаторы; внешнее имя сначала проверяют по allowlist.
Traversable|array $rowsarray или Traversable строк COPY text. Это не массив массивов и не CSV-объекты; разделители, backslash и переводы строк нужно кодировать по правилам COPY.
string $separator (по умолчанию ' ')Один символ между полями; по умолчанию tab.
string $null_as (по умолчанию '\\\\N')Отдельный маркер SQL NULL; по умолчанию \N. Пустая строка остаётся пустой строкой.

Возврат и договор. Возвращает true при полном успехе и false при отказе. Каждый элемент iterable должен быть строкой COPY text, обычно с переводом строки; порядок полей должен совпадать с таблицей, separator/null_as — с фактическим форматом.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Приложение валидирует и кодирует каждую строку
  2. pg_copy_from открывает COPY FROM
  3. PostgreSQL принимает поток в одной операции
  4. после успеха таблица содержит все строки
Загрузить три строки одним COPYПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE copy_products (id integer, name text, note text)');
$rows = [
    "1\tРучка\tсиняя\n",
    "2\tБлокнот\t\\N\n",
    "3\tЛинейка\t30 см\n",
];
$ok = pg_copy_from($db, 'copy_products', $rows);
if (!$ok) { throw new RuntimeException(pg_last_error($db)); }
$result = pg_query($db, 'SELECT count(*) AS total, count(note) AS with_note FROM copy_products');
echo pg_fetch_result($result, 0, 'total'), '|', pg_fetch_result($result, 0, 'with_note'), "\n";
Ожидаемый результат
3|2

Каждый элемент — уже сформированная строка COPY text с переводом строки. Стандартный маркер \N стал SQL NULL, а не текстом; count(note) поэтому равен двум. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отдать строки генератором без большого PHP-массиваПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE copy_events (id integer, label text)');
$rows = (function (): Generator {
    for ($id = 1; $id <= 4; $id++) {
        yield $id . "\tevent-" . $id . "\n";
    }
})();
var_dump(pg_copy_from($db, 'copy_events', $rows));
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM copy_events'), 0, 0), "\n";
Ожидаемый результат
bool(true)
4

Начиная с PHP 8.5 rows принимает iterable, поэтому строки можно генерировать по одной. Это экономит PHP-память, но весь COPY остаётся одной операцией: ошибка одной строки отклоняет импорт. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_copy_to(), pg_put_copy_data(), pg_query_params().

pg_copy_toВыгрузить всю таблицу в массив строк формата COPY text.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_copy_to(PgSql\Connection $connection, string $table_name, string $separator = '	', string $null_as = '\\\\N'): array|false

Выгрузить всю таблицу в массив строк формата COPY text.

Запись pg_copy_to(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $table_nameДоверенное имя целой таблицы. Условие WHERE эта функция не принимает.
string $separator (по умолчанию ' ')Один символ между полями.
string $null_as (по умолчанию '\\\\N')Маркер SQL NULL, который не должен пересекаться с обычными данными без корректного escaping.

Возврат и договор. Возвращает массив транспортных строк либо false. Функция выгружает таблицу целиком без произвольного WHERE/ORDER BY; для выборочной или огромной выгрузки обычно применяют явный COPY (SELECT ...) TO STDOUT специализированным инструментом.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PostgreSQL читает строки таблицы
  2. поля кодируются separator/null_as
  3. PHP получает массив строк с linefeed
  4. код сохраняет/передаёт их как транспортный формат
Получить строки COPY и увидеть маркер NULLПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE export_products (id integer, note text)');
pg_query($db, "INSERT INTO export_products VALUES (1, 'ok'), (2, NULL)");
$rows = pg_copy_to($db, 'export_products', "|", "<NULL>");
if ($rows === false) { throw new RuntimeException(pg_last_error($db)); }
foreach ($rows as $row) { echo rtrim($row, "\r\n"), "\n"; }
Ожидаемый результат
1|ok
2|<NULL>

Функция возвращает транспортные строки, а не ассоциативные массивы. Явный null_as позволяет отличить SQL NULL от пустого текста в выгрузке. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сделать учебный round trip COPY TO → COPY FROMПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE source_rows (id integer, title text)');
pg_query($db, 'CREATE TEMP TABLE restored_rows (id integer, title text)');
pg_query($db, "INSERT INTO source_rows VALUES (1, 'one'), (2, 'two')");
$dump = pg_copy_to($db, 'source_rows');
if ($dump === false || !pg_copy_from($db, 'restored_rows', $dump)) { throw new RuntimeException('COPY не выполнен'); }
echo pg_fetch_result(pg_query($db, 'SELECT string_agg(title, chr(44) ORDER BY id) FROM restored_rows'), 0, 0), "\n";
Ожидаемый результат
one,two

Round trip работает при одинаковой структуре и одинаковых separator/null_as. Это не полноценный backup: здесь нет схемы, индексов, прав, sequence и согласованного snapshot; для резервной копии нужны pg_dump/pg_restore. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_copy_from(), pg_query(), pg_put_copy_data().

pg_put_lineПередать одну строку данных в уже открытый legacy COPY FROM STDIN.

PHP 4.0.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_put_line(PgSql\Connection $connection, string $query): bool

Передать одну строку данных в уже открытый legacy COPY FROM STDIN.

Запись pg_put_line(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionПередавайте явный PgSql\Connection; legacy overload без него deprecated с PHP 8.1.
string $queryОдна строка COPY data. Здесь имя аргумента query историческое: это не произвольный SQL.

Возврат и договор. Возвращает true/false для отправки строки. После всех данных нужно явно отправить два символа \. с переводом строки и вызвать pg_end_copy. Официальная документация предупреждает, что после pg_put_line многие операции large object могут перестать работать; предпочитайте pg_copy_from либо новую pg_put_copy_data.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. pg_query открывает COPY FROM STDIN
  2. каждый pg_put_line передаёт строку формата COPY
  3. строка \. завершает данные
  4. pg_end_copy синхронизирует клиент и сервер
Передать строки старым COPY-протоколомПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE legacy_copy (id integer, label text)');
$copy = pg_query($db, 'COPY legacy_copy FROM STDIN');
if (!$copy instanceof PgSql\Result) { throw new RuntimeException(pg_last_error($db)); }
pg_put_line($db, "1\talpha\n");
pg_put_line($db, "2\tbeta\n");
pg_put_line($db, "\\.\n");
if (!pg_end_copy($db)) { throw new RuntimeException(pg_last_error($db)); }
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM legacy_copy'), 0, 0), "\n";
Ожидаемый результат
2

У legacy-пары pg_put_line/pg_end_copy приложение само посылает строку \. и затем синхронизирует соединение. Для обычного массива pg_copy_from проще и меньше подвержен протокольным ошибкам. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Передать SQL NULL и пустую строку как разные значенияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE legacy_nulls (id integer, value text)');
pg_query($db, 'COPY legacy_nulls FROM STDIN');
pg_put_line($db, "1\t\\N\n");
pg_put_line($db, "2\t\n");
pg_put_line($db, "\\.\n");
pg_end_copy($db);
$result = pg_query($db, "SELECT count(*) FILTER (WHERE value IS NULL), count(*) FILTER (WHERE value = '') FROM legacy_nulls");
echo pg_fetch_result($result, 0, 0), '|', pg_fetch_result($result, 0, 1), "\n";
Ожидаемый результат
1|1

В COPY text \N — SQL NULL, а пустое поле — пустая строка. Эта разница критична для импортов: «нет значения» и «значение есть, но пустое» часто означают разные бизнес-состояния. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_end_copy(), pg_copy_from(), pg_put_copy_data().

pg_end_copyЗавершить legacy COPY после pg_put_line и синхронизировать соединение.

PHP 4.0.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_end_copy(?PgSql\Connection $connection = null): bool

Завершить legacy COPY после pg_put_line и синхронизировать соединение.

Запись pg_end_copy(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явное соединение, находящееся в legacy COPY. null использует deprecated default connection.

Возврат и договор. Возвращает true при успешном завершении и false, если COPY отклонён. Без вызова соединение остаётся в COPY-состоянии и последующие команды приводят к ошибкам.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. данные и маркер \. уже отправлены
  2. pg_end_copy ждёт финальный ответ COPY
  3. false сообщает об ошибке всей операции
  4. после успеха соединение снова готово к SQL
Вернуть соединение в обычный режим после COPYПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE end_copy_items (id integer)');
pg_query($db, 'COPY end_copy_items FROM STDIN');
pg_put_line($db, "7\n");
pg_put_line($db, "\\.\n");
var_dump(pg_end_copy($db));
echo pg_fetch_result(pg_query($db, 'SELECT id FROM end_copy_items'), 0, 0), "\n";
Ожидаемый результат
bool(true)
7

Успешный pg_end_copy означает, что COPY завершён и соединение снова принимает обычный SQL. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить false, если одна строка не соответствует типуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE end_copy_numbers (id integer)');
pg_query($db, 'COPY end_copy_numbers FROM STDIN');
pg_put_line($db, "not-an-integer\n");
pg_put_line($db, "\\.\n");
$ok = @pg_end_copy($db);
var_dump($ok);
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM end_copy_numbers'), 0, 0), "\n";
Ожидаемый результат
bool(false)
0

COPY выполняется как одна операция: несовместимая строка делает pg_end_copy false, и ранее переданные строки этой операции не появляются в таблице. Подробность берут из закрытого журнала, не показывают пользователю. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_put_line(), pg_copy_from(), pg_result_error().

pg_put_copy_dataПередать данные в открытую COPY FROM STDIN через современную libpq API.

PHP 8.4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_put_copy_data(PgSql\Connection $connection, string $cmd): int

Передать данные в открытую COPY FROM STDIN через современную libpq API.

Запись pg_put_copy_data(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $cmdОдна или несколько порций COPY data. Это не SQL; формат должен точно соответствовать COPY command.

Возврат и договор. Возвращает 1 при постановке в очередь, 0 если non-blocking очередь пока не принимает данные, и -1 при ошибке. При 0 ждут writable и повторяют именно ту же порцию; при -1 отменяют COPY через pg_put_copy_end(error), снимают финальный результат и не продолжают импорт. На проверенной PHP 8.5.11 порция передаётся без автоматического newline, поэтому строку COPY text завершайте \n самостоятельно.

Отказ. 0 — временное backpressure, порцию нужно повторить после writable; -1 — ошибка, после которой текущую COPY надо явно отменить и дочитать результат. Бросать исключение без abort оставляет соединение в COPY-состоянии.

Что происходит
  1. COPY FROM STDIN уже вернул PGSQL_COPY_IN
  2. строка форматируется и явно завершается newline
  3. pg_put_copy_data ставит точные байты в очередь
  4. pg_put_copy_end завершает или отменяет весь COPY
Передать современные COPY-строки без маркера \.Пример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function waitCopySocket(PgSql\Connection $connection, int $read, int $write, int $deadline): void
{
    $socket = pg_socket($connection);
    if ($socket === false) { throw new RuntimeException('Нет COPY-сокета'); }
    while (true) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Истёк COPY deadline'); }
        $ready = pg_socket_poll($socket, $read, $write, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания COPY-сокета'); }
        if ($ready > 0) { return; }
        usleep(10_000);
    }
}

function flushCopyOutput(PgSql\Connection $connection, int $deadline): void
{
    while (true) {
        $flushed = pg_flush($connection);
        if ($flushed === false) { throw new RuntimeException('Ошибка отправки COPY'); }
        if ($flushed === true) { return; }
        waitCopySocket($connection, 0, 1, $deadline);
    }
}

function drainCopyResults(PgSql\Connection $connection, int $deadline): array
{
    while (pg_connection_busy($connection)) {
        waitCopySocket($connection, 1, 0, $deadline);
        if (!pg_consume_input($connection)) { throw new RuntimeException('Ошибка чтения COPY-ответа'); }
    }
    $results = [];
    while (($result = pg_get_result($connection)) instanceof PgSql\Result) {
        $results[] = $result;
    }
    return $results;
}

function queueCopyEnd(PgSql\Connection $connection, int $deadline, ?string $error = null): PgSql\Result
{
    try {
        do {
            $status = pg_put_copy_end($connection, $error);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Сигнал COPY end не поставлен'); }
        flushCopyOutput($connection, $deadline);
        $results = drainCopyResults($connection, $deadline);
        if ($results === []) { throw new RuntimeException('Нет финального COPY result'); }
        return $results[array_key_last($results)];
    } catch (Throwable $errorObject) {
        try { pg_close($connection); } catch (Throwable) {}
        throw $errorObject;
    }
}

function abortCopy(PgSql\Connection $connection, string $message): void
{
    $cleanupDeadline = hrtime(true) + 2_000_000_000;
    try {
        queueCopyEnd($connection, $cleanupDeadline, $message);
    } catch (Throwable) {
        try { pg_close($connection); } catch (Throwable) {}
    }
}

function queueCopyData(PgSql\Connection $connection, string $data, int $deadline): void
{
    try {
        do {
            $status = pg_put_copy_data($connection, $data);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Порция COPY отклонена'); }
    } catch (Throwable $error) {
        abortCopy($connection, 'Клиент не смог отправить порцию');
        throw $error;
    }
}

pg_query($db, 'CREATE TEMP TABLE modern_copy (id integer, label text)');
$copy = pg_query($db, 'COPY modern_copy FROM STDIN');
if (pg_result_status($copy) !== PGSQL_COPY_IN) { throw new RuntimeException('COPY не открыт'); }
$deadline = hrtime(true) + 5_000_000_000;
foreach (["1\talpha\n", "2\tbeta\n"] as $line) { queueCopyData($db, $line, $deadline); }
$final = queueCopyEnd($db, $deadline);
if (!$final instanceof PgSql\Result || pg_result_status($final) !== PGSQL_COMMAND_OK) { throw new RuntimeException('Сервер не подтвердил COPY'); }
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM modern_copy'), 0, 0), "\n";
Ожидаемый результат
2

Каждая строка заканчивается явным newline. Краткая официальная страница утверждает, что newline добавляется автоматически, но фактический PHP 8.5.11/php-src передаёт PQputCopyData буфер ровно заданной длины: без \n две порции склеились бы. В отличие от pg_put_line строка \. не отправляется: завершение сигнализирует pg_put_copy_end. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить код постановки строки в очередьПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function waitCopySocket(PgSql\Connection $connection, int $read, int $write, int $deadline): void
{
    $socket = pg_socket($connection);
    if ($socket === false) { throw new RuntimeException('Нет COPY-сокета'); }
    while (true) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Истёк COPY deadline'); }
        $ready = pg_socket_poll($socket, $read, $write, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания COPY-сокета'); }
        if ($ready > 0) { return; }
        usleep(10_000);
    }
}

function flushCopyOutput(PgSql\Connection $connection, int $deadline): void
{
    while (true) {
        $flushed = pg_flush($connection);
        if ($flushed === false) { throw new RuntimeException('Ошибка отправки COPY'); }
        if ($flushed === true) { return; }
        waitCopySocket($connection, 0, 1, $deadline);
    }
}

function drainCopyResults(PgSql\Connection $connection, int $deadline): array
{
    while (pg_connection_busy($connection)) {
        waitCopySocket($connection, 1, 0, $deadline);
        if (!pg_consume_input($connection)) { throw new RuntimeException('Ошибка чтения COPY-ответа'); }
    }
    $results = [];
    while (($result = pg_get_result($connection)) instanceof PgSql\Result) {
        $results[] = $result;
    }
    return $results;
}

function queueCopyEnd(PgSql\Connection $connection, int $deadline, ?string $error = null): PgSql\Result
{
    try {
        do {
            $status = pg_put_copy_end($connection, $error);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Сигнал COPY end не поставлен'); }
        flushCopyOutput($connection, $deadline);
        $results = drainCopyResults($connection, $deadline);
        if ($results === []) { throw new RuntimeException('Нет финального COPY result'); }
        return $results[array_key_last($results)];
    } catch (Throwable $errorObject) {
        try { pg_close($connection); } catch (Throwable) {}
        throw $errorObject;
    }
}

function abortCopy(PgSql\Connection $connection, string $message): void
{
    $cleanupDeadline = hrtime(true) + 2_000_000_000;
    try {
        queueCopyEnd($connection, $cleanupDeadline, $message);
    } catch (Throwable) {
        try { pg_close($connection); } catch (Throwable) {}
    }
}

function queueCopyData(PgSql\Connection $connection, string $data, int $deadline): void
{
    try {
        do {
            $status = pg_put_copy_data($connection, $data);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Порция COPY отклонена'); }
    } catch (Throwable $error) {
        abortCopy($connection, 'Клиент не смог отправить порцию');
        throw $error;
    }
}

pg_query($db, 'CREATE TEMP TABLE copy_status (value text)');
pg_query($db, 'COPY copy_status FROM STDIN');
$deadline = hrtime(true) + 5_000_000_000;
queueCopyData($db, "hello\n", $deadline);
echo "Строка принята клиентской библиотекой\n";
$final = queueCopyEnd($db, $deadline);
if (!$final instanceof PgSql\Result || pg_result_status($final) !== PGSQL_COMMAND_OK) { throw new RuntimeException('Сервер не подтвердил COPY'); }
Ожидаемый результат
Строка принята клиентской библиотекой

Код 1 относится к постановке данных в очередь libpq. Окончательный успех COPY подтверждается только после завершения операции; 0 возможен в non-blocking mode, -1 означает ошибку. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_put_copy_end(), pg_copy_from(), pg_flush().

pg_put_copy_endСообщить PostgreSQL об успешном конце COPY data либо принудительно отменить COPY.

PHP 8.4+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_put_copy_end(PgSql\Connection $connection, ?string $error = null): int

Сообщить PostgreSQL об успешном конце COPY data либо принудительно отменить COPY.

Запись pg_put_copy_end(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
?string $error (по умолчанию null)null для успешного окончания; диагностическая строка для отмены операции. Не включайте в неё секреты или персональные данные.

Возврат и договор. Возвращает 1 при постановке сигнала в очередь, 0 при временной нехватке места в non-blocking mode и -1 при ошибке. При 0 ждут writable и повторяют вызов с тем же error. error = null завершает поток нормально; непустой error заставляет сервер отклонить всю COPY-операцию. После принятого сигнала нужно снять финальный PgSql\Result через pg_get_result.

Отказ. 0 означает backpressure и требует повторения после writable; -1 означает, что сигнал не поставлен. Если даже аварийный end нельзя поставить, это соединение нельзя безопасно возвращать в pool: его закрывают/заменяют.

Что происходит
  1. последняя порция данных отправлена
  2. null означает нормальное завершение, строка error — отмену
  3. pg_get_result подтверждает COMMAND_OK/FATAL_ERROR
  4. соединение возвращается из COPY-состояния
Зафиксировать окончание потока данныхПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function waitCopySocket(PgSql\Connection $connection, int $read, int $write, int $deadline): void
{
    $socket = pg_socket($connection);
    if ($socket === false) { throw new RuntimeException('Нет COPY-сокета'); }
    while (true) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Истёк COPY deadline'); }
        $ready = pg_socket_poll($socket, $read, $write, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания COPY-сокета'); }
        if ($ready > 0) { return; }
        usleep(10_000);
    }
}

function flushCopyOutput(PgSql\Connection $connection, int $deadline): void
{
    while (true) {
        $flushed = pg_flush($connection);
        if ($flushed === false) { throw new RuntimeException('Ошибка отправки COPY'); }
        if ($flushed === true) { return; }
        waitCopySocket($connection, 0, 1, $deadline);
    }
}

function drainCopyResults(PgSql\Connection $connection, int $deadline): array
{
    while (pg_connection_busy($connection)) {
        waitCopySocket($connection, 1, 0, $deadline);
        if (!pg_consume_input($connection)) { throw new RuntimeException('Ошибка чтения COPY-ответа'); }
    }
    $results = [];
    while (($result = pg_get_result($connection)) instanceof PgSql\Result) {
        $results[] = $result;
    }
    return $results;
}

function queueCopyEnd(PgSql\Connection $connection, int $deadline, ?string $error = null): PgSql\Result
{
    try {
        do {
            $status = pg_put_copy_end($connection, $error);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Сигнал COPY end не поставлен'); }
        flushCopyOutput($connection, $deadline);
        $results = drainCopyResults($connection, $deadline);
        if ($results === []) { throw new RuntimeException('Нет финального COPY result'); }
        return $results[array_key_last($results)];
    } catch (Throwable $errorObject) {
        try { pg_close($connection); } catch (Throwable) {}
        throw $errorObject;
    }
}

function abortCopy(PgSql\Connection $connection, string $message): void
{
    $cleanupDeadline = hrtime(true) + 2_000_000_000;
    try {
        queueCopyEnd($connection, $cleanupDeadline, $message);
    } catch (Throwable) {
        try { pg_close($connection); } catch (Throwable) {}
    }
}

function queueCopyData(PgSql\Connection $connection, string $data, int $deadline): void
{
    try {
        do {
            $status = pg_put_copy_data($connection, $data);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Порция COPY отклонена'); }
    } catch (Throwable $error) {
        abortCopy($connection, 'Клиент не смог отправить порцию');
        throw $error;
    }
}

pg_query($db, 'CREATE TEMP TABLE copy_end_ok (id integer)');
pg_query($db, 'COPY copy_end_ok FROM STDIN');
$deadline = hrtime(true) + 5_000_000_000;
queueCopyData($db, "9\n", $deadline);
$final = queueCopyEnd($db, $deadline);
echo "Конец поставлен в очередь\n";
echo $final instanceof PgSql\Result && pg_result_status($final) === PGSQL_COMMAND_OK ? "COPY подтверждён\n" : "COPY не подтверждён\n";
echo pg_fetch_result(pg_query($db, 'SELECT id FROM copy_end_ok'), 0, 0), "\n";
Ожидаемый результат
Конец поставлен в очередь
COPY подтверждён
9

1 означает, что сигнал окончания принят клиентской библиотекой. Финальный PgSql\Result нужно обязательно снять через pg_get_result; только его PGSQL_COMMAND_OK подтверждает серверный успех. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Осознанно отменить импорт сообщением об ошибкеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function waitCopySocket(PgSql\Connection $connection, int $read, int $write, int $deadline): void
{
    $socket = pg_socket($connection);
    if ($socket === false) { throw new RuntimeException('Нет COPY-сокета'); }
    while (true) {
        $remainingMs = (int) ceil(($deadline - hrtime(true)) / 1_000_000);
        if ($remainingMs <= 0) { throw new RuntimeException('Истёк COPY deadline'); }
        $ready = pg_socket_poll($socket, $read, $write, $remainingMs);
        if ($ready < 0) { throw new RuntimeException('Ошибка ожидания COPY-сокета'); }
        if ($ready > 0) { return; }
        usleep(10_000);
    }
}

function flushCopyOutput(PgSql\Connection $connection, int $deadline): void
{
    while (true) {
        $flushed = pg_flush($connection);
        if ($flushed === false) { throw new RuntimeException('Ошибка отправки COPY'); }
        if ($flushed === true) { return; }
        waitCopySocket($connection, 0, 1, $deadline);
    }
}

function drainCopyResults(PgSql\Connection $connection, int $deadline): array
{
    while (pg_connection_busy($connection)) {
        waitCopySocket($connection, 1, 0, $deadline);
        if (!pg_consume_input($connection)) { throw new RuntimeException('Ошибка чтения COPY-ответа'); }
    }
    $results = [];
    while (($result = pg_get_result($connection)) instanceof PgSql\Result) {
        $results[] = $result;
    }
    return $results;
}

function queueCopyEnd(PgSql\Connection $connection, int $deadline, ?string $error = null): PgSql\Result
{
    try {
        do {
            $status = pg_put_copy_end($connection, $error);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Сигнал COPY end не поставлен'); }
        flushCopyOutput($connection, $deadline);
        $results = drainCopyResults($connection, $deadline);
        if ($results === []) { throw new RuntimeException('Нет финального COPY result'); }
        return $results[array_key_last($results)];
    } catch (Throwable $errorObject) {
        try { pg_close($connection); } catch (Throwable) {}
        throw $errorObject;
    }
}

function abortCopy(PgSql\Connection $connection, string $message): void
{
    $cleanupDeadline = hrtime(true) + 2_000_000_000;
    try {
        queueCopyEnd($connection, $cleanupDeadline, $message);
    } catch (Throwable) {
        try { pg_close($connection); } catch (Throwable) {}
    }
}

function queueCopyData(PgSql\Connection $connection, string $data, int $deadline): void
{
    try {
        do {
            $status = pg_put_copy_data($connection, $data);
            if ($status === 0) { waitCopySocket($connection, 0, 1, $deadline); }
        } while ($status === 0);
        if ($status !== 1) { throw new RuntimeException('Порция COPY отклонена'); }
    } catch (Throwable $error) {
        abortCopy($connection, 'Клиент не смог отправить порцию');
        throw $error;
    }
}

pg_query($db, 'CREATE TEMP TABLE copy_end_abort (id integer)');
pg_query($db, 'COPY copy_end_abort FROM STDIN');
$deadline = hrtime(true) + 5_000_000_000;
queueCopyData($db, "1\n", $deadline);
$final = queueCopyEnd($db, $deadline, 'Проверка файла не пройдена');
echo "Отмена отправлена\n";
echo $final instanceof PgSql\Result && pg_result_status($final) === PGSQL_FATAL_ERROR ? "Импорт отклонён\n" : "Неожиданный статус\n";
echo pg_fetch_result(pg_query($db, 'SELECT count(*) FROM copy_end_abort'), 0, 0), "\n";
Ожидаемый результат
Отмена отправлена
Импорт отклонён
0

Непустой error просит сервер провалить COPY. Финальный PgSql\Result имеет PGSQL_FATAL_ERROR, уже переданные строки не фиксируются; это удобно, если поздняя проверка файла обнаружила нарушение. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_put_copy_data(), pg_end_copy(), pg_transaction_status().

pg_get_notifyЗабрать следующее PostgreSQL-уведомление для каналов, на которые выполнен LISTEN.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_get_notify(PgSql\Connection $connection, int $mode = PGSQL_ASSOC): array|false

Забрать следующее PostgreSQL-уведомление для каналов, на которые выполнен LISTEN.

Запись pg_get_notify(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
int $mode (по умолчанию PGSQL_ASSOC)PGSQL_ASSOC для читаемых ключей, PGSQL_NUM для числовых, PGSQL_BOTH для обоих наборов.

Возврат и договор. Возвращает массив с message, pid и обычно payload либо false, если очередь пуста. Перед проверкой текущая реализация ext/pgsql сама просит libpq потребить доступный input; ожидание readable всё равно остаётся задачей event loop. mode задаёт ассоциативные, числовые или оба индекса. NOTIFY может быть потерян при разрыве и не заменяет надёжную очередь или таблицу задач.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. сессия выполнила LISTEN channel
  2. другая транзакция выполняет NOTIFY/pg_notify
  3. libpq принимает событие
  4. pg_get_notify снимает один сигнал из очереди
Получить канал и payload уведомленияПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'LISTEN manual_events');
pg_query($db, "NOTIFY manual_events, 'order-42'");
$notify = pg_get_notify($db, PGSQL_ASSOC);
if ($notify === false) { throw new RuntimeException('Уведомление не получено'); }
echo $notify['message'], '|', $notify['payload'], "\n";
Ожидаемый результат
manual_events|order-42

LISTEN действует до конца сессии, а payload — короткий сигнал, не очередь задач. Обработчик по order-42 обычно перечитывает актуальное состояние из таблицы. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отличить пустую очередь уведомленийПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'LISTEN empty_manual_channel');
var_dump(pg_get_notify($db, PGSQL_ASSOC));
Ожидаемый результат
bool(false)

false означает, что сейчас готового NOTIFY нет. В event loop сначала ждут readable через pg_socket; затем можно сразу вызвать pg_get_notify, потому что текущая php-src сама выполняет PQconsumeInput. Явный pg_consume_input нужен, когда цикл отдельно обслуживает общий входной буфер, но не является обязательной парой перед каждым get_notify. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_consume_input(), pg_socket(), pg_get_pid().

pg_traceВключить низкоуровневую трассировку обмена libpq с PostgreSQL в файл.

PHP 4.0.1+; trace_mode с PHP 8.3 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_trace(string $filename, string $mode = 'w', ?PgSql\Connection $connection = null, int $trace_mode = 0): bool

Включить низкоуровневую трассировку обмена libpq с PostgreSQL в файл.

Запись pg_trace(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
string $filenameПолный локальный путь, доступный PHP-процессу и находящийся вне публичного каталога.
string $mode (по умолчанию 'w')Режим fopen, обычно w для нового файла или a для осознанного добавления.
?PgSql\Connection $connection (по умолчанию null)Передавайте явное соединение; null/default deprecated как неявный контекст.
int $trace_mode (по умолчанию 0)С PHP 8.3 комбинация PGSQL_TRACE_SUPPRESS_TIMESTAMPS и PGSQL_TRACE_REGRESS_MODE для воспроизводимого формата.

Возврат и договор. Возвращает true, если trace включён, и false при отказе открыть/настроить файл. Формат предназначен для диагностики протокола; он способен раскрыть запросы и данные и требует закрытого хранения.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PHP открывает локальный файл в заданном mode
  2. libpq пишет сообщения frontend/backend
  3. проблемный запрос воспроизводится кратко
  4. pg_untrace прекращает запись, файл защищается/удаляется
На время записать клиентский протокол в закрытый файлПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$path = tempnam(sys_get_temp_dir(), 'php-pgsql-trace-');
if ($path === false || !chmod($path, 0600)) { throw new RuntimeException('Закрытый temp-файл не создан'); }
try {
    $started = pg_trace($path, 'w', $db, PGSQL_TRACE_SUPPRESS_TIMESTAMPS);
    if (!$started) { throw new RuntimeException('Trace не включён'); }
    if (pg_query($db, 'SELECT 42') === false) { throw new RuntimeException('Учебный запрос не выполнен'); }
    pg_untrace($db);
    clearstatcache(true, $path);
    echo is_file($path) && filesize($path) > 0 ? "Trace записан\n" : "Trace пуст\n";
} finally {
    pg_untrace($db);
    if (is_file($path) && !unlink($path)) { throw new RuntimeException('Trace-файл не удалён'); }
}
Ожидаемый результат
Trace записан

Trace может содержать SQL и значения протокола, поэтому tempnam создаёт уникальный файл вне web-root, chmod ограничивает доступ, а finally удаляет путь. Это диагностика libpq, а не обычный application log. Внутри ext/pgsql поток trace зарегистрирован для автоматического закрытия при завершении запроса: на Windows отдельный временный подкаталог нельзя надёжно удалить до этого момента, поэтому пример не создаёт такой каталог. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать невозможность открыть trace-файлПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$started = @pg_trace(__DIR__, 'w', $db);
var_dump($started);
Ожидаемый результат
bool(false)

Путь к каталогу нельзя открыть как файл. Проверяйте false и права заранее; не продолжайте с предположением, что диагностические данные записываются. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_untrace(), pg_last_error(), pg_result_error().

pg_untraceОстановить трассировку, ранее включённую pg_trace для соединения.

PHP 4.0.1+; возвращает literal true с PHP 8.2 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_untrace(?PgSql\Connection $connection = null): true

Остановить трассировку, ранее включённую pg_trace для соединения.

Запись pg_untrace(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
?PgSql\Connection $connection (по умолчанию null)Явное соединение, для которого включался trace. null использует deprecated default connection.

Возврат и договор. С PHP 8.2 всегда возвращает true. Повторный вызов допустим; наличие и полноту trace-файла проверяют отдельно.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. pg_trace уже настроил запись
  2. pg_untrace отключает callback libpq
  3. файл закрывается/становится доступен анализу
  4. секретный диагностический файл удаляется по политике
Явно остановить начатую трассировкуПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$path = tempnam(sys_get_temp_dir(), 'php-pgsql-untrace-');
if ($path === false || !chmod($path, 0600)) { throw new RuntimeException('Закрытый temp-файл не создан'); }
try {
    if (!pg_trace($path, 'w', $db, PGSQL_TRACE_SUPPRESS_TIMESTAMPS)) { throw new RuntimeException('Trace не включён'); }
    if (pg_query($db, 'SELECT 1') === false) { throw new RuntimeException('Учебный запрос не выполнен'); }
    var_dump(pg_untrace($db));
} finally {
    pg_untrace($db);
    if (is_file($path) && !unlink($path)) { throw new RuntimeException('Trace-файл не удалён'); }
}
Ожидаемый результат
bool(true)

С PHP 8.2 функция имеет return type true. finally нужен, чтобы служебный файл удалился и при исключении. Уникальный системный temp-файл безопаснее отдельного каталога: ext/pgsql закрывает связанный PHP-поток только при завершении запроса, что на Windows мешает немедленно удалить родительский каталог. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Безопасно вызвать остановку повторноПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
var_dump(pg_untrace($db));
var_dump(pg_untrace($db));
Ожидаемый результат
bool(true)
bool(true)

Функция всегда возвращает true, поэтому это подтверждение вызова, а не доказательство, что trace прежде был активен или файл содержал данные. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_trace().

pg_escape_byteaПреобразовать PHP binary string в escaped-представление PostgreSQL bytea.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_escape_bytea(PgSql\Connection $connection, string $string): string

Преобразовать PHP binary string в escaped-представление PostgreSQL bytea.

Запись pg_escape_bytea(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвное соединение задаёт правила escaping; legacy overload без него deprecated.
string $stringPHP binary string: может содержать NUL и не-UTF-8 байты.

Возврат и договор. Возвращает escaped string, но не добавляет весь SQL-контекст и не выполняет запрос. Не выводите бинарные данные напрямую в HTML; при ручной сборке используйте явный ::bytea, а в новом коде рассмотрите параметр с hex + decode.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. PHP-строка содержит произвольные байты
  2. pg_escape_bytea кодирует backslash/NUL/непечатные байты
  3. результат помещается только в bytea literal
  4. при чтении pg_unescape_bytea возвращает исходные байты
Сохранить и вернуть байты с NUL и 0xFFПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$bytes = "A\x00B\xFFC";
$escaped = pg_escape_bytea($db, $bytes);
$result = pg_query($db, "SELECT '{$escaped}'::bytea AS payload");
$raw = pg_fetch_result($result, 0, 'payload');
$restored = pg_unescape_bytea($raw);
echo bin2hex($restored), "\n";
Ожидаемый результат
410042ff43

bin2hex делает бинарный результат видимым и безопасным для терминала. pg_escape_bytea подготавливает содержимое bytea для ручного SQL literal; для нового кода удобнее передавать hex через параметр и decode($1, 'hex'). Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сохранить небольшой бинарный фрагмент в bytea-колонкеПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
pg_query($db, 'CREATE TEMP TABLE bytea_assets (id integer, payload bytea)');
$bytes = "PNG\x00demo";
$escaped = pg_escape_bytea($db, $bytes);
pg_query($db, "INSERT INTO bytea_assets VALUES (1, '{$escaped}'::bytea)");
$stored = pg_fetch_result(pg_query($db, 'SELECT octet_length(payload) FROM bytea_assets'), 0, 0);
echo $stored, " bytes\n";
Ожидаемый результат
8 bytes

bytea подходит небольшим бинарным данным, которые должны быть транзакционно связаны со строкой. Большие файлы часто выгоднее хранить в object storage, а в БД — путь, размер, MIME и hash. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_unescape_bytea(), pg_query_params(), pg_lo_import().

pg_unescape_byteaПреобразовать полученное от PostgreSQL bytea-представление в исходную PHP binary string.

PHP 4.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_unescape_bytea(string $string): string

Преобразовать полученное от PostgreSQL bytea-представление в исходную PHP binary string.

Запись pg_unescape_bytea(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
string $stringТекстовое PostgreSQL bytea-представление, полученное из результата запроса; не произвольная base64-строка.

Возврат и договор. Возвращает строку, которая может содержать NUL и любые байты. Это содержимое, а не готовый безопасный HTTP-ответ: MIME, размер, права доступа и Content-Disposition проверяет приложение. Контрольные суммы и их границы подробно разобраны в справочнике Hash.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. pg_fetch_* получает wire/text bytea
  2. pg_unescape_bytea декодирует escape/hex представление
  3. PHP получает точные байты
  4. приложение проверяет hash/MIME/право и использует данные
Преобразовать текстовое bytea-представление обратно в байтыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$result = pg_query($db, "SELECT decode('000aff', 'hex') AS payload");
$wire = pg_fetch_result($result, 0, 'payload');
$bytes = pg_unescape_bytea($wire);
echo bin2hex($bytes), "\n";
Ожидаемый результат
000aff

pg_fetch_result возвращает текстовое представление bytea протокола; pg_unescape_bytea превращает его в PHP-строку из трёх байтов. Выводится hex, иначе NUL и 0xFF были бы невидимы/небезопасны. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить целостность полученных бинарных данныхПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
$original = "file\x00contents";
$escaped = pg_escape_bytea($db, $original);
$wire = pg_fetch_result(pg_query($db, "SELECT '{$escaped}'::bytea"), 0, 0);
$restored = pg_unescape_bytea($wire);
echo hash_equals(hash('sha256', $original), hash('sha256', $restored)) ? "Hash совпал\n" : "Данные повреждены\n";
Ожидаемый результат
Hash совпал

Для файла сравнивают сохранённый hash и размер, а затем уже отдают байты с проверенным Content-Type. Сама функция не распознаёт формат и не проверяет содержимое на безопасность. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_escape_bytea(), pg_lo_read().

pg_set_error_verbosityВременно изменить подробность форматированных сообщений pg_last_error() и pg_result_error() для одного PostgreSQL-соединения.

PHP 5.1+; объект PgSql\Connection с PHP 8.1 · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_set_error_verbosity(PgSql\Connection $connection, int $verbosity): int|false

Временно изменить подробность форматированных сообщений pg_last_error() и pg_result_error() для одного PostgreSQL-соединения.

Запись pg_set_error_verbosity(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный PgSql\Connection. Исторический вызов через default connection deprecated с PHP 8.1; передавайте $db.
int $verbosityПередавайте одну документированную константу: PGSQL_ERRORS_TERSE, PGSQL_ERRORS_DEFAULT или PGSQL_ERRORS_VERBOSE. PGSQL_ERRORS_SQLSTATE зависит от того, обнаружила ли сборка ext/pgsql соответствующую возможность libpq; не комбинируйте режимы как флаги.

Возврат и договор. Возвращает предыдущий уровень или false при отказе. Официальные режимы: PGSQL_ERRORS_TERSE (severity, основной текст и позиция), PGSQL_ERRORS_DEFAULT (дополнительно detail, hint и context) и PGSQL_ERRORS_VERBOSE (все доступные поля). PGSQL_ERRORS_SQLSTATE формирует severity и SQLSTATE, когда сборка ext/pgsql обнаружила соответствующую возможность libpq; иначе PHP определяет её как совместимый alias TERSE. Настройка хранится в PGconn до следующего переключения или закрытия/переподключения и действует только на будущие сообщения этого соединения: уже созданные PgSql\Result не меняются, SQL и server log не перенастраиваются.

Отказ. Неверный тип вызывает TypeError. Официальный договор разрешает только перечисленные константы; не используйте произвольные integers и их битовые комбинации. Текущая проверка php-src устроена как bitmask, поэтому некоторые недокументированные комбинации способны пройти, а на проверенной Windows DLL PHP 8.5.11/libpq 16.15 нулевые PGSQL_ERRORS_TERSE/fallback PGSQL_ERRORS_SQLSTATE, наоборот, возвращают false. Это расхождение реализации, не публичный API. VERBOSE способен раскрыть пути и имена внутренних функций, поэтому подробную строку сохраняют в закрытом журнале.

Что происходит
  1. Приложение сохраняет текущий режим, полученный от функции
  2. libpq меняет формат будущих сообщений только в этом PgSql\Connection
  3. Новый failed PGresult получает формат выбранной подробности
  4. finally возвращает прежний режим перед повторным использованием соединения
Получить прежний уровень и гарантированно вернуть егоПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function verbosityName(int|false $mode): string
{
    return match ($mode) {
        PGSQL_ERRORS_DEFAULT => 'DEFAULT',
        PGSQL_ERRORS_VERBOSE => 'VERBOSE',
        false => 'FAILURE',
        default => "mode={$mode}",
    };
}

$original = pg_set_error_verbosity($db, PGSQL_ERRORS_DEFAULT);
if ($original === false) {
    throw new RuntimeException('Не удалось зафиксировать исходный режим');
}

try {
    $previous = pg_set_error_verbosity($db, PGSQL_ERRORS_VERBOSE);
    echo 'До переключения: ', verbosityName($previous), "
";
} finally {
    $beforeRestore = pg_set_error_verbosity($db, $original);
    echo 'Перед восстановлением: ', verbosityName($beforeRestore), "
";
    echo 'Восстановлен: ', verbosityName($original), "
";
}
Ожидаемый результат
До переключения: DEFAULT
Перед восстановлением: VERBOSE
Восстановлен: DEFAULT

Функция возвращает не новый, а прежний режим соединения. Поэтому первое возвращённое значение можно сохранить, временно усилить журналирование и восстановить настройку в finally даже при исключении между этими действиями. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Сравнить DEFAULT и VERBOSE на двух новых PGresultПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function failedResult(PgSql\Connection $db, string $sql): PgSql\Result
{
    if (pg_send_query($db, $sql) === false) {
        throw new RuntimeException('Не удалось отправить запрос');
    }
    $result = pg_get_result($db);
    if (!$result instanceof PgSql\Result) {
        throw new RuntimeException('Сервер не вернул PGresult');
    }
    return $result;
}

$original = pg_set_error_verbosity($db, PGSQL_ERRORS_DEFAULT);
if ($original === false) {
    throw new RuntimeException('Не удалось зафиксировать исходный режим');
}

try {
    $defaultResult = failedResult(
        $db,
        'SELECT * FROM manual_missing_error_verbosity'
    );
    $defaultText = pg_result_error($defaultResult);

    pg_set_error_verbosity($db, PGSQL_ERRORS_VERBOSE);
    $verboseResult = failedResult(
        $db,
        'SELECT * FROM manual_missing_error_verbosity'
    );
    $verboseText = pg_result_error($verboseResult);

    echo 'DEFAULT содержит SQLSTATE в тексте: ',
        str_contains($defaultText, '42P01') ? 'да' : 'нет', "
";
    echo 'VERBOSE содержит SQLSTATE в тексте: ',
        str_contains($verboseText, '42P01') ? 'да' : 'нет', "
";
    echo 'VERBOSE содержит LOCATION: ',
        str_contains($verboseText, 'LOCATION:') ? 'да' : 'нет', "
";
    echo 'SQLSTATE из поля PGresult: ',
        pg_result_error_field($verboseResult, PGSQL_DIAG_SQLSTATE), "
";
    echo 'Старый PGresult изменился: ',
        pg_result_error($defaultResult) === $defaultText ? 'нет' : 'да', "
";
} finally {
    pg_set_error_verbosity($db, $original);
}
Ожидаемый результат
DEFAULT содержит SQLSTATE в тексте: нет
VERBOSE содержит SQLSTATE в тексте: да
VERBOSE содержит LOCATION: да
SQLSTATE из поля PGresult: 42P01
Старый PGresult изменился: нет

Один и тот же ошибочный SQL создаёт два разных объекта результата. VERBOSE добавляет SQLSTATE и место в исходниках PostgreSQL к форматированной строке, но структурированный SQLSTATE лучше читать отдельным diagnostic field. Уже созданный PGresult после переключения не переписывается. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_result_error(), pg_result_error_field(), pg_last_error().

pg_set_error_context_visibilityУправлять показом серверного поля CONTEXT в будущих форматированных ошибках, предупреждениях и notices одного соединения.

PHP 8.3+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_set_error_context_visibility(PgSql\Connection $connection, int $visibility): int

Управлять показом серверного поля CONTEXT в будущих форматированных ошибках, предупреждениях и notices одного соединения.

Запись pg_set_error_context_visibility(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный PgSql\Connection. Настройка хранится в client connection и не переносится автоматически на другие соединения.
int $visibilityПередавайте ровно одну из PGSQL_SHOW_CONTEXT_NEVER, PGSQL_SHOW_CONTEXT_ERRORS или PGSQL_SHOW_CONTEXT_ALWAYS; это режимы, а не флаги для OR.

Возврат и договор. Возвращает прежний режим. PGSQL_SHOW_CONTEXT_NEVER скрывает context в форматированной строке; PGSQL_SHOW_CONTEXT_ERRORS (режим по умолчанию) показывает его для ошибок; PGSQL_SHOW_CONTEXT_ALWAYS также включает его в warning и notice. Это presentation-настройка текущего PGconn до следующего переключения или закрытия/переподключения: она влияет на весь код, разделяющий соединение, но не меняет SQL и server log. Структурированное поле PGSQL_DIAG_CONTEXT конкретного PGresult остаётся доступным, если сервер его прислал.

Отказ. Неверный тип вызывает TypeError. Документированный вход — только три PGSQL_SHOW_CONTEXT_*; текущая php-src bitmask-проверка отвергает часть иных integers через ValueError, но может пропустить некоторые комбинации, поэтому такое поведение нельзя использовать как расширение API. Изменение касается только будущих сообщений соединения. Полный context может раскрыть внутренний SQL и структуру проекта, поэтому его пишут в закрытый журнал.

Что происходит
  1. Вложенная PL/pgSQL-операция создаёт diagnostic field CONTEXT
  2. libpq форматирует новое сообщение по visibility текущего соединения
  3. pg_result_error показывает либо скрывает секцию CONTEXT
  4. pg_result_error_field читает исходное поле, а finally восстанавливает настройку
Сохранить прежнюю видимость CONTEXT и восстановить еёПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function contextModeName(int $mode): string
{
    return match ($mode) {
        PGSQL_SHOW_CONTEXT_NEVER => 'NEVER',
        PGSQL_SHOW_CONTEXT_ERRORS => 'ERRORS',
        PGSQL_SHOW_CONTEXT_ALWAYS => 'ALWAYS',
        default => "mode={$mode}",
    };
}

$original = pg_set_error_context_visibility(
    $db,
    PGSQL_SHOW_CONTEXT_NEVER
);

try {
    $previous = pg_set_error_context_visibility(
        $db,
        PGSQL_SHOW_CONTEXT_ALWAYS
    );
    echo 'До переключения: ', contextModeName($previous), "
";
} finally {
    $beforeRestore = pg_set_error_context_visibility($db, $original);
    echo 'Перед восстановлением: ', contextModeName($beforeRestore), "
";
    echo 'Восстановлен: ', contextModeName($original), "
";
}
Ожидаемый результат
До переключения: NEVER
Перед восстановлением: ALWAYS
Восстановлен: ERRORS

Новое соединение по умолчанию использует ERRORS. Первый вызов временно ставит NEVER и возвращает прежний ERRORS; второй возвращает NEVER. finally получает состояние перед восстановлением и возвращает соединению исходный ERRORS. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увидеть путь ошибки PL/pgSQL при ALWAYS и скрыть его при NEVERПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';
function contextFailure(PgSql\Connection $db): PgSql\Result
{
    $sql = <<<'SQL'
DO $$
BEGIN
    PERFORM 1 / 0;
END
$$
SQL;
    if (pg_send_query($db, $sql) === false) {
        throw new RuntimeException('Не удалось отправить DO');
    }
    $result = pg_get_result($db);
    if (!$result instanceof PgSql\Result) {
        throw new RuntimeException('Сервер не вернул PGresult');
    }
    return $result;
}

$original = pg_set_error_context_visibility(
    $db,
    PGSQL_SHOW_CONTEXT_NEVER
);

try {
    $withoutContext = contextFailure($db);
    $withoutText = pg_result_error($withoutContext);

    pg_set_error_context_visibility($db, PGSQL_SHOW_CONTEXT_ALWAYS);
    $withContext = contextFailure($db);
    $withText = pg_result_error($withContext);

    echo 'NEVER, строка CONTEXT: ',
        str_contains($withoutText, 'CONTEXT:') ? 'показана' : 'скрыта', "
";
    echo 'ALWAYS, строка CONTEXT: ',
        str_contains($withText, 'CONTEXT:') ? 'показана' : 'скрыта', "
";
    echo 'Сырое PGSQL_DIAG_CONTEXT при NEVER: ',
        pg_result_error_field($withoutContext, PGSQL_DIAG_CONTEXT) !== null
            ? 'доступно'
            : 'отсутствует',
        "
";
    echo 'Старый PGresult изменился: ',
        pg_result_error($withoutContext) === $withoutText ? 'нет' : 'да', "
";
} finally {
    pg_set_error_context_visibility($db, $original);
}
Ожидаемый результат
NEVER, строка CONTEXT: скрыта
ALWAYS, строка CONTEXT: показана
Сырое PGSQL_DIAG_CONTEXT при NEVER: доступно
Старый PGresult изменился: нет

Деление на ноль внутри DO создаёт настоящий серверный context: какой SQL выполнялся и на какой строке PL/pgSQL. Visibility меняет форматированную строку pg_result_error, но не стирает структурированное поле PGSQL_DIAG_CONTEXT из PGresult. Для машинной диагностики читайте fields, для закрытого журнала выбирайте разумную полноту. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_result_error(), pg_result_error_field(), pg_last_error(), pg_last_notice().

pg_lo_createСоздать пустой PostgreSQL large object и получить его OID для последующей записи.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_create(PgSql\Connection $connection, int|string|null $object_id = null): string|int|false

Создать пустой PostgreSQL large object и получить его OID для последующей записи.

Запись pg_lo_create(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
int|string|null $object_id (по умолчанию null)Необязательный желаемый OID как int|string. Обычно его не задают: сервер безопасно выбирает свободный идентификатор.

Возврат и договор. Возвращает OID как int|string либо false. OID — идентификатор в базе, а не содержимое и не путь. Передавайте явный $db; вариант без соединения устарел. Весь жизненный цикл large-object API должен идти внутри транзакции.

Отказ. false означает, что сервер не создал объект; неверный тип/формат OID вызывает TypeError или ValueError. После SQL-ошибки откатите транзакцию. Для небольших документов, читаемых целиком, колонка bytea проще; для крупных медиа с CDN, версиями и lifecycle часто удобнее объектное хранилище.

Что происходит
  1. Код начинает транзакцию
  2. PostgreSQL создаёт пустой large object
  3. PHP получает OID-ссылку
  4. Код записывает ссылку либо удаляет объект и завершает транзакцию
Создать объект и увидеть, что OID — только ссылкаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('BEGIN не выполнен'); }
        try {
            $oid = pg_lo_create($db);
            if ($oid === false) { throw new RuntimeException('Large object не создан'); }
            echo (is_int($oid) || is_string($oid)) ? "Получен OID\n" : "Неожиданный тип\n";

            $lob = pg_lo_open($db, $oid, 'w+');
            if ($lob === false) { throw new RuntimeException('Large object не открыт'); }
            pg_lo_write($lob, 'invoice.pdf bytes');
            pg_lo_close($lob);

            var_dump((string) $oid !== 'invoice.pdf bytes');
            if (!pg_lo_unlink($db, $oid)) { throw new RuntimeException('OID не удалён'); }
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
Получен OID
bool(true)

Сервер выдаёт числовой идентификатор. Он позволяет позже открыть отдельный объект, но сам не содержит имя, MIME-тип или байты документа; эти метаданные приложение хранит в обычной таблице. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отменить создание вместе с транзакциейПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        $transaction = false;
        $lob = null;
        try {
            if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('BEGIN не выполнен'); }
            $transaction = true;
            $oid = pg_lo_create($db);
            if ($oid === false) { throw new RuntimeException('Large object не создан'); }
            if (pg_query($db, 'ROLLBACK') === false) { throw new RuntimeException('ROLLBACK не выполнен'); }
            $transaction = false;

            if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('Проверочная транзакция не начата'); }
            $transaction = true;
            $lob = @pg_lo_open($db, $oid, 'r');
            var_dump($lob === false);
            if ($lob instanceof PgSql\Lob) {
                if (!pg_lo_close($lob)) { throw new RuntimeException('Дескриптор не закрыт'); }
                $lob = null;
            }
            if (pg_query($db, 'ROLLBACK') === false) { throw new RuntimeException('Проверочный ROLLBACK не выполнен'); }
            $transaction = false;
        } finally {
            if ($lob instanceof PgSql\Lob) { @pg_lo_close($lob); }
            if ($transaction) { @pg_query($db, 'ROLLBACK'); }
        }
Ожидаемый результат
bool(true)

Создание transactional: после ROLLBACK прежний OID нельзя открыть. Это полезно при загрузке документа вместе с записью заказа — неудачная операция не оставляет бесхозный объект. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_open(), pg_lo_unlink(), pg_lo_import().

pg_lo_openОткрыть large object по OID и получить PgSql\Lob — дескриптор с текущей позицией.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_open(PgSql\Connection $connection, int|string $oid, string $mode): PgSql\Lob|false

Открыть large object по OID и получить PgSql\Lob — дескриптор с текущей позицией.

Запись pg_lo_open(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
int|string $oidOID как int|string из pg_lo_create/pg_lo_import или доверенной строки таблицы; это не содержимое файла.
string $moder — чтение, w — запись, r+/w+ — чтение и запись. Это не полный аналог fopen; выбирайте минимально нужный доступ.

Возврат и договор. Возвращает PgSql\Lob либо false. Поддерживаемые режимы текущего ext/pgsql: r, w, r+ и w+. Никогда не используйте w/w+ как способ создать объект по отсутствующему OID: проверенный runtime возвращает false после ошибки транзакции, а даже при срабатывании fallback PgSql\Lob не сообщает выбранный сервером OID. Сначала pg_lo_create, сохранение OID, затем pg_lo_open. Не закрывайте $db раньше дескриптора.

Отказ. false и warning возможны для отсутствующего OID или отказа PostgreSQL; неверный mode вызывает ValueError. Ошибка открытия способна перевести транзакцию в failed state — выполните ROLLBACK. Документированный/php-src fallback автосоздания для w/w+ ненадёжен: на проверенном runtime он уже выполняется внутри failed transaction и не создаёт доступный объект; если на другой связке сработает, новый OID всё равно не возвращается вызывающему коду.

Что происходит
  1. Таблица приложения отдаёт OID
  2. pg_lo_open проверяет объект и режим
  3. Дескриптор хранит текущую позицию в байтах
  4. Чтение/запись заканчивается pg_lo_close
Открыть сохранённый объект и прочитать его байтыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $writer = pg_lo_open($db, $oid, 'w+');
            if ($writer === false) { throw new RuntimeException('Нет дескриптора'); }
            pg_lo_write($writer, 'manual.pdf');
            pg_lo_close($writer);

            $reader = pg_lo_open($db, $oid, 'r');
            if ($reader === false) { throw new RuntimeException('Нет чтения'); }
            echo pg_lo_read($reader, 64), "\n";
            pg_lo_close($reader);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
manual.pdf

Режим r открывает существующий OID только для чтения. Соединение остаётся открытым до pg_lo_close, затем объект явно удаляется из учебной базы. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать отсутствующий OIDПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        $missingOid = '4294967294';
        $lob = @pg_lo_open($db, $missingOid, 'r');
        var_dump($lob === false);
        pg_query($db, 'ROLLBACK');
Ожидаемый результат
bool(true)

OID мог остаться в строке после неправильного удаления файла. pg_lo_open возвращает false и серверная транзакция после ошибки требует ROLLBACK; посетителю дают понятное «файл недоступен», а техническую причину журналируют. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не перепутать режим rw с r+Пример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            try {
                pg_lo_open($db, $oid, 'rw');
            } catch (ValueError $error) {
                echo "Для чтения и записи нужен r+\n";
            }
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
Для чтения и записи нужен r+

Страница PHP Manual всё ещё называет режим rw, но php-src 8.5.11 и живой runtime принимают r, w, r+ и w+. Карточка следует проверенному поведению текущей версии. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Не полагаться на автосоздание отсутствующего OID в режиме wПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        $requestedOid = pg_lo_create($db);
        if ($requestedOid === false || !pg_lo_unlink($db, $requestedOid)) {
            pg_query($db, 'ROLLBACK');
            throw new RuntimeException('Не удалось подготовить отсутствующий OID');
        }

        $lob = @pg_lo_open($db, $requestedOid, 'w');
        var_dump($lob === false);
        echo pg_transaction_status($db) === PGSQL_TRANSACTION_INERROR
            ? "Транзакция требует ROLLBACK\n"
            : "Неожиданное состояние\n";
        if ($lob instanceof PgSql\Lob) { pg_lo_close($lob); }
        pg_query($db, 'ROLLBACK');
Ожидаемый результат
bool(true)
Транзакция требует ROLLBACK

Страница PHP Manual и fallback в php-src предполагают попытку создать large object для w/w+, если первый lo_open не нашёл OID. На проверенной связке PostgreSQL 18.6, PHP 8.5 и ext/pgsql первый отказ уже переводит транзакцию в error state, поэтому последующий lo_creat возвращает 0, pg_lo_open предупреждает и возвращает false. @ скрывает только ожидаемое предупреждение учебного опыта; состояние проверяется явно и затем выполняется ROLLBACK. Даже на стеке, где fallback сработает, PgSql\Lob не сообщает выбранный сервером новый OID. Надёжный путь один: pg_lo_create, сохранить его OID и открыть именно этот объект. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_create(), pg_lo_close(), pg_lo_read().

pg_lo_writeЗаписать байты в открытую large-object позицию.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_write(PgSql\Lob $lob, string $data, ?int $length = null): int|false

Записать байты в открытую large-object позицию.

Запись pg_lo_write(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Lob $lobОткрытый PgSql\Lob с правом записи; соединение и транзакция ещё активны.
string $dataСтрока PHP как последовательность байт. В проверенной PHP 8.5.11 сборке NUL внутри строки отвергается ValueError, поэтому для произвольного бинарного файла используйте pg_lo_import.
?int $length (по умолчанию null)Максимум байт для записи. null означает strlen($data); значение больше длины или отрицательное вызывает ValueError.

Возврат и договор. Возвращает число фактически записанных байт либо false. Необязательный length ограничивает число байт от начала $data и должен лежать между 0 и strlen($data). Запись начинается в текущей позиции дескриптора.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. pg_lo_open создаёт дескриптор записи
  2. data остаётся последовательностью байт
  3. length при необходимости ограничивает её
  4. Курсор сдвигается на реально записанное число байт
Записать строку и увидеть число байтПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            $written = pg_lo_write($lob, 'ABCDEF');
            echo "Записано: {$written} байт\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
Записано: 6 байт

Возврат — фактическое число записанных байт, поэтому его проверяют, а не предполагают успешную полную запись. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Ограничить запись по байтам, а не по символамПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            $written = pg_lo_write($lob, 'ёж', 2);
            pg_lo_seek($lob, 0, PGSQL_SEEK_SET);
            $saved = pg_lo_read($lob, 10);
            echo $saved, '|bytes=', $written, "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
ё|bytes=2

В UTF-8 буква «ё» занимает два байта. Не передавайте mb_strlen как length: граница, попавшая внутрь многобайтного символа, сохранит повреждённую последовательность. Для бинарных данных считают именно байты. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Учесть NUL-ограничение текущего ext/pgsqlПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            try {
                pg_lo_write($lob, "A\0B");
            } catch (ValueError $error) {
                echo "NUL отклонён до записи\n";
            }
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
NUL отклонён до записи

Живой PHP 8.5.11/ext/pgsql выбрасывает ValueError для NUL внутри $data: реализация разбирает аргумент как path string. Для произвольного бинарного файла с NUL используйте pg_lo_import либо передавайте безопасные блоки без NUL после осознанного выбора формата. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Проверить нулевую длину без изменения объектаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            $written = pg_lo_write($lob, 'ABC', 0);
            echo 'written=', $written, '|position=', pg_lo_tell($lob), "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
written=0|position=0

Исходник и живой PHP 8.5.11 разрешают length=0: ничего не записано и курсор не сдвинулся. Это точнее официальной фразы «greater than zero» на текущей странице. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_open(), pg_lo_tell(), pg_lo_seek(), pg_lo_read().

pg_lo_readПрочитать до заданного числа байт из текущей позиции PgSql\Lob.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_read(PgSql\Lob $lob, int $length = 8192): string|false

Прочитать до заданного числа байт из текущей позиции PgSql\Lob.

Запись pg_lo_read(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Lob $lobОткрытый PgSql\Lob с правом чтения внутри активной транзакции.
int $length (по умолчанию 8192)Максимум байт. 0 даёт пустую строку; отрицательное значение вызывает ValueError.

Возврат и договор. Возвращает строку (включая пустую строку в конце объекта) либо false при ошибке. length — число байт; стандартное значение 8192. Для крупного объекта повторяйте чтение блоками, не загружая всё в память.

Отказ. false означает ошибку чтения и проверяется раньше пустой строки EOF. length < 0 вызывает ValueError. При ошибке закрывайте/откатывайте транзакцию; не передавайте false в echo, strlen или HTTP-ответ как будто это пустой файл.

Что происходит
  1. Дескриптор знает текущий byte offset
  2. pg_lo_read запрашивает не больше length байт
  3. Строка PHP может содержать любые байты
  4. Курсор сдвигается; цикл останавливается на пустой строке
Читать большой объект небольшими блокамиПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'ABCDEFGHIJ');
            pg_lo_seek($lob, 0, PGSQL_SEEK_SET);
            while (true) {
                $chunk = pg_lo_read($lob, 4);
                if ($chunk === false) { throw new RuntimeException('Ошибка чтения large object'); }
                if ($chunk === '') { break; }
                echo '[', $chunk, ']';
            }
            echo "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
[ABCD][EFGH][IJ]

Цикл получает 4, 4 и 2 байта; пустая строка означает конец. Сначала отдельно проверяйте false, если отказ нужно отличать от EOF. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Безопасно проверить бинарные байтыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        $source = tempnam(__DIR__, 'pglo-read-');
        if ($source === false) { throw new RuntimeException('Нет временного файла'); }
        file_put_contents($source, "A\0B\xFF");
        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_import($db, $source);
            if ($oid === false) { throw new RuntimeException('Импорт не выполнен'); }
            $lob = pg_lo_open($db, $oid, 'r');
            $data = pg_lo_read($lob, 4);
            if ($data === false) { throw new RuntimeException('Ошибка чтения large object'); }
            echo bin2hex($data), '|bytes=', strlen($data), "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        } finally {
            if (is_file($source)) { unlink($source); }
        }
Ожидаемый результат
410042ff|bytes=4

Строка PHP сохраняет NUL и 0xFF; echo бинарного содержимого в диагностический терминал неудобен, поэтому пример показывает hex. В HTTP сначала задают корректный Content-Type и контролируют доступ. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_open(), pg_lo_seek(), pg_lo_read_all().

pg_lo_read_allПередать все оставшиеся байты large object напрямую в output PHP.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_read_all(PgSql\Lob $lob): int

Передать все оставшиеся байты large object напрямую в output PHP.

Запись pg_lo_read_all(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Lob $lobОткрытый PgSql\Lob с правом чтения. Его текущая позиция определяет начало прямого вывода.

Возврат и договор. Возвращает число выведенных байт, не строку. Читает от текущей позиции до конца. Для HTTP до вызова проверьте авторизацию и задайте Content-Type/Content-Length/Content-Disposition; после начала бинарного вывода не добавляйте HTML или отладочный текст.

Отказ. По официальному контракту возвращается int; ошибка низкоуровневого чтения завершает цикл, поэтому HTTP-код должен не начинать ответ до проверки прав/существования. Для контролируемой обработки ошибок и range-ответов чаще удобнее цикл pg_lo_read.

Что происходит
  1. Код заранее выставляет курсор и HTTP-заголовки
  2. Функция читает блоки из PostgreSQL
  3. PHP немедленно пишет блоки в output
  4. Возврат сообщает общее число байт
Увидеть прямой вывод и отдельный счётчикПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'PDF-DATA');
            pg_lo_seek($lob, 0, PGSQL_SEEK_SET);
            ob_start();
            $count = pg_lo_read_all($lob);
            $output = ob_get_clean();
            echo $output, '|count=', $count, "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            if (ob_get_level() > 0) { ob_end_clean(); }
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
PDF-DATA|count=8

Функция не возвращает содержимое: она пишет его прямо в output и возвращает только число байт. Буфер используется здесь лишь для видимой проверки поведения. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Вывести остаток от текущей позицииПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'HEAD:BODY');
            pg_lo_seek($lob, 5, PGSQL_SEEK_SET);
            ob_start();
            $count = pg_lo_read_all($lob);
            $output = ob_get_clean();
            echo $output, '|bytes=', $count, "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            if (ob_get_level() > 0) { ob_end_clean(); }
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
BODY|bytes=4

Живой стенд подтверждает важную границу: текущая реализация выводит от текущего курсора до конца, а не автоматически перематывает объект. Для полного ответа сначала выполняют pg_lo_seek(..., 0, PGSQL_SEEK_SET). Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_read(), pg_lo_seek(), pg_lo_tell().

pg_lo_seekПереместить текущую позицию чтения/записи внутри large object.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_seek(PgSql\Lob $lob, int $offset, int $whence = SEEK_CUR): bool

Переместить текущую позицию чтения/записи внутри large object.

Запись pg_lo_seek(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Lob $lobОткрытый PgSql\Lob внутри той же транзакции.
int $offsetСмещение в байтах; знак интерпретируется вместе с whence.
int $whence (по умолчанию SEEK_CUR)PGSQL_SEEK_SET, PGSQL_SEEK_CUR или PGSQL_SEEK_END. Другие значения вызывают ValueError.

Возврат и договор. Возвращает true при успехе и false при отказе. offset измеряется в байтах. PGSQL_SEEK_SET считает от начала, PGSQL_SEEK_CUR — от текущей позиции, PGSQL_SEEK_END — от конца.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код выбирает точку отсчёта
  2. PostgreSQL вычисляет новый byte offset
  3. Дескриптор запоминает позицию
  4. Следующий read/write начинается там
Перейти к третьему байту и заменить два байтаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'ABCDE');
            pg_lo_seek($lob, 2, PGSQL_SEEK_SET);
            pg_lo_write($lob, 'xy');
            pg_lo_seek($lob, 0, PGSQL_SEEK_SET);
            echo pg_lo_read($lob, 5), "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
ABxyE

SEEK_SET считает байты от начала: позиции 2 и 3 перезаписаны, длина объекта не изменилась. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Прочитать последние два байта относительно концаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'abcdef');
            var_dump(pg_lo_seek($lob, -2, PGSQL_SEEK_END));
            echo pg_lo_read($lob, 2), "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
bool(true)
ef

Отрицательное смещение уместно вместе с SEEK_END. Позиции и размеры относятся к байтам; это основа для фрагментарной выдачи, но HTTP Range ещё требует проверки границ и заголовков. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_tell(), pg_lo_read(), pg_lo_write().

pg_lo_tellПолучить текущую позицию дескриптора от начала large object.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_tell(PgSql\Lob $lob): int

Получить текущую позицию дескриптора от начала large object.

Запись pg_lo_tell(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Lob $lobОткрытый PgSql\Lob. После pg_lo_close этот объект использовать нельзя.

Возврат и договор. Возвращает byte offset как int; отрицательное значение означает ошибку. Это позиция следующего чтения/записи, а не размер объекта и не число символов.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Дескриптор хранит текущий cursor
  2. pg_lo_tell читает offset
  3. PHP получает число байт от начала
  4. Код сохраняет progress или сверяет seek
Проверить позицию после записи и seekПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'ABCDE');
            echo 'after-write=', pg_lo_tell($lob), "\n";
            pg_lo_seek($lob, 2, PGSQL_SEEK_SET);
            echo 'after-seek=', pg_lo_tell($lob), "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
after-write=5
after-seek=2

После записи курсор стоит сразу за последним байтом. pg_lo_tell позволяет проверить возобновляемую передачу или вычислить следующий блок. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Отличить байтовую позицию от числа символовПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'ёж');
            echo 'bytes=', pg_lo_tell($lob), '|text=2 symbols', "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
bytes=4|text=2 symbols

Две русские буквы занимают четыре UTF-8 байта. Offset из large-object API нельзя напрямую считать позицией символа. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_seek(), pg_lo_write(), pg_lo_read().

pg_lo_truncateИзменить длину large object до точного числа байт.

PHP 5.6+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_truncate(PgSql\Lob $lob, int $size): bool

Изменить длину large object до точного числа байт.

Запись pg_lo_truncate(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Lob $lobPgSql\Lob, открытый с правом записи.
int $sizeНовая длина в байтах. Проверяйте неотрицательность и допустимый предел до вызова.

Возврат и договор. Возвращает true при успехе и false при отказе. Меньший size удаляет хвост, больший добавляет нулевые байты. Позиция дескриптора автоматически не становится равной новой длине — выставляйте её явно.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Код выбирает новую byte length
  2. PostgreSQL укорачивает либо расширяет объект
  3. Удалённый хвост исчезает в этой транзакции
  4. Код отдельно устанавливает cursor для следующей операции
Укоротить объект до трёх байтПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'abcdef');
            var_dump(pg_lo_truncate($lob, 3));
            pg_lo_seek($lob, 0, PGSQL_SEEK_SET);
            echo pg_lo_read($lob, 10), "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
bool(true)
abc

Байты после новой длины потеряны. Операция входит в транзакцию, но после COMMIT восстановить хвост можно только из отдельной версии/резервной копии. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Увеличить объект и увидеть нулевые байтыПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'abc');
            pg_lo_truncate($lob, 5);
            pg_lo_seek($lob, 0, PGSQL_SEEK_SET);
            echo bin2hex(pg_lo_read($lob, 5)), "\n";
            pg_lo_close($lob);
            pg_lo_unlink($db, $oid);
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
6162630000

При увеличении новые позиции читаются как нулевые байты. Это не текстовые символы «0»; бинарный формат должен уметь интерпретировать такую длину. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_tell(), pg_lo_seek(), pg_lo_write().

pg_lo_closeЗакрыть PgSql\Lob и освободить серверный descriptor.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_close(PgSql\Lob $lob): bool

Закрыть PgSql\Lob и освободить серверный descriptor.

Запись pg_lo_close(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Lob $lobОткрытый PgSql\Lob. Соединение нельзя закрывать раньше него.

Возврат и договор. Возвращает true при успехе и false при низкоуровневом отказе. Закрытие не удаляет large object и не завершает транзакцию. После вызова этот экземпляр PgSql\Lob больше не используется.

Отказ. Где сигнатура допускает false, проверяйте его до чтения результата. Ошибка SQL обычно дополнительно создаёт warning; TypeError/ValueError относятся к неверному PHP-вызову. Сырое сообщение БД записывают в закрытый журнал, посетителю дают безопасный ответ.

Что происходит
  1. Чтение/запись завершены
  2. pg_lo_close освобождает descriptor
  3. OID и его байты остаются в базе
  4. Код удаляет объект при необходимости и завершает транзакцию
Закрыть дескриптор до удаления OIDПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_write($lob, 'done');
            var_dump(pg_lo_close($lob));
            var_dump(pg_lo_unlink($db, $oid));
            pg_query($db, 'COMMIT');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
bool(true)
bool(true)

close освобождает descriptor, но не удаляет сам объект. Для окончательного удаления нужен отдельный pg_lo_unlink. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Поймать повторное использование закрытого дескриптораПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        pg_query($db, 'BEGIN');
        try {
            $oid = pg_lo_create($db);
            $lob = pg_lo_open($db, $oid, 'w+');
            pg_lo_close($lob);
            var_dump(@pg_lo_close($lob) === false);
            pg_query($db, 'ROLLBACK');
        } catch (Throwable $error) {
            pg_query($db, 'ROLLBACK');
            throw $error;
        }
Ожидаемый результат
bool(true)

На проверенном PHP 8.5.11 повторный close даёт warning и false; вызов низкоуровневого descriptor может испортить транзакцию, поэтому пример откатывает её. Держите владение дескриптором в одном коротком блоке и закрывайте его один раз. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_open(), pg_lo_unlink().

pg_lo_importСоздать large object из файла, доступного локальному PHP-процессу.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_import(PgSql\Connection $connection, string $filename, int|string|null $oid = null): string|int|false

Создать large object из файла, доступного локальному PHP-процессу.

Запись pg_lo_import(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
string $filenameПолный/разрешённый локальный путь на машине PHP. Никогда не принимайте путь посетителя без allowlist и проверки upload.
int|string|null $oid (по умолчанию null)Необязательный желаемый OID как int|string; безопасный обычный путь — позволить серверу выбрать свободный.

Возврат и договор. Возвращает новый OID как int|string либо false. Файл читается на машине PHP/libpq; веб-серверный пользователь должен иметь право чтения. Необязательный OID обычно не задают.

Отказ. false означает, что чтение/импорт не состоялись; open_basedir также может запретить путь. Транзакцию откатывают и не сохраняют ссылку. pg_lo_import не проверяет, что файл действительно является изображением/PDF и не выполняет антивирусную проверку.

Что происходит
  1. PHP проверяет разрешённый upload/temp file
  2. pg_lo_import читает локальные байты
  3. PostgreSQL создаёт large object
  4. Приложение сохраняет OID и метаданные в той же транзакции
Импортировать локальный файл и прочитать его из БДПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        $directory = null;
        $source = null;
        $transaction = false;
        $lob = null;
        try {
            $directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php-pglo-import-' . bin2hex(random_bytes(8));
            if (!mkdir($directory, 0700)) { throw new RuntimeException('Закрытый temp-каталог не создан'); }
            $source = $directory . DIRECTORY_SEPARATOR . 'input.bin';
            $payload = "report\n2026";
            if (file_put_contents($source, $payload) !== strlen($payload)) { throw new RuntimeException('Файл записан не полностью'); }

            if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('BEGIN не выполнен'); }
            $transaction = true;
            $oid = pg_lo_import($db, $source);
            if ($oid === false) { throw new RuntimeException('Импорт не выполнен'); }
            $lob = pg_lo_open($db, $oid, 'r');
            if ($lob === false) { throw new RuntimeException('Импортированный объект не открыт'); }
            $bytes = pg_lo_read($lob, 100);
            if ($bytes === false) { throw new RuntimeException('Объект не прочитан'); }
            echo str_replace("\n", '|', $bytes), "\n";
            if (!pg_lo_close($lob)) { throw new RuntimeException('Дескриптор не закрыт'); }
            $lob = null;
            if (!pg_lo_unlink($db, $oid)) { throw new RuntimeException('Large object не удалён'); }
            if (pg_query($db, 'COMMIT') === false) { throw new RuntimeException('COMMIT не выполнен'); }
            $transaction = false;
        } finally {
            if ($lob instanceof PgSql\Lob) { @pg_lo_close($lob); }
            if ($transaction) { @pg_query($db, 'ROLLBACK'); }
            if (is_string($source) && is_file($source) && !unlink($source)) { throw new RuntimeException('Temp-файл не удалён'); }
            if (is_string($directory) && is_dir($directory) && !rmdir($directory)) { throw new RuntimeException('Temp-каталог не удалён'); }
        }
Ожидаемый результат
report|2026

Путь читается процессом PHP на его машине. Файл создан во временном каталоге примера, large object удалён в транзакции, локальный файл удалён в finally. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Обработать отсутствующий локальный файлПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        $directory = null;
        $missing = null;
        $transaction = false;
        try {
            $directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php-pglo-missing-' . bin2hex(random_bytes(8));
            if (!mkdir($directory, 0700)) { throw new RuntimeException('Закрытый temp-каталог не создан'); }
            $missing = $directory . DIRECTORY_SEPARATOR . 'absent.bin';
            if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('BEGIN не выполнен'); }
            $transaction = true;
            $oid = @pg_lo_import($db, $missing);
            var_dump($oid === false);
        } finally {
            if ($transaction) { @pg_query($db, 'ROLLBACK'); }
            if (is_string($missing) && is_file($missing) && !unlink($missing)) { throw new RuntimeException('Неожиданный файл не удалён'); }
            if (is_string($directory) && is_dir($directory) && !rmdir($directory)) { throw new RuntimeException('Temp-каталог не удалён'); }
        }
Ожидаемый результат
bool(true)

Не существующий или недоступный PHP-процессу путь даёт false. Это путь клиента libpq — машины PHP, а не каталог на PostgreSQL-сервере. До импорта проверяют upload error, размер, MIME/сигнатуру и разрешённый временный путь. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_export(), pg_lo_open(), pg_lo_unlink().

pg_lo_exportСохранить байты large object в файл на локальной файловой системе PHP-процесса.

PHP 4.2+ · pgsql; сервер PostgreSQL; доступность отдельных функций зависит от libpq

pg_lo_export(PgSql\Connection $connection, int|string $oid, string $filename): bool

Сохранить байты large object в файл на локальной файловой системе PHP-процесса.

Запись pg_lo_export(...) вызывает функцию. Аргументы идут между круглыми скобками; возвращаемое значение можно сохранить справа от =. $db в примерах — явный PgSql\Connection, $result — результат конкретного SQL-запроса. Как работает вызов функции, как читать массив результата.

ПараметрЧто передать
PgSql\Connection $connectionЯвный объект PgSql\Connection от pg_connect/pg_pconnect; не полагайтесь на deprecated-соединение по умолчанию.
int|string $oidOID разрешённого пользователю large object как int|string.
string $filenameЛокальный путь на машине PHP. Каталог должен существовать и быть доступен для записи; имя строит сервер, а не посетитель.

Возврат и договор. Возвращает true при успехе и false при отказе. Путь относится к машине PHP/libpq, не к серверу PostgreSQL. Функция перезаписывает целевой файл согласно возможностям libpq/ОС; приложение управляет правами, именем и последующим удалением.

Отказ. false означает, что object не прочитан либо файл не создан. Возможны отсутствующий OID, права БД, open_basedir, права ОС, несуществующий каталог и нехватка места. Не считайте наличие файла доказательством полного экспорта без true/размера/хэша там, где целостность важна.

Что происходит
  1. Код проверяет доступ к OID
  2. PHP выбирает безопасный локальный target
  3. libpq читает object и пишет файл
  4. Код проверяет результат и удаляет временный файл по lifecycle
Экспортировать объект во временный локальный файлПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        $directory = null;
        $source = null;
        $target = null;
        $transaction = false;
        try {
            $directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php-pglo-export-' . bin2hex(random_bytes(8));
            if (!mkdir($directory, 0700)) { throw new RuntimeException('Закрытый temp-каталог не создан'); }
            $source = $directory . DIRECTORY_SEPARATOR . 'input.bin';
            $target = $directory . DIRECTORY_SEPARATOR . 'output.bin';
            $payload = "A\0B";
            if (file_put_contents($source, $payload) !== strlen($payload)) { throw new RuntimeException('Исходный файл записан не полностью'); }

            if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('BEGIN не выполнен'); }
            $transaction = true;
            $oid = pg_lo_import($db, $source);
            if ($oid === false) { throw new RuntimeException('Импорт не выполнен'); }
            if (!pg_lo_export($db, $oid, $target)) { throw new RuntimeException('Экспорт не выполнен'); }
            $bytes = file_get_contents($target);
            if ($bytes === false) { throw new RuntimeException('Экспортированный файл не прочитан'); }
            echo bin2hex($bytes), '|bytes=', strlen($bytes), "\n";
            if (!pg_lo_unlink($db, $oid)) { throw new RuntimeException('Large object не удалён'); }
            if (pg_query($db, 'COMMIT') === false) { throw new RuntimeException('COMMIT не выполнен'); }
            $transaction = false;
        } finally {
            if ($transaction) { @pg_query($db, 'ROLLBACK'); }
            if (is_string($source) && is_file($source) && !unlink($source)) { throw new RuntimeException('Исходный temp-файл не удалён'); }
            if (is_string($target) && is_file($target) && !unlink($target)) { throw new RuntimeException('Экспортированный temp-файл не удалён'); }
            if (is_string($directory) && is_dir($directory) && !rmdir($directory)) { throw new RuntimeException('Temp-каталог не удалён'); }
        }
Ожидаемый результат
410042|bytes=3

Экспорт сохраняет точные бинарные байты. Целевой файл удаляется в finally, объект — через pg_lo_unlink до COMMIT. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Получить false для недоступного каталогаПример для указанного окружения
<?php
declare(strict_types=1);

require __DIR__ . '/pgsql-bootstrap.php';

        $directory = null;
        $target = null;
        $transaction = false;
        $lob = null;
        try {
            $directory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'php-pglo-bad-export-' . bin2hex(random_bytes(8));
            if (!mkdir($directory, 0700)) { throw new RuntimeException('Закрытый temp-каталог не создан'); }
            $target = $directory . DIRECTORY_SEPARATOR . 'missing' . DIRECTORY_SEPARATOR . 'output.bin';
            if (pg_query($db, 'BEGIN') === false) { throw new RuntimeException('BEGIN не выполнен'); }
            $transaction = true;
            $oid = pg_lo_create($db);
            if ($oid === false) { throw new RuntimeException('Large object не создан'); }
            $lob = pg_lo_open($db, $oid, 'w+');
            if ($lob === false) { throw new RuntimeException('Large object не открыт'); }
            if (pg_lo_write($lob, 'data') !== 4) { throw new RuntimeException('Запись неполна'); }
            if (!pg_lo_close($lob)) { throw new RuntimeException('Дескриптор не закрыт'); }
            $lob = null;
            var_dump(@pg_lo_export($db, $oid, $target) === false);
            if (!pg_lo_unlink($db, $oid)) { throw new RuntimeException('Large object не удалён'); }
            if (pg_query($db, 'COMMIT') === false) { throw new RuntimeException('COMMIT не выполнен'); }
            $transaction = false;
        } finally {
            if ($lob instanceof PgSql\Lob) { @pg_lo_close($lob); }
            if ($transaction) { @pg_query($db, 'ROLLBACK'); }
            if (is_string($target) && is_file($target) && !unlink($target)) { throw new RuntimeException('Неожиданный файл не удалён'); }
            if (is_string($directory) && is_dir($directory) && !rmdir($directory)) { throw new RuntimeException('Temp-каталог не удалён'); }
        }
Ожидаемый результат
bool(true)

PostgreSQL не создаёт каталог на машине PHP. Сервис заранее выбирает собственный разрешённый temp/export directory, проверяет свободное место и не строит путь напрямую из имени пользователя. Пример выполнен на изолированной учебной базе PostgreSQL 18.6 через PHP 8.5.11/ext/pgsql (libpq 16.15). Для своего запуска задайте PGHOST, PGPORT, PGDATABASE, PGUSER и PGPASSWORD; скачиваемый пример содержит pgsql-bootstrap.php. Не подставляйте рабочие реквизиты в код.

Связанные операции: pg_lo_import(), pg_lo_read(), pg_lo_unlink().

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.