×
Мы обрабатываем cookies, чтобы сделать наш сайт удобнее и персонализированнее для вас. Подробнее: политика использования «cookies» и «политики конфиденциальности».

Для самостоятельной настройки ознакомьтесь с инструкцией

Дополнительные настройки cookies в браузерах

Файлы cookie автоматически загружаются в ваш браузер при посещении веб-сайта. У вас есть возможность управлять этими файлами. Если Вы не согласны с использованием файлов cookies, запретите их сохранение на своём устройстве, удалите уже имеющиеся файлы cookies через настройки браузера или прекратите использование сайта.

При отключении обработки cookie наш сайт продолжит функционировать, однако будут использоваться исключительно необходимые технические файлы, без которых работа ресурса невозможна.

Инструкция по отключению cookies
Принять
Настроить
Отклонить

ДОКУМЕНТАЦИЯ

Выберите версию, форк и язык для СУБД Digital Q.DataBase, чтобы прочитать или скачать всю документацию.
Техподдержка
Документация
Диасофт
Авторские права © 2016–2025 ООО "Диасофт Экосистема"
Скачать всю документацию:

4.3.4. Использование главных переменных

4.3.4.1. Обзор
4.3.4.2. Declare Sections
4.3.4.3. Получение результатов запроса
4.3.4.4. Сопоставление типов
4.3.4.5. Обработка непримитивных типов данных SQL
4.3.4.6. Индикаторы

В Раздел 4.3.3 вы видели, как можно выполнять операторы SQL из программы с внедренным SQL. Некоторые из этих операторов использовали только фиксированные значения и не предоставляли возможности вставки пользовательских значений в операторы или обработки программой значений, возвращаемых запросом. Подобные операторы не имеют практической ценности в реальных приложениях. В данном разделе подробно объясняется, как можно передавать данные между программой на языке C и внедренными операторами SQL с помощью простого механизма под названием главные переменные. В программе с внедренным SQL операторы SQL рассматриваются как «гости» в программном коде на языке C, который является главным языком. Поэтому переменные программы на языке C называются главными переменными.

Другим способом обмена значениями между серверными процессами PostgreSQL и приложениями препроцессора ECPG является использование дескрипторов SQL, описанных в Раздел 4.3.7.

4.3.4.1. Обзор #

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

EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2);

Данный оператор ссылается на две C-переменные с именами v1 и v2 а также использует обычный строковый литерал SQL, чтобы продемонстрировать отсутствие ограничений на использование только одного типа данных.

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

4.3.4.2. Declare Sections #

Для передачи данных из программы в базу данных (например, в качестве параметров запроса) или обратно из базы данных в программу, соответствующие C-переменные должны быть объявлены в специально обозначенных секциях. Это необходимо для того, чтобы препроцессор внедренного SQL мог их идентифицировать.

Данная секция начинается с оператора:

EXEC SQL BEGIN DECLARE SECTION;

и заканчивается оператором:

EXEC SQL END DECLARE SECTION;

Между этими строками должны располагаться обычные объявления переменных на языке C, например:

int   x = 4;
char  foo[16], bar[16];

Как можно заметить, переменной можно по желанию присвоить начальное значение. Область видимости переменной определяется местом расположения секции объявлений DECLARE SECTION в программе. Вы также можете объявлять переменные, используя следующий синтаксис, который неявно создает секцию объявлений:

EXEC SQL int i = 4;

В программе можно использовать любое количество секций объявлений.

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

Определение структуры или объединения также должно быть приведено внутри DECLARE секции. В противном случае препроцессор не сможет обработать эти типы данных, так как ему не будет известно их определение.

4.3.4.3. Получение результатов запроса #

Теперь вы должны иметь возможность передавать данные, сформированные вашей программой, в команду SQL. Но как получить результаты запроса? Для этой цели внедренный SQL предоставляет специальные варианты обычных команд SELECT и FETCH. Данные команды имеют специальное предложение INTO предложение, которое определяет, в каких главных переменных должны быть сохранены полученные значения. SELECT используется для запроса, возвращающего только одну строку, а FETCH используется для запроса, который возвращает несколько строк с использованием курсора.

Ниже приведен пример:

/*
 * предполагается наличие такой таблицы:
 * CREATE TABLE test1 (a int, b varchar(50));
 */

EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;

Таким образом, предложение INTO предложение располагается между списком SELECT и предложение FROM предложение. Количество элементов в списке SELECT и списке после предложение INTO (также называемом целевым списком) должно быть одинаковым.

Ниже приведен пример использования команды FETCH:

EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test;

 ...

do
{
    ...
    EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
    ...
} while (...);

Здесь предложение INTO предложение указывается после всех стандартных предложений.

4.3.4.4. Сопоставление типов #

Когда приложения, использующие препроцессор ECPG, обмениваются значениями между сервером PostgreSQL и приложением на языке C, например, при получении результатов запроса от сервера или выполнении операторов SQL с входными параметрами, значения должны преобразовываться между типами данных PostgreSQL и типами переменных основного языка (в данном случае типами данных языка C). Одно из основных преимуществ препроцессора ECPG заключается в том, что в большинстве случаев это преобразование выполняется автоматически.

В этом отношении существует два вида типов данных: некоторые простые типы данных PostgreSQL, такие как integer и text, могут считываться и записываться приложением напрямую. К другим типам данных PostgreSQL, таким как timestamp и тип numeric доступ возможен только через специальные функции библиотеки; см. Раздел 4.3.4.4.2.

Таблица 4.3.1 показывает, какие типы данных PostgreSQL соответствуют определенным типам данных C. При необходимости отправить или получить значение определенного типа данных PostgreSQL, следует объявить переменную C соответствующего типа в секции объявлений DECLARE SECTION.

Таблица 4.3.1. Соответствие между типами данных PostgreSQL и типами переменных C

Тип данных PostgreSQLТип главной переменной
smallintshort
integerint
bigintlong long int
тип данных decimalтип данных decimal[a]
тип numericтип numeric[a]
realfloat
double precisionтип данных double
smallserialshort
serialint
bigseriallong long int
тип oidunsigned int
character(n), varchar(n), textchar[n+1], VARCHAR[n+1]
имяchar[NAMEDATALEN]
timestamptimestamp[a]
тип данных intervalтип данных interval[a]
datedate[a]
booleanbool[b]
byteachar *, bytea[n]

[a] Доступ к данному типу возможен только через специальные библиотечные функции; см. Раздел 4.3.4.4.2.

[b] объявлено в ecpglib.h если не является встроенным


4.3.4.4.1. Обработка символьных строк #

Для обработки строковых типов данных SQL, таких как varchar и text, существует два возможных способа объявления главных переменных.

Первый способ заключается в использовании char[], массив тип данных char, что является наиболее распространенным способом обработки символьных данных в языке C.

EXEC SQL BEGIN DECLARE SECTION;
    char str[50];
EXEC SQL END DECLARE SECTION;

Обратите внимание, что вы должны самостоятельно контролировать длину данных. Если вы используете данную главную переменную в качестве целевой переменной запроса, возвращающего строку длиной более 49 символов, произойдет переполнение буфера.

Другой способ заключается в использовании типа данных VARCHAR тип, который является специальным типом, предоставляемым препроцессором ECPG. Определение массива типа VARCHAR преобразуется в именованный struct для каждой переменной. Объявление вида:

VARCHAR var[180];

преобразуется в:

struct varchar_var { int len; char arr[180]; } var;

Элемент структуры arr содержит строку, включая завершающий нулевой байт. Таким образом, для хранения строки в VARCHAR главной переменной, ее необходимо объявить с указанием длины, включающей завершающий нулевой байт. Элемент len содержит длину строки, хранящейся в arr без завершающего нулевого байта. Если главная переменная используется в качестве входного параметра для запроса и значения strlen(arr) и len различаются, то используется меньшее из них.

VARCHAR может быть записано в верхнем или нижнем регистре, но не в смешанном.

тип данных char и VARCHAR главные переменные также могут содержать значения других типов SQL, которые будут храниться в их строковом представлении.

4.3.4.4.2. Доступ к специальным типам данных #

препроцессор ECPG включает в себя специальные типы, упрощающие взаимодействие с определенными специальными типами данных сервера PostgreSQL. В частности, реализована поддержка для тип numeric, тип данных decimal, date, timestamp, и тип данных interval типов. Эти типы данных нельзя эффективно сопоставить с примитивными типами главных переменных (такими как int, long long int, или char[]), поскольку они имеют сложную внутреннюю структуру. Приложения работают с данными типами, объявляя главные переменные специальных типов и обращаясь к ним при помощи функций библиотеки pgtypes. Библиотека pgtypes, подробно описанная в Раздел 4.3.6 содержит основные функции для работы с этими типами данных, благодаря чему не требуется отправлять запрос на сервер SQL, например, только для того, чтобы прибавить тип данных interval к метке времени.

В следующих подразделах описываются эти специальные типы данных. Для получения более подробной информации о функциях библиотеки pgtypes см. Раздел 4.3.6.

4.3.4.4.2.1. timestamp, date #

Ниже приведен шаблон для обработки timestamp переменных в приложении на основе препроцессора ECPG.

Во-первых, программа должна включать заголовочный файл для timestamp типа данных:

#include 

Затем объявите главную переменную типа timestamp в секции объявлений DECLARE SECTION:

EXEC SQL BEGIN DECLARE SECTION;
timestamp ts;
EXEC SQL END DECLARE SECTION;

После считывания значения в главную переменную обработайте его с помощью функций библиотеки pgtypes. В следующем примере timestamp значение преобразуется в текстовую форму (ASCII) с помощью PGTYPEStimestamp_to_asc() функция:

EXEC SQL SELECT now()::timestamp INTO :ts;

printf("ts = %s\n", PGTYPEStimestamp_to_asc(ts));

Этот пример выведет результат, подобный следующему:

ts = 2010-06-27 18:03:56.949343

Кроме того, аналогичным образом можно обрабатывать тип DATE. Программа должна включать заголовочный файл pgtypes_date.h, объявлять главную переменную типа date и преобразовывать значение DATE в текстовую форму, используя PGTYPESdate_to_asc() функцию. Для получения подробных сведений о функциях библиотеки pgtypes см. раздел Раздел 4.3.6.

4.3.4.4.2.2. тип данных interval #

Обработка типа тип данных interval тип также аналогичен timestamp и date типов. Однако для значения типа тип данных interval требуется явно выделять память. Другими словами, память под переменную должна выделяться в куче, а не в стеке.

Ниже приведен пример программы:

#include 
#include 
#include 

int
main(void)
{
EXEC SQL BEGIN DECLARE SECTION;
    interval *in;
EXEC SQL END DECLARE SECTION;

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

    in = PGTYPESinterval_new();
    EXEC SQL SELECT '1 min'::interval INTO :in;
    printf("interval = %s\n", PGTYPESinterval_to_asc(in));
    PGTYPESinterval_free(in);

    EXEC SQL COMMIT;
    EXEC SQL DISCONNECT ALL;
    return 0;
}

4.3.4.4.2.3. тип numeric, тип данных decimal #

Обработка типа тип numeric и тип данных decimal типов аналогична работе с тип данных interval типом данных: необходимо объявить указатель, выделить объем памяти в куче и обращаться к переменной, используя функции библиотеки pgtypes. Для получения подробной информации о функциях библиотеки pgtypes см. Раздел 4.3.6.

Специальные функции не предусмотрены для тип данных decimal типа. Приложению необходимо преобразовать его в тип numeric переменную с помощью функции библиотеки pgtypes для выполнения дальнейшей обработки.

Ниже представлен пример программы для обработки тип numeric и тип данных decimal переменных типа.

#include 
#include 
#include 

EXEC SQL WHENEVER SQLERROR STOP;

int
main(void)
{
EXEC SQL BEGIN DECLARE SECTION;
    numeric *num;
    numeric *num2;
    decimal *dec;
EXEC SQL END DECLARE SECTION;

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

    num = PGTYPESnumeric_new();
    dec = PGTYPESdecimal_new();

    EXEC SQL SELECT 12.345::numeric(4,2), 23.456::decimal(4,2) INTO :num, :dec;

    printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 0));
    printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 1));
    printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 2));

    /* Преобразование типа данных decimal в тип numeric для отображения значения decimal. */
    num2 = PGTYPESnumeric_new();
    PGTYPESnumeric_from_decimal(dec, num2);

    printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 0));
    printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 1));
    printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 2));

    PGTYPESnumeric_free(num2);
    PGTYPESdecimal_free(dec);
    PGTYPESnumeric_free(num);

    EXEC SQL COMMIT;
    EXEC SQL DISCONNECT ALL;
    return 0;
}

4.3.4.4.2.4. bytea #

Обработка типа bytea тип аналогичен типу VARCHAR. Определение массива типа bytea преобразуется в именованную структуру для каждой переменной. Объявление вида:

bytea var[180];

преобразуется в:

struct bytea_var { int len; char arr[180]; } var;

Поле структуры arr хранит данные в двоичном формате. Оно также может обрабатывать '\0' как часть данных, в отличие от VARCHAR. Данные преобразуются из/в шестнадцатеричный формат и отправляются/принимаются библиотекой ecpglib.

Примечание

bytea переменная может использоваться только тогда, когда bytea_output установлено значение hex.

4.3.4.4.3. Главные переменные со сложными типами данных #

В качестве главной переменной можно также использовать массивы, определения типов (typedef), структуры и указатели.

4.3.4.4.3.1. Массивы #

Существует два варианта использования массивов в качестве главных переменных. Первый способ — это хранение текстовых строк в типах данных char[] или VARCHAR[], как описано в Раздел 4.3.4.4.1. Второй вариант использования заключается в получении нескольких строк из результата запроса без использования курсора. Без использования массива для обработки результата запроса, состоящего из нескольких строк, требуется использовать курсор и FETCH команду. Однако при использовании главных переменных в виде массивов можно получить сразу несколько строк. Длина массива должна быть определена так, чтобы она позволяла вместить все строки, иначе, скорее всего, произойдет переполнение буфера.

Следующий пример сканирует pg_database системный каталог pg_database и выводит все значения типа oid и имена доступных баз данных:

int
main(void)
{
EXEC SQL BEGIN DECLARE SECTION;
    int dbid[8];
    char dbname[8][16];
    int i;
EXEC SQL END DECLARE SECTION;

    memset(dbname, 0, sizeof(char)* 16 * 8);
    memset(dbid, 0, sizeof(int) * 8);

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

    /* Retrieve multiple rows into arrays at once. */
    EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database;

    for (i = 0; i < 8; i++)
        printf("oid=%d, dbname=%s\n", dbid[i], dbname[i]);

    EXEC SQL COMMIT;
    EXEC SQL DISCONNECT ALL;
    return 0;
}

Данный пример демонстрирует следующий результат. (Точные значения зависят от локальных условий.)

oid=1, dbname=template1
oid=11510, dbname=template0
oid=11511, dbname=postgres
oid=313780, dbname=testdb
oid=0, dbname=
oid=0, dbname=
oid=0, dbname=

4.3.4.4.3.2. Структуры #

Для одновременного получения значений нескольких столбцов можно использовать структуру, имена членов которой соответствуют именам столбцов в результате запроса. Структура позволяет обрабатывать значения нескольких столбцов в одной главной переменной.

В следующем примере извлекаются типы oid, имена и размеры доступных баз данных из pg_database системной таблицы с использованием функции pg_database_size() функцию. В данном примере переменная типа структура dbinfo_t с элементами, имена которых соответствуют каждому столбцу в SELECT результат используется для получения одной строки результата без размещения нескольких главных переменных в FETCH операторе.

EXEC SQL BEGIN DECLARE SECTION;
    typedef struct
    {
       int oid;
       char datname[65];
       long long int size;
    } dbinfo_t;

    dbinfo_t dbval;
EXEC SQL END DECLARE SECTION;

    memset(&dbval, 0, sizeof(dbinfo_t));

    EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database;
    EXEC SQL OPEN cur1;

    /* когда достигнут конец набора результатов, выход из цикла while */
    EXEC SQL WHENEVER NOT FOUND DO BREAK;

    while (1)
    {
        /* Выборка нескольких столбцов в одну структуру. */
        EXEC SQL FETCH FROM cur1 INTO :dbval;

        /* Вывод полей структуры. */
        printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, dbval.size);
    }

    EXEC SQL CLOSE cur1;

В данном примере демонстрируется следующий результат. (Точные значения зависят от локальных условий.)

oid=1, datname=template1, size=4324580
oid=11510, datname=template0, size=4243460
oid=11511, datname=postgres, size=4324580
oid=313780, datname=testdb, size=8183012

Главные переменные-структуры «принимать» столько же столбцов, сколько полей содержит структура. Дополнительные столбцы могут быть присвоены другим главным переменным. Например, приведенную выше программу можно перестроить следующим образом, при этом size будет находиться вне структуры:

EXEC SQL BEGIN DECLARE SECTION;
    typedef struct
    {
       int oid;
       char datname[65];
    } dbinfo_t;

    dbinfo_t dbval;
    long long int size;
EXEC SQL END DECLARE SECTION;

    memset(amp;dbval, 0, sizeof(dbinfo_t));

    EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database;
    EXEC SQL OPEN cur1;

    /* при достижении конца набора результатов выйти из цикла while */
    EXEC SQL WHENEVER NOT FOUND DO BREAK;

    while (1)
    {
        /* Выборка нескольких столбцов в одну структуру. */
        EXEC SQL FETCH FROM cur1 INTO :dbval, :size;

        /* Вывод полей структуры. */
        printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, size);
    }

    EXEC SQL CLOSE cur1;

4.3.4.4.3.3. Typedefs #

Используйте typedef ключевое слово для сопоставления новых типов с уже существующими типами.

EXEC SQL BEGIN DECLARE SECTION;
    typedef char mychartype[40];
    typedef long serial_t;
EXEC SQL END DECLARE SECTION;

Обратите внимание, что вы также можете использовать:

EXEC SQL TYPE serial_t IS long;

Это объявление не обязательно должно быть частью секции объявлений; то есть вы также можете записывать определения typedef как обычные операторы языка C.

Любое слово, которое вы объявляете как typedef не может быть использовано в качестве ключевого слова SQL в команда EXEC SQL командах далее в той же самой программе. Например, следующий код не будет работать:

EXEC SQL BEGIN DECLARE SECTION;
    typedef int start;
EXEC SQL END DECLARE SECTION;
...
EXEC SQL START TRANSACTION;

препроцессор ECPG сообщит о синтаксической ошибке для оператора START TRANSACTION, так как он больше не распознает слово START как ключевое слово SQL, а только как определение типа typedef. (Если вы столкнулись с подобным конфликтом и переименование typedef представляется нецелесообразным, вы можете написать SQL-команду, используя динамический SQL.)

Примечание

В Digital Q.DataBase версиях до v16 использование ключевых слов SQL в качестве имен typedef, скорее всего, приводило к синтаксическим ошибкам, связанным с использованием самого типа typedef, а не с использованием данного имени в качестве ключевого слова SQL. Новое поведение с меньшей вероятностью будет вызывать проблемы при перекомпиляции существующего приложения ECPG в новой Digital Q.DataBase версии с новыми ключевыми словами.

4.3.4.4.3.4. Указатели #

Вы можете объявлять указатели на наиболее распространенные типы данных. Обратите внимание, однако, что нельзя использовать указатели в качестве целевых переменных в запросах без функции автоматического выделения памяти. См. Раздел 4.3.7 для получения дополнительной информации о функции автоматического выделения памяти.

EXEC SQL BEGIN DECLARE SECTION;
    int   *intp;
    char **charp;
EXEC SQL END DECLARE SECTION;

4.3.4.5. Обработка непримитивных типов данных SQL #

В данном разделе содержится информация о том, как обрабатывать нескалярные и пользовательские типы данных уровня SQL в приложениях на базе препроцессора ECPG. Обратите внимание, что это отличается от обработки главных переменных непримитивных типов, описанной в предыдущем разделе.

4.3.4.5.1. Массивы #

Многомерные массивы уровня SQL напрямую в препроцессоре ECPG не поддерживаются. Одномерные массивы уровня SQL могут быть сопоставлены с главными переменными типа массив C и наоборот. Однако при создании оператора препроцессор ECPG не имеет информации о типах столбцов, поэтому он не может проверить, передается ли массив C в соответствующий массив уровня SQL. При обработке результата выполнения SQL-оператора препроцессор ECPG обладает необходимой информацией и проверяет, являются ли оба объекта массивами.

Если запрос обращается к элементам массива по отдельности, это позволяет избежать использования массивов в препроцессоре ECPG. В таком случае следует использовать главную переменную, тип которой может быть сопоставлен с типом элементов массива. Например, если типом столбца является массив integer, может быть использована главная переменная типа int can be used. Также, если типом элемента является varchar или text, может быть использована главная переменная типа char[] или VARCHAR[] can be used.

Ниже приведен пример. Предположим, имеется следующая таблица:

CREATE TABLE t3 (
    ii integer[]
);

testdb=> SELECT * FROM t3;
     ii
-------------
 {1,2,3,4,5}
(1 row)

Следующий пример программы извлекает 4-й элемент массива и сохраняет его в главную переменную типа int:

EXEC SQL BEGIN DECLARE SECTION;
int ii;
EXEC SQL END DECLARE SECTION;

EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[4] FROM t3;
EXEC SQL OPEN cur1;

EXEC SQL WHENEVER NOT FOUND DO BREAK;

while (1)
{
    EXEC SQL FETCH FROM cur1 INTO :ii ;
    printf("ii=%d\n", ii);
}

EXEC SQL CLOSE cur1;

В данном примере выводится следующий результат:

ii=4

Для сопоставления нескольких элементов массива с несколькими элементами главной переменной типа массив каждый элемент столбца массива и каждый элемент массива главной переменной должны обрабатываться отдельно, например:

EXEC SQL BEGIN DECLARE SECTION;
int ii_a[8];
EXEC SQL END DECLARE SECTION;

EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[1], ii[2], ii[3], ii[4] FROM t3;
EXEC SQL OPEN cur1;

EXEC SQL WHENEVER NOT FOUND DO BREAK;

while (1)
{
    EXEC SQL FETCH FROM cur1 INTO :ii_a[0], :ii_a[1], :ii_a[2], :ii_a[3];
    ...
}

Еще раз обратите внимание на то, что

EXEC SQL BEGIN DECLARE SECTION;
int ii_a[8];
EXEC SQL END DECLARE SECTION;

EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii FROM t3;
EXEC SQL OPEN cur1;

EXEC SQL WHENEVER NOT FOUND DO BREAK;

while (1)
{
    /* WRONG */
    EXEC SQL FETCH FROM cur1 INTO :ii_a;
    ...
}

не будет работать корректно в данном случае, так как невозможно напрямую сопоставить столбец типа массива с главной переменной типа массива.

Другой способ обхода заключается в хранении массивов в их внешнем строковом представлении в главных переменных, имеющих char[] или VARCHAR[]. Для получения более подробной информации о данном представлении см. Раздел 2.5.15.2. Обратите внимание: это означает, что к массиву нельзя будет обращаться как к обычному массиву в главной программе (без дополнительной обработки, выполняющей синтаксический анализ текстового представления).

4.3.4.5.2. Составные типы #

Составные типы напрямую не поддерживаются в препроцессоре ECPG, но возможен простой способ решения этой проблемы. Доступные варианты аналогичны описанным выше для массивов: либо осуществляйте доступ к каждому атрибуту отдельно, либо используйте внешнее строковое представление.

Для приведенных ниже примеров предположим, что созданы следующие тип и таблица:

CREATE TYPE comp_t AS (intval integer, textval varchar(32));
CREATE TABLE t4 (compval comp_t);
INSERT INTO t4 VALUES ( (256, 'PostgreSQL') );

Наиболее очевидным решением является доступ к каждому атрибуту по отдельности. Следующая программа извлекает данные из таблицы примера путем выбора каждого атрибута типа comp_t по отдельности:

EXEC SQL BEGIN DECLARE SECTION;
int intval;
varchar textval[33];
EXEC SQL END DECLARE SECTION;

/* Поместите каждый элемент столбца составного типа в список SELECT. */
EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4;
EXEC SQL OPEN cur1;

EXEC SQL WHENEVER NOT FOUND DO BREAK;

while (1)
{
    /* Извлеките каждый элемент столбца составного типа в главные переменные. */
    EXEC SQL FETCH FROM cur1 INTO :intval, :textval;

    printf("intval=%d, textval=%s\n", intval, textval.arr);
}

EXEC SQL CLOSE cur1;

Чтобы улучшить этот пример, главные переменные для хранения значений в FETCH команде, могут быть объединены в одну структуру. Для получения более подробных сведений об использовании главной переменной в форме структуры см. раздел Раздел 4.3.4.4.3.2. Для перехода к использованию структуры пример можно изменить следующим образом. Две главные переменные, intval и textval, становятся членами структуры comp_t , и сама структура указывается в FETCH команде.

EXEC SQL BEGIN DECLARE SECTION;
typedef struct
{
    int intval;
    varchar textval[33];
} comp_t;

comp_t compval;
EXEC SQL END DECLARE SECTION;

/* Поместите каждый элемент столбца составного типа в список SELECT. */ */ EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4; EXEC SQL OPEN cur1;  EXEC SQL WHENEVER NOT FOUND DO BREAK;  while (1) {     /* Поместить все значения из списка SELECT в одну структуру. */     EXEC SQL FETCH FROM cur1 INTO :compval;      printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr); }  EXEC SQL CLOSE cur1;

Хотя в команде используется структура, FETCH имена атрибутов в предложении SELECT предложения указываются одно за другим. Этот процесс можно улучшить, используя * для запроса всех атрибутов значения составного типа.

...
EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).* FROM t4;
EXEC SQL OPEN cur1;

EXEC SQL WHENEVER NOT FOUND DO BREAK;

while (1)
{
    /* Поместить все значения из списка SELECT в одну структуру. */
    EXEC SQL FETCH FROM cur1 INTO :compval;

    printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr);
}
...

Таким образом, составные типы можно сопоставлять со структурами практически бесшовно, даже если препроцессор ECPG не распознает сам составной тип.

Наконец, значения составных типов также можно сохранять в их внешнем строковом представлении в главных переменных типа char[] или VARCHAR[]. Однако в этом случае прямой доступ к полям значения из главной программы затруднен.

4.3.4.5.3. Базовые типы данных, определенные пользователем #

Новые базовые типы, определенные пользователем, не поддерживаются препроцессором ECPG напрямую. Вы можете использовать внешнее строковое представление и главные переменные типа char[] или VARCHAR[], и данное решение является подходящим и достаточным для многих типов.

Ниже приведен пример использования типа данных complex из примера в Раздел 5.1.13. Внешнее строковое представление этого типа имеет вид (%f,%f), которое определено в функциях complex_in() и complex_out() функции в Раздел 5.1.13. В следующем примере значения комплексного типа вставляются (1,1) и (3,3) в столбцы a и b, после чего выбираются из таблицы.

EXEC SQL BEGIN DECLARE SECTION;
    varchar a[64];
    varchar b[64];
EXEC SQL END DECLARE SECTION;

    EXEC SQL INSERT INTO test_complex VALUES ('(1,1)', '(3,3)');

    EXEC SQL DECLARE cur1 CURSOR FOR SELECT a, b FROM test_complex;
    EXEC SQL OPEN cur1;

    EXEC SQL WHENEVER NOT FOUND DO BREAK;

    while (1)
    {
        EXEC SQL FETCH FROM cur1 INTO :a, :b;
        printf("a=%s, b=%s\n", a.arr, b.arr);
    }

    EXEC SQL CLOSE cur1;

Данный пример выводит следующий результат:

a=(1,1), b=(3,3)

Другой способ обхода заключается в отказе от прямого использования пользовательских типов данных в препроцессоре ECPG. Вместо этого можно создать функцию или оператор приведения типов для преобразования пользовательского типа в примитивный тип, поддерживаемый препроцессором ECPG. Однако следует учитывать, что приведения типов, особенно неявные, должны добавляться в систему типов с большой осторожностью.

Например,

CREATE FUNCTION create_complex(r double, i double) RETURNS complex
LANGUAGE SQL
IMMUTABLE
AS $$ SELECT $1 * complex '(1,0')' + $2 * complex '(0,1)' $$;

После данного определения следующий фрагмент кода

EXEC SQL BEGIN DECLARE SECTION;
double a, b, c, d;
EXEC SQL END DECLARE SECTION;

a = 1;
b = 2;
c = 3;
d = 4;

EXEC SQL INSERT INTO test_complex VALUES (create_complex(:a, :b), create_complex(:c, :d));

дает тот же результат, что и

EXEC SQL INSERT INTO test_complex VALUES ('(1,2)', '(3,4)');

4.3.4.6. Индикаторы #

Приведенные выше примеры не обрабатывают значения NULL. Фактически, примеры выборки данных приведут к возникновению ошибки, если из базы данных будет получено значение NULL. Для передачи значений NULL в базу данных или их получения из базы данных необходимо добавить спецификацию второй главной переменной к каждой главной переменной, содержащей данные. Эта вторая главная переменная называется индикаторной переменной и содержит флаг, указывающий, является ли значение пустым (NULL); в таком случае значение основной главной переменной игнорируется. Ниже приведен пример, в котором корректно обрабатывается получение значений NULL:

EXEC SQL BEGIN DECLARE SECTION;
VARCHAR val;
int val_ind;
EXEC SQL END DECLARE SECTION:

 ...

EXEC SQL SELECT b INTO :val :val_ind FROM test1;

Индикаторная переменная val_ind будет равна нулю, если значение не равно NULL, и будет отрицательной, если значение равно NULL. (См. Раздел 4.3.16 для включения поведения, специфичного для Oracle.)

У индикатора есть и другая функция: если значение индикатора положительное, это означает, что значение не является NULL, но оно было усечено при записи в главную переменную.

Если аргумент -r no_indicator передается препроцессору ecpg, он работает в «no-indicator» режим. В режиме no-indicator, если переменная-индикатор не указана, значения NULL передаются (на входе и на выходе) для строковых типов данных как пустая строка, а для целочисленных типов — как минимально возможное значение для данного типа (например, INT_MIN для int).

Наверх
свяжитесь
с нами
контакты
Для прямой связи с нами вы можете использовать контакты ниже, либо оставить заявку через форму обратной связи, и мы обязательно свяжемся с вами

*поля обязательные к заполнению