36.4. 使用主机变量

Section 36.3中,您了解了如何从嵌入式 SQL 程序执行 SQL 语句。这些语句中的某些仅使用固定值,而没有提供将用户提供的值插入语句中或让程序处理查询返回的值的方法。这些类型的语句在实际应用程序中并不是 true 有用的。本节详细说明如何使用称为* host variables 的简单机制在 C 程序和嵌入式 SQL 语句之间传递数据。在嵌入式 SQL 程序中,我们认为 SQL 语句是 C 程序代码中的 guests (即宿主语言)。因此,C 程序的变量称为主机变量

在 PostgreSQL 后端和 ECPG 应用程序之间交换值的另一种方法是使用Section 36.7中描述的 SQLDescriptors。

36.4.1. Overview

在嵌入式 SQL 中,在 C 程序和 SQL 语句之间传递数据特别简单。不必让程序将数据粘贴到语句中(这会带来各种复杂性,例如正确地引用值),您只需将 C 变量的名称写入 SQL 语句中,并以冒号作为前缀。例如:

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

该语句引用了两个名为v1v2的 C 变量,并且还使用了常规的 SQL 字符串 Literals,以说明您不受限于使用一种数据或另一种数据。

在 SQL 语句中插入 C 变量的这种样式在 SQL 语句中需要值表达式的任何地方都有效。

36 .4.2. 声明部分

要将数据从程序传递到数据库(例如,作为查询中的参数)或将数据从数据库传递回程序,需要在专门标记的部分中声明要包含此数据的 C 变量,因此使嵌入式 SQL 预处理器知道它们。

本节开始于:

EXEC SQL BEGIN DECLARE SECTION;

并以:

EXEC SQL END DECLARE SECTION;

在这两行之间,必须有普通的 C 变量声明,例如:

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

如您所见,您可以选择为变量分配初始值。变量的范围由其声明部分在程序中的位置确定。您还可以使用以下语法声明变量,该语法隐式创建一个声明部分:

EXEC SQL int i = 4;

您可以根据需要在程序中具有多个声明部分。

声明也作为普通 C 变量回显到输出文件,因此无需再次声明它们。在这些特殊部分之外,通常可以声明不打算在 SQL 命令中使用的变量。

结构或联合的定义也必须在DECLARE部分中列出。否则,预处理器将无法处理这些类型,因为它不知道定义。

36 .4.3. 检索查询结果

现在,您应该能够将程序生成的数据传递到 SQL 命令中。但是,如何检索查询结果?为此,嵌入式 SQL 提供了常用命令SELECTFETCH的特殊变体。这些命令具有一个特殊的INTO子句,该子句指定将检索值存储在哪些主机变量中。SELECT用于仅返回单行的查询,而FETCH用于使用游标返回多行的查询。

这是一个例子:

