在 第 34.3 节 中,您了解了如何从嵌入式 SQL 程序中执行 SQL 语句。这些语句中有些只使用固定值,并且没有办法将用户提供的值插入语句或让程序处理查询返回的值。这类语句在实际应用程序中并不是非常有用。本节将详细解释如何使用称为宿主变量的简单机制,在 C 程序和嵌入式 SQL 语句之间传递数据。在嵌入式 SQL 程序中,我们将 SQL 语句视为 目标语言 代码中作为 宿主语言 的guest。因此,C 程序的变量被称为宿主变量。
在 PostgreSQL 后端和 ECPG 应用程序之间交换值的另一种方法是使用 SQL 描述符,第 34.7 节 中对此进行了描述。
在 C 程序和 SQL 语句之间传递数据在嵌入式 SQL 中特别简单。您可以简单地将 C 变量的名称写入 SQL 语句(加上前缀冒号),而不用让程序将数据粘贴到语句中,而这会带来各种复杂性,例如正确引用值。例如
EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2);
此语句引用了两个名为 v1
和 v2
的 C 变量,还使用了一个常规的 SQL 字符串文本,以说明您并不局限于使用一种数据或另一种数据。
这种在 SQL 语句中插入 C 变量的风格适用于在 SQL 语句中预期值表达式的任何位置。
若要将数据从程序传递到数据库(例如,作为查询中的参数),或将数据从数据库传递回程序,打算包含此数据的 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
部分内。否则,预处理器将无法处理这些类型,因为它不知道定义是什么。
现在,您应该能够将程序产生的数据传递到 SQL 命令中了。但如何检索查询结果?为此,嵌入式 SQL 提供了通常的 SELECT
和 FETCH
命令的特殊变体。这些命令有一个特殊 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
子句显示在 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
子句显示在所有常规子句之后。
当 ECPG 应用程序在 PostgreSQL 服务器和 C 应用程序之间交换值时(例如,从服务器检索查询结果或使用输入参数执行 SQL 语句),这些值需要在 PostgreSQL 数据类型和主机语言变量类型(具体来说为 C 语言数据类型)之间转换。ECPG 的一个要点在于,在大多数情况下,它会自动执行此操作。
在这方面,有两种数据类型:应用程序可以直接读写一些 PostgreSQL 简单数据类型,例如 integer
和 text
。其他 PostgreSQL 数据类型(例如 timestamp
和 numeric
)只能通过特殊库函数访问;请参见 34.4.4.2 节。
表 34.1 展示了 PostgreSQL 数据类型与哪些 C 数据类型相对应。当您想要发送或接收指定 PostgreSQL 数据类型的值时,您应该在 declare 部分中声明相应 C 数据类型的 C 变量。
表 34.1. PostgreSQL 数据类型和 C 变量类型之间的映射
PostgreSQL 数据类型 | 宿主变量类型 |
---|---|
smallint |
short |
integer |
int |
bigint |
long long int |
decimal |
decimal [a] |
numeric |
numeric [a] |
real |
float |
double precision |
double |
smallserial |
short |
serial |
int |
bigserial |
long long int |
oid |
unsigned int |
character( , varchar( , text |
char[ , VARCHAR[ |
name |
char[NAMEDATALEN] |
timestamp |
timestamp [a] |
interval |
interval [a] |
date |
date [a] |
boolean |
bool [b] |
bytea |
char * , bytea[ |
[a] 只能通过特殊库函数访问此类型;请参阅 第 34.4.4.2 节。 [b] 仅在非原生情况下声明在 |
若要处理 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 类型的值,这些值将以其字符串形式存储。
ECPG 包含一些特殊类型,可帮助您轻松与 PostgreSQL 服务器中的一些特殊数据类型进行交互。特别是,它已实现对 numeric
、decimal
、date
、timestamp
和 interval
类型的支持。这些数据类型不能有效地映射到原始主机变量类型(如 int
、long long int
或 char[]
),因为它们具有复杂的内部结构。应用程序通过在特殊类型中声明主机变量并使用 pgtypes 库中的函数访问它们来处理这些类型。pgtypes 库在 第 34.6 节 中详细描述,它包含处理这些类型的基本函数,这样您就不需要将查询发送到 SQL 服务器,例如只为时间戳添加一个区间。
以下小节描述了这些特殊数据类型。有关 pgtypes 库函数的更多详细信息,请参见 第 34.6 节。
以下是 ECPG 主机应用程序中处理 timestamp
变量的模式。
首先,程序必须包含 timestamp
类型头文件
#include <pgtypes_timestamp.h>
接下来,在 declare 部分中将主机变量声明为类型 timestamp
EXEC SQL BEGIN DECLARE SECTION; timestamp ts; EXEC SQL END DECLARE SECTION;
在读取值到主机变量后,使用 pgtypes 库函数对其进行处理。在以下示例中,timestamp
值使用 PGTYPEStimestamp_to_asc()
函数转换为文本(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
,将主机变量声明为 date 类型,并使用 PGTYPESdate_to_asc()
函数将 DATE 值转换为文本形式。有关 pgtypes 库函数的更多详细信息,请参见 第 34.6 节。
处理 interval
类型的变量与 timestamp
和 date
类型类似。但是,您需要显式地分配 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; }
处理 numeric
和 decimal
类型的变量与处理 interval
类型的变量类似:您需要定义一个指针、在堆中分配一些内存空间,并使用 pgtypes 库函数访问变量。有关 pgtypes 库函数的更多详细信息,请参阅 第 34.6 节。
没有专门针对 decimal
类型的变量提供的函数。应用程序必须使用 pgtypes 库函数,将此类变量转换为 numeric
类型的变量,以便进行进一步处理。
以下是一个处理 numeric
和 decimal
类型的变量的示例程序。
#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; }
处理 bytea
类型的变量与处理 VARCHAR
类型的变量类似。将 bytea
类型的数组的定义转换为针对每个变量的命名结构。声明类似于
bytea var[180];
将转换成
struct bytea_var { int len; char arr[180]; } var;
成员 arr
托管二进制格式的数据。与 VARCHAR
不同的是,它还可以处理数据中的 '\0'
。ecpglib 会将数据从/转换为十六进制格式并发送/接收它。
bytea
类型的变量只能在将 bytea_output 设置为 hex
时使用。
您还可以将数组、typedef、结构和指针用作宿主变量。
在以下两种使用情况下,数组可作为宿主变量。其一,如 第 34.4.4.1 节 中所述,这是一种在 char[]
或 VARCHAR[]
中存储某些文本字符串的方法。其二,在不使用游标的情况下从查询结果中检索多个行。如果不使用数组,要处理由多行组成的查询结果,则需要使用游标和 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=
如果结构的成员名称与查询结果的列名称匹配,则可以使用该结构一次检索多个列。结构使得在单个主机变量中处理多个列值成为可能。
以下示例从 pg_database
系统表和使用 pg_database_size()
函数中检索 OID、名称和可用数据库的大小。在此示例中,使用名为 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; /* 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;
使用 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;
此声明不需要成为声明部分;也就是说,您还可以像常规 C 语句一样编写类型定义。
您声明为 typedef
的任何单词都无法在同一程序中随后的 EXEC SQL
命令中用作 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 命令。)
在 v16 之前的 PostgreSQL 版本中,将 SQL 关键字用作 typedef 名称可能会导致与使用 typedef 本身而不是将该名称用作 SQL 关键字相关的语法错误。当在具有新关键字的新 PostgreSQL 版本中重新编译现有的 ECPG 应用时,较新的行为不太可能导致问题。
此部分包含有关在 ECPG 应用程序中如何处理非标量和用户定义 SQL 级数据类型的的信息。请注意,它与上一部分中描述的非基本类型的主机变量的处理不同。
ECPG 中不受直接支持多维 SQL 级数组。一维 SQL 级数组可以映射到 C 数组主机变量,反之亦然。但是,在创建语句时,ecpg 并不了解列的类型,因此它无法检查 C 数组是否输入到相应的 SQL 级数组中。在处理 SQL 语句的输出时,ecpg 拥有必要的信息,因此检查两者是否都是数组。
如果查询单独访问数组的元素,那么这可以避免在 ECPG 中使用数组。然后,应使用能够映射到元素类型的变量的主机变量。例如,如果列类型是 integer
数组,则可以使用类型为 int
的主机变量。此外,如果元素类型是 varchar
或 text
,则可以使用类型为 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
要将多个数组元素映射到数组类型的多个元素中,则必须要单独管理数组列的每个元素和主机变量数组的每个元素,例如
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[]
的主机变量。有关此表示法的更多详细信息,请参阅第 8.15.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
命令中值的宿主变量收集到一个结构中。有关结构形式中宿主变量的更多详细信息,请参见第 34.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; /* 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 本身不理解复合类型,也可以几乎无缝地将复合类型映射到结构中。
最后,还可以将复合类型值以其外部字符串表示形式存储在类型为 char[]
或 VARCHAR[]
的宿主变量中。但通过这种方式,无法轻松地从宿主程序访问值字段。
ECPG 直接不支持新的用户定义基本类型。可以使用外部字符串表示形式和类型为 char[]
或 VARCHAR[]
的宿主变量,此解决方案确实适用于许多类型,并且已足矣。
以下是一个示例,使用 第 36.13 节中示例中的数据类型 complex
。该类型的外部字符串表示形式为 (%f,%f)
,在 第 36.13 节中的函数 complex_in()
和 complex_out()
函数中定义。以下示例将复杂类型值 '(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)');
上述示例不处理空值。实际上,如果检索示例从数据库中获取空值,将引发错误。为了能够将空值传递至数据库或由此处检索空值,则需要向包含数据的每个主机变量追加第二个主机变量规范。这个第二个主机变量称为指示符,它包含一个标志,该标志说明该数据是否为空,在这种情况下,将忽略真实主机变量的值。以下示例正确处理了空值检索
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 的情况下将为负数。(请参阅 第 34.16 部分 启用特定于 Oracle 的行为。)
指示符具有其他功能:如果指示符值为正,则意味着该值不为空,但存储在主机变量中时该值被截断了。
如果向预处理器 ecpg
传递参数 -r no_indicator
,它将在 “no-indicator” 模式下工作。在 no-indicator 模式下,如果没有指定指示符变量,则空值对于字符字符串类型来说(输入和输出)表示为空字符串,对于整数类型来说表示类型的最小可能值(例如,对于 int
来说是 INT_MIN
)。