Pg lo open: примеры (PHP)

Функция pg_lo_open: управление большими объектами в PostgreSQL из PHP
Раздел: Базы данных (PostgreSQL)
pg_lo_open(PgSql\Connection $connection, int $oid, string $mode): PgSql\Lob|false

Описание функции pg_lo_open

Функция pg_lo_open в PHP открывает существующий большой объект (Large Object, LOB) в базе данных PostgreSQL для чтения или записи. Она является частью модуля PostgreSQL, предоставляющего низкоуровневый API для работы с данными, которые неудобно хранить в полях обычных таблиц (например, изображения, документы, аудиофайлы).

Когда используется

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

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

Функция имеет два варианта сигнатуры:

  • pg_lo_open($connection, $oid, $mode) — устаревшая форма (не рекомендуется с PHP 7.4).
  • pg_lo_open($connection, $oid, $mode) — предпочтительная форма с явным указанием соединения.

Параметры:

  1. $connection (ресурс соединения PgSql\Connection) — обязательный параметр, представляющий соединение с сервером PostgreSQL, полученное через pg_connect().
  2. $oid (int) — обязательный идентификатор большого объекта (OID), который необходимо открыть. OID обычно получают через pg_lo_create() или pg_lo_import().
  3. $mode (string) — обязательная строка, определяющая режим доступа. Возможные значения: "r" (только чтение), "w" (только запись), "rw" (чтение и запись).

Возвращаемое значение: в случае успеха функция возвращает ресурс большого объекта (ресурс (resource) или объект PgSql\Lob в PHP 8), либо false в случае ошибки.

Короткие примеры использования

Пример 1: Открытие для чтения
<?php
$conn = pg_connect("dbname=test user=postgres");
$oid = 12345; // Допустим, это OID существующего объекта
$descriptor = pg_lo_open($conn, $oid, "r");
if ($descriptor) {
    echo "Объект открыт для чтения.";
} else {
    echo "Ошибка открытия объекта.";
}
?>
Объект открыт для чтения.
Пример 2: Открытие для записи
<?php
$conn = pg_connect("dbname=test user=postgres");
$oid = pg_lo_create($conn); // Создаем новый объект и получаем его OID
$descriptor = pg_lo_open($conn, $oid, "w");
if ($descriptor) {
    $data = "Данные для записи";
    pg_lo_write($descriptor, $data);
    pg_lo_close($descriptor);
    echo "Запись завершена. OID: " . $oid;
}
?>
Запись завершена. OID: 12346
Пример 3: Попытка открыть несуществующий объект
<?php
$conn = pg_connect("dbname=test user=postgres");
$descriptor = pg_lo_open($conn, 999999, "r"); // Неверный OID
var_dump($descriptor);
?>
bool(false)

Похожие функции в PHP

Создает большой объект из файла в локальной файловой системе. Функция полезна для быстрого импорта готовых файлов без ручного открытия и записи.

Сохраняет большой объект в файл. Альтернатива ручному чтению через pg_lo_open и pg_lo_read.

pg_lo_read_all()

Считывает весь большой объект и сразу отправляет его непосредственно в браузер или другой вывод. Удобно для отдачи файлов, но не подходит для обработки данных в памяти.

Когда что использовать

pg_lo_open предпочтительнее, когда требуется потоковая обработка (по частям) или необходимо записывать данные порциями. pg_lo_import/pg_lo_export подходят для простого импорта/экспорта целиком. pg_lo_read_all используется для прямой отправки файла клиенту.

Аналоги в других языках и СУБД

Python (библиотека psycopg2)

Работа через методы lo_open(), lo_read(), lo_write() объекта Large Object. Отличие: более объектно-ориентированный интерфейс.

import psycopg2
conn = psycopg2.connect("dbname=test")
conn.autocommit = True
lob = conn.lobject(oid=12345, mode='r')
data = lob.read(100)
print(data)
lob.close()
b'Содержимое объекта...'
JavaScript (Node.js, библиотека pg)

Прямого аналога низкоуровневого API может не быть. Часто большие объекты обрабатываются через чтение полей типа BYTEA или через потоковые интерфейсы.

// Пример работы с BYTEA (не точный аналог)
const result = await pool.query('SELECT blob_data FROM files WHERE id=$1', [1]);
const buffer = result.rows[0].blob_data; // Данные уже в памяти

