Pg query params: примеры (PHP)
pg_query_params(PgSql\Connection $connection, string $query, array $params): PgSql\Result|falseФункция pg_query_params выполняет SQL-запрос к базе данных PostgreSQL с использованием параметризованных запросов. Это основной рекомендуемый метод для безопасного выполнения команд SQL, так как он предотвращает SQL-инъекции за счет отделения данных от кода запроса.
Функция используется при необходимости выполнить запрос с внешними или пользовательскими данными. Например, при обработке форм авторизации, поиска или любых операций вставки и обновления данных.
Синтаксис:
resource|false pg_query_params(resource $connection = ?, string $query, array $params)
Аргументы функции:
- $connection (ресурс соединения): Ресурс подключения к базе данных PostgreSQL, полученный из pg_connect() или pg_pconnect(). Если не указан, используется последнее открытое соединение.
- $query (строка): SQL-запрос. Позиционные параметры обозначаются как $1, $2, $3 и т.д. в теле запроса.
- $params (массив): Массив значений для подстановки вместо позиционных параметров в запросе. Количество элементов должно соответствовать количеству параметров в запросе. Все значения обрабатываются как строки.
Возвращаемое значение: ресурс результата запроса при успешном выполнении или false в случае ошибки.
// Подключение к базе данных
$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
)
$result = pg_query_params(
$conn,
'INSERT INTO products (name, price) VALUES ($1, $2)',
['Ноутбук', 89999]
);
if ($result) {
echo 'Добавлено строк: ' . pg_affected_rows($result);
}
Добавлено строк: 1
$result = pg_query_params(
$conn,
'UPDATE orders SET shipped_at = $1 WHERE id = $2',
[null, 100]
);
Функция pg_query выполняет SQL-запрос, но не поддерживает параметризацию напрямую. Её используют для статических запросов без переменных данных, например, для создания таблиц. При подстановке переменных необходимо вручную экранировать значения с помощью pg_escape_string(), что менее безопасно и удобно.
Это двухэтапный подход: сначала запрос подготавливается с помощью pg_prepare (создается именованный подготовленный оператор), затем выполняется многократно через pg_execute с разными параметрами. Это предпочтительнее при многократном выполнении одного запроса с разными данными, так как план запроса кэшируется на стороне сервера.
pg_query_params обычно удобнее для единичных или редко повторяющихся запросов.
import psycopg2
conn = psycopg2.connect('dbname=test')
cur = conn.cursor()
cur.execute('SELECT * FROM users WHERE id = %s', (42,))
print(cur.fetchone())
Используется позиционный параметр %s, хотя само значение подставляется безопасно.
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.
$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 это не привело к обратно несовместимым изменениям.
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 'Транзакция отменена';
}
$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);
$binaryData = file_get_contents('image.png');
$result = pg_query_params(
$conn,
'INSERT INTO files (name, data) VALUES ($1, $2)',
['image.png', $binaryData]
);
// Данные будут корректно закодированы.
$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'];
}
$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]
);
// В чистом PostgreSQL расширении исключения не бросаются.
// Но можно сэмулировать проверку.
$result = pg_query_params($conn, 'SELECT * FROM not_existing_table', []);
if (!$result) {
$error = pg_last_error($conn);
// Логирование ошибки $error
throw new RuntimeException('Ошибка базы данных: ' . $error);
}