ecpg(Oracle Pro*c兼容)
可以在所谓的 Oracle compatibility mode 下运行。
如果该模式处于活动状态,则它会尝试表现得像 Oracle 的 Pro*C 一样。
具体来说,此模式通过三种方式更改 ecpg(Oracle Pro*c兼容)
:
填充字符数组,用于接收具有指定长度的尾随空格的字符串类型
零字节终止这些字符数组,并在发生截断时设置指示符变量
当字符数组接收到空字符串类型时,将null指示符设置为-1
在Oracle兼容模式下,支持从数据库读取数据到一个char类型变量中。
Oracle-兼容模式支持一种与Section 33.7.2中所述不同的结构。如下:
struct SQLDA { int N; /* Number of entries */ char **V; /* Variables */ int *L; /* Variable lengths */ short *T; /* Variable types */ short **I; /* Indicators */ int F; /* Count of variables discovered by DESCRIBE */ char **S; /* Variable names */ short *M; /* Variable name maximum lengths */ short *C; /* Variable name actual lengths */ char **X; /* Indicator names */ short *Y; /* Indicator name maximum lengths */ short *Z; /* Indicator name actual lengths */ };
以下是每个描述符变量的说明:
N 变量
用途: 指定可以描述的选择列表项或占位符的最大数量。
使用方法:
在 DESCRIBE 之前: 使用 sqlald()
设置 N
为描述符数组的维度。
在 DESCRIBE 之后: 将 N 重置为实际描述的变量数量,该数量存储在 F 中。
V 变量
用途: 指向存储选择列表或绑定变量值的数据缓冲区地址数组。
使用方法:
对于选择描述符,在执行 FETCH 命令之前分配数据缓冲区并设置此数组。
对于绑定描述符,在执行 OPEN 命令之前设置此数组。
L 变量
用途: 指向存储在数据缓冲区中的选择列表或绑定变量值长度的数组。
使用方法:
对于选择描述符,DESCRIBE SELECT LIST 会将长度数组设置为每个选择列表项的最大预期长度。
在 FETCH 之前,可能需要重置一些长度以满足实际需要。
对于绑定描述符,在执行 OPEN 命令之前必须设置长度数组。
T 变量
用途: 指向选择列表或绑定变量值的数据类型代码数组。这些代码决定了 Oracle 数据在存储到由 V 数组元素指向的数据缓冲区时如何转换。
使用方法:
对于选择描述符,DESCRIBE SELECT LIST 会将数据类型代码数组设置为选择列表项的内部数据类型。
在 FETCH 之前,可能需要重置一些数据类型以便更易于处理。
对于绑定描述符,DESCRIBE BIND VARIABLES 会将数据类型代码数组设置为零。在执行 OPEN 命令之前,必须设置每个元素的数据类型代码。
I 变量
用途: 指向存储指示变量值的数据缓冲区地址的数组。
使用方法:
对于选择描述符,在执行 FETCH 命令之前必须设置地址数组。如果返回的选择列表值为 NULL,则指示变量值设置为 -1。
对于绑定描述符,在执行 OPEN 命令之前必须设置地址数组。如果指示变量值为 -1,则绑定变量值为 NULL。
F 变量
用途: 实际找到的选择列表项或占位符的数量。
使用方法:
由 DESCRIBE 设置。如果 F 小于零,表示描述的选择列表项或占位符数量超出了描述符的分配大小。
S 变量
用途: 指向存储选择列表或占位符名称的数据缓冲区地址的数组。
使用方法:
使用 sqlald()
分配数据缓冲区,并将其地址存储在 S 数组中。
M 变量
用途: 存储选择列表或占位符名称的数据缓冲区的最大长度数组。
使用方法:
使用 sqlald()
设置 M 数组中的元素。
C 变量
用途: 存储选择列表或占位符名称的当前长度数组。
使用方法:
由 DESCRIBE 设置。在 DESCRIBE 之后,数组包含每个选择列表或占位符名称的字符数。
X 变量
用途: 指向存储指示变量名称的数据缓冲区地址的数组。仅适用于绑定描述符。
使用方法:
使用 sqlald()
分配数据缓冲区,并将其地址存储在 X 数组中。
Y 变量
用途: 存储指示变量名称的数据缓冲区的最大长度数组。仅适用于绑定描述符。
使用方法:
使用 sqlald()
设置 Y 数组中的元素。
Z 变量
用途: 存储指示变量名称的当前长度数组。仅适用于绑定描述符。
使用方法:
由 DESCRIBE BIND VARIABLES 设置。在 DESCRIBE 之后,数组包含每个指示变量名称的字符数。
DESCRIBE BIND VARIABLES 将占位符的描述放入绑定描述符。在我们的例子中,DESCRIBE 准备好 bind_des,如下所示:
EXEC SQL DESCRIBE BIND VARIABLES FOR sql_stmt INTO bind_des;
DESCRIBE BIND VARIABLES 语句必须在 PREPARE 语句之后,但在 OPEN 语句之前。
如果动态 SQL 语句是查询,则 DESCRIBE SELECT LIST 语句必须在 OPEN 语句之后,但在 FETCH 语句之前。DESCRIBE SELECT LIST 将选择列表项的描述放入选择描述符。在我们的例子中,DESCRIBE 准备好 select_des,如下所示:
EXEC SQL DESCRIBE SELECT LIST FOR sql_stmt INTO select_des;
通过访问数据字典,DESCRIBE 设置每个选择列表值的长度和数据类型。
FETCH 从活动集合中返回一行,将选择列表值存储在数据缓冲区中,并将游标推进到活动集合中的下一行。如果没有更多的行,FETCH 将 sqlca.sqlcode 设置为“未找到数据”的错误代码。在我们的例子中,FETCH 将列的值返回到 select_des,如下所示:
EXEC SQL FETCH emp_cursor USING DESCRIPTOR select_des;
要一次提取多行,请使用指定行数的 FETCH 语句。以下示例演示如何将指定数量的行(FETCH_ROWS_COUNT)提取到选择描述符区域(outp_sqlda)中:
EXEC SQL FOR :fetch_rows_count FETCH mycur1 USING DESCRIPTOR outp_sqlda;
#include <stdlib.h> #include <string.h> #include <limits.h> // exec sql include ../regression; exec sql include sqlda.h; exec sql include pgtypes_numeric.h; exec sql whenever sqlerror stop; /* These shouldn't be under DECLARE SECTION */ SQLDA *inp_sqlda, *outp_sqlda; static void dump_sqlda(SQLDA *sqlda, int row) { int i; if (sqlda == NULL) { printf("dump_sqlda called with NULL sqlda\n"); return; } for (i = 0; i < sqlda->F; i++) { short* ptr = (short*)sqlda->I[i]; if (ptr[row] == -1) printf("name sqlda descriptor: '%s' value NULL'\n", sqlda->S[i]); else { switch (sqlda->T[i]) { case 96: printf("name sqlda descriptor: '%s' value '%s'\n", sqlda->S[i], sqlda->V[i] + row * sqlda->L[i]); break; case 3: printf("name sqlda descriptor: '%s' value %d\n", sqlda->S[i], *(int *)(sqlda->V[i] + row * sqlda->L[i])); break; case 22: printf("name sqlda descriptor: '%s' value %f\n", sqlda->S[i], *(double *)(sqlda->V[i] + row * sqlda->L[i])); break; case 2: { char *val; val = PGTYPESnumeric_to_asc((numeric*)(sqlda->V[i] + row * sqlda->L[i]), -1); printf("name sqlda descriptor: '%s' value NUMERIC '%s'\n", sqlda->S[i], val); PGTYPESchar_free(val); break; } default: printf("type = %d \n", sqlda->T[i]); } } } } int main (void) { exec sql begin declare section; char *stmt1 = "SELECT * FROM t1"; char *stmt2 = "SELECT * FROM t1 WHERE id = :bindID"; int rec; int id; int fetch_rows_count; exec sql end declare section; char msg[128]; ECPGdebug(1, stderr); // exec sql connect to REGRESSDB1 as regress1; exec sql connect to postgres as regress1; exec sql drop table if exists t1; exec sql set datestyle to iso; exec sql create table t1( id integer, t text, d1 numeric, d2 float8, c char(10)); exec sql insert into t1 values (1, 'a', 1.0, 1, 'a'), (2, null, null, null, null), (4, 'd', 4.0, 4, 'd'); exec sql commit; /* SQLDA test for getting all records from a table */ outp_sqlda = NULL; outp_sqlda = sqlald(10, 20, 20); outp_sqlda->N = 10; exec sql prepare st_id1 from :stmt1; exec sql declare mycur1 cursor for st_id1; exec sql open mycur1; exec sql whenever not found do break; // exec sql describe st_id1 into outp_sqlda; exec sql describe select list for st_id1 into outp_sqlda; outp_sqlda->N = outp_sqlda->F; for (int i = 0; i < outp_sqlda->F; i++) { outp_sqlda->V[i] = calloc(1, 100); outp_sqlda->I[i] = calloc(1, 100); } rec = 0; while (1) { exec sql fetch mycur1 using descriptor outp_sqlda; printf("FETCH RECORD %d\n", ++rec); dump_sqlda(outp_sqlda, 0); } exec sql whenever not found continue; exec sql close mycur1; exec sql deallocate prepare st_id1; for (int i = 0; i < outp_sqlda->F; i++) { free(outp_sqlda->V[i]); free(outp_sqlda->I[i]); } sqlclu(outp_sqlda); /* SQLDA test for getting FETCH_ROWS_COUNT records from a table */ printf("FETCH_ROWS_COUNT RECORD \n"); outp_sqlda = NULL; outp_sqlda = sqlald(10, 20, 20); outp_sqlda->N = 10; exec sql prepare st_id1 from :stmt1; exec sql open mycur1; // exec sql describe st_id1 into outp_sqlda; exec sql describe select list for st_id1 into outp_sqlda; outp_sqlda->N = outp_sqlda->F; /* 注意,获取多行记录时,一定要分配足够的空间 */ for (int i = 0; i < outp_sqlda->F; i++) { outp_sqlda->V[i] = calloc(1, 200); outp_sqlda->I[i] = calloc(1, 20); } fetch_rows_count = 3; /* 每次提取 FETCH_ROWS_COUNT 条数据到选择描述区 */ /* sqlca.sqlerrd[2] 保存当前处理成功的总行数 */ EXEC SQL FOR :fetch_rows_count FETCH mycur1 USING DESCRIPTOR outp_sqlda; for (int iRow = 0; iRow < sqlca.sqlerrd[2]; iRow++) /*循环行*/ { dump_sqlda(outp_sqlda, iRow); } exec sql close mycur1; exec sql deallocate prepare st_id1; for (int i = 0; i < outp_sqlda->F; i++) { free(outp_sqlda->V[i]); free(outp_sqlda->I[i]); } sqlclu(outp_sqlda); /* SQLDA test for getting one record using an input descriptor */ /* Input sqlda has to be built manually */ inp_sqlda = sqlald(10, 20, 20); outp_sqlda = sqlald(10, 20, 20); printf("EXECUTE RECORD 4\n"); id = 4; exec sql prepare st_id1 from :stmt2; // exec sql declare mycur1 cursor for st_id1; exec sql DESCRIBE BIND VARIABLES FOR st_id1 into inp_sqlda; printf("inp_sqlda->F = %d \n", inp_sqlda->F); printf("inp_sqlda->S[0] = %s \n", inp_sqlda->S[0]); printf("inp_sqlda->C[0] = %d \n", inp_sqlda->C[0]); inp_sqlda->N = inp_sqlda->F; printf("inp_sqlda->N = %d \n", inp_sqlda->N); inp_sqlda->V[0] = (char *)&id; inp_sqlda->T[0] = 3; /* ECPGt_int */ inp_sqlda->L[0] = sizeof(id); exec sql open mycur1 USING DESCRIPTOR inp_sqlda;; exec sql whenever not found do break; // exec sql describe st_id1 into outp_sqlda; exec sql describe select list for st_id1 into outp_sqlda; outp_sqlda->N = outp_sqlda->F; for (int i = 0; i < outp_sqlda->F; i++) { outp_sqlda->V[i] = calloc(1, 200); outp_sqlda->I[i] = calloc(1, 20); } rec = 0; while (1) { exec sql fetch mycur1 using descriptor outp_sqlda; printf("FETCH RECORD %d\n", ++rec); dump_sqlda(outp_sqlda, 0); } exec sql whenever not found continue; exec sql close mycur1; exec sql deallocate prepare st_id1; for (int i = 0; i < outp_sqlda->F; i++) { free(outp_sqlda->V[i]); free(outp_sqlda->I[i]); } sqlclu(inp_sqlda); sqlclu(outp_sqlda); /* End test */ exec sql drop table t1; exec sql commit; exec sql disconnect; return 0; }
sqlald()
sqlald()
函数为描述符分配存储空间。
SQLDA * sqlald(unsigned int max_vars, unsigned int max_name, unsigned int max_ind_name)
max_vars
:表示描述符中能够存储的最大变量数。
max_name
:如果非零,则为存储变量名的数组分配空间。
max_ind_name
:如果非零,则为存储指示器变量名的数组分配空间。
函数返回的 SQLDA *
是一个指向描述符结构体的指针。如果分配成功,则返回该指针;否则返回零。
sqlclu()
sqlclu()
释放 sqlda
结构本身。
Sqlnul()
T[i]
的高位存储着第 i 个查询列表项的
NULL \ NOT NULL
状态信息。在执行 OPEN 或 FETCH
命令前必须清除该位,这可以通过调用函数 sqlnul()
来完成。函数 sqlnul() 的语法如下:
sqlnul(unsigned short *value_type,unsigned short *type_code,int*null_status);
参数说明如下:
values_type:指向无符号短整数变量的指针,该变量存储着查询列表项的数据代码类型,而数据类型存储在 T[i]中。
type_code:指向无符号短整数变量的指针,该变量返回了查询列表项的数据类型代码。
null_status:指向整数变量的指针,该变量返回了查询列表项的 NULLNULL 状态。其中,1 表示允许该列为空,而 0 表示不允许该列为空。