32.10. Функции, связанные с командой COPY#

32.10. Функции, связанные с командой COPY

32.10. Функции, связанные с командой COPY

Команда COPY в Tantor SE имеет опции для чтения из или записи в сетевое соединение, используемое libpq. Функции, описанные в этом разделе, позволяют приложениям использовать эту возможность, предоставляя или потребляя скопированные данные.

Весь процесс заключается в том, что приложение сначала выполняет SQL-команду COPY с помощью функции PQexec или одной из эквивалентных функций. Ответ на это (если в команде нет ошибок) будет объект PGresult с кодом состояния PGRES_COPY_OUT или PGRES_COPY_IN (в зависимости от указанного направления копирования). Затем приложение должно использовать функции этого раздела для приема или передачи строк данных. По завершении передачи данных возвращается другой объект PGresult, указывающий на успешность или неудачу передачи. Его статус будет PGRES_COMMAND_OK в случае успеха или PGRES_FATAL_ERROR, если возникла проблема. В этот момент можно выполнять дополнительные SQL-команды с помощью функции PQexec. (Невозможно выполнять другие SQL-команды с использованием того же соединения во время выполнения операции COPY).

Если команда COPY выполняется с помощью PQexec в строке, которая может содержать дополнительные команды, приложение должно продолжать получать результаты с помощью PQgetResult после завершения последовательности COPY. Только когда PQgetResult возвращает NULL, можно быть уверенным, что строка команды PQexec завершена и безопасно выполнять дополнительные команды.

Функции этого раздела должны выполняться только после получения статуса результата PGRES_COPY_OUT или PGRES_COPY_IN из PQexec или PQgetResult.

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

PQnfields

Возвращает количество столбцов (полей), которые будут скопированы.

PQbinaryTuples

0 указывает на то, что общий формат копирования является текстовым (строки разделены символами перехода строки, столбцы разделены разделительными символами и т. д.). 1 указывает на то, что общий формат копирования является двоичным. См. COPY для получения дополнительной информации.

PQfformat

Возвращает код формата (0 для текста, 1 для двоичного) для каждого столбца операции копирования. Коды формата для каждого столбца всегда будут равны нулю, когда общий формат копирования является текстовым, но двоичный формат может поддерживать как текстовые, так и двоичные столбцы. (Однако, на данный момент, в текущей реализации COPY в двоичной копии присутствуют только двоичные столбцы; поэтому коды формата для каждого столбца всегда соответствуют общему формату).

32.10.1. Функции для отправки данных COPY

Эти функции используются для отправки данных во время COPY FROM STDIN. Они завершатся неудачей, если вызываются, когда соединение не находится в состоянии COPY_IN.

PQputCopyData

Отправляет данные на сервер во время состояния COPY_IN.

int PQputCopyData(PGconn *conn,
                  const char *buffer,
                  int nbytes);

Передает данные COPY в указанный buffer длиной nbytes на сервер. Результат равен 1, если данные были поставлены в очередь, ноль, если они не были поставлены в очередь из-за заполненных буферов (это происходит только в неблокирующем режиме), или -1, если произошла ошибка. (Используйте PQerrorMessage для получения подробностей, если возвращаемое значение равно -1. Если значение равно нулю, ожидайте готовности к записи и повторите попытку).

Приложение может разделить потоковые данные COPY на буферные загрузки любого удобного размера. Границы загрузки буфера не имеют семантического значения при отправке. Содержимое потоковых данных должно соответствовать ожидаемому формату данных команды COPY; см. COPY для получения подробной информации.

PQputCopyEnd

Отправляет серверу индикацию конца данных во время состояния COPY_IN.

int PQputCopyEnd(PGconn *conn,
                 const char *errormsg);

Сообщение об ошибке COPY_IN успешно завершается, если errormsg равно NULL. Если errormsg не равно NULL, то COPY принудительно завершается с ошибкой, где строка, на которую указывает errormsg, используется в качестве сообщения об ошибке. (Однако не следует предполагать, что это точное сообщение об ошибке вернется с сервера, поскольку сервер может уже завершить COPY по своим собственным причинам).

