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

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

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

Если команда 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 в двоичной копии фигурируют только двоичные столбцы, поэтому в настоящее время форматы столбцов всегда соответствуют общему формату.)



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

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

PQputCopyData

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

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

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

Приложение может разделить поток данных 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. Ожидать появления этого результата можно обычным образом. Затем вернитесь к обычной операции.


Функции для приема данных 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 установлен (отличен от нуля), функция PQgetCopyData не будет блокироваться, ожидая данных; она вернет ноль, если команда COPY еще выполняется, но ни одна завершенная строка не доступна. (В этом случает дождитесь готовности к записи, а затем вызовите функцию PQconsumeInput, прежде чем снова вызвать PQgetCopyData.) Когда параметр async не установлен (ноль), функция PQgetCopyData будет блокироваться, пока данные не станут доступны или не завершится операция.

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


Устаревшие функции для 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; они могут посылать часть строки или сразу несколько строк.

Примечание
С версиями протокола QHB ниже 3.0 приложению нужно было явно передать в качестве последней строки два символа (\.), чтобы показать серверу, что оно закончило передачу данных 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 в командной строке.