34.2. Управление подключениями к базе данных#
34.2. Управление подключениями к базе данных
Этот раздел описывает, как открывать, закрывать и переключаться между соединениями с базой данных.
34.2.1. Подключение к серверу базы данных
Один подключается к базе данных с помощью следующего оператора:
EXEC SQL CONNECT TOtarget
[ASconnection-name
] [USERuser-name
];
Цель target
может быть указана следующими способами:
-
dbname
[@hostname
][:port
] -
tcp:postgresql://
hostname
[:port
][/dbname
][?options
] -
unix:postgresql://localhost[:
port
][/dbname
][?options
] - SQL-строковый литерал, содержащий одну из вышеуказанных форм
- ссылка на переменную символьного типа, содержащую одну из вышеуказанных форм (см. примеры)
-
DEFAULT
Целевое подключение DEFAULT
инициирует подключение к
базе данных по умолчанию от имени пользователя по умолчанию. В этом случае нельзя указать отдельное
имя пользователя или имя подключения.
Если вы указываете цель подключения напрямую (то есть не в виде строкового литерала или ссылки на переменную), то компоненты цели проходят обычный SQL-анализ; это означает, что, например, hostname
должен выглядеть как один или несколько SQL-идентификаторов, разделенных точками, и эти идентификаторы будут приведены к нижнему регистру, если не заключены в двойные кавычки. Значения любых options
должны быть SQL-идентификаторами, целыми числами или ссылками на переменные. Конечно, вы можете поместить практически что угодно в SQL-идентификатор, заключив его в двойные кавычки.
На практике, вероятно, менее подвержено ошибкам использование строкового литерала (заключённого в апострофы) или ссылки на переменную, чем написание цели подключения напрямую.
Также существуют различные способы указать имя пользователя:
-
username
-
username
/password
username
IDENTIFIED BYpassword
username
USINGpassword
Как указано выше, параметры username
и
password
могут быть идентификатором SQL, строковым литералом SQL или ссылкой на символьную переменную.
Если цель подключения включает какие-либо options
,
они состоят из
спецификаций
,
разделенных амперсандами (keyword
=value
&
).
Разрешенные ключевые слова такие же, как и те, которые распознает
libpq (см.
Раздел 32.1.2). Пробелы игнорируются перед
любым keyword
или value
,
хотя не внутри или после них. Обратите внимание, что нет способа
записать &
внутри value
.
Обратите внимание, что при указании соединения через сокет
(с префиксом unix:
), имя хоста должно быть
точно localhost
. Чтобы выбрать нестандартный
каталог сокета, укажите путь каталога в качестве значения
опции host
в
части options
цели.
connection-name
используется для обработки
нескольких соединений в одной программе. Он можно опустить, если
программа использует только одно соединение. Самое последнее открытое
соединение становится текущим соединением, которое используется по умолчанию
при выполнении SQL-запроса (см. далее в этой
главе).
Вот несколько примеров команд CONNECT
:
EXEC SQL CONNECT TO [email protected]; EXEC SQL CONNECT TO tcp:postgresql://sql.mydomain.com/mydb AS myconnection USER john; EXEC SQL BEGIN DECLARE SECTION; const char *target = "[email protected]"; const char *user = "john"; const char *passwd = "secret"; EXEC SQL END DECLARE SECTION; ... EXEC SQL CONNECT TO :target USER :user USING :passwd; /* or EXEC SQL CONNECT TO :target USER :user/:passwd; */
Последний пример использует функцию, называемую ссылками на символьные переменные. В последующих разделах вы увидите, как переменные C могут быть использованы в SQL-запросах, если вы добавите к ним префикс двоеточия.
Будьте в курсе, что формат целевого подключения не указан в стандарте SQL. Поэтому, если вы хотите разрабатывать переносимые приложения, вам может понадобиться использовать что-то, основанное на последнем примере выше, чтобы инкапсулировать строку целевого подключения где-то.
Если ненадежные пользователи имеют доступ к базе данных, которая не приняла
безопасный шаблон использования схем,
начните каждую сессию с удаления общедоступных схем, доступных для записи,
из search_path
. Например,
добавьте options=-c search_path=
в
, или
выполните options
EXEC SQL SELECT pg_catalog.set_config('search_path', '',
false);
после подключения. Это соображение не является специфичным для
ECPG; оно применимо к любому интерфейсу для выполнения произвольных SQL-команд.
34.2.2. Выбор соединения
SQL-инструкции в программе встроенного SQL по умолчанию выполняются на текущем соединении, то есть на самом недавно открытом. Если приложению необходимо управлять несколькими соединениями, то есть три способа обработки этой ситуации.
Первый вариант - явно выбрать соединение для каждого SQL-запроса, например:
EXEC SQL AT connection-name
SELECT ...;
Этот параметр особенно подходит, если приложению необходимо использовать несколько соединений в смешанном порядке.
Если ваше приложение использует несколько потоков выполнения, они не могут одновременно использовать одно и то же соединение. Вы должны явно контролировать доступ к соединению (используя мьютексы) или использовать отдельное соединение для каждого потока.
Второй вариант - выполнить оператор для переключения текущего соединения. Этот оператор выглядит так:
EXEC SQL SET CONNECTION connection-name
;
Этот параметр особенно удобен, если требуется выполнить много операторов на одном соединении.
Вот пример программы, управляющей несколькими соединениями с базой данных:
#include <stdio.h> EXEC SQL BEGIN DECLARE SECTION; char dbname[1024]; EXEC SQL END DECLARE SECTION; int main() { EXEC SQL CONNECT TO testdb1 AS con1 USER testuser; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; EXEC SQL CONNECT TO testdb2 AS con2 USER testuser; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; EXEC SQL CONNECT TO testdb3 AS con3 USER testuser; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; /* This query would be executed in the last opened database "testdb3". */ EXEC SQL SELECT current_database() INTO :dbname; printf("current=%s (should be testdb3)\n", dbname); /* Using "AT" to run a query in "testdb2" */ EXEC SQL AT con2 SELECT current_database() INTO :dbname; printf("current=%s (should be testdb2)\n", dbname); /* Switch the current connection to "testdb1". */ EXEC SQL SET CONNECTION con1; EXEC SQL SELECT current_database() INTO :dbname; printf("current=%s (should be testdb1)\n", dbname); EXEC SQL DISCONNECT ALL; return 0; }
Этот пример произведет следующий вывод:
current=testdb3 (should be testdb3) current=testdb2 (should be testdb2) current=testdb1 (should be testdb1)
Третий вариант - это объявить идентификатор SQL, связанный с соединением, например:
EXEC SQL ATconnection-name
DECLAREstatement-name
STATEMENT; EXEC SQL PREPAREstatement-name
FROM :dyn-string
;
После того, как вы связали идентификатор SQL с соединением, вы можете выполнять динамический SQL без использования AT-предложения. Обратите внимание, что эта опция работает подобно директивам препроцессора, поэтому связь активна только в файле.
Вот пример программы, использующей эту опцию:
#include <stdio.h> EXEC SQL BEGIN DECLARE SECTION; char dbname[128]; char *dyn_sql = "SELECT current_database()"; EXEC SQL END DECLARE SECTION; int main(){ EXEC SQL CONNECT TO postgres AS con1; EXEC SQL CONNECT TO testdb AS con2; EXEC SQL AT con1 DECLARE stmt STATEMENT; EXEC SQL PREPARE stmt FROM :dyn_sql; EXEC SQL EXECUTE stmt INTO :dbname; printf("%s\n", dbname); EXEC SQL DISCONNECT ALL; return 0; }
Этот пример создаст такой вывод, даже если используется соединение по умолчанию testdb:
postgres
34.2.3. Закрытие соединения
Для закрытия соединения используйте следующий оператор:
EXEC SQL DISCONNECT [connection
];
Соединение connection
может быть указано
следующими способами:
-
connection-name
-
CURRENT
-
ALL
Если не указано имя соединения, текущее соединение закрывается.
Хорошим стилем является то, что приложение всегда явно отключается от каждого открытого соединения.