/*
 * assume this table:
 * 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子句出现在选择列表和FROM子句之间。选择列表和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子句出现在所有常规子句之后。

36 .4.4. 类型 Map

当 ECPG 应用程序在 PostgreSQL 服务器和 C 应用程序之间交换值时,例如从服务器检索查询结果或使用 Importing 参数执行 SQL 语句时,需要在 PostgreSQL 数据类型和宿主语言变量类型(C 语言数据)之间转换这些值。类型,具体而言)。 ECPG 的要点之一是,在大多数情况下,它会自动执行此操作。

在这方面,有两种数据类型:一些简单的 PostgreSQL 数据类型,例如integertext,可以由应用程序直接读取和写入。其他 PostgreSQL 数据类型,例如timestampnumeric只能通过特殊的库函数进行访问;参见Section 36.4.4.2

Table 36.1显示哪些 PostgreSQL 数据类型对应于哪些 C 数据类型。当您希望发送或接收给定 PostgreSQL 数据类型的值时,应在声明部分中声明对应 C 数据类型的 C 变量。

表 36.1. PostgreSQL 数据类型和 C 变量类型之间的 Map

PostgreSQL 数据类型主机变量类型
smallintshort
integerint
bigintlong long int
decimaldecimal [a]
numericnumeric [a]
realfloat
double precisiondouble
smallserialshort
serialint
bigseriallong long int
oidunsigned int
character(n) , varchar(n) , textchar[n+1] , VARCHAR[n+1] [b]
namechar[NAMEDATALEN]
timestamptimestamp [a]
intervalinterval [a]
datedate [a]
booleanbool [c]
byteachar *
[a]此类型只能通过特殊的库函数进行访问;参见Section 36.4.4.2


[b]ecpglib.h中声明

如果不是本机,则在ecpglib.h中声明[c]

36 .4.4.1. 处理字符串

要处理诸如varchartext之类的 SQL 字符串数据类型,有两种方法可以声明主机变量。

一种方法是使用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中存储的字符串的长度,而没有终止的零字节。当主机变量用作查询的 Importing 时,如果strlen(arr)len不同,则使用较短的变量。

VARCHAR可以大写或小写,但不能大写。

charVARCHAR主机变量还可以保存其他 SQL 类型的值,这些值将以其字符串形式存储。

36 .4.4.2. 访问特殊数据类型

ECPG 包含一些特殊类型,可以帮助您轻松地与 PostgreSQL 服务器中的某些特殊数据类型进行交互。特别是,它已经实现了对numericdecimaldatetimestampinterval类型的支持。这些数据类型不能有用地 Map 到原始主机变量类型(例如intlong long intchar[]),因为它们具有复杂的内部结构。应用程序pass 语句特殊类型的宿主变量并使用 pgtypes 库中的函数来访问它们来处理这些类型。 pg_types 库(在Section 36.6中进行了详细描述)包含处理这些类型的基本函数,因此您无需仅向时间戳记添加间隔就可以向 SQL Server 发送查询。

以下小节描述了这些特殊的数据类型。有关 pgtypes 库函数的更多详细信息,请参见Section 36.6

36 .4.4.2.1. 时间戳,日期

这是在 ECPG 主机应用程序中处理timestamp变量的模式。

首先,程序必须包含timestamp类型的头文件:

#include <pgtypes_timestamp.h>

接下来,在声明部分中将主机变量声明为timestamp类型:

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

在将值读入宿主变量后,使用 pgtypes 库函数对其进行处理。在以下示例中,使用PGTYPEStimestamp_to_asc()函数将timestamp值转换为文本(ASCII)形式:

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,将主机变量声明为日期类型,然后使用PGTYPESdate_to_asc()函数将 DATE 值转换为文本形式。有关 pgtypes 库函数的更多详细信息,请参见Section 36.6

36.4.4.2.2. interval

interval类型的处理也类似于timestampdate类型。但是,需要为interval类型的值显式分配内存。换句话说,变量的存储空间必须分配在堆存储器中,而不是在堆栈存储器中。

这是一个示例程序:

#include <stdio.h>
#include <stdlib.h>
#include <pgtypes_interval.h>

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;
}
36 .4.4.2.3. 数字,十进制

numericdecimal类型的处理与interval类型类似:它需要定义一个指针,在堆上分配一些内存空间,并使用 pgtypes 库函数访问该变量。有关 pgtypes 库函数的更多详细信息,请参见Section 36.6

没有专门针对decimal类型的功能。应用程序必须使用 pgtypes 库函数将其转换为numeric变量,以进行进一步处理。

这是处理numericdecimal类型变量的示例程序。

#include <stdio.h>
#include <stdlib.h>
#include <pgtypes_numeric.h>

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));

    /* Convert decimal to numeric to show a decimal value. */
    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;
}

36 .4.4.3. 具有非基本类型的主机变量

作为主机变量,您还可以使用数组,typedef,结构和指针。

36.4.4.3.1. Arrays

数组有两个用例作为宿主变量。第一种是将一些文本字符串存储在char[]VARCHAR[]中的方法,如Section 36.4.4.1中所述。第二个用例是在不使用游标的情况下从查询结果中检索多行。如果没有数组,则要处理由多行组成的查询结果,需要使用游标和FETCH命令。但是使用数组主机变量,可以一次接收多行。必须将数组的长度定义为能够容纳所有行,否则可能会发生缓冲区溢出。

以下示例扫描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=
36.4.4.3.2. Structures

成员名称与查询结果的列名称匹配的结构可用于一次检索多个列。该结构允许在单个主机变量中处理多个列值。

