34.3. Функции для исполнения команд
После того как соединение с сервером было успешно установлено, функции, описанные в этом разделе, используются для выполнения SQL-запросов и команд.
34.3.1. Главные функции
PQexecПередаёт команду серверу и ожидает результата.
PGresult *PQexec(PGconn *conn, const char *command);
Возвращает указатель на
PGresultили, возможно, пустой указатель (null). Как правило, возвращается непустой указатель, исключением являются ситуации нехватки памяти или серьёзные ошибки, такие, как невозможность отправки команды серверу. Для проверки возвращаемого значения на наличие ошибок следует вызвать функциюPQresultStatus(в случае нулевого указателя она возвратитPGRES_FATAL_ERROR). Для получения дополнительной информации о таких ошибках используйте функциюPQerrorMessage.
Строка команды может включать в себя более одной SQL-команды (которые разделяются точкой с запятой). Несколько запросов, отправленных с помощью одного вызова PQexec, обрабатываются в рамках одной транзакции, если только команды BEGIN/COMMIT не включены явно в строку запроса, чтобы разделить его на несколько транзакций. (Подробнее о том, как сервер обрабатывает строки, включающие несколько команд, рассказывается в Подразделе 53.2.2.1.) Однако обратите внимание, что возвращаемая структура PGresult описывает только результат последней из выполненных команд, содержащихся в строке запроса. Если одна из команд завершается сбоем, то обработка строки запроса на этом останавливается, и возвращённая структура PGresult описывает состояние ошибки.
PQexecParamsОтправляет команду серверу и ожидает результата. Имеет возможность передать параметры отдельно от текста SQL-команды.
PGresult *PQexecParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);PQexecParamsподобнаPQexec, но предлагает дополнительную функциональность: значения параметров могут быть указаны отдельно от самой строки-команды, а результаты запроса могут быть затребованы либо в текстовом, либо в двоичном формате.Параметры функции следующие:
connОбъект, описывающий подключение, через которое пересылается команда.
commandСтрока SQL-команды, которая должна быть выполнена. Если используются параметры, то в строке команды на них ссылаются, как
$1,$2и т. д.nParamsЧисло предоставляемых параметров. Оно равно длине массивов
paramTypes[],paramValues[],paramLengths[]иparamFormats[]. (Указатели на массивы могут быть равныNULL, когдаnParamsравно нулю.)paramTypes[]Предписывает, посредством OID, типы данных, которые должны быть назначены параметрам. Если значение
paramTypesравноNULLили какой-либо отдельный элемент в массиве равен нулю, тогда сервер самостоятельно определит тип данных для параметра точно таким же образом, как он сделал бы для литеральной строки, тип которой не указан.paramValues[]Указывает фактические значения параметров. Нулевой указатель в этом массиве означает, что соответствующий параметр равен null; в противном случае указатель указывает на текстовую строку, завершающуюся нулевым символом (для текстового формата), или на двоичные данные в формате, которого ожидает сервер (для двоичного формата).
paramLengths[]Указывает фактические длины данных для параметров, представленных в двоичном формате. Он игнорируется для параметров, имеющих значение null, и для параметров, представленных в текстовом формате. Указатель на массив может быть нулевым, когда нет двоичных параметров.
paramFormats[]Указывает, являются ли параметры текстовыми (поместите нуль в элемент массива, соответствующий такому параметру) или двоичными (поместите единицу в элемент массива, соответствующий такому параметру). Если указатель на массив является нулевым, тогда все параметры считаются текстовыми строками.
Значения, переданные в двоичном формате, требуют знания внутреннего представления, которого ожидает сервер. Например, целые числа должны передаваться с использованием сетевого порядка байтов. Передача значений типа
numericтребует знания формата, в котором их хранит сервер; это реализовано вsrc/backend/utils/adt/numeric.c::numeric_send()иsrc/backend/utils/adt/numeric.c::numeric_recv().resultFormatТребует указать ноль, чтобы получить результаты в текстовом формате, или единицу, чтобы получить результаты в двоичном формате. (В настоящее время нет возможности получить различные столбцы результата в разных форматах, хотя это и возможно на уровне протокола, лежащего в основе подключений.)
Главным преимуществом PQexecParams перед PQexec является возможность отделить значения параметров от строки запроса. Это позволяет обойтись без кавычек и экранирующих символов, манипулирование которыми бывает трудоёмким и часто приводит к ошибкам.
В отличие от PQexec, PQexecParams позволяет включать не более одной SQL-команды в строку запроса. (В ней могут содержаться точки с запятой, однако может присутствовать не более одной непустой команды.) Это ограничение накладывается базовым протоколом, но оно приносит и некоторую пользу в качестве дополнительной защиты от атак методом SQL-инъекций.
Подсказка
Указание типов параметров с помощью OID является трудоёмким, особенно если вы предпочитаете не указывать явно значений OID в вашей программе. Однако вы можете избежать этого даже в случаях, когда сервер самостоятельно не может определить тип параметра или выбирает не тот тип, который вы хотите. В строке SQL-команды добавьте явное приведение типа для этого параметра, чтобы показать, какой тип данных вы будете отправлять. Например:
SELECT * FROM mytable WHERE x = $1::bigint;
Это приведёт к тому, что параметр $1 будет считаться имеющим тип bigint, в то время как по умолчанию ему был бы назначен тот же самый тип, что и x. Такое явное принятие решения о типе параметра либо с помощью описанного метода, либо путём задания числового OID строго рекомендуется, когда значения параметров отправляются в двоичном формате, поскольку двоичный формат имеет меньшую избыточность, чем текстовый, и поэтому гораздо менее вероятно, что сервер обнаружит ошибку несоответствия типов, допущенную вами.
PQprepareОтправляет запрос, чтобы создать подготовленный оператор с конкретными параметрами, и ожидает завершения.
PGresult *PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes);PQprepareсоздаёт подготовленный оператор для последующего исполнения с помощьюPQexecPrepared. Благодаря этому, команды, которые будут выполняться многократно, не потребуется разбирать и планировать каждый раз; за подробностями обратитесь к PREPARE.Функция создаёт подготовленный оператор с именем
stmtNameиз строкиquery, которая должна содержать единственную SQL-команду.stmtNameможет быть пустой строкой"", тогда будет создан неименованный оператор (в таком случае любой уже существующий неименованный оператор будет автоматически заменён), в противном случае, если имя оператора уже определено в текущем сеансе работы, будет ошибка. Если используются параметры, то в запросе к ним обращаются таким образом:$1,$2и т. д.nParamsпредставляет число параметров, типы данных для которых указаны в массивеparamTypes[]. (Указатель на массив может быть равенNULL, когда значениеnParamsравно нулю.)paramTypes[]указывает, посредством OID, типы данных, которые будут назначены параметрам. ЕслиparamTypesравенNULLили какой-либо элемент в этом массиве равен нулю, то сервер назначает тип данных соответствующему параметру точно таким же способом, как он сделал бы для литеральной строки, не имеющей типа. Также в запросе можно использовать параметры с номерами, большими, чемnParams; типы данных для них сервер также сможет подобрать. (См. описаниеPQdescribePrepared, где сказано, как можно определить, какие типы данных были подобраны).Как и при вызове
PQexec, результатом является объектPGresult, содержимое которого показывает успех или сбой на стороне сервера. Нулевой указатель означает нехватку памяти или невозможность вообще отправить команду. Для получения дополнительной информации о таких ошибках используйтеPQerrorMessage.
Подготовленные операторы для использования с PQexecPrepared можно также создать путём исполнения SQL-команд PREPARE. Также, хотя никакой функции libpq для удаления подготовленного оператора не предусмотрено, для этой цели можно воспользоваться SQL-командой DEALLOCATE.
PQexecPreparedОтправляет запрос на исполнение подготовленного оператора с данными параметрами и ожидает результата.
PGresult *PQexecPrepared(PGconn *conn, const char *stmtName, int nParams, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);PQexecPreparedподобнаPQexecParams, но команда, подлежащая исполнению, указывается путём передачи имени предварительно подготовленного оператора вместо передачи строки запроса. Эта возможность позволяет командам, которые вызываются многократно, подвергаться разбору и планированию только один раз, а не при каждом их исполнении. Оператор должен быть подготовлен предварительно в рамках текущего сеанса работы.Параметры идентичны
PQexecParams, за исключением того, что вместо строки запроса передаётся имя подготовленного оператора и отсутствует параметрparamTypes[](он не нужен, поскольку типы данных для параметров подготовленного оператора были определены при его создании).PQdescribePreparedПередаёт запрос на получение информации об указанном подготовленном операторе и ожидает завершения.
PGresult *PQdescribePrepared(PGconn *conn, const char *stmtName);
PQdescribePreparedпозволяет приложению получить информацию о предварительно подготовленном операторе.Для ссылки на неименованный оператор значение
stmtNameможет быть пустой строкой""илиNULL, в противном случае оно должно быть именем существующего подготовленного оператора. В случае успешного выполнения возвращаетсяPGresultсо статусомPGRES_COMMAND_OK. ФункцииPQnparamsиPQparamtypeпозволяют извлечь изPGresultинформацию о параметрах подготовленного оператора, а функцииPQnfields,PQfname,PQftypeи т. п. предоставляют информацию о результирующих столбцах данного оператора (если они есть).PQdescribePortalПередаёт запрос на получение информации об указанном портале и ожидает завершения.
PGresult *PQdescribePortal(PGconn *conn, const char *portalName);
PQdescribePortalпозволяет приложению получить информацию о предварительно созданном портале. (libpq не предоставляет прямого доступа к порталам, но вы можете использовать эту функцию для ознакомления со свойствами курсора, созданного с помощью SQL-командыDECLARE CURSOR.)Для ссылки на неименованный портал значение
portalNameможет быть пустой строкой""илиNULL, в противном случае оно должно быть именем существующего портала. В случае успешного завершения возвращаетсяPGresultсо статусомPGRES_COMMAND_OK. С помощью функцийPQnfields,PQfname,PQftypeи т. д. можно извлечь изPGresultинформацию о результирующих столбцах данного портала (если они есть).
Структура PGresult содержит результат, возвращённый сервером. Разработчики приложений libpq должны тщательно поддерживать абстракцию PGresult. Для получения доступа к содержимому PGresult используйте функции доступа, описанные ниже. Избегайте непосредственного обращения к полям структуры PGresult, поскольку они могут измениться в будущем.
PQresultStatusВозвращает статус результата выполнения команды.
ExecStatusType PQresultStatus(const PGresult *res);
PQresultStatusможет возвращать одно из следующих значений:PGRES_EMPTY_QUERYСтрока, отправленная серверу, была пустой.
PGRES_COMMAND_OKУспешное завершение команды, не возвращающей никаких данных.
PGRES_TUPLES_OKУспешное завершение команды, возвращающей данные (такой, как
SELECTилиSHOW).PGRES_COPY_OUTНачат перенос данных Copy Out (с сервера).
PGRES_COPY_INНачат перенос данных Copy In (на сервер).
PGRES_BAD_RESPONSEОтвет сервера не был распознан.
PGRES_NONFATAL_ERRORПроизошла некритическая ошибка (уведомление или предупреждение).
PGRES_FATAL_ERRORПроизошла критическая ошибка.
PGRES_COPY_BOTHНачат перенос данных Copy In/Out (на сервер и с сервера). Эта функция в настоящее время используется только для потоковой репликации, поэтому такой статус не должен иметь место в обычных приложениях.
PGRES_SINGLE_TUPLEСтруктура
PGresultсодержит только одну результирующую строку, возвращённую текущей командой. Этот статус имеет место только тогда, когда для данного запроса был выбран режим построчного вывода (см. Раздел 34.6).PGRES_PIPELINE_SYNCPGresultпредставляет точку синхронизации в конвейерном режиме, запрошеннуюPQpipelineSync. Этот статус возможен, только если выбран конвейерный режим.PGRES_PIPELINE_ABORTEDСтруктура
PGresultпредставляет конвейер, получивший ошибку от сервера. ФункцияPQgetResultдолжна вызываться неоднократно, и каждый раз она будет возвращать этот код состояния до конца текущего конвейера, после чего она вернётPGRES_PIPELINE_SYNCи сможет возобновиться нормальная обработка.
Если статус результата
PGRES_TUPLES_OKилиPGRES_SINGLE_TUPLE, тогда для извлечения строк, возвращённых запросом, можно использовать функции, описанные ниже. Обратите внимание, что командаSELECT, даже когда она не извлекает ни одной строки, всё же показываетPGRES_TUPLES_OK.PGRES_COMMAND_OKпредназначен для команд, которые никогда не возвращают строки (INSERTилиUPDATEбез использования предложенияRETURNINGи др.). ОтветPGRES_EMPTY_QUERYможет указывать на наличие ошибки в клиентском программном обеспечении.Результат со статусом
PGRES_NONFATAL_ERRORникогда не будет возвращён напрямую функциейPQexecили другими функциями исполнения запросов; вместо этого результаты такого вида передаются обработчику уведомлений (см. Раздел 34.13).PQresStatusПреобразует значение перечислимого типа, возвращённое функцией
PQresultStatus, в строковую константу, описывающую код статуса. Вызывающая функция не должна освобождать память, на которую указывает возвращаемый указатель.char *PQresStatus(ExecStatusType status);
PQresultErrorMessageВозвращает сообщение об ошибке, связанное с командой, или пустую строку, если ошибки не произошло.
char *PQresultErrorMessage(const PGresult *res);
Если произошла ошибка, то возвращённая строка будет включать завершающий символ новой строки. Вызывающая функция не должна напрямую освобождать память, на которую указывает возвращаемый указатель. Она будет освобождена, когда соответствующий указатель
PGresultбудет передан функцииPQclear.Если непосредственно после вызова
PQexecилиPQgetResultвызвать функциюPQerrorMessage(для данного подключения), то она возвратит ту же самую строку, что иPQresultErrorMessage(для данного результата). ОднакоPGresultсохранит своё сообщение об ошибке до тех пор, пока не будет уничтожен, в то время как сообщение об ошибке, связанное с данным подключением, будет изменяться при выполнении последующих операций. Воспользуйтесь функциейPQresultErrorMessage, когда вы хотите узнать статус, связанный с конкретной структуройPGresult; используйте функциюPQerrorMessage, когда вы хотите узнать статус выполнения самой последней операции на данном соединении.PQresultVerboseErrorMessageВозвращает переформатированную версию сообщения об ошибке, связанного с объектом
PGresult.char *PQresultVerboseErrorMessage(const PGresult *res, PGVerbosity verbosity, PGContextVisibility show_context);В некоторых ситуациях клиент может захотеть получить более подробную версию ранее выданного сообщения об ошибке. Эту потребность удовлетворяет функция
PQresultVerboseErrorMessage, формируя сообщение, которое было бы выдано функциейPQresultErrorMessage, если бы заданный уровень детализации был текущим для соединения в момент заполненияPGresult. Если же вPGresultне содержится ошибка, вместо этого выдаётся сообщение «PGresult is not an error result» (PGresult — не результат с ошибкой). Возвращаемое этой функцией сообщение завершается переводом строки.В отличие от многих других функций, извлекающих данные из
PGresult, результат этой функции — новая размещённая в памяти строка. Когда эта строка будет не нужна, вызывающий код должен освободить её место, вызвавPQfreemem().При нехватке памяти может быть возвращёно NULL.
PQresultErrorFieldВозвращает индивидуальное поле из отчёта об ошибке.
char *PQresultErrorField(const PGresult *res, int fieldcode);
fieldcodeэто идентификатор поля ошибки; см. символические константы, перечисленные ниже. ЕслиPGresultне содержит ошибки или предупреждения или не включает указанное поле, то возвращаетсяNULL. Значения полей обычно не включают завершающий символ новой строки. Вызывающая функция не должна напрямую освобождать память, на которую указывает возвращаемый указатель. Она будет освобождена, когда соответствующий указательPGresultбудет передан функцииPQclear.Доступны следующие коды полей:
PG_DIAG_SEVERITYСерьёзность; поле может содержать
ERROR,FATALилиPANIC(в сообщении об ошибке) либоWARNING,NOTICE,DEBUG,INFOилиLOG(в сообщении-уведомлении) либо локализованный перевод одного из этих значений. Присутствует всегда.PG_DIAG_SEVERITY_NONLOCALIZEDСерьёзность; поле может содержать
ERROR,FATALилиPANIC(в сообщении об ошибке) либоWARNING,NOTICE,DEBUG,INFOилиLOG(в сообщении-уведомлении). Это поле подобноPG_DIAG_SEVERITY, но его содержимое никогда не переводится. Присутствует только в отчётах, выдаваемых PostgreSQL версии 9.6 и новее.PG_DIAG_SQLSTATEКод ошибки в соответствии с соглашением о кодах SQLSTATE. Код SQLSTATE идентифицирует тип случившейся ошибки; он может использоваться клиентскими приложениями, чтобы выполнять конкретные операции (такие, как обработка ошибок) в ответ на конкретную ошибку базы данных. Список возможных кодов SQLSTATE приведён в Приложении A. Это поле не подлежит локализации. Оно всегда присутствует.
PG_DIAG_MESSAGE_PRIMARYГлавное сообщение об ошибке, предназначенное для прочтения пользователем. Как правило составляет всего одну строку. Это поле всегда присутствует.
PG_DIAG_MESSAGE_DETAILНеобязательное дополнительное сообщение об ошибке, передающее более детальную информацию о проблеме. Может занимать несколько строк.
PG_DIAG_MESSAGE_HINTПодсказка: необязательное предположение о том, что можно сделать в данной проблемной ситуации. Оно должно отличаться от детальной информации в том смысле, что оно предлагает совет (возможно, и неподходящий), а не просто факты. Может занимать несколько строк.
PG_DIAG_STATEMENT_POSITIONСтрока, содержащая десятичное целое число, указывающее позицию расположения ошибки в качестве индекса в оригинальной строке оператора. Первый символ имеет позицию 1, при этом позиции измеряются в символах а не в байтах.
PG_DIAG_INTERNAL_POSITIONЭто поле определяется точно так же, как и поле
PG_DIAG_STATEMENT_POSITION, но оно используется, когда позиция местонахождения ошибки относится к команде, сгенерированной внутренними модулями, а не к команде, представленной клиентом. Когда появляется это поле, то всегда появляется и полеPG_DIAG_INTERNAL_QUERY.PG_DIAG_INTERNAL_QUERYТекст команды, сгенерированной внутренними модулями, завершившейся сбоем. Это мог бы быть, например, SQL-запрос, выданный функцией на языке PL/pgSQL.
PG_DIAG_CONTEXTХарактеристика контекста, в котором произошла ошибка. В настоящее время она включает вывод стека вызовов активных функций процедурного языка и запросов, сгенерированных внутренними модулями. Стек выводится по одному элементу в строке, при этом первым идет самый последний из элементов (самый недавний вызов).
PG_DIAG_SCHEMA_NAMEЕсли ошибка была связана с конкретным объектом базы данных, то в это поле будет записано имя схемы, содержащей данный объект.
PG_DIAG_TABLE_NAMEЕсли ошибка была связана с конкретной таблицей, то в это поле будет записано имя таблицы. (Для получения имени схемы для данной таблицы обратитесь к полю, содержащему имя схемы.)
PG_DIAG_COLUMN_NAMEЕсли ошибка была связана с конкретным столбцом таблицы, то в это поле будет записано имя столбца. (Чтобы идентифицировать таблицу, обратитесь к полям, содержащим имена схемы и таблицы.)
PG_DIAG_DATATYPE_NAMEЕсли ошибка была связана с конкретным типом данных, то в это поле будет записано имя типа данных. (Чтобы получить имя схемы, которой принадлежит этот тип данных, обратитесь к полю, содержащему имя схемы.)
PG_DIAG_CONSTRAINT_NAMEЕсли ошибка была связана с конкретным ограничением, то в это поле будет записано имя ограничения. Чтобы получить имя таблицы или домена, связанных с этим ограничением, обратитесь к полям, перечисленным выше. (С этой целью индексы рассматриваются как ограничения, даже если они и не были созданы с помощью синтаксиса для создания ограничений.)
PG_DIAG_SOURCE_FILEИмя файла, содержащего позицию в исходном коде, для которой было выдано сообщение об ошибка.
PG_DIAG_SOURCE_LINE