Pg lo open в MySQL

В MySQL нет точного аналога больших объектов PostgreSQL. Для хранения бинарных данных используются типы BLOB, а доступ к ним происходит через стандартные запросы SELECT/UPDATE, что может быть менее эффективно для очень больших файлов.

-- Чтение BLOB
SELECT file_data FROM blobs WHERE id = 1;

Типичные ошибки

Ошибка 1: Использование неверного OID

Попытка открыть объект по несуществующему или некорректному идентификатору.

<?php
$conn = pg_connect("dbname=test");
$descriptor = pg_lo_open($conn, 'invalid_oid', "r"); // OID должен быть integer
var_dump($descriptor);
?>
bool(false)
Ошибка 2: Отсутствие транзакции

Операции с большими объектами в PostgreSQL должны выполняться внутри транзакции. При использовании pg_lo_open с режимом записи вне транзакции может возникнуть ошибка.

<?php
$conn = pg_connect("dbname=test");
// pg_query($conn, "BEGIN"); // Транзакция не начата
$oid = pg_lo_create($conn);
$descriptor = pg_lo_open($conn, $oid, "w"); // Может не сработать
?>
Ошибка 3: Неправильный режим доступа

Указание недопустимой строки режима.

<?php
$conn = pg_connect("dbname=test");
$oid = 12345;
$descriptor = pg_lo_open($conn, $oid, "x"); // Недопустимый режим 'x'
var_dump($descriptor);
?>
bool(false)

Изменения в последних версиях PHP

PHP 8.0

Функция pg_lo_open() теперь возвращает экземпляр класса PgSql\Lob вместо ресурса (resource). Тип возвращаемого значения указан как PgSql\Lob|false. Это часть общей тенденции замены ресурсов на объекты.

PHP 7.4

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

Расширенные примеры использования

Пример 1: Построчное чтение текстового файла из большого объекта
Пример php
<?php
$conn = pg_connect("dbname=test");
pg_query($conn, "BEGIN");
$oid = 12345; // OID объекта с текстом
$descriptor = pg_lo_open($conn, $oid, "r");
if ($descriptor) {
    $buffer = '';
    while (($chunk = pg_lo_read($descriptor, 4096)) !== false) {
        $buffer .= $chunk;
        // Обрабатываем буфер, находя строки
        $lines = explode("\n", $buffer);
        $buffer = array_pop($lines); // Последний фрагмент, возможно, неполная строка
        foreach ($lines as $line) {
            echo "Строка: $line<br>";
        }
    }
    if (!empty($buffer)) {
        echo "Последняя строка: $buffer<br>";
    }
    pg_lo_close($descriptor);
}
pg_query($conn, "COMMIT");
?>
Пример 2: Запись данных из сетевого потока (часть upload)
Пример php
<?php
$conn = pg_connect("dbname=test");
pg_query($conn, "BEGIN");
$oid = pg_lo_create($conn);
$descriptor = pg_lo_open($conn, $oid, "w");
if ($descriptor) {
    $input = fopen('php://input', 'rb');
    while (!feof($input)) {
        $chunk = fread($input, 8192);
        pg_lo_write($descriptor, $chunk);
    }
    fclose($input);
    pg_lo_close($descriptor);
    echo "Файл загружен в объект с OID: $oid";
}
pg_query($conn, "COMMIT");
?>
Пример 3: Копирование одного большого объекта в другой с изменением размера блока
Пример php
<?php
function copy_lob($conn, $src_oid, $dst_oid, $block_size = 2048) {
    pg_query($conn, "BEGIN");
    $src = pg_lo_open($conn, $src_oid, "r");
    $dst = pg_lo_open($conn, $dst_oid, "w");
    if (!$src || !$dst) {
        return false;
    }
    $total = 0;
    while (($data = pg_lo_read($src, $block_size)) !== false && strlen($data) > 0) {
        pg_lo_write($dst, $data);
        $total += strlen($data);
    }
    pg_lo_close($src);
    pg_lo_close($dst);
    pg_query($conn, "COMMIT");
    return $total;
}
$conn = pg_connect("dbname=test");
$bytes = copy_lob($conn, 12345, 12346, 8192);
echo "Скопировано $bytes байт.";
?>
Скопировано 1048576 байт.

PHP pg_lo_open function comments

En
Pg lo open Open a large object