Pg query params: примеры (PHP)

Работа с pg_query_params в PHP для PostgreSQL
Раздел: Базы данных (PostgreSQL)
pg_query_params(PgSql\Connection $connection, string $query, array $params): PgSql\Result|false
Описание функции pg_query_params

Функция pg_query_params выполняет SQL-запрос к базе данных PostgreSQL с использованием параметризованных запросов. Это основной рекомендуемый метод для безопасного выполнения команд SQL, так как он предотвращает SQL-инъекции за счет отделения данных от кода запроса.

Функция используется при необходимости выполнить запрос с внешними или пользовательскими данными. Например, при обработке форм авторизации, поиска или любых операций вставки и обновления данных.

Синтаксис:

resource|false pg_query_params(resource $connection = ?, string $query, array $params)

Аргументы функции:

  1. $connection (ресурс соединения): Ресурс подключения к базе данных PostgreSQL, полученный из pg_connect() или pg_pconnect(). Если не указан, используется последнее открытое соединение.
  2. $query (строка): SQL-запрос. Позиционные параметры обозначаются как $1, $2, $3 и т.д. в теле запроса.
  3. $params (массив): Массив значений для подстановки вместо позиционных параметров в запросе. Количество элементов должно соответствовать количеству параметров в запросе. Все значения обрабатываются как строки.

Возвращаемое значение: ресурс результата запроса при успешном выполнении или false в случае ошибки.

Краткие примеры использования
Простой SELECT-запрос
// Подключение к базе данных
$conn = pg_connect('host=localhost dbname=test user=postgres');

// Запрос с одним параметром
$result = pg_query_params($conn, 'SELECT * FROM users WHERE id = $1', [42]);

// Обработка результата
while ($row = pg_fetch_assoc($result)) {
    print_r($row);
}
Array
(
    [id] => 42
    [name] => Иван Иванов
    [email] => ivan@example.com
)
INSERT-запрос с несколькими параметрами
$result = pg_query_params(
    $conn,
    'INSERT INTO products (name, price) VALUES ($1, $2)',
    ['Ноутбук', 89999]
);
if ($result) {
    echo 'Добавлено строк: ' . pg_affected_rows($result);
}
Добавлено строк: 1
Использование NULL
$result = pg_query_params(
    $conn,
    'UPDATE orders SET shipped_at = $1 WHERE id = $2',
    [null, 100]
);
Похожие функции в PHP

Функция pg_query выполняет SQL-запрос, но не поддерживает параметризацию напрямую. Её используют для статических запросов без переменных данных, например, для создания таблиц. При подстановке переменных необходимо вручную экранировать значения с помощью pg_escape_string(), что менее безопасно и удобно.

pg_prepare и pg_execute

Это двухэтапный подход: сначала запрос подготавливается с помощью pg_prepare (создается именованный подготовленный оператор), затем выполняется многократно через pg_execute с разными параметрами. Это предпочтительнее при многократном выполнении одного запроса с разными данными, так как план запроса кэшируется на стороне сервера.

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

Аналоги в других языках
Python (psycopg2)
import psycopg2
conn = psycopg2.connect('dbname=test')
cur = conn.cursor()
cur.execute('SELECT * FROM users WHERE id = %s', (42,))
print(cur.fetchone())

Используется позиционный параметр %s, хотя само значение подставляется безопасно.

JavaScript (Node.js с pg)
const { Client } = require('pg');
const client = new Client();
await client.connect();
const res = await client.query('SELECT * FROM users WHERE id = $1', [42]);
console.log(res.rows[0]);

Синтаксис очень похож на PHP: позиционные параметры $1, $2.

MySQLi (PHP)
$stmt = $mysqli->prepare('SELECT * FROM users WHERE id = ?');
$stmt->bind_param('i', 42);
$stmt->execute();
$result = $stmt->get_result();

Вместо позиционных параметров используются ? (placeholders), а типы данных указываются явно в bind_param.

