文章目录
- 一、不使用SQLDA描述符范围的游标操作
- 1.1 oracle 案例
- 1.1.1 使用游标获取数据
- 1.1.2 对于fetch结果集怎么去利用
- 1.2 LightDB 案例
- 1.2.1 使用游标获取数据
- 1.2.2 对于fetch结果集怎么去利用
- 3 总结:不同项
- 二、使用SQLDA描述符范围的游标操作
- 2.1 Oracle样例
- 2.1.1 使用SQLDA描述符范围的游标获取数据
- 2.2 LightDB样例
- 2.2.1 使用SQLDA描述符范围的游标获取数据
- 3 总结:不同项
- 三、总结
今天我们讲解的是关于pro*c迁移指南中的游标模块;这个模块我们分为两部分来讲:
(一)、不使用SQLDA描述符范围的游标操作
(二)、使用SQLDA描述符范围的游标操作
讲解方法主要为:oracle案例分析和LightDB案例分析,对比,列出不同点
一、不使用SQLDA描述符范围的游标操作
1.1 oracle 案例
1.1.1 使用游标获取数据
#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct fetch_results
{
int a;
char b[10];
int c;
}fr;int main() {EXEC SQL BEGIN DECLARE SECTION;char *uid ; //连接串信息自己补充int tmp;static fr arr[6];EXEC SQL END DECLARE SECTION;EXEC SQL CONNECT :uid;//连接是否成功if (sqlca.sqlcode == 0)printf("是否成功连接:%d\n",sqlca.sqlcode);else{printf("是否成功连接:%d\n",sqlca.sqlcode);return -1;}//声明游标exec sql declare cur cursor for select * from test;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);//打开游标exec sql open cur;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);//获取数据exec sql fetch cur into :arr;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);printf("获取行数:%d\n", sqlca.sqlerrd[2]);//关闭游标exec sql fetch cur into :arr;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);//断开连接EXEC SQL COMMIT WORK RELEASEexit(0);
}
上面给出的列子中,fetch获取结果集的行数,我们可以通过sqlca.sqlerrd[2]来确定,oracle中sqlca.sqlerrd[2]被用来记录获取的结果集的行数。
我们发现sqlca.sqlerrd[2]的值刚好与arr数组的大小是相等,随着arr数组的大小的变动sqlca.sqlerrd[2]也会变动。
1.1.2 对于fetch结果集怎么去利用
这里我们用打印的方式进行显示
while (1){exec sql fetch cur into :arr; Rows = sqlca.sqlerrd[2] - DealRows;for (i=0;i<Rows;i++) { printf("a:%d,b:%s,c:%d", arr[i].a,arr[i].b,arr[i].c);} DealRows = sqlca.sqlerrd[2]; if (sqlca.sqlcode==1403){iFetchCount=sqlca.sqlerrd[2];break;}}
上述程序需要稍加解析:我们添加了三个变量去完成这个算法;分别为Rows、DealRows、iFetchCount。它们代表的意思如下:
Rows:这次fetch获取的行数;
DealRows:已经获取的行数
iFetchCount:获取总行数;
为什么我们会多此一举在这里使用三个变量进行运算哪?
这是有使用场景决定的,当我们查询的结果集的长度超出了我们目标的数组大小(获取的实际长度为目标数组的大小),就需要用到上面这段代码了。还需要再强调一次,oracle中sqlca.sqlerrd[2]被用来记录获取的结果集的行数。
好的,知道了这些我们再来看代码
Rows = sqlca.sqlerrd[2] - DealRows; Rows等于总行数 - 已经获取过的结果集行数;
DealRows = sqlca.sqlerrd[2]; 已经获取过的结果集行数;
**iFetchCount=sqlca.sqlerrd[2];**最后判断no_data_found,获得fetch总行数。
1.2 LightDB 案例
1.2.1 使用游标获取数据
#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct fetch_results
{
int a;
char b[10];
int c;
}fr;int main() {Test_cf cf ;EXEC SQL BEGIN DECLARE SECTION;char *uid ; //连接串信息自己补充int tmp;static aa1_a arr[6];EXEC SQL END DECLARE SECTION;EXEC SQL CONNECT :uid;//连接是否成功if (sqlca.sqlcode == 0)printf("是否成功连接:%d\n",sqlca.sqlcode);else{printf("是否成功连接:%d\n",sqlca.sqlcode);return -1;}//声明游标exec sql declare cur cursor for select * from test;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);//打开游标exec sql open cur;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);//获取数据exec sql fetch cur into :arr;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);printf("获取行数:%d\n", sqlca.sqlerrd[2]);//关闭游标exec sql fetch cur into :arr;fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);//断开连接EXEC SQLdisconnect;exit(0);
}
对比发现,与oracle存在两个不同点:
(1)断开连接不同;
(2)LightDB fetch into 语法只能获取一行,不是由目标数组的大小决定的
因此我们在对于fetch结果集怎么去利用的方法肯定不一致
1.2.2 对于fetch结果集怎么去利用
这里我们用打印的方式进行显示
while (1){exec sql fetch 6 cur into :arr; iFetchCount += sqlca.sqlerrd[2];Rows = sqlca.sqlerrd[2];for (i=0;i<Rows;i++){printf("a:%d,b:%s,c:%d", arr[i].a,arr[i].b,arr[i].c);}DealRows = iFetchCount;if ( sqlca.sqlcode==ECPG_NOT_FOUND){break;}}
我们发现虽然代码看起来差不多,但是却存在3个细节上的差异:
(1)fetch into变成了fetch count into,为了兼容oracle其长度设置成oraclearr数组的长度,即可;
(2)添加的变量一样,但是算法发生了变化:
Rows = sqlca.sqlerrd[2]; 当前获取的行数;
DealRows = iFetchCount; 已经获取的行数;
iFetchCount += sqlca.sqlerrd[2]; 获取的总行数;
(3)no_data_found的错误码不一致了,LightDB返回的错误码为100,oracle返回的错误码为1403
3 总结:不同项
(1)fetch into获取的长度不一样;导致获取字段结果的算法不一致;
(2)错误码不一样
(3)断开连接的方式不一样(关于连接有空再单独介绍)
二、使用SQLDA描述符范围的游标操作
这一章我们会在不使用SQLDA描述符范围的游标操作的基础上进行讲解。
我们需要对于使用SQLDA描述符范围的游标操作,给一个总体的介绍
在使用SQLDA描述符范围游标的过主要分为两部分。查询的目标列表和绑定两部分。
2.1 Oracle样例
2.1.1 使用SQLDA描述符范围的游标获取数据
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlca.h>
#include <sqlda.h>int main() {
{ char sqlstr[1000] = "select * from test";EXEC SQL PREPARE DYSQLSA FROM :sqlstr;EXEC SQL DECLARE Cursorbase CURSOR FOR DYSQLSA;EXEC SQL DESCRIBE BIND VARIABLES for DYSQLSA INTO BindUnit;for (i = 0 ; i < BindUnit->N; i++){ //这里设置L、V、I、T等类型}EXEC SQL OPEN Cursorbase USING DESCRIPTOR BindUnit;EXEC SQL DESCRIBE SELECT LIST for DYSQLSA INTO SelectUnit;for (i=0; i < SelectUnit->F; i++){//这里设置L、V、I、T等switch (type){ //类型处理} }for(;;){ EXEC SQL FOR :array_size FETCH Cursorbase USING DESCRIPTOR SelectUnit;for (j = 0 ;j < sqlca.sqlerrd[2] - iRowCount; j ++){for (i=0; i < SelectUnit->F; i++){ //对每个字段进行处理} }iRowCount = sqlca.sqlerrd[2];if (sqlca.sqlcode == 1403) break; } EXEC SQL commit;EXEC SQL CLOSE cur;
}
我们需要说明一下几点:
(1)EXEC SQL DESCRIBE BIND VARIABLES for DYSQLSA INTO BindUnit; DESCRIBE 已将 F 设置为已处理 SQL 语句中找到的占位符的实际数量。
这个BindUnit主要针对需要绑定的变量:
举个例:
select * from test;这个查询语句就不存在需要绑定的变量,因此不需要对BindUnit进行设置;
select * from test where a := :var;这就涉及到对描述符进行赋值部分oracle 官方sqlda
(2)相较于不使用SQLDA描述符范围的游标操作,在打开游标时我们会使用到SQLDA描述符范围:
EXEC SQL OPEN Cursorbase USING DESCRIPTOR BindUnit;
(3) EXEC SQL DESCRIBE SELECT LIST for DYSQLSA INTO SelectUnit; DESCRIBE 已将 F 设置为查询选择列表中的实际项数。如果 SQL 语句不是查询,则 F 设置为零。
(4) EXEC SQL FOR :array_size FETCH Cursorbase USING DESCRIPTOR SelectUnit; for :arrary_size指定获取的行数;
(5)释放游标的过程为:
EXEC SQL commit;EXEC SQL CLOSE cur; (commit提交之后并不影响close游标)。
2.2 LightDB样例
2.2.1 使用SQLDA描述符范围的游标获取数据
针对上面的程序,LightDB目前采用的方案主要是改写。
改写的主要内容分为:
(1) SQLDA类型不支持,需要替换成sqlda_t
sqlda_t *SelectUnit ;
sqlda_t *BindUnit;
(2)EXEC SQL OPEN Cursorbase USING DESCRIPTOR BindUnit; 部分改写;
if (是否有绑定参数)EXEC SQL EXECUTE ExecSQLs USING SQL DESCRIPTOR indesc;elseEXEC SQL EXECUTE ExecSQLs;
原因:LightDB不支持没有绑定参数的使用SQLDA描述符范围
(3)EXEC SQL DESCRIBE BIND VARIABLES for DYSQLSA INTO BindUnit;
for ( j = 0 ; j < sqlpkg->paramcount; j ++){BindUnit->sqlvar[j].sqldata = (char *)malloc(sqlpkg->params[j].val.data_size+1); memcpy(BindUnit->sqlvar[j].sqldata, sqlpkg->params[j].val.data, sqlpkg->params[j].val.data_size );BindUnit->sqlvar[j].sqldata[sqlpkg->params[j].val.data_size] = '\0';BindUnit->sqlvar[j].sqltype = 1; BindUnit->sqlvar[j].sqllen = val.data_size;BindUnit->sqln = sqlpkg->paramcount;}
(4) EXEC SQL DESCRIBE SELECT LIST for DYSQLSA INTO SelectUnit; 语法是不支持的,这部分在LightDB中是由内核完成的
(5) EXEC SQL FOR :array_size FETCH Cursorbase USING DESCRIPTOR SelectUnit; 语法是不支持的,但是可以通过fetch into解决获取行数的问题
EXEC SQL FETCH :array_size CursorbaseSuper INTO DESCRIPTOR SelectUnit;
(6) commit和close执行位置问题,LightDB只支持先close后commit
EXEC SQL CLOSE cur; EXEC SQL commit;
3 总结:不同项
(1)SQLDA类型不支持
(2)EXEC SQL OPEN Cursorbase USING DESCRIPTOR BindUnit使用时有条件限制
(3)EXEC SQL DESCRIBE BIND VARIABLES for DYSQLSA INTO BindUnit;语法不支持
(4)EXEC SQL DESCRIBE SELECT LIST for DYSQLSA INTO SelectUnit;语法不支持
(5)EXEC SQL FOR :array_size FETCH Cursorbase USING DESCRIPTOR SelectUnit;不支持
(6)commit和close执行位置问题
隐藏不支持项,在于对各个绑定或者获取字段时其类型的转化是有差别的,以及在第一部分出现的不支持项,在这部分依旧是不支持项。
三、总结
针对所列举的不支持项,LightDB目前正在全力适配中,相信不久之后上述问题就能解决。