33.5. Динамический SQL#

33.5. Динамический SQL

33.5. Динамический SQL #

Во многих случаях конкретные SQL-запросы, которые должно выполнить приложение, известны на момент написания приложения. Однако в некоторых случаях SQL-запросы формируются во время выполнения или предоставляются внешним источником. В таких случаях нельзя вставить SQL-запросы непосредственно в исходный код на языке C, но есть возможность вызвать произвольные SQL-запросы, которые вы предоставляете в виде строки переменной.

33.5.1. Выполнение операторов без получения результирующего набора #

Самый простой способ выполнить произвольный SQL-оператор - использовать команду EXECUTE IMMEDIATE. Например:

EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "CREATE TABLE test1 (...);";
EXEC SQL END DECLARE SECTION;

EXEC SQL EXECUTE IMMEDIATE :stmt;

EXECUTE IMMEDIATE может использоваться для выполнения SQL-запросов, которые не возвращают набор результатов (например, DDL, INSERT, UPDATE, DELETE). Вы не можете выполнить запросы, которые извлекают данные (например, SELECT) таким образом. Следующий раздел описывает, как это сделать.

33.5.2. Выполнение оператора с входными параметрами #

Более мощный способ выполнения произвольных SQL-запросов - это подготовить их один раз и выполнять подготовленный запрос столько раз, сколько вам нужно. Также возможно подготовить обобщенную версию запроса и затем выполнять конкретные версии, заменяя параметры. При подготовке запроса, вместо параметров следует написать вопросительные знаки. Например:

EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "INSERT INTO test1 VALUES(?, ?);";
EXEC SQL END DECLARE SECTION;

EXEC SQL PREPARE mystmt FROM :stmt;
 ...
EXEC SQL EXECUTE mystmt USING 42, 'foobar';

Когда вам больше не нужен подготовленный запрос, вы должны его деаллоцировать:

EXEC SQL DEALLOCATE PREPARE name;

33.5.3. Выполнение оператора с набором результатов #

Для выполнения SQL-запроса с одной строкой результата можно использовать команду EXECUTE. Чтобы сохранить результат, добавьте предложение INTO.

EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
int v1, v2;
VARCHAR v3[50];
EXEC SQL END DECLARE SECTION;

EXEC SQL PREPARE mystmt FROM :stmt;
 ...
EXEC SQL EXECUTE mystmt INTO :v1, :v2, :v3 USING 37;

Команда EXECUTE может иметь предложение INTO, предложение USING, оба предложения или ни одного из них.

Если ожидается, что запрос вернет более одной строки результата, следует использовать курсор, как показано в следующем примере. (См. Раздел 33.3.2 для получения более подробной информации о курсоре).

EXEC SQL BEGIN DECLARE SECTION;
char dbaname[128];
char datname[128];
char *stmt = "SELECT u.usename as dbaname, d.datname "
             "  FROM pg_database d, pg_user u "
             "  WHERE d.datdba = u.usesysid";
EXEC SQL END DECLARE SECTION;

EXEC SQL CONNECT TO testdb AS con1 USER testuser;
EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;

EXEC SQL PREPARE stmt1 FROM :stmt;

EXEC SQL DECLARE cursor1 CURSOR FOR stmt1;
EXEC SQL OPEN cursor1;

EXEC SQL WHENEVER NOT FOUND DO BREAK;

while (1)
{
    EXEC SQL FETCH cursor1 INTO :dbaname,:datname;
    printf("dbaname=%s, datname=%s\n", dbaname, datname);
}

EXEC SQL CLOSE cursor1;

EXEC SQL COMMIT;
EXEC SQL DISCONNECT ALL;