Документация по PostgreSQL 9.4.1 | |||
---|---|---|---|
Пред. | Уровень выше | Глава 31. libpq — библиотека для языка C | След. |
31.21. Примеры программ
Эти и другие примеры можно найти в каталоге src/test/examples в дистрибутиве исходного кода.
Пример 31-1. Первая программа, демонстрирующая использование libpq
/* * testlibpq.c * * Тестирование libpq (версии для C), клиентской библиотеки PostgreSQL. */ #include <stdio.h> #include <stdlib.h> #include <libpq-fe.h> static void exit_nicely(PGconn *conn) { PQfinish(conn); exit(1); } int main(int argc, char **argv) { const char *conninfo; PGconn *conn; PGresult *res; int nFields; int i, j; /* * Если пользователь передаёт параметр в командной строке, использовать его как * строку соединения; иначе принять по умолчанию dbname=postgres и использовать * переменные среды или значения по умолчанию для всех других параметров соединения. */ if (argc > 1) conninfo = argv[1]; else conninfo = "dbname = postgres"; /* Установить подключение к базе данных */ conn = PQconnectdb(conninfo); /* Убедиться, что соединение с сервером установлено успешно */ if (PQstatus(conn) != CONNECTION_OK) { fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn)); exit_nicely(conn); } /* * В нашем тестовом сценарии задействуется курсор, а работать с ним нужно * в блоке транзакции. Мы могли бы сделать всё то же самое, обойдясь одним * PQexec() с запросом "select * from pg_database", но это было бы * слишком просто для хорошего примера. */ /* Начать блок транзакции */ res = PQexec(conn, "BEGIN"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } /* * Когда PGresult становится не нужен, его нужно очистить (используя PQclear) * во избежание утечек памяти */ PQclear(res); /* * Выбрать строки из pg_database, системного каталога баз данных */ res = PQexec(conn, "DECLARE myportal CURSOR FOR select * from pg_database"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } PQclear(res); res = PQexec(conn, "FETCH ALL in myportal"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } /* сначала напечатать имена атрибутов */ nFields = PQnfields(res); for (i = 0; i < nFields; i++) printf("%-15s", PQfname(res, i)); printf("\n\n"); /* затем напечатать строки */ for (i = 0; i < PQntuples(res); i++) { for (j = 0; j < nFields; j++) printf("%-15s", PQgetvalue(res, i, j)); printf("\n"); } PQclear(res); /* закрыть портал ... ошибки нас больше не интересуют ... */ res = PQexec(conn, "CLOSE myportal"); PQclear(res); /* завершить транзакцию */ res = PQexec(conn, "END"); PQclear(res); /* закрыть подключение к базе данных и провести очистку */ PQfinish(conn); return 0; }
Пример 31-2. Вторая программа, демонстрирующая использование libpq
/* * testlibpq2.c * Тестирование интерфейса асинхронных уведомлений * * Запустите эту программу, затем в psql в другом окне выполните * NOTIFY TBL2; * Повторите эту команду четыре раза, чтобы программа завершилась. * * Либо, если хочется чего-то поинтереснее, попробуйте следующее: * подготовьте базу данных следующими командами * (они содержатся в src/test/examples/testlibpq2.sql): * * CREATE TABLE TBL1 (i int4); * * CREATE TABLE TBL2 (i int4); * * CREATE RULE r1 AS ON INSERT TO TBL1 DO * (INSERT INTO TBL2 VALUES (new.i); NOTIFY TBL2); * * и проделайте это четыре раза: * * INSERT INTO TBL1 VALUES (10); */ #ifdef WIN32 #include <windows.h> #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/time.h> #include <sys/types.h> #include "libpq-fe.h" static void exit_nicely(PGconn *conn) { PQfinish(conn); exit(1); } int main(int argc, char **argv) { const char *conninfo; PGconn *conn; PGresult *res; PGnotify *notify; int nnotifies; /* * Если пользователь передаёт параметр в командной строке, использовать его как * строку соединения; иначе принять по умолчанию dbname=postgres и использовать * переменные среды или значения по умолчанию для всех других параметров соединения. */ if (argc > 1) conninfo = argv[1]; else conninfo = "dbname = postgres"; /* Установить подключение к базе данных */ conn = PQconnectdb(conninfo); /* Убедиться, что соединение с сервером установлено успешно */ if (PQstatus(conn) != CONNECTION_OK) { fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn)); exit_nicely(conn); } /* * Выполнить команду LISTEN для получения уведомлений, исходящих от NOTIFY в правиле. */ res = PQexec(conn, "LISTEN TBL2"); if (PQresultStatus(res) != PGRES_COMMAND_OK) { fprintf(stderr, "LISTEN command failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } /* * Когда PGresult становится не нужен, его нужно очистить (используя PQclear) * во избежание утечек памяти */ PQclear(res); /* Выйти после получения четырёх уведомлений. */ nnotifies = 0; while (nnotifies < 4) { /* * Подождать, пока что-либо не произойдёт с соединением. Мы ожидаем данные, * используя select(2), но вы можете также использовать poll() или подобные * средства. */ int sock; fd_set input_mask; sock = PQsocket(conn); if (sock < 0) break; /* этого не должно случиться */ FD_ZERO(&input_mask); FD_SET(sock, &input_mask); if (select(sock + 1, &input_mask, NULL, NULL, NULL) < 0) { fprintf(stderr, "select() failed: %s\n", strerror(errno)); exit_nicely(conn); } /* Теперь нужно обработать поступивший сигнал */ PQconsumeInput(conn); while ((notify = PQnotifies(conn)) != NULL) { fprintf(stderr, "ASYNC NOTIFY of '%s' received from backend PID %d\n", notify->relname, notify->be_pid); PQfreemem(notify); nnotifies++; } } fprintf(stderr, "Done.\n"); /* закрыть подключение к базе данных и провести очистку */ PQfinish(conn); return 0; }
Пример 31-3. Третья программа, демонстрирующая использование libpq
/* * testlibpq3.c * Тестирование передачи параметров и двоичного ввода/вывода. * * Прежде чем запускать эту программу, наполните базу данных, выполнив следующие команды * (они содержатся в src/test/examples/testlibpq3.sql): * * CREATE TABLE test1 (i int4, t text, b bytea); * * INSERT INTO test1 values (1, 'joe''s place', '\\000\\001\\002\\003\\004'); * INSERT INTO test1 values (2, 'ho there', '\\004\\003\\002\\001\\000'); * * Ожидаемый вывод: * * tuple 0: got * i = (4 bytes) 1 * t = (11 bytes) 'joe's place' * b = (5 bytes) \000\001\002\003\004 * * tuple 0: got * i = (4 bytes) 2 * t = (8 bytes) 'ho there' * b = (5 bytes) \004\003\002\001\000 */ #ifdef WIN32 #include <windows.h> #endif #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <sys/types.h> #include "libpq-fe.h" /* for ntohl/htonl */ #include <netinet/in.h> #include <arpa/inet.h> static void exit_nicely(PGconn *conn) { PQfinish(conn); exit(1); } /* * Эта функция печатает результат запроса, полученный в двоичном формате * из таблицы, определённой как показано в комментарии выше. Мы выделили её, * потому что функция main() делает это дважды. */ static void show_binary_results(PGresult *res) { int i, j; int i_fnum, t_fnum, b_fnum; /* PQfnumber используется, чтобы не привязываться к порядку полей в результате */ i_fnum = PQfnumber(res, "i"); t_fnum = PQfnumber(res, "t"); b_fnum = PQfnumber(res, "b"); for (i = 0; i < PQntuples(res); i++) { char *iptr; char *tptr; char *bptr; int blen; int ival; /* Получить значения полей (мы не учитываем то, что они могут быть равны NULL!) */ iptr = PQgetvalue(res, i, i_fnum); tptr = PQgetvalue(res, i, t_fnum); bptr = PQgetvalue(res, i, b_fnum); /* * INT4 представляется в двоичном виде в сетевом порядке байт, так что лучше * привести это значение к локальному порядку байт. */ ival = ntohl(*((uint32_t *) iptr)); /* * Двоичное представление типа TEXT — это ... текст, и так как libpq * довольно мило добавила в конец строки нулевой байт, с результатом можно * прекрасно работать как со строкой в стиле C. * * Двоичное представление BYTEA — это набор байт, который может включать и * нулевые байты, так что мы должны смотреть на длину поля. */ blen = PQgetlength(res, i, b_fnum); printf("tuple %d: got\n", i); printf(" i = (%d bytes) %d\n", PQgetlength(res, i, i_fnum), ival); printf(" t = (%d bytes) '%s'\n", PQgetlength(res, i, t_fnum), tptr); printf(" b = (%d bytes) ", blen); for (j = 0; j < blen; j++) printf("\\%03o", bptr[j]); printf("\n\n"); } } int main(int argc, char **argv) { const char *conninfo; PGconn *conn; PGresult *res; const char *paramValues[1]; int paramLengths[1]; int paramFormats[1]; uint32_t binaryIntVal; /* * Если пользователь передаёт параметр в командной строке, использовать его как * строку соединения; иначе принять по умолчанию dbname=postgres и использовать * переменные среды или значения по умолчанию для всех других параметров соединения. */ if (argc > 1) conninfo = argv[1]; else conninfo = "dbname = postgres"; /* Установить подключение к базе данных */ conn = PQconnectdb(conninfo); /* Убедиться, что соединение с сервером установлено успешно */ if (PQstatus(conn) != CONNECTION_OK) { fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn)); exit_nicely(conn); } /* * Эта программа призвана проиллюстрировать использование PQexecParams() с * внешними параметрами, а также двоичную передачу данных. * * Первый пример передаёт параметры в текстовом формате, а результаты получает * в двоичном. Но используя внешние параметры, мы можем избежать * множества кропотливых операций, связанных с экранированием и кавычками, даже * когда данные текстовые. Обратите внимание, нам не пришлось делать ничего особенного * с апострофом, содержащимся в значении параметра. */ /* Это значение параметра, поступающее извне */ paramValues[0] = "joe's place"; res = PQexecParams(conn, "SELECT * FROM test1 WHERE t = $1", 1, /* один параметр */ NULL, /* пусть его тип сервер определит сам */ paramValues, NULL, /* длины параметров не задаются, так параметр текстовый */ NULL, /* по умолчанию все параметры текстовые */ 1); /* результаты запрашиваются в двоичном виде */ if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } show_binary_results(res); PQclear(res); /* * Во втором примере мы передаём целочисленный параметр в двоичной форме * и результаты получаем так же в двоичном виде. * * Хотя мы говорим PQexecParams, что оставляем выбор типа параметра * на усмотрение сервера, на самом деле мы подталкиваем его к нужному решению, * выполняя приведение символа в тексте запроса. Это хорошая подстраховка при * передаче двоичных параметров. */ /* Перевести целочисленное значение "2" в сетевой порядок байт */ binaryIntVal = htonl((uint32_t) 2); /* Сформировать массивы параметров для PQexecParams */ paramValues[0] = (char *) &binaryIntVal; paramLengths[0] = sizeof(binaryIntVal); paramFormats[0] = 1; /* binary */ res = PQexecParams(conn, "SELECT * FROM test1 WHERE i = $1::int4", 1, /* один параметр */ NULL, /* пусть его тип сервер определит сам */ paramValues, paramLengths, paramFormats, 1); /* результаты запрашиваются в двоичном виде */ if (PQresultStatus(res) != PGRES_TUPLES_OK) { fprintf(stderr, "SELECT failed: %s", PQerrorMessage(conn)); PQclear(res); exit_nicely(conn); } show_binary_results(res); PQclear(res); /* закрыть подключение к базе данных и провести очистку */ PQfinish(conn); return 0; }
Пред. | Начало | След. |
Сборка программ с libpq | Уровень выше | Большие объекты |