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 表示不允许该列为空。