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

Использование pg_lo_tell для определения позиции в крупных объектах
Раздел: Базы данных (PostgreSQL)
pg_lo_tell(PgSql\Lob $lob): int

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

Назначение и контекст использования

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

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

Функция принимает один обязательный аргумент:

  • $lob - ресурс (resource), представляющий открытый крупный объект. Этот ресурс должен быть получен с помощью функций pg_lo_open() или pg_lo_create().

Возвращаемое значение: целое число (int), указывающее текущее смещение (позицию в байтах) от начала объекта. В случае возникновения ошибки функция возвращает false.

Примеры использования
Определение позиции после записи

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

<?php
$connection = pg_connect("dbname=test");
$lobId = pg_lo_create($connection); // Создаем новый объект
$handle = pg_lo_open($connection, $lobId, "w"); // Открываем для записи
$data = "Пример данных";
pg_lo_write($handle, $data); // Пишем данные
$position = pg_lo_tell($handle); // Получаем текущую позицию
echo "Позиция после записи: ".$position; // Должно быть равно длине строки
pg_lo_close($handle); // Закрываем объект
?>
Позиция после записи: 25
Определение позиции при чтении

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

<?php
$connection = pg_connect("dbname=test");
$lobId = 12345; // Существующий OID объекта
$handle = pg_lo_open($connection, $lobId, "r"); // Открываем для чтения
$chunk = pg_lo_read($handle, 10); // Читаем 10 байт
$position = pg_lo_tell($handle); // Получаем текущую позицию
echo "Прочитано 10 байт. Текущая позиция: ".$position;
pg_lo_close($handle);
?>
Прочитано 10 байт. Текущая позиция: 10
Похожие функции в PHP
  • pg_lo_seek($lob, $offset, $whence = PGSQL_SEEK_CUR) - устанавливает позицию указателя в крупном объекте. Функция pg_lo_tell часто используется после pg_lo_seek для проверки нового положения.
  • pg_lo_read($lob, $length = 8192) - читает данные из объекта, автоматически перемещая указатель. Позицию после чтения можно проверить с помощью pg_lo_tell.
  • pg_lo_write($lob, $data) - записывает данные в объект, также смещая указатель. pg_lo_tell помогает определить размер записанных данных.
  • filesize() и ftell() - аналоги для работы с обычными файлами в файловой системе. pg_lo_tell выполняет роль ftell() для виртуальных файлов-объектов внутри PostgreSQL.

Функцию pg_lo_tell предпочтительно использовать именно при работе с крупными объектами PostgreSQL, в то время как ftell() применяется для стандартных файловых потоков.

Альтернативы в других языках
Python (библиотека psycopg2)

В Python для работы с крупными объектами используется метод seek() и tell() у объекта LargeObject.

import psycopg2
conn = psycopg2.connect("dbname=test")
conn.autocommit = True
lob = conn.lobject(12345, 'rb') # Открытие объекта
lob.read(10)
position = lob.tell() # Получение позиции
print(f"Текущая позиция: {position}")
lob.close()
Текущая позиция: 10
JavaScript (Node.js, библиотека pg)

В Node.js прямой аналог может отсутствовать, так как работа с крупными объектами часто организуется через потоковое чтение/запись или SELECT-запросы к полям типа BYTEA/OID. Позицию можно отслеживать вручную через размер прочитанных чанков.

// Пример ручного отслеживания позиции при чтении
let position = 0;
const chunkSize = 1024;
// ... в цикле чтения
position += chunkSize;
console.log(`Текущая позиция: ${position}`);

Pg lo tell в MySQL

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

Типичные ошибки
Передача неверного ресурса

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

<?php
$connection = pg_connect("dbname=test");
$position = pg_lo_tell($connection); // Ошибка: $connection не ресурс объекта
echo $position;
?>
Warning: pg_lo_tell() expects parameter 1 to be resource, resource given...
Использование закрытого объекта

Попытка получить позицию после закрытия объекта приведет к ошибке.

<?php
$connection = pg_connect("dbname=test");
$handle = pg_lo_open($connection, 12345, "r");
pg_lo_close($handle);
$position = pg_lo_tell($handle); // Ресурс уже закрыт
?>
Warning: pg_lo_tell(): supplied resource is not a valid large-object resource...
Использование объекта не в том режиме

Функция работает с объектами, открытыми как для чтения, так и для записи. Однако если объект не был корректно открыт, возникнет ошибка.

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

Начиная с PHP 8.0, модуль PostgreSQL (pgsql) значительно обновлен. Функции для работы с крупными объектами, включая pg_lo_tell, теперь в случае ошибки выбрасывают исключения Exception, если не установлен обработчик ошибок.

В PHP 8.1 и 8.2 существенных изменений в поведении или сигнатуре функции pg_lo_tell не было. Рекомендуется использовать строгий режим типизации (declare(strict_types=1)) для избежания неявных преобразований типов аргументов.

Расширенные примеры
Отслеживание прогресса чтения с резервированием

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

Пример php
<?php
declare(strict_types=1);

function readLargeObjectWithProgress($connection, int $lobId, int $startFrom = 0) {
    $handle = pg_lo_open($connection, $lobId, "r");
    if ($startFrom > 0) {
        pg_lo_seek($handle, $startFrom, PGSQL_SEEK_SET);
    }

    $chunkSize = 8192;
    $totalRead = $startFrom;
    $savedPosition = $startFrom;

    while (!feof($handle)) { // Условная проверка конца объекта
        $data = pg_lo_read($handle, $chunkSize);
        if ($data === false || $data === '') break;

        $totalRead += strlen($data);
        $currentPos = pg_lo_tell($handle); // Текущая позиция после чтения
        echo "Прочитано: $currentPos байт\n";

        // Пример условия для имитации сбоя и сохранения позиции
        if ($totalRead > 50000) {
            $savedPosition = $currentPos;
            echo "Симуляция сбоя. Сохранена позиция: $savedPosition\n";
            break;
        }
    }
    pg_lo_close($handle);
    return $savedPosition;
}

$conn = pg_connect("dbname=test");
$lastPosition = readLargeObjectWithProgress($conn, 12345, 0);
// При следующем запуске можно передать $lastPosition для возобновления
?>
Прочитано: 8192 байт
Прочитано: 16384 байт
...
Симуляция сбоя. Сохранена позиция: 57344
Сравнение позиций при записи из нескольких источников

Запись в объект из двух разных строк с проверкой промежуточных позиций.

Пример php
<?php
$conn = pg_connect("dbname=test");
$lobId = pg_lo_create($conn);
$handle = pg_lo_open($conn, $lobId, "w");

$data1 = "Первая часть данных";
$data2 = "Вторая часть данных";

pg_lo_write($handle, $data1);
$pos1 = pg_lo_tell($handle);
echo "После первой записи: $pos1\n";

pg_lo_write($handle, $data2);
$pos2 = pg_lo_tell($handle);
echo "После второй записи: $pos2\n";

echo "Длина data2: ".($pos2 - $pos1)."\n";
pg_lo_close($handle);
?>
После первой записи: 37
После второй записи: 74
Длина data2: 37

PHP pg_lo_tell function comments

En
Pg lo tell Returns current seek position a of large object