# 36.4.使用主机变量

36.4.1. 概述

36.4.2. 申报章节

36.4.3. 检索查询结果

36.4.4. 类型映射

36.4.5. 处理非原始SQL数据类型

36.4.6. 指标

在里面第36.3节您了解了如何从嵌入式SQL程序执行SQL语句。其中一些语句只使用固定值,没有提供将用户提供的值插入语句或让程序处理查询返回的值的方法。这些类型的语句在实际应用中并不真正有用。本节详细介绍了如何使用名为宿主变量在嵌入式SQL程序中,我们认为SQL语句是客人在C程序代码中主语因此,C程序的变量被称为宿主变量.

PostgreSQL后端和ECPG应用程序之间交换值的另一种方法是使用SQL描述符,如中所述第36.7节.

# 36.4.1.概述

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

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

此语句引用两个名为v1v2还使用常规的SQL字符串文字,以说明您不受限制地使用一种或另一种数据。

这种在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];

如您所见,您可以选择为变量指定初始值。变量的作用域由其声明部分在程序中的位置决定。您还可以使用以下语法声明变量,这些语法隐式创建一个declare节:

EXEC SQL int i = 4;

一个程序中可以有任意多个declare节。

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

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

# 36.4.3.检索查询结果

现在,您应该能够将程序生成的数据传递到SQL命令中。但是如何检索查询结果呢?为此,嵌入式SQL提供了常用命令的特殊变体选择取来.这些命令有一个特殊的进入子句,指定检索到的值要存储在哪些主机变量中。选择用于只返回单行的查询,以及取来用于使用光标返回多行的查询。

下面是一个例子:

