您的 GBase 8s ESQL/C 程序可以下表总结的 SQL 语句来操纵 sqlda 结构。 表 22. 可用于操纵 sqlda 结构的 SQL 语句 SQL 语句 用途 请参阅 DESCRIBE...INTO 分配 sqlda 结构,并以关于列列表列的信 息初始化该结构 为 sqlda 结构分配内 存 初始化 sqlda 结构 表 23. 可用于操纵 sqlda 结构的 SQL 语句:使用游标的 SELECT 和 EXECUTE FUNCTION 语句 SQL 语句 用途 请参阅 OPEN...USING DESCRIPTOR FETCH...USING DESCRIPTOR 从指定的 sqlda 结构取得任何输入参数 将该行的内容放至 sqlda 结构内 指定输入参 数值 将列值放至 sqlda 结构 内 表 24. 可用于操纵 sqlda 结构的 SQL 语句:仅返回一行的 SELECT 和 EXECUTE FUNCTION 语句 SQL 语句 用途 请参阅 EXECUTE...INTO DESCRIPTOR 将该单个行的内容放至 sqlda 结构内 将列值放至 sqlda 结构 内 表 25. 可用于操纵 sqlda 结构的 SQL 语句:非 SELECT 语句 SQL 语句 用途 请参阅 EXECUTE...USING DESCRIPTOR 从指定的 sqlda 结构取得任何输入信息 指定输入参 数值 表 26. 可用于操纵 sqlda 结构的 SQL 语句:使用插入游标的 INSERT 语句 GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 567 - SQL 语句 用途 请参阅 PUT...USING DESCRIPTOR 在它从指定的 sqlda 结构获得列值之后, 将一行放至插入缓冲区内 处理未知的 列列表 此外,您的 GBase 8s ESQL/C 程序可以下列方式管理 sqlda 结构: 声明一个指向sqlda 结构的变量指针。 将值指定给 sqlda 字段来以丢失的列信息提供数据库服务器。 从 sqlda 字段取得信息来访问从数据库服务器接收的列信息。 释放分配给 sqlda 结构的内存,当以它停止您的程序时。 定义 sqlda 结构 GBase 8s ESQL/Csqlda.h 头文件定义 sqlda 结构。 要定义 sqlda 结构,GBase 8s ESQL/C 程序必须采取下列行动: 包括 sqlda.h 头文件来在您的程序中为 sqlda 提供声明 GBase 8s ESQL/C 预处理器自动地包括 sqlhdr.h 文件,其包括 sqlda.h 头文件。 声明变量名称作为指向 sqlda 结构的指针 下列代码行声明 da_ptr 变量作为 sqlda 指针: struct sqlda *da_ptr; 重要: 指向 sqlda 结构的指针不是 GBase 8s ESQL/C 主变量。因此,您不需要以关 键字 EXEC SQL 或美元 ($) 符号先于该语句声明。 此外, 在该程序会中, 您不以冒号 (:) 或美元($)符号先于任何对指针的引用。 为 sqlda 结构分配内存 在您定义主变量作为指向 sqlda 结构的指针之后, 您必须确保为此结构的所有部分分 配内存,如下: 要为 sqlda 结构自身分配内存,请使用 DESCRIBE...INTO 语句。 下列 DESCRIBE 语句获得关于准备好的语句 st_id 的信息, 为 sqlda 结构分配内存, 并将 sqlda 结构的地址放至指针 da_ptr 中: EXEC SQL describe st_id into da_ptr; 要为 sqlvar_struct 结构分配内存,请采取下列行动: GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 568 - 如果该准备好的语句是 SELECT(不带有 INTO TEMP 子句)、INSERT 或 EXECUTE FUNCTION 语句, 则 DESCRIBE...INTO 语句可为 sqlvar_struct 结构分配空间。 如果准备了某些其他 SQL 语句,且您想要在数据库服务器中发送或接收列,则您的 程序必须为 sqlvar_struct 结构分配空间。 要为 sqldata 字段的数据分配内存,请确保该数据类型与正确的词边界一致。 如果您使用 sqlda 结构来定义输入参数,则您不可使用 DESCRIBE 语句。因此,您 的程序必须显式地同时为 sqlda 结构和 sqlvar_struct 结构分配内存。 初始化 sqlda 结构 要发送或接收数据库中的列值, 您的 GBase 8s ESQL/C 程序必须初始化 sqlda 结构, 以便于它描述该准备好的语句的未知的列。 要初始化 sqlda 结构,您必须执行下列步骤: 将 sqlvar 字段设置为初始化的 sqlvar_struct 结构的地址。 设置 sqld 字段来指示未知的列数(以及相关联的 sqlvar_struct 结构)。 除了为 sqlda 结构分配内存,DESCRIBE...INTO 语句还以关于该准备好的 语句的信息初始化此结构。DESCRIBE...INTO 可提供的信息,依赖于它已描述 了哪个 SQL 语句。 如果准备好的语句是 SELECT (不带有 INTO TEMP 子句) 、 INSERT 或 EXECUTE FUNCTION 语句,则 DESCRIBE...INTO 语句可确定关于列列表中列的信息。因此, DESCRIBE...INTO 语句采取下列行动来初始化 sqlda 结构: 它为 sqlda 结构分配内存。 它设置 sqlda.sqld 字段,其包含以数据初始化了的 sqlvar_struct 结构的数目。此值为 列列表中的列和表达式的数目(SELECT 和 INSERT),或返回的值的数目(EXECUTE FUNCTION)。 它为组件 sqlvar_struct 结构分配内存, 对于列列表中的每一列或表达式 (SELECT 和 INSERT) , 或对于每一返回的值 (EXECUTE FUNCTION) , 对应一个 sqlvar_struct 结构。 它将 sqlda.sqlvar 字段设置为 DESCRIBE 为该 sqlvar_struct 结构分配的内存的初始 地址。 它描述在准备好的 SELECT(不带有 INTO TEMP)、EXECUTE FUNCTION 或 INSERT 语句中每一未知的列。 DESCRIBE...INTO 语句为每一列初始化 sqlvar_struct 结构 的字段,如下: 它初始化 sqltype、sqllen 和 sqlname 字段(对于 CHAR 类型数据,或对于 GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 569 - DATETIME 或 INTERVAL 数据的限定符),来提供关于该列的来自数据库的信息。 对于大多数数据类型,sqllen 字段保存以字节计的该列的长度。如果该列为集合类型 (SET、 MULTISET 或 LIST) 、 row 类型 (命名的或未命名的) 或 opaque 类型, 则 sqllen 字段为零。 它将 sqldata 和 sqlind 字段初始化为空。 重要: 不像系统描述符区域那样,带有 sqlda 指针的 DESCRIBE 不为列数据 (sqldata 字段)分配内存。在您的程序从数据库服务器收到列值之前,它必须分配此数据 空间。 DESCRIBE 语句提供关于列列表的列的信息。因此,您通常在准备好了 SELECT(不带有 INTO TEMP 子句)、INSERT 或 EXECUTE FUNCTION 语句之后, 使用 DESCRIBE...INTO。DESCRIBE...INTO 语句不仅初始化 sqlda 结构,而且 返回准备好的 SQL 语句的类型。 下列 DESCRIBE 语句还为一个 sqlda 结构和为两个 sqlvar_struct 数据结构(一个 为 customer_num 列,另一个为 company 列)分配内存,然后,以分配给 sqlvar_struct 结构的内存的初始地址来初始化指针 da_ptr->sqlvar: EXEC SQL prepare st_id 'select customer_num, company from customer where customer_num = ?'; EXEC SQL describe st_id into da_ptr; 前面的 DESCRIBE...INTO 语句返回 SQLCODE 值 0,来指示该准备好的语句为 SELECT 语句。 下图展示此 DESCRIBE...INTO 语句可以初始化的样例 sqlda 结构。 图 5. 两列的样例 sqlda 结构 GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 570 - 如果准备好了某其他 SQL 语句, 则该 DESCRIBE...INTO 语句不可初始化 sqlda 结 构。要发送或接收该数据库中的列值,您的程序必须显式地执行此初始化,如下: 为组件 sqlvar_struct 结构分配内存,每一列一个 sqlvar_struct 结构。 您可使用诸如 malloc() 或 calloc() 这样的系统内存分配函数,并指定地址为 sqlvar, 如下: da_ptr->sqlvar = (struct sqlvar_struct *) calloc(count, sizeof(struct sqlvar_struct)); 执行下列任务来描述每一未知的列: 设置 sqlda.sqld 字段,其包含以数据初始化了的 sqlvar_struct 结构的数目。此值为准 备好的语句中未知的列的数目。 初始化每一 sqlvar_struct 结构的字段。 设置 sqltype、 sqllen 和 sqlname 字段 (对于 CHAR 类型数据, 或对于 DATETIME 或 INTERVAL 数据的限定符),来将关于列的信息提供给数据库服务器。 要提供列数据,您的程序还必须为此数据分配空间,并将每一 GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 571 - sqlvar_struct 结构的 sqldata 字段设置为此空间内的恰当位置。 如果您将列 数据发送给数据库服务器,则请务必正确地设置 sqlind 字段。 如果您使用 sqlda 结构来定义输入参数,则您不可使用 DESCRIBE 语句来 初始化 sqlda 结构。您的代码必须显式地设置 sqlda 结构的恰当的字段,来 定义输入参数。 为列数据分配内存 sqlda 结构为 sqlvar_struct 结构的 sqldata 字段中的每一列存储一个指向该数据的 指针。 不像 DESCRIBE...USING SQL DESCRIPTOR 语句那样, DESCRIBE...INTO 语句不 为此数据分配内存。当 DESCRIBE...INTO 语句为 sqlda 指针分配内存时,它将每一 sqlvar_struct 结构的 sqldata 字段初始化为空。 要发送或接收数据库中的列数据,您的 GBase 8s ESQL/C 程序必须执行下列任务: 为该列数据分配内存。 将与该列相关联的 sqlvar_struct 结构的 sqldata 字段,设置为为该列数据分配的内存 的地址。 要为 sqldata 字段分配内存, 您可使用系统内存分配函数, 比如 malloc() 或 calloc()。 作为对 malloc() 系统内存分配函数的替代,您可程序可为该数据缓冲区声明静态的字符缓 冲区。下图展示从名为 data_buff 的静态字符缓冲区分配列数据的代码片段。 图 6. 从静态的字符缓冲区分配列数据 static char data_buff[1024]; struct sqlda *sql_descp; struct sqlvar_struct * col_ptr; short cnt, pos; int size; ⋮ for(col_ptr=sql_descp->sqlvar, cnt=pos=0; cnt < sql_descp->sqld; cnt++, col_ptr++) { pos = (short)rtypalign(pos, col_ptr->sqltype); col_ptr->sqldata = &data_buf[pos]; GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 572 - size = rtypmsize(col_ptr->sqltype, col_ptr->sqllen); pos += size; } 您可在 for 循环内以一系列系统内存分配调用来替代 图 1中的代码片段。 然而,系统内存分配调用可能代价高昂,因此,更加高效的方式,通常是分配 单个内存,然后将指针对准至该内存区域内。 当您分配列数据时,请务必为列数据类型格式化分配了的内存。此数据类型为 GBase 8s ESQL/C 或定义在 sqltypes.h 头文件中的 SQL 数据类型之一。请使 得分配了的内存足够大,以容纳该列中数据的最大大小。 您还必须确保每一列的数据开始于内存中正确的词边界上。在许多硬件平台上,整数 和其他数值数据类型必须开始于词边界上。C 语言内存分配例程分配与包括结构的任何数 据类型恰当一致的内存,但该例程不对该结构的组成部件执行校准。 使用正确的词边界,确保数据类型与机器无关。为了在此任务中辅助您, GBase 8s ESQL/C 提供下列内存管理函数: 对于指定的数据类型,rtypalign() 函数返回下一个正确的词边界的位置。 此函数接受两个参数:在数据缓冲区中的当前位置,以及整数 GBase 8s ESQL/C 或 您想要为其分配空间的 SQL 数据类型。 rtypmsize() 函数返回内存的字节数,您必须为指定的 GBase 8s ESQL/C 或 SQL 数 据类型分配该内存。 此函数接受两个参数: 整数 GBase 8s ESQL/C 或对于每一列值的 SQL 数据类型 (在 sqltype 中)和长度(在 sqllen 中)。 当您为 DATETIME 或 INTERVAL 数据类型分配内存时,您可采取下列行动,来设 置 dtime_t 和 intrvl_t 结构中的限定符: 请使用在相关联的 sqlda 的 sqllen 字段中的值。 以该值与 datatime.h 头文件定义的宏组成一个不同的限定符。 将数据类型限定符设置为 0,并使得数据库服务器在访存期间设置此限定符。对于 DATETIME 值, 该数据类型限定符为 dtime_t 结构的 dt_qual 字段。 对于 INTERVAL 值, 该数据类型限定符为 intrvl_t 结构的 in_qual 字段。 要获取为 sqldata 字段分配内存的示例,请参阅 demo3.ec 和 unload.ec 演示程序, GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 573 - 以 GBase 8s ESQL/C 提供这些程序。 指定并从 sqlda 结构取得值 当您以动态 SQL 使用 sqlda 结构时,您必须以 C 语言语句将信息移入和移除它。 指定值 要将值指定给 sqlda 和 sqlvar_struct 结构中的字段,请使用常规的 C 语言赋值给 恰当的结构的字段。例如: da_ptr->sqld = 1; da_ptr->sqlvar[0].sqldata = compny_data; da_ptr->sqlvar[0].sqltype = SQLCHAR; /* CHAR data type */ da_ptr->sqlvar[0].sqllen = 21; /* column is CHAR(20) */ 设置 sqlda 字段来为 WHERE 子句中的输入参数提供值(指定输入参数值), 或在您使用 DESCRIBE...INTO 语句来填充 sqlda 结构之后来修改字段的内容 (为列数据分配内存)。 取得值 要从 sqlda 字段取得值,您还必须使用来自该结构的字段的常规的 C 语言赋值。例 如: count = da_ptr->sqld; /* Allow for the trailing null character in C character arrays */ if (da_ptr->sqlvar[0].sqltype == SQLCHAR) a_ptr->sqlvar[0].sqllen += 1; /* Allocate a separate buffer per column */ da_ptr->sqlvar[0].sqldata = malloc(col_ptr->sqllen); 通常, 您取得 sqlda 字段值来检测 SELECT、 INSERT 或 EXECUTE FUNCTION 语句中列的描述。您还可能需要访问这些列,来将由数据库服务器返回的列值 从 sqlda 结构复制至主变量 (将列值放至 sqlda 结构内) 。 在后者的情况下, 您可能需要更改 sqllen 来说明正确的缓冲区长度。例如,您必须对 CHAR 数 据类型增加 sqllen。如果您未增加 sqllen,则会截断访存到的数据的最后一 个字符,因为 FETCH 会假设 sqllen 值为该缓冲区的大小,并会将缓冲区中的 最后位置用作零来终止该字符串。 GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 574 - 主变量的数据类型必须与 sqlda 结构中相关联的字段的类型相兼容。当您 解释 sqltype 字段时,请确保您使用匹配您的环境的数据类型值。对于某些数 据类型,X/Open 值不同于 GBase 8s 值。 指定输入参数值 由于 DESCRIBE...INTO 语句不分析 WHERE 子句,因此,您的程序必须显 式地分配 sqlda 结构和 sqlvar_struct 结构。要描述输入参数,您必须确定 输入参数的数目以及它们的数据类型,并将此信息存储在分配了的 sqlda 结构 中。 当您执行参数化的语句时,您必须包括 USING DESCRIPTOR 子句来指定 sqlda 结 构作为输入参数值的位置,如下: 对于 SELECT 语句的 WHERE 子句的输入参数,请使用 OPEN...USING DESCRIPTOR 语句。 此语句处理顺序的、 滚动的、 保存或更新游标。 如果您确定该 SELECT 仅返回一行,则可使用 EXECUTE...INTO...USING SQL DESCRIPTOR 语句,而不使用游 标。 对于非 SELECT 语句的 WHERE 子句中的输入参数,诸如 DELETE 或 UPDATE, 请使用 EXECUTE...USING DESCRIPTOR 语句。 对于 INSERT 语句的 VALUES 子句中的输入参数, 请使用 EXECUTE...USING SQL DESCRIPTOR 语句。如果该 INSERT 与插入游标相关联,则请使用 PUT...USING DESCRIPTOR 语句。 将列值放至 sqlda 结构内 当您动态地创建 SELECT 语句时,您不可使用 FETCH 的 INTO host_var 子句,因 为您不可在准备好的语句中命名主变量。要将列值访存至 sqlda 结构内,请使用 FETCH 的 USING DESCRIPTOR 子句, 而不用 INTO 子句。 FETCH...USING DESCRIPTOR 语句 将每一列值放至它的 sqlvar_struct 结构的 sqldata 字段内。 使用 FETCH...USING DESCRIPTOR 语句, 假设游标与准备好的语句相关联。 您必须 总是为 SELECT 语句和游标函数(返回多行的 EXECUTE FUNCTION 语句)使用游标。 然而,如果这些语句之一仅返回一行,则您可省略游标,并以 EXECUTE...INTO DESCRIPTOR 语句将列值检索至 sqlda 结构内。 重要: 如果您执行返回多行的 SELECT 语句或用户定义的函数,且未将该语句与游 标相关联, 则您的程序生成运行时刻错误。 当您将单个 SELECT (或 EXECUTE FUNCTION) 语句与游标相关联时,GBase 8s ESQL/C 不生成错误。因此,总是将动态 SELECT 或 EXECUTE FUNCTION 语句与游标相关联, 并使用 FETCH...USING DESCRIPTOR 语句来 将列值从此游标检索至 sqlda 结构,是一种好的做法。 GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 575 - 一旦列值在 sqlda 结构中, 您就可将这些值从 sqldata 移至恰当的主变量。 在运行时 刻, 您必须使用 sqllen 和 sqltype 字段来确定该主变量的数据类型。 您可能需要在 sqltype 字段中的数据类型与保存返回的值的主变量的 GBase 8s ESQL/C 数据类型之间执行数据 类型或长度转换。 释放分配给 sqlda 结构的内存 一旦您以 sqlda 结构结束,就请释放相关联的内存。如果您执行多个 DESCRIBE 语 句,且忽略由这些语句分配的内存,则您的应用程序可能冲击内存限制,且数据库服务器 可能退出。 如果在 Windows(TM) 操作系统上运行您的应用程序,且使用多线程库,则请使用 GBase 8s ESQL/C 函数 SqlFreeMem() 来释放 sqlda 结构所占的内存,如此示例所示: SqlFreeMem(sqlda_ptr, SQLDA_FREE); 否则,请使用标准 C 库 free() 函数,如此示例所示: free(sqlda_ptr); 对于同一准备好的语句, 如果您的 GBase 8s ESQL/C 程序执行 DESCRIBE 语句多次, 并为每一 DESCRIBE 分配单独的 sqlda 结构,则它必须显式地释放每一 sqlda 结构。下 图展示一个示例。 图 7. 为同一准备好的语句释放多个 sqlda 结构 EXEC SQL prepare qid from 'select * from customer'; EXEC SQL describe qid into sqldaptr1; EXEC SQL describe qid into sqldaptr2; EXEC SQL describe qid into sqldaptr3; ⋮ ¹ free(sqldaptr1); free(sqldaptr2); free(sqldaptr3); 如果你的程序为列数据分配了空间,则您还必须释放分配给 sqldata 字段的内存。 GBase 8s ESQL/C 编程指南 南大通用数据技术股份有限公司 - 576 -