Типичные ошибки
Несоответствие количества параметров
// Ошибка: в запросе 1 параметр, а передано 2 значения.
$result = pg_query_params(
    $conn,
    'SELECT * FROM users WHERE id = $1',
    [42, 'extra']
);
if (!$result) {
    echo pg_last_error($conn); // Ошибка будет выведена
}
Warning: pg_query_params(): Query failed: ERROR:  bind message supplies 2 parameters, but prepared statement "pgsql_php_..." requires 1
Передача несуществующего соединения
$conn = false; // Предположим, подключение не удалось
$result = pg_query_params($conn, 'SELECT 1', []);
var_dump($result);
bool(false)
Неправильная нумерация параметров
// Параметры должны нумероваться с $1 подряд.
$result = pg_query_params($conn, 'SELECT $2, $1', ['A', 'B']);
$row = pg_fetch_row($result);
print_r($row);
Array
(
    [0] => B
    [1] => A
)

Запрос выполнится, но это может запутать. Параметр $2 получит второе значение из массива.

История изменений

Начиная с PHP 5.1.0, функция pg_query_params доступна в стандартной поставке модуля pgsql. Существенных изменений в сигнатуре или поведении функции в последних основных версиях PHP (7.x, 8.x) не было. Функция стабильна и широко используется.

В PHP 8, как и для всего расширения pgsql, активно развивается типизация, но для pg query params это не привело к обратно несовместимым изменениям.

Расширенные примеры
Использование в транзакции
Пример php
pg_query($conn, 'BEGIN');
try {
    $res1 = pg_query_params($conn, 'UPDATE accounts SET balance = balance - $1 WHERE id = $2', [1000, 1]);
    $res2 = pg_query_params($conn, 'UPDATE accounts SET balance = balance + $1 WHERE id = $2', [1000, 2]);
    // Если оба запроса успешны, подтверждаем транзакцию.
    pg_query($conn, 'COMMIT');
    echo 'Транзакция завершена';
} catch (Exception $e) {
    pg_query($conn, 'ROLLBACK');
    echo 'Транзакция отменена';
}
Получение результата как массив объектов
Пример php
$result = pg_query_params($conn, 'SELECT id, name FROM cities WHERE country = $1', ['RU']);
$cities = [];
while ($obj = pg_fetch_object($result)) {
    // $obj->id, $obj->name
    $cities[] = $obj;
}
print_r($cities);
Запрос с типом данных BYTEA (бинарные данные)
Пример php
$binaryData = file_get_contents('image.png');
$result = pg_query_params(
    $conn,
    'INSERT INTO files (name, data) VALUES ($1, $2)',
    ['image.png', $binaryData]
);
// Данные будут корректно закодированы.
Именованные псевдонимы в результирующих колонках
Пример php
$result = pg_query_params(
    $conn,
    'SELECT count(*) as user_count, status FROM users GROUP BY status',
    [] // Без параметров, но функция все равно работает
);
while ($row = pg_fetch_assoc($result)) {
    echo $row['user_count'] . ' пользователей со статусом ' . $row['status'];
}
Использование с LIMIT и OFFSET (пагинация)
Пример php
$page = 2;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$result = pg_query_params(
    $conn,
    'SELECT * FROM posts ORDER BY created_at DESC LIMIT $1 OFFSET $2',
    [$perPage, $offset]
);
Обработка ошибок с try-catch (в сочетании с PDO стилем)
Пример php
// В чистом PostgreSQL расширении исключения не бросаются.
// Но можно сэмулировать проверку.
$result = pg_query_params($conn, 'SELECT * FROM not_existing_table', []);
if (!$result) {
    $error = pg_last_error($conn);
    // Логирование ошибки $error
    throw new RuntimeException('Ошибка базы данных: ' . $error);
}

PHP pg_query_params function comments

En
Pg query params Submits a command to the server and waits for the result, with the ability to pass parameters separately from the SQL command text