/*
 * 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;

所以进入子句出现在选择列表和从…起条款选择列表中的元素数以及之后的列表进入(也称为目标列表)必须相等。

下面是一个使用命令的示例取来:

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

这是进入子句出现在所有普通子句之后。

# 36.4.4.类型映射

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

在这方面,有两种数据类型:一些简单的PostgreSQL数据类型,例如整数文本,可由应用程序直接读写。其他PostgreSQL数据类型,例如时间戳数字的只能通过特殊的图书馆功能访问;看见第36.4.4.2节.

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

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

PostgreSQL数据类型 主机变量类型
短整型 短的
整数 智力
比基特 双长整型
十进制的 十进制的[a]
数字的 数字的[a]
真实的 浮动
双精度 双重的
smallserial 短的
电视连续剧 智力
大系列 双长整型
老年人 无符号整型
性格(*n*), 瓦尔查尔(*n*), 文本 炭[*n*+1], 瓦尔查尔[*n*+1]
名称 char[NAMEDATALEN]
时间戳 时间戳[a]
间隔 间隔[a]
日期 日期[a]
布尔值 布尔[b]
二进制数据 炭*, 拜茶[*n*]
[a] 这种类型只能通过特殊的库函数访问;看见第36.4.4.2节.

[b] 声明于ecpglib。H如果不是本地人

# 36.4.4.1.处理字符串

处理SQL字符串数据类型,例如瓦尔查尔文本,有两种可能的方法来声明主机变量。

一种方法是使用char[],一系列烧焦,这是C语言中处理字符数据最常用的方法。

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

注意,你必须自己注意长度。如果将此主机变量用作返回超过49个字符的字符串的查询的目标变量,则会发生缓冲区溢出。

另一种方法是使用瓦尔查尔类型,这是ECPG提供的特殊类型。类型数组上的定义瓦尔查尔被转换为命名的结构对于每个变量。声明如下:

VARCHAR var[180];

转换为:

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

成员承载包含终止零字节的字符串。因此,将字符串存储在瓦尔查尔主机变量,主机变量的声明长度必须包括零字节终止符。成员伦恩保存存储在中的字符串的长度没有终止零字节。当主机变量用作查询的输入时,如果斯特伦(arr)伦恩如果不同,则使用较短的。

瓦尔查尔可以用大写或小写书写,但不能用混合大写。

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

# 36.4.4.2.访问特殊数据类型

ECPG包含一些特殊类型,可以帮助您轻松地与PostgreSQL server中的一些特殊数据类型进行交互。特别是,它已经实施了对数字的, 十进制的, 日期, 时间戳间隔类型。这些数据类型无法有效地映射到原始主机变量类型(例如智力, 双长整型char[]),因为它们有一个复杂的内部结构。应用程序通过在特殊类型中声明主机变量并使用pgtypes库中的函数访问它们来处理这些类型。pgtypes库,在中详细介绍第36.6节包含处理这些类型的基本函数,例如,不需要仅为向时间戳添加间隔而向SQL server发送查询。

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

# 36.4.4.2.1.时间戳,日期

这里有一个处理模式时间戳ECPG主机应用程序中的变量。

首先,程序必须包含时间戳类型:

#include <pgtypes_timestamp.h>

接下来,将一个主机变量声明为type时间戳在声明部分:

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

将值读入宿主变量后,使用pgtypes库函数对其进行处理。在下面的示例中时间戳将值转换为文本(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

此外,日期类型也可以用同样的方式处理。该计划必须包括PGU日期。H,将主机变量声明为日期类型,并使用PGTYPESdate_to_asc()作用有关pgtypes库函数的更多详细信息,请参阅第36.6节.

# 36.4.4.2.2.间歇

处理间隔类型也类似于时间戳日期类型。然而,需要为一个应用程序分配内存间隔显式输入值。换句话说,变量的内存空间必须在堆内存中分配,而不是在堆栈内存中分配。

以下是一个示例程序:

#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.数字的,十进制的

处理数字的十进制的类型与间隔类型:它需要定义一个指针,在堆上分配一些内存空间,并使用pgtypes库函数访问变量。有关pgtypes库函数的更多详细信息,请参阅第36.6节.

没有专门为十进制的类型应用程序必须将其转换为数字的变量使用pgtypes库函数进行进一步处理。

下面是一个程序处理示例数字的十进制的类型变量。

#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.2.4.拜茶

处理二进制数据类型与瓦尔查尔.类型数组上的定义二进制数据转换为每个变量的命名结构。声明如下:

bytea var[180];

转换为:

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

成员托管二进制格式的数据。它还可以处理'\0'作为数据的一部分瓦尔查尔.数据从/转换为十六进制格式,并由ecpglib发送/接收。

# 笔记

二进制数据变量只能在以下情况下使用:二进制数据_输出即将十六进制.

# 36.4.4.3.具有非基本类型的宿主变量

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

# 36.4.4.3.1.阵列

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

下面的示例扫描pg_数据库系统表,并显示可用数据库的所有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.结构

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

以下示例从中检索可用数据库的OID、名称和大小pg_数据库系统表和使用pg_数据库_大小()作用在本例中,结构变量数据库信息成员的名称与选择result用于检索一个结果行,而无需将多个主机变量放入取来陈述

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

结构宿主变量“吸收”的列数与结构中的字段数一样多。可以将其他列指定给其他主机变量。例如,上面的程序也可以像这样进行重组大小结构之外的变量:

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

使用类型定义关键字将新类型映射到现有类型。

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;

此声明不需要是declare部分的一部分。

# 36.4.4.3.4.指针

可以声明指向最常见类型的指针。但是请注意,如果没有自动分配,就不能将指针用作查询的目标变量。看见第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.阵列

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

如果查询访问元素这样就避免了在ECPG中使用数组。然后,应该使用类型可以映射到元素类型的宿主变量。例如,如果列类型是数组整数,类型的宿主变量智力可以使用。如果元素类型为瓦尔查尔文本,类型的宿主变量char[]瓦查尔[]可以使用。

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

CREATE TABLE t3 (
    ii integer[]
);

testdb=> SELECT * FROM t3;
     ii
#### 36.4.5.2. Composite Types

 Composite types are not directly supported in ECPG, but an easy workaround is possible. The available workarounds are similar to the ones described for arrays above: Either access each attribute separately or use the external string representation.

 For the following examples, assume the following type and table:

创建类型comp_t AS(intval integer,textval varchar(32));创建表t4(compval comp_t);插入t4值((256,‘PostgreSQL’));

 The most obvious solution is to access each attribute separately. The following program retrieves data from the example table by selecting each attribute of the type `comp_t` separately:

EXEC SQL开始声明部分;int intval;瓦查尔[33]; EXEC SQL END DECLARE部分;

/将复合类型列的每个元素都放入选择列表中。/EXEC SQL为SELECT(compval)声明cur1游标。intval(compval)。t4的textval;EXEC SQL OPEN cur1;

无论何时未找到EXEC SQL,都不要中断;

而(1){/将复合类型列的每个元素提取到主机变量中。/EXEC SQL从cur1获取到:intval,:textval;

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

}

EXEC SQL CLOSE cur1;

 To enhance this example, the host variables to store values in the `FETCH` command can be gathered into one structure. For more details about the host variable in the structure form, see [Section 36.4.4.3.2](ecpg-variables.html#ECPG-VARIABLES-STRUCT). To switch to the structure, the example can be modified as below. The two host variables, `intval` and `textval`, become members of the `comp_t` structure, and the structure is specified on the `FETCH` command.

EXEC SQL开始声明部分;typedef结构{int intval;varchar textval[33]; } 比较;

comp_t compval;EXEC SQL END DECLARE部分;

/将复合类型列的每个元素都放入选择列表中。/EXEC SQL为SELECT(compval)声明cur1游标。intval(compval)。t4的textval;EXEC SQL OPEN cur1;

无论何时未找到EXEC SQL,都不要中断;

而(1){/将选择列表中的所有值放入一个结构中。/EXEC SQL FETCH FROM cur1 INTO:compval;

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

}

EXEC SQL CLOSE cur1;

 Although a structure is used in the `FETCH` command, the attribute names in the `SELECT` clause are specified one by one. This can be enhanced by using a `*` to ask for all attributes of the composite type value.

... EXEC SQL为SELECT(compval)声明cur1游标。*从t4开始;EXEC SQL OPEN cur1;

无论何时未找到EXEC SQL,都不要中断;

而(1){/将选择列表中的所有值放入一个结构中。/EXEC SQL FETCH FROM cur1 INTO:compval;

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

} ...

 This way, composite types can be mapped into structures almost seamlessly, even though ECPG does not understand the composite type itself.

 Finally, it is also possible to store composite type values in their external string representation in host variables of type `char[]` or `VARCHAR[]`. But that way, it is not easily possible to access the fields of the value from the host program.

#### 36.4.5.3. User-Defined Base Types

 New user-defined base types are not directly supported by ECPG. You can use the external string representation and host variables of type `char[]` or `VARCHAR[]`, and this solution is indeed appropriate and sufficient for many types.

 Here is an example using the data type `complex` from the example in [Section 38.13](xtypes.html). The external string representation of that type is `(%f,%f)`, which is defined in the functions `complex_in()` and `complex_out()` functions in [Section 38.13](xtypes.html). The following example inserts the complex type values `(1,1)` and `(3,3)` into the columns `a` and `b`, and select them from the table after that.

EXEC SQL开始声明部分;瓦查尔a[64]; 瓦查尔b[64]; EXEC SQL END DECLARE部分;

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;
 This example shows following result:

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

 Another workaround is avoiding the direct use of the user-defined types in ECPG and instead create a function or cast that converts between the user-defined type and a primitive type that ECPG can handle. Note, however, that type casts, especially implicit ones, should be introduced into the type system very carefully.

 For example,

CREATE函数CREATE_complex(r double,i double)将复杂语言SQL作为$$SELECT$1返回不可变复杂的“(1,0')”+$2复合物(0,1)$$;

 After this definition, the following

EXEC SQL开始声明部分;双a,b,c,d;EXEC SQL END DECLARE部分;

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

EXEC SQL插入测试复杂值(创建复杂值(:a,:b),创建复杂值(:c,:d));

 has the same effect as

EXEC SQL插入测试_复杂值(“(1,2)”,“(3,4)”;

### 36.4.6. Indicators

 The examples above do not handle null values. In fact, the retrieval examples will raise an error if they fetch a null value from the database. To be able to pass null values to the database or retrieve null values from the database, you need to append a second host variable specification to each host variable that contains data. This second host variable is called the *indicator* and contains a flag that tells whether the datum is null, in which case the value of the real host variable is ignored. Here is an example that handles the retrieval of null values correctly:

EXEC SQL开始声明部分;瓦尔查尔·瓦尔;国际货币基金组织;EXEC SQL END DECLARE部分:

...

EXEC SQL选择b到:val:val_ind FROM test1;

 The indicator variable `val_ind` will be zero if the value was not null, and it will be negative if the value was null.

 The indicator has another function: if the indicator value is positive, it means that the value is not null, but it was truncated when it was stored in the host variable.

 If the argument `-r no_indicator` is passed to the preprocessor `ecpg`, it works in “no-indicator” mode. In no-indicator mode, if no indicator variable is specified, null values are signaled (on input and output) for character string types as empty string and for integer types as the lowest possible value for type (for example, `INT_MIN` for `int`).