pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
在第 33.4 节中,你已经看到如何从嵌入式 SQL 程序中执行 SQL 语句。其中一些语句只使用固定值,既无法把用户提供的值插入语句中,也无法让程序处理查询返回的值。这类语句在实际应用中并不真正有用。本节将详细解释如何使用一种称为主变量的简单机制,在 C 程序与嵌入式 SQL 语句之间传递数据。在嵌入式 SQL 程序中,我们把 SQL 语句视为 C 程序代码中的客体,而 C 是主语言。因此,C 程序中的变量被称为主变量。
在嵌入式 SQL 中,在 C 程序和 SQL 语句之间传递数据特别简单。我们不必让程序把数据拼接进语句中去,那样会带来诸如正确为值加引号等各种复杂问题;只需在 SQL 语句中写出 C 变量的名称,并在前面加上一个冒号即可。例如:
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声明节中。否则预处理器无法处理这些类型,因为它不知道它们的定义。
你也可以把数组、typedef、结构体和指针用作主变量。此外,还存在一些只在 ECPG 中使用的特殊类型的主变量。
下面是主变量的一些例子:
数组声明最常见的用途之一可能就是像这样分配一个 char 数组:
EXEC SQL BEGIN DECLARE SECTION;
char str[50];
EXEC SQL END DECLARE SECTION;
注意你必须自己处理长度。如果你把这个主变量用作一个返回多于 49 个字符的字符串的查询的目标变量,就会发生缓冲区溢出。
使用 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;
此声明不必放在声明区段内。
你可以声明指向最常见类型的指针。但要注意,只有使用自动内存分配时,才能将指针用作查询的目标变量。关于自动内存分配的更多信息,参见第 33.9 节。
EXEC SQL BEGIN DECLARE SECTION;
int *intp;
char **charp;
EXEC SQL END DECLARE SECTION;
ECPG 包含一些特殊类型,帮助你轻松地与来自 SQL 服务器的数据交互。例如,它实现了对varchar、numeric、date、timestamp和interval类型的支持。第 33.8 节包含处理这些类型的基本函数,这样例如你就不需要仅仅为了把一个区间加到一个时间戳上而向 SQL 服务器发送一个查询。
特殊类型VARCHAR会被转换成每个变量的一个命名的struct。像这样的声明:
VARCHAR var[180];
会被转换为:
struct varchar_var { int len; char arr[180]; } var;
这个结构适合与varchar类型的 SQL 数据交互。
SELECT INTO 和 FETCH INTO现在你应该能够把程序产生的数据传递到一个 SQL 命令中了。但是怎么检索一个查询的结果呢?为此,嵌入式 SQL 提供了常规命令SELECT和FETCH的特殊变体。这些命令有一个特殊的INTO子句,它指定被检索到的值要被存储在哪些主变量中。
这里是一个例子:
/* * 假定有下列表: * 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子句出现在所有正常子句的后面。
上面的示例没有处理空值。实际上,如果读取示例从数据库取得空值,就会抛出错误。要向数据库传入空值或从数据库取得空值,需要在每个包含数据的主变量后,再指定第二个主变量。第二个主变量称为指示符,其中包含一个标志,表示该数据是否为空值;如果为空值,就会忽略实际主变量的值。下面的示例能正确处理空值的读取:
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 在数据非空时为零,在数据为空值时为负数。
指示符有另一种功能:如果指示符值为正,它表示值不为空,但是当它被存储在主变量中时已被截断。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。