Результат равен 1, если сообщение о завершении было отправлено; или в неблокирующем режиме это может указывать только на то, что сообщение о завершении было успешно поставлено в очередь. (В неблокирующем режиме, чтобы быть уверенным, что данные были отправлены, следует дождаться готовности к записи и вызвать PQflush, повторяя это до тех пор, пока он не вернет ноль). Ноль указывает на то, что функция не смогла поставить сообщение о завершении в очередь из-за полных буферов; это произойдет только в неблокирующем режиме. (В этом случае дождитесь готовности к записи и повторите вызов PQputCopyEnd). Если произошла серьезная ошибка, возвращается -1; вы можете использовать PQerrorMessage для получения подробной информации.

После успешного вызова PQputCopyEnd, вызовите PQgetResult, чтобы получить окончательный статус выполнения команды COPY. Можно дождаться доступности этого результата обычным способом. Затем вернитесь к нормальной работе.

32.10.2. Функции для получения данных COPY

Эти функции используются для получения данных во время COPY TO STDOUT. Они завершатся неудачей, если вызываются, когда соединение не находится в состоянии COPY_OUT.

PQgetCopyData

Получает данные от сервера во время состояния COPY_OUT.

int PQgetCopyData(PGconn *conn,
                  char **buffer,
                  int async);

Попытки получить еще одну строку данных от сервера во время COPY. Данные всегда возвращаются по одной строке; если доступна только частичная строка, она не возвращается. Успешное получение строки данных включает выделение блока памяти для хранения данных. Параметр buffer должен быть не-NULL. *buffer устанавливается для указания на выделенную память или на NULL в случаях, когда буфер не возвращается. Результат, отличный от NULL, должен быть освобожден с помощью PQfreemem, когда он больше не нужен.

Когда строка успешно возвращается, возвращаемое значение - это количество байтов данных в строке (оно всегда будет больше нуля). Возвращаемая строка всегда завершается нулевым символом, хотя это, вероятно, полезно только для текстовой команды COPY. Результат равный нулю указывает на то, что COPY все еще выполняется, но строка еще не доступна (это возможно только при установленном значении async в true). Результат -1 указывает на то, что COPY завершен. Результат -2 указывает на то, что произошла ошибка (см. PQerrorMessage для получения причины).

Когда параметр async равен true (не нулю), PQgetCopyData не будет блокироваться в ожидании ввода; он вернет ноль, если COPY все еще выполняется, но нет доступных полных строк. (В этом случае дождитесь готовности к чтению и затем вызовите PQconsumeInput перед повторным вызовом PQgetCopyData). Когда параметр async равен false (нулю), PQgetCopyData будет блокироваться до тех пор, пока данные не станут доступными или операция не завершится.

После того, как PQgetCopyData возвращает -1, вызовите PQgetResult для получения окончательного статуса выполнения команды COPY. Можно дождаться доступности этого результата обычным способом. Затем вернитесь к нормальной работе.

32.10.3. Устаревшие функции для COPY

Эти функции представляют более старые методы обработки COPY. Хотя они все еще работают, они устарели из-за плохой обработки ошибок, неудобных методов обнаружения конца данных и отсутствия поддержки двоичных или неблокирующих передач.

PQgetline

Читает строку символов (переданную сервером) с символом перехода строки в буферную строку размером length.

int PQgetline(PGconn *conn,
              char *buffer,
              int length);

Эта функция копирует до length-1 символов в буфер и преобразует завершающий символ перехода строки в нулевой байт. PQgetline возвращает EOF в конце ввода, 0, если вся строка была прочитана, и 1, если буфер заполнен, но завершающий символ перехода строки еще не был прочитан.

Обратите внимание, что приложение должно проверить, состоит ли новая строка из двух символов \., что указывает на то, что сервер закончил отправку результатов команды COPY. Если приложение может получать строки, длина которых превышает length-1 символов, необходимо быть внимательным, чтобы правильно распознать строку \. (и не ошибочно принять конец длинной строки данных за строку-терминатор).

PQgetlineAsync

Читает строку данных COPY (передаваемую сервером) в буфер без блокировки.

int PQgetlineAsync(PGconn *conn,
                   char *buffer,
                   int bufsize);

Эта функция аналогична PQgetline, но ее можно использовать приложениями, которым необходимо асинхронно читать данные COPY, то есть без блокировки. После выполнения команды COPY и получения ответа PGRES_COPY_OUT, приложение должно вызывать PQconsumeInput и PQgetlineAsync до обнаружения сигнала о конце данных.

В отличие от PQgetline, эта функция берет на себя ответственность за обнаружение конца данных.

На каждый вызов, PQgetlineAsync вернет данные, если полная строка данных доступна во входном буфере libpq. В противном случае, данные не возвращаются, пока не придет остальная часть строки. Функция возвращает -1, если обнаружен маркер конца копируемых данных, или 0, если данные недоступны, или положительное число, указывающее количество возвращенных байт данных. Если возвращается -1, вызывающая сторона должна следующим вызвать PQendcopy, а затем вернуться к нормальной обработке.

Возвращаемые данные не будут выходить за границы строки данных. Если возможно, целая строка будет возвращена одновременно. Но если буфер, предоставленный вызывающей стороной, слишком мал, чтобы вместить строку, отправленную сервером, то будет возвращена частичная строка данных. С текстовыми данными это можно обнаружить, проверив, является ли последний возвращенный байт символом "\n" или нет. (В бинарном COPY необходимо фактически разобрать формат данных COPY для принятия эквивалентного решения). Возвращаемая строка не завершается нулевым символом. (Если нужно добавить завершающий нулевой символ, убедитесь, что передаете bufsize, на единицу меньший, чем фактически доступное место).

PQputline

Отправляет нуль-терминированную строку на сервер. Возвращает 0 в случае успешной отправки и EOF в случае невозможности отправки строки.

int PQputline(PGconn *conn,
              const char *string);

Данные потока COPY, отправляемого серией вызовов PQputline, имеют тот же формат, что и данные, возвращаемые PQgetlineAsync, за исключением того, что приложения не обязаны отправлять ровно одну строку данных за каждый вызов PQputline; допустимо отправлять неполные строки или несколько строк за один вызов.

Примечание

Перед протоколом 3.0 Tantor SE было необходимо, чтобы приложение явно отправляло два символа \. в качестве последней строки, чтобы указать серверу, что оно закончило отправку данных COPY. Хотя такой вариант работает, он устарел, и особое значение \. может быть удалено в будущем релизе. Достаточно вызвать PQendcopy после отправки фактических данных.

PQputnbytes

Отправляет ненулевую строку без завершающего символа на сервер. Возвращает 0 в случае успеха и EOF в случае невозможности отправить строку.

int PQputnbytes(PGconn *conn,
                const char *buffer,
                int nbytes);

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

PQendcopy

Синхронизирует с сервером.

int PQendcopy(PGconn *conn);

Эта функция ожидает, пока сервер завершит копирование. Она должна быть вызвана либо после отправки последней строки на сервер с помощью PQputline, либо после получения последней строки от сервера с помощью PQgetline. Она должна быть вызвана, иначе сервер будет выйти из синхронизации с клиентом. После возврата из этой функции сервер готов принять следующую SQL-команду. Возвращаемое значение равно 0 в случае успешного выполнения, в противном случае ненулевое значение. (Используйте PQerrorMessage для получения подробностей, если возвращаемое значение ненулевое).

При использовании PQgetResult приложение должно отвечать на результат PGRES_COPY_OUT путем выполнения повторяющихся вызовов PQgetline, за которыми следует вызов PQendcopy после обнаружения строки-терминатора. Затем оно должно вернуться к циклу PQgetResult, пока PQgetResult не вернет нулевой указатель. Аналогично результат PGRES_COPY_IN обрабатывается серией вызовов PQputline, за которыми следует вызов PQendcopy, затем возврат к циклу PQgetResult. Это обеспечит правильное выполнение команды COPY, встроенной в серию команд SQL.

Старые приложения, скорее всего, отправляют команду COPY через PQexec и предполагают, что транзакция завершена после PQendcopy. Это будет работать правильно только в том случае, если COPY является единственной командой SQL в строке команды.