34.13. Приложения на C++
ECPG обеспечивает поддержку языка C++ в ограниченном объёме. Некоторые её особенности описаны в этом разделе.
Препроцессор ecpg
принимает входной файл, написанный на C (или языке, подобном C) со встраиваемыми командами SQL, преобразует встроенные команды SQL в конструкции языка C и в результате формирует файл .c
. Объявления библиотечных функций, вызываемых в конструкциях C, которые генерирует ecpg
, заворачиваются в блоки extern "C" { ... }
при использовании C++, так что они должны прозрачно работать в C++.
Однако вообще говоря, препроцессор ecpg
понимает только C; он не воспринимает особый синтаксис и зарезервированные слова языка C++. Поэтому какой-то код SQL, встроенный в код приложения на C++, в котором используются сложные особенности C++, может корректно не обработаться препроцессором или не работать как ожидается.
Надёжный подход к применению внедрённого кода SQL в приложении на C++ заключается в том, чтобы скрыть вызовы ECPG в модуле C, который будет вызываться приложением на C++ для работы с базой данных и который будет скомпонован с остальным кодом C++. Подробнее это описано в Подразделе 34.13.2.
34.13.1. Область видимости переменных среды
Препроцессор ecpg
имеет понимание области видимости переменных в C. С языком C это довольно просто, так как область видимости переменных определяется их блоками кода. В C++, однако, переменные-члены класса задействуются не в том блоке кода, в каком они объявлены, так что препроцессор ecpg
не сможет корректно определить область видимости таких переменных.
Например, в следующем случае препроцессор ecpg
не сможет найти определение переменной dbname
в методе test
, так что произойдёт ошибка.
class TestCpp { EXEC SQL BEGIN DECLARE SECTION; char dbname[1024]; EXEC SQL END DECLARE SECTION; public: TestCpp(); void test(); ~TestCpp(); }; TestCpp::TestCpp() { EXEC SQL CONNECT TO testdb1; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; } void Test::test() { EXEC SQL SELECT current_database() INTO :dbname; printf("current_database = %s\n", dbname); } TestCpp::~TestCpp() { EXEC SQL DISCONNECT ALL; }
При обработке данного кода будет выдано сообщение:
ecpg test_cpp.pgc
test_cpp.pgc:28: ERROR: variable "dbname" is not declared
(test_cpp.pgc:28: ОШИБКА: переменная "dbname" не объявлена)
Для решения этой проблемы можно немного изменить метод test
и задействовать в нём локальную переменную для промежуточного хранения. Но предложенный подход нельзя считать хорошим, так как это портит код и снижает производительность.
void TestCpp::test() { EXEC SQL BEGIN DECLARE SECTION; char tmp[1024]; EXEC SQL END DECLARE SECTION; EXEC SQL SELECT current_database() INTO :tmp; strlcpy(dbname, tmp, sizeof(tmp)); printf("current_database = %s\n", dbname); }
34.13.2. Разработка приложения на C++ с внешним модулем на C
Если вы поняли технические ограничения препроцессора ecpg
с C++, вы можете прийти к заключению, что для использования ECPG в приложениях на C++ лучше связывать код C с кодом C++ на стадии компоновки, а не внедрять команды SQL непосредственно в код на C++. В данном разделе показывается, как отделить встраиваемые команды SQL от кода приложения на C++, на простом примере. В этом примере приложение реализуется на C++, а взаимодействие с сервером Postgres Pro построено на C и ECPG.
Для сборки нужно создать три типа файлов: файл на C (*.pgc
), заголовочный файл и файл на C++:
test_mod.pgc
Модуль подпрограмм будет выполнять SQL-команды, встроенные в C. Этот код нужно будет преобразовать в
test_mod.c
с помощью препроцессора.#include "test_mod.h" #include <stdio.h> void db_connect() { EXEC SQL CONNECT TO testdb1; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; } void db_test() { EXEC SQL BEGIN DECLARE SECTION; char dbname[1024]; EXEC SQL END DECLARE SECTION; EXEC SQL SELECT current_database() INTO :dbname; printf("current_database = %s\n", dbname); } void db_disconnect() { EXEC SQL DISCONNECT ALL; }
test_mod.h
Заголовочный файл с объявлениями функций в модуле на языке C (
test_mod.pgc
). Он включается вtest_cpp.cpp
. Объявления в этом файле должны заключаться в блокextern "C"
, так как он будет связываться с модулем C++.#ifdef __cplusplus extern "C" { #endif void db_connect(); void db_test(); void db_disconnect(); #ifdef __cplusplus } #endif
test_cpp.cpp
Основной код приложения, содержащий функцию
main
, а также, в данном примере, класс C++.#include "test_mod.h" class TestCpp { public: TestCpp(); void test(); ~TestCpp(); }; TestCpp::TestCpp() { db_connect(); } void TestCpp::test() { db_test(); } TestCpp::~TestCpp() { db_disconnect(); } int main(void) { TestCpp *t = new TestCpp(); t->test(); return 0; }
Для сборки приложения проделайте следующее. Преобразуйте test_mod.pgc
в test_mod.c
с помощью ecpg
, а затем получите test_mod.o
, скомпилировав test_mod.c
компилятором C:
ecpg -o test_mod.c test_mod.pgc cc -c test_mod.c -o test_mod.o
После этого получите test_cpp.o
, скомпилировав test_cpp.cpp
компилятором C++:
c++ -c test_cpp.cpp -o test_cpp.o
Наконец, свяжите полученные объектные файлы, test_cpp.o
и test_mod.o
, в один исполняемый файл, выполнив компоновку под управлением компилятора C++:
c++ test_cpp.o test_mod.o -lecpg -o test_cpp
33.7. Canceling Queries in Progress
A client application can request cancellation of a command that is still being processed by the server, using the functions described in this section.
PQgetCancel
Creates a data structure containing the information needed to cancel a command issued through a particular database connection.
PGcancel *PQgetCancel(PGconn *conn);
PQgetCancel
creates aPGcancel
object given aPGconn
connection object. It will returnNULL
if the givenconn
isNULL
or an invalid connection. ThePGcancel
object is an opaque structure that is not meant to be accessed directly by the application; it can only be passed toPQcancel
orPQfreeCancel
.PQfreeCancel
Frees a data structure created by
PQgetCancel
.void PQfreeCancel(PGcancel *cancel);
PQfreeCancel
frees a data object previously created byPQgetCancel
.PQcancel
Requests that the server abandon processing of the current command.
int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize);
The return value is 1 if the cancel request was successfully dispatched and 0 if not. If not,
errbuf
is filled with an explanatory error message.errbuf
must be a char array of sizeerrbufsize
(the recommended size is 256 bytes).Successful dispatch is no guarantee that the request will have any effect, however. If the cancellation is effective, the current command will terminate early and return an error result. If the cancellation fails (say, because the server was already done processing the command), then there will be no visible result at all.
PQcancel
can safely be invoked from a signal handler, if theerrbuf
is a local variable in the signal handler. ThePGcancel
object is read-only as far asPQcancel
is concerned, so it can also be invoked from a thread that is separate from the one manipulating thePGconn
object.
PQrequestCancel
PQrequestCancel
is a deprecated variant ofPQcancel
.int PQrequestCancel(PGconn *conn);
Requests that the server abandon processing of the current command. It operates directly on the
PGconn
object, and in case of failure stores the error message in thePGconn
object (whence it can be retrieved byPQerrorMessage
). Although the functionality is the same, this approach is not safe within multiple-thread programs or signal handlers, since it is possible that overwriting thePGconn
's error message will mess up the operation currently in progress on the connection.