下面的示例使用pg_database系统表并使用pg_database_size()函数检索 OID,可用数据库的名称和大小。在此示例中,具有名称与SELECT结果中每一列匹配的成员的结构变量dbinfo_t用于检索一个结果行,而无需在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;

    /* when end of result set reached, break out of while loop */
    EXEC SQL WHENEVER NOT FOUND DO BREAK;

    while (1)
    {
        /* Fetch multiple columns into one structure. */
        EXEC SQL FETCH FROM cur1 INTO :dbval;

        /* Print members of the structure. */
        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(&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;

    /* when end of result set reached, break out of while loop */
    EXEC SQL WHENEVER NOT FOUND DO BREAK;

    while (1)
    {
        /* Fetch multiple columns into one structure. */
        EXEC SQL FETCH FROM cur1 INTO :dbval, :size;

        /* Print members of the structure. */
        printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, size);
    }

    EXEC SQL CLOSE cur1;
36.4.4.3.3. Typedefs

使用typedef关键字将新类型 Map 到已经存在的类型。

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;

该声明不必是声明部分的一部分。

36.4.4.3.4. Pointers

您可以声明指向最常见类型的指针。但是请注意,如果没有自动分配,则不能将指针用作查询的目标变量。有关自动分配的更多信息,请参见Section 36.7

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

36 .4.5. 处理非原始 SQL 数据类型

本节包含有关如何在 ECPG 应用程序中处理非标量和用户定义的 SQL 级数据类型的信息。请注意,这与非基本类型的主机变量的处理不同,如上一节所述。

36.4.5.1. Arrays

ECPG 不直接支持多维 SQL 级数组。一维 SQL 级数组可以 Map 到 C 数组主机变量,反之亦然。但是,在创建语句 ecpg 时,它不知道列的类型,因此它无法检查 C 数组是否 Importing 到相应的 SQL 级别的数组中。在处理 SQL 语句的输出时,ecpg 具有必要的信息,因此可以检查两者是否均为数组。

如果查询分别访问数组的元素,则可以避免在 ECPG 中使用数组。然后,应使用类型可以 Map 到元素类型的宿主变量。例如,如果列类型是integer的数组,则可以使用类型int的主机变量。同样,如果元素类型为varchartext,则可以使用类型为char[]VARCHAR[]的主机变量。

这是一个例子。假设下表:

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

要将多个数组元素 Map 到数组类型主机变量中的多个元素,必须分别 Management 数组列的每个元素和主机变量数组的每个元素,例如:

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;
    ...
}

在这种情况下将无法正常工作,因为您无法直接将数组类型列 Map 到数组主机变量。

另一个解决方法是将数组以其外部字符串表示形式存储在char[]VARCHAR[]类型的主机变量中。有关此表示形式的更多详细信息,请参见Section 8.15.2。请注意,这意味着无法自然地将数组作为主机程序中的数组进行访问(没有进一步的处理来解析文本表示形式)。

36 .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;

/* Put each element of the composite type column in the SELECT list. */
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)
{
    /* Fetch each element of the composite type column into host variables. */
    EXEC SQL FETCH FROM cur1 INTO :intval, :textval;

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

EXEC SQL CLOSE cur1;

为了增强此示例,可以将用于存储FETCH命令中的值的主机变量收集到一个结构中。有关结构形式中的宿主变量的更多详细信息,请参见Section 36.4.4.3.2。要切换到该结构,可以对示例进行如下修改。 intvaltextval这两个主机变量成为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;

/* Put each element of the composite type column in the SELECT list. */
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)
{
    /* Put all values in the SELECT list into one structure. */
    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)
{
    /* Put all values in the SELECT list into one structure. */
    EXEC SQL FETCH FROM cur1 INTO :compval;

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

这样,即使 ECPG 无法理解复合类型本身,复合类型也可以几乎无缝地 Map 到结构中。

最后,还可以将复合类型值以其外部字符串表示形式存储在char[]VARCHAR[]类型的主机变量中。但是那样,就不可能轻易地从宿主程序访问值的字段。

36 .4.5.3. 用户定义的基本类型

ECPG 不直接支持新的用户定义的基本类型。您可以使用外部字符串表示形式和char[]VARCHAR[]类型的主机变量,并且该解决方案确实适用于许多类型。

这是一个使用Section 38.12中的数据类型complex的示例。该类型的外部字符串表示形式是(%f,%f),它在Section 38.12中的函数complex_in()complex_out()中定义。以下示例将复杂类型值(1,1)(3,3)插入到ab列中,然后从表中选择它们。

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 可以处理的原始类型之间进行转换的函数或强制转换。但是请注意,类型转换,尤其是隐式类型转换,应非常小心地引入类型系统。

For example,

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)');

36.4.6. Indicators

上面的示例不处理空值。实际上,如果检索示例从数据库中获取空值,则将引发错误。为了能够将空值传递给数据库或从数据库中检索空值,您需要向每个包含数据的主机变量附加第二个主机变量规范。第二个宿主变量称为* indicator *,其中包含一个标志,该标志指示数据是否为空,在这种情况下,实际宿主变量的值将被忽略。这是一个正确处理空值的示例:

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;

如果该值不为 null,则指示符变量val_ind将为零;如果该值为 null,则其将为负。

该 Metrics 还有另一个功能:如果 Metrics 值为正,则表示该值不为 null,但是在将其存储在宿主变量中时被截断了。

如果参数-r no_indicator传递给预处理器ecpg,则它将在“无指示器”模式下工作。在无指示符模式下,如果未指定指示符变量,则将字符串类型的空值(在 Importing 和输出上)发 signal 通知为空字符串,将整数类型的空值作为类型的最小可能值(例如INT_MIN表示int)。