Prayer

在一般中寻求卓越
posts - 1256, comments - 190, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

关于动态SQL

Posted on 2008-09-10 10:36 Prayer 阅读(2309) 评论(0)  编辑 收藏 引用 所属分类: 数据库,SQLC/C++

前几天一个朋友要我写点关于数据库编程方面的东西,可一直由于工作比较忙,到现在已经一个多星期了,正好烟草的项目由于最终方案的原因而停止了,新的ATM的P端的程序昨天基本已经顺利调整完了。相信今天上午是个清闲的上午,就写点关于动态SQL方面的东西吧。


嵌入SQL语言都是静态SQL语言,即在编译时已经确定了引用的表和列。主变量不改变表和列信息。我们使用主变量改变查询参数,但是不能用主变量代替表名或列名。否则,系统报错。动态SQL语句就是来解决这个问题。
动态SQL语句的目的是,不是在编译时确定SQL的表和列,而是让程序在运行时提供,并将SQL语句文本传给DBMS执行。静态SQL语句在编译时已经生成执行计划。而动态SQL语句,只有在执行时才产生执行计划。动态SQL语句首先执行PREPARE语句要求DBMS分析、确认和优化语句,并为其生成执行计划。DBMS还设置SQLCODE以表明语句中发现的错误。当程序执行完“PREPARE”语句后,就可以用EXECUTE语句执行执行计划,并设置SQLCODE,以表明完成状态。
  使用动态SQL,共分成四种方法:

方法     支持的SQL语句                                      实现方法

1        该语句内不包含宿主变量,该语句不是查询语句         execute immediate
2        该语句内包含输入宿主变量 ,该语句不是查询语句      prepare和execute
3        包含已知数目的输入宿主变量或列的查询               prepare和fetch
4        包含未知数目的输入宿主变量或列的查询               prepare和fetch,用描述符
按照功能和处理上的划分,动态SQL应该分成两类来解释:动态修改和动态查询。方法1和方法2完成动态修改。方法3和方法4完成了动态查询。

一、动态修改
方法1和方法2完成动态修改。对于方法1,表示要执行一个完整的T-SQL语句,该语句没有宿主变量,不是一个查询语句。因为没有宿主变量来带入不同的参数,所以不能通过方法1来重复执行修改语句。具体语法为:
exec sql [at connection_name] execute immediate
{: host_variable | string};
其中,host_variable和string是存放完整T-SQL语句。
例:提示用户输入被更新书的条件,然后组合成为一个完整的SQL语句,并执行更新。
exec sql begin declare section;
CS_CHAR sqlstring[200];
exec sql end declare section;
char cond[150];
exec sql whenever sqlerror call err_p();
exec sql whenever sqlwarning call warn_p();
strcpy(sqlstring,
"update titles set price=price*1.10 where ");
printf("Enter search condition:");
scanf("%s", cond);
strcat(sqlstring, cond);
exec sql execute immediate :sqlstring;
exec sql commit work;
对于方法2,可以执行一个包含输入宿主变量的动态修改语句。该方法要使用PREPARE语句和EXECUTE语句。PREPARE语句是动态SQL语句独有的语句。其语法为:
   PREPARE 语句名  FROM 宿主变量|字符串
该语句接收含有SQL语句串的宿主变量,并把该语句送到DBMS。DBMS编译语句并生成执行计划。在语句串中包含一个“?”表明参数,当执行语句时,DBMS需要参数来替代这些“?”。PREPRARE执行的结果是,DBMS用语句名标志准备后的语句。SQL SERVER编译后的语句以临时存储过程的形式存放在缓冲区中。语句名类似于游标名,是一个SQL标识符。在执行SQL语句时,EXECUTE语句后面是这个语句名。请看下面这个例子:
EXEC SQL BEGIN DECLARE SECTION;
char        prep[] = "INSERT INTO mf_table VALUES(?,?,?)";
char        name[30];
char        car[30];
double         num;
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE prep_stat FROM :prep;
while (SQLCODE == 0)
{
   strcpy(name, "Elaine");
   strcpy(car, "Lamborghini");
   num = 4.9;
   EXEC SQL EXECUTE prep_stat USING :name, :car, :num;
}
  在这个例子中,prep_stat是语句名,prep宿主变量的值是一个INSERT语句,包含了三个参数(3个“?”)。PREPARE的作用是,DBMS编译这个语句并生成执行计划,并把语句名标志这个准备后的语句。值得注意的是,PREPARE中的语句名的作用范围为整个程序,所以不允许在同一个程序中使用相同的语句名在多个PREPARE语句中。
   EXECUTE语句是动态SQL独有的语句。它的语法如下:
EXECUTE 语句名 USING  宿主变量 | DESCRIPTOR 描述符名
请看上面这个例子中的“EXEC SQL EXECUTE prep_stat USING :name, :car, :num;”语句,它的作用是,请求DBMS执行PREPARE语句准备好的语句。当要执行的动态语句中包含一个或多个参数标志时,在EXECUTE语句必须为每一个参数提供值,如::name、:car和:num。这样的话,EXECUTE语句用宿主变量值逐一代替准备语句中的参数标志(“?”),从而,为动态执行语句提供了输入值。
使用主变量提供值,USING子句中的主变量数必须同动态语句中的参数标志数一致,而且每一个主变量的数据类型必须同相应参数所需的数据类型相一致。各主变量也可以有一个伴随主变量的指示符变量。当处理EXECUTE语句时,如果指示符变量包含一个负值,就把NULL值赋予相应的参数标志。除了使用主变量为参数提供值,也可以通过SQLDA提供值(关于这个,后边会讲到)。

二、动态游标

使用动态游标可以完成方法3。
   游标分为静态游标和动态游标两类。对于静态游标,在定义游标时就已经确定了完整的SELECT语句。在SELECT语句中可以包含主变量来接收输入值。当执行游标的OPEN语句时,主变量的值被放入SELECT语句。在OPEN语句中,不用指定主变量,因为在DECLARE CURSOR语句中已经放置了主变量。请看下面静态游标的例子:
EXEC SQL BEGIN DECLARE SECTION;
char szLastName[] = "White";
char szFirstName[30];
EXEC SQL END DECLARE SECTION;

EXEC SQL
   DECLARE author_cursor CURSOR FOR
   SELECT au_fname FROM authors WHERE au_lname = :szLastName;

EXEC SQL OPEN author_cursor;

EXEC SQL FETCH author_cursor INTO :szFirstName;
动态游标和静态游标不同。以下是动态游标使用的句法:
1)、声明游标:
 对于动态游标,在DECLARE CURSOR语句中不包含SELECT语句。而是,定义了在PREPARE中的语句名,PREPARE语句规定与查询相关的语句名称。具体语法为:
exec sql [at connection_name] declare cursor_name
cursor for statement_name;
如:EXEC SQL DECLARE author_cursor CURSOR FOR select_statement;
值得注意的是,声明动态游标是一个可执行语句,应该在PREPARE语句后执行。
2)、打开游标
完整语法为:OPEN 游标名 [USING 主变量名 | DESCRIPTOR 描述名]
   在动态游标中,OPEN语句的作用是使DBMS定位相关的游标在第一行查询结果前。当OPEN语句成功执行完毕后,游标处于打开状态,并为FETCH语句做准备。OPEN语句执行一条由PREPARE语句预编译的语句。如果动态查询正文中包含有一个或多个参数标志时,OPEN语句必须为这些参数提供参数值。USING子句的作用就是规定参数值。可以使用主变量提供参数值,也可以通过描述名(即SQLDA)提供参数值。如:EXEC SQL OPEN author_cursor USING :szLastName;。
3)、取一行值
   FETCH语法为:FETCH 游标名 INTO USING DESCRIPTOR 描述符名。
动态FETCH语句的作用是,把游标移到下一行,并把这一行的各列值送到SQLDA中。注意的是,静态FETCH语句的作用是用主变量表接收查询到的列值。在方法3中,使用的是静态FETCH语句获得值。动态FETCH语句只在方法4中使用。
4)、关闭游标
如:EXEC SQL CLOSE c1;
关闭游标的同时,会释放由游标添加的锁和放弃未处理的数据。在关闭游标前,该游标必须已经声明和打开。另外,程序终止时,系统会自动关闭所有打开的游标。
总之,在动态游标的DECLARE CURSOR语句中不包含SELECT语句。而是,定义了在PREPARE中的语句名,用PREPARE语句规定与查询相关的语句名称。当PREPARE语句中的语句包含了参数,那么在OPEN语句中必须指定提供参数值的主变量或SQLDA。动态DECLARE CURSOR语句是一个可执行语句。该子句必须在OPEN、FETCH、CLOSE语句之前使用。请看下面这个例子,描述了完成方法3的五个步骤:PREPARE、DECLARE、OPEN、FETCH和CLOSE。
……
EXEC SQL BEGIN DECLARE SECTION;
char szCommand[] = "SELECT au_fname FROM authors WHERE au_lname = ?";
char szLastName[] = "White";
char szFirstName[30];
EXEC SQL END DECLARE SECTION;
EXEC SQL  PREPARE select_statement FROM :szCommand;
EXEC SQL DECLARE author_cursor CURSOR FOR select_statement;
EXEC SQL OPEN author_cursor USING :szLastName;
EXEC SQL FETCH author_cursor INTO :szFirstName;
EXEC SQL CLOSE author_cursor;
………
 下面是一个实现方法3的实际例子。提示用户输入排序的条件,并把符合条件的书信息显示出来。
……
exec sql begin declare section;
CS_CHAR sqlstring[200];
CS_FLOAT bookprice,condprice;
CS_CHAR booktitle[200];
exec sql end declare section;
char orderby[150];
exec sql whenever sqlerror call err_p();
exec sql whenever sqlwarning call warn_p();
strcpy(sqlstring,
"select title,price from titles\
where price>? order by ");
printf("Enter the order by clause:");
scanf("%s", orderby);
strcat(sqlstring, orderby);
exec sql prepare select_state from :sqlstring;
exec sql declare select_cur cursor for
select_state;
condprice = 10; /* 可以提示用户输入这个值*/
exec sql open select_cur using :condprice;
exec sql whenever not found goto end;
for (;;)
{
exec sql fetch select_cur
into :booktitle,:bookprice;
printf("%20s %bookprice=%6.2f\n",
booktitle, bookprice);
}
end:
exec sql close select_cur;
exec sql commit work;
………

三、SQLDA

要实现方法4,则需要使用SQLDA(也可以使用SQL Descriptors,请读者参阅帮助信息)。可以通过SQLDA为嵌入SQL语句提供不确定的输入数据和从嵌入SQ语句中输出不确定数据。理解SQLDA的结构是理解动态SQL的关键。
我们知道,动态SQL语句在编译时可能不知道有多少列信息。在嵌入SQL语句中,这些不确定的数据是通过SQLDA完成的。SQLDA的结构非常灵活,在该结构的固定部分,指明了多少列等信息(如下图中的sqld=2,表示为两列信息),在该结构的后面,有一个可变长的结构(sd_column结构),说明每列的信息。


             SQLDA结构
Sd_Sqld=2
Sd_column
……


                                 
Sd_datafmt
Sd_Sqllen
Sd_sqldata
…..
                                  
Sd_datafmt
Sd_Sqllen
Sd_Sqldata
…..

                          
  具体SQLDA的结构在sqlda.h中定义,是:
typedef struct _sqlda
{
CS_SMALLINT sd_sqln;
CS_SMALLINT sd_sqld;
struct _sd_column
{
CS_DATAFMT sd_datafmt;
CS_VOID *sd_sqldata;
CS_SMALLINT sd_sqlind;
   CS_INT sd_sqllen;
CS_VOID*sd_sqlmore;
} sd_column[1];
} syb_sqlda;
typedef syb_sqlda SQLDA;

从上面这个定义看出,SQLDA是一种由两个不同部分组成的可变长数据结构。从位于SQLDA开端的sd_sqln到sd_sqld为固定部分,用于标志该SQLDA,并规定这一特定的SQLDA的长度。而后是一个或多个sd_column结构 ,用于标志列数据或参数。当用SQLDA把参数送到执行语句时,每一个参数都是一个sd_column结构;当用SQLDA返回输出列信息时,每一列都是一个sd_column 结构。具体每个元素的含义为:
lSd_Sqln。分配的sd_column结构的个数。等价于可以允许的最大输入参数的个数或输出列的个数。
lSd_Sqld。目前使用的sd_column结构的个数。
lSd_column[].sd_datafmt。标志同列相关的CS_DATAFMT结构。
lSd_column[].sd_Sqldata。指向数据的地址。注意,仅仅是一个地址。
lSd_column[].sd_sqllen。sd_sqldata指向的数据的长度。
lSd_column[].sd_Sqlind。代表是否为NULL。如果该列不允许为NULL,则该字段不赋值;如果该列允许为NULL,则:该字段若为0,表示数据值不为NULL,若为-1,表示数据值为NULL。
lSd_column[].sd_sqlmore。保留为将来使用。

下面我们来看一个具体的例子。这个例子是通过output_descriptor查询数据库中的数据,是通过input_descriptor传递参数。这个例子的作用是,模拟一个动态查询,并显示查询结果。动态查询的执行过程如下:
1)、如同构造动态UPDATE语句或DELETE语句的方法一样,程序在缓冲器中构造一个有效的SELECT语句。
2)、程序用PREPARE语句把动态查询语句送到DBMS,DBMS准备、确认和优化语句,并生成一个应用计划。
3)、动态DECLARE CURSOR语句说明查询游标,动态DECLARE CURSOR语句规定与动态SELECT语句有关的语句名称。如:例子中的statement。
4)、程序用DESCRIBE语句请求DBMS提供SQLDA中描述信息,即告诉程序有多少列查询结果、各列名称、数据类型和长度。DESCRIBE语句只用于动态查询。具体见下一节。
5)、为SQLDA申请存放一列查询结果的存储块(即:sqldata指向的数据区),也为SQLDA的列的指示符变量申请空间。程序把数据区地址和指示符变量地址送入SQLDA,以告诉DBMS向何处回送查询结果。
6)、动态格式的OPEN语句。即打开存放查询到的数据集(动态SELECT语句产生的数据)的第一行。
7)、动态格式的FETCH语句把游标当前行的结果送到SQLDA。(动态FETCH语句和静态FETCH语句的不同是:静态FETCH语句规定了用主变量接收数据;而动态FETCH语句是用SQLDA接收数据。)并把游标指向下一行结果集。
8)、CLOSE语句关闭游标。
具体程序如下:

exec sql include sqlca;
exec sql include sqlda;
...
/*input_ descriptor是通过SQLDA传递参数,output_descriptor是通过SQLDA返回列数据*/
SQLDA *input_descriptor, *output_descriptor;
CS_SMALLINT small;
CS_CHAR character[20];
/*申请空间*/
input_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*设置参数的最大个数*/
input_descriptor->sqlda_sqln = 3;
/*申请空间*/
output_descriptor = (SQLDA *)malloc(SYB_SQLDA_SIZE(3));
/*设置列数的最大值*/
output_descriptor->sqlda_sqln = 3;
*p_retcode = CS_SUCCEED;
/*连接数据库服务器*/
exec sql connect "sa" identified by password;
/* 创建一张example表,并插入一些例子数据,用于演示SQLDA的使用*/
exec sql drop table example;
exec sql create table example (fruit char(30), number int);
exec sql insert example values ('tangerine', 1);
exec sql insert example values ('pomegranate', 2);
exec sql insert example values ('banana', 3);
/* 准备和描述查询语句*/
exec sql prepare statement from
         "select fruit from example where number = ?";
/*describe语句的作用是,将查询所需要的参数信息存放在input_descriptor中*/
exec sql describe input statement using descriptor input_descriptor;
/*设置SQLDA中指向参数数据的地址信息(sqldata)和数据长度(sqlda_sqllen)*/
input_descriptor->sqlda_column[0].sqlda_datafmt.datatype =CS_SMALLINT_TYPE;
input_descriptor->sqlda_column[0].sqlda_sqldata = &small;
input_descriptor->sqlda_column[0].sqlda_sqllen = sizeof(small);
small = 2;
/*将查询语句的列信息存放在output_descriptor中*/
exec sql describe output statement using descriptor output_descriptor;
if (output_descriptor->sqlda_sqld != 1 ||
output_descriptor->sqlda_column[0].sqlda_datafmt.datatype !=
CS_CHAR_TYPE)
FAIL;
else
printf("first describe output \n");
/*设置存放列数据的地址信息*/
output_descriptor->sqlda_column[0].sqlda_sqldata = character;
output_descriptor->sqlda_column[0].sqlda_datafmt.maxlength = 20;
/*通过input_descriptor将输入参数带入查询语句,并将结果通过output_descriptor带出*/
exec sql execute statement into descriptor output_descriptor \
using descriptor input_descriptor;
/*打印结果---单行结果*/
printf("expected pomegranate, got %s\n",character);
/*释放申请的内存空间*/
exec sql deallocate prepare statement;
/* 多行结果示例。对多行查询语句做准备和描述操作*/
exec sql prepare statement from \
"select number from example where fruit = ?";
/*为多行结果声明游标*/
exec sql declare c cursor for statement;

exec sql describe input statement using descriptor input_descriptor;
/*设置查询的参数地址信息*/
input_descriptor->sqlda_column->sqlda_sqldata = character;
input_descriptor->sqlda_column->sqlda_datafmt.maxlength =CS_NULLTERM;
/*设置参数值为banana,也可以提示用户输入这些信息*/
strcpy(character, "banana");
input_descriptor->sqlda_column->sqlda_sqllen = CS_NULLTERM;
/*打开游标*/
exec sql open c using descriptor input_descriptor;
/*设置输出列的信息*/
exec sql describe output statement using descriptor output_descriptor;
/*设置存放数据的地址信息*/
output_descriptor->sqlda_column->sqlda_sqldata = character;
output_descriptor->sqlda_column->sqlda_datafmt.datatype =CS_CHAR_TYPE;
output_descriptor->sqlda_column->sqlda_datafmt.maxlength = 20;
output_descriptor->sqlda_column->sqlda_sqllen = 20;
output_descriptor->sqlda_column->sqlda_datafmt.format =
(CS_FMT_NULLTERM | CS_FMT_PADBLANK);
exec sql fetch c into descriptor output_descriptor;
/*打印列的数据*/
printf("expected pomegranate, got %s\n", character);
exec sql commit work;
……….
  上面这个例子是典型的动态查询程序。该程序中演示了PREPARE语句和DESCRIBE语句的处理方式,以及为程序中检索到的数据分配空间。要注意程序中如何设置sqlda_column结构中的的各个变量。这个程序也演示了OPEN、FETCH和CLOSE语句在动态查询中的应用。值得注意的是,FETCH语句只使用了SQLDA,不使用主变量。由于程序中预先申请了sqlda_column结构中的SQLDATA空间,所以DBMS知道将查询到的数据保存在何处。该程序还考虑了查询数据为NULL的处理。
  值得注意的是,SQDA结构不是SQL标准。每个数据库厂商的实现方式有可能不同。

四、DESCRIBE语句
 该语句只有动态SQL才有。该语句是在PREPARE语句之后,在OPEN语句之前使用。该语句的作用是,设置SQLDA中的描述信息,如:列名、数据类型和长度等。DESCRIBE语句的语法为:
DESCRIBE 语句名 INTO 描述符名
    如:exec sql describe output statement using descriptor output_descriptor;。
在执行DESCRIBE前,用户必须给出SQLDA中的SQLN的值(表示最多有多少列),该值也说明了SQLDA中最多有多少个sqlda_column结构。然后,执行DESCRIBE语句,该语句填充每一个sqlda_column结构。每个sqlda_column结构中的相应列为:
lSd_datafmt结构:列名等信息。
lSd_sqllen列:给出列的长度。
  注意,sd_sqldata列不填充。由程序在FETCH语句之前,给出数据缓冲器地址和指示符地址。
<!--[if !supportLineBreakNewLine]-->
下边的话,给出两个例子吧,方便对上边讲到的东西做个对比:

1)、TELECOM程序
  该程序是模拟电信费用查询。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined ( DB2 )
#define SQLNOTFOUND 100
#include <sql.h>
#elif defined ( ORA7 )
#define SQLNOTFOUND 1403
#endif

#if defined (SYBASE)
#define SQLNOTFOUND 100
#endif


EXEC SQL INCLUDE sqlca;

 
EXEC SQL BEGIN DECLARE SECTION;
   char user[30];
   char passwd[30];
char Usr_name[61];
char Dev_no[9]; 
long Call_flg;
char Called_arno[11];
char Called_no[15];
char Call_dat[21];
double Call_dur;  
double Call_rate;  
double Call_fee;  
double Add_fee;
char as_dev_no[9];
EXEC SQL END DECLARE SECTION;
void main(){
char statusbuf[1024], s[30];
/*连接到SQL SERVER服务器*/
printf("\nplease enter your userid ");
gets(user);
printf("\npassword ");
gets(passwd);
exec sql connect :user identified by :passwd;
exec sql use pubs2;
/*输入想要查询的电话号码*/
   printf("\nPlease enter the telephone number:");
gets(as_dev_no );
  /*声明游标*/
EXEC SQL DECLARE c1 CURSOR FOR
SELECT bas_infot.Usr_name, auto10a_list.Dev_no, auto10a_list.Call_flg, auto10a_list.Called_arno, auto10a_list.Called_no
, auto10a_list.Call_dat, auto10a_list.Call_dur, auto10a_list.Call_rate, auto10a_list.Call_fee,    
FROM auto10a_list, bas_infot
WHERE ( auto10a_list.Dev_no = bas_infot.Dev_no ) AND auto10a_list.Dev_no = :as_dev_no;
/*打开游标,指向查询相关电话信息的结果集*/
EXEC SQL OPEN c1;  /* :rk.2:erk. */
do{
/*取出一行数据到各个变量*/
EXEC SQL FETCH c1 INTO
:Usr_name, :Dev_no, :Call_flg, :Called_arno, :Called_no, :Call_dat, :Call_dur, :Call_rate, :Call_fee, :Add_fee;

if( (sqlca.sqlcode == SQLNOTFOUND) || (sqlca.sqlcode <0) )
break;
   /*显示数据*/
printf("%s,%s,%d,%s,%s,%s,%7.0f,%8.3f,%7.2f,%6.2f\n"
, Usr_name, Dev_no, Call_flg, Called_arno, Called_no, Call_dat, Call_dur, Call_rate, Call_fee, Add_fee );
}while(1);
EXEC SQL CLOSE c1; 
EXEC SQL DEALLOCATE CURSOR c1;
Exec sql disconnect all;
   return (0);
}
2)、 ADHOC程序
该程序的功能是:用户输入任意SQL语句,并执行和打印结果。


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Defines for BINDING */
/*初试化SQLDA*/
int init_da (SQLDA **DAPointer, int DAsqln);
/*为存放列数据的sd_column结构申请空间*/
int alloc_host_vars (SQLDA *sqldaPointer);
/*释放SQLDA所申请的空间*/
void free_da (SQLDA *sqldaPointer);
/*获取列名信息*/
char * readColName (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
/*获取列数据*/
char * readCol (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer);
#ifdef __cplusplus
}
#endif
/*定义最大列数*/
#define MAX_COLUMNS255

#define MAX_CHAR_FOR_DOUBLE20
#define MAX_CHAR_FOR_LONG15
#define MAX_CHAR_FOR_DATETIME30
#define MAX_CHAR_FOR_DEFAULT100
EXEC SQL INCLUDE SQLCA ;
EXEC SQL INCLUDE SQLDA ;

#define SQLSTATE sqlca.sqlstate
#define SQLCODE sqlca.sqlcode
/*处理SQL语句*/
int process_statement( char * ) ;
int main() {
  int rc ;
  char st[1024];
  char tmpstr[1024];
/*获得SQL语句*/
printf("Please enter the any sql statement:");
  gets( st);
  /* 处理该语句 */
  rc = process_statement( st ) ;
/*打印处理结果*/
  printf( "%d", rc);
  printf("the sqlcode is %d",SQLCODE);
}
/******************************************************************************
* FUNCTION : process_statement
* 处理sql  语句
*****************************************************************************/
int process_statement ( char * sqlInput ) {

  int counter = 0 ;
  SQLDA * sqldaPointer ;
  short sqlda_d ; /* Total columns */
  short idx;
  char buffer[4096];
  char varname[1024];
  char colnamelist[4096];
  EXEC SQL BEGIN DECLARE SECTION ;
     char st[1024] ;
  EXEC SQL END DECLARE SECTION ;
  strcpy( st, sqlInput ) ;
  /* 为SQLDA结构申请空间" */
  if (init_da( &sqldaPointer, MAX_COLUMNS ) == -1)
{
        return -1;
}
/*准备SQL语句*/
  EXEC SQL PREPARE statement1 from :st ;
  if (SQLCODE < 0)
{
        free_da(sqldaPointer);
    return SQLCODE;
    }
/*获取查询列的信息到SQLDA结构*/
  EXEC SQL DESCRIBE statement1 USING DESCRIPTOR sqldaPointer ;
/* 如果SQLCODE为0,则表示为SELECT语句 */
  if ( SQLCODE != 0 ) {
free_da(sqldaPointer);
return SQLCODE;
  } /* end if */
  sqlda_d = sqldaPointer->sd_sqld ;
  if ( sqlda_d > 0 ) {
     /* 为存放列数据的sd_column结构申请空间 */
     if (alloc_host_vars( sqldaPointer ) == -1)
{free_da(sqldaPointer);
return -1;
}
     /*声明游标*/
     EXEC SQL DECLARE pcurs CURSOR FOR statement1 ;
    /打开游标*/
     EXEC SQL OPEN pcurs ;
     if (SQLCODE < 0)
     return SQLCODE;
     /*取一行数据到SQLDA结构*/
     EXEC SQL FETCH pcurs INTO DESCRIPTOR sqldaPointer;
     if (SQLCODE < 0)
{
 EXEC SQL CLOSE pcurs ;
 return SQLCODE;
       }
     /*显示列标题 */
     colnamelist[0] = 0;
     for ( idx=0; idx< sqlda_d; idx++)
{ strcat(colnamelist, readColName(sqldaPointer, idx, buffer));
 if (idx < sqlda_d -1)
strcat(colnamelist, ",");
}
     /* 显示行数据*/
     while ( SQLCODE == 0 ) {
      counter++ ;
        for ( idx=0; idx< sqlda_d; idx++)     
         printf("%s",readCol(sqldaPointer, idx, buffer));
    EXEC SQL FETCH pcurs INTO DESCRIPTOR sqldaPointer ;
     }  /* endwhile */
    /*关闭游标*/
     EXEC SQL CLOSE pcurs ;
 EXEC SQL DEALLOCATE CURSOR pcurs;
     /* 释放为SQLDA申请的空间 */
     free_da( sqldaPointer ) ;
  } else { /* 不是SELECT语句*/
     EXEC SQL EXECUTE statement1 ;
     free_da( sqldaPointer ) ;
     if (SQLCODE < 0)
return SQLCODE;
  }  /* end if */
  return( 0 ) ;
}     /* end of program : ADHOC.CP */

/******************************************************************************* PROCEDURE : init_da
*为SQLDA分配空间。使用SQLDASIZE 获得SQLDA的大小。如果返回-1,则表示分配
*空间不成功。
******************************************************************************/
int init_da (SQLDA **DAPointer, int DAsqln) {
int idx;
  *DAPointer = (SQLDA *)malloc(SYB_SQLDA_SIZE(DAsqln));
  if (*DAPointer == NULL)
     return (-1);
  memset (*DAPointer, '\0', SYB_SQLDA_SIZE(DAsqln));
  (*DAPointer)->sd_sqln = DAsqln;
  (*DAPointer)->sd_sqld = 0;
  return 0;
}
/******************************************************************************* FUNCTION : alloc_host_vars
*为存放列数据的sd_column结构申请空间。如果返回-1,则表示不能获得足够内存。
******************************************************************************/
int alloc_host_vars (SQLDA *sqldaPointer) {
  short idx;
 
  for (idx = 0; idx < sqldaPointer->sd_sqld; idx++) {
     switch (sqldaPointer->sd_column[idx].sd_datafmt.datatype ) {
case CS_CHAR_TYPE:
case CS_VARCHAR_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
 sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (sqldaPointer->sd_column[idx].sd_sqllen + 1 );
sqldaPointer->sd_column[idx].sd_sqllen ++;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;

case CS_TINYINT_TYPE:
case CS_SMALLINT_TYPE:
case CS_INT_TYPE:
case CS_VOID_TYPE:
case CS_USHORT_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
 sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_LONG);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_LONG;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;

case CS_REAL_TYPE:
case CS_FLOAT_TYPE:
case CS_BIT_TYPE:
case CS_MONEY_TYPE:
case CS_MONEY4_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DOUBLE);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DOUBLE;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;

case CS_DATETIME_TYPE:
case CS_DATETIME4_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DATETIME);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DATETIME;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;

case CS_NUMERIC_TYPE:
case CS_DECIMAL_TYPE:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (sqldaPointer->sd_column[idx].sd_datafmt.precision + 3 );
sqldaPointer->sd_column[idx].sd_sqllen = sqldaPointer->sd_column[idx].sd_datafmt.precision + 3;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;

default:
sqldaPointer->sd_column[idx].sd_datafmt.datatype = CS_CHAR_TYPE;
 sqldaPointer->sd_column[idx].sd_sqldata = (char *) malloc (MAX_CHAR_FOR_DEFAULT);
sqldaPointer->sd_column[idx].sd_sqllen = MAX_CHAR_FOR_DEFAULT;
sqldaPointer->sd_column[idx].sd_datafmt.format = CS_FMT_NULLTERM;
break;
     } /* endswitch */
     if (sqldaPointer->sd_column[idx].sd_sqldata == NULL) {
return (-1);
     }
  } /* endfor */
 return 0;
}
/*******************************************************************************  FUNCTION : free_da
*  释放SQLDA申请的空间。
******************************************************************************/
void free_da (SQLDA *sqldaPointer) {
  short idx;
  for (idx = 0; idx < sqldaPointer->sd_sqld; idx++) {
     free (sqldaPointer->sd_column[idx].sd_sqldata);
  } /* endfor */
  free (sqldaPointer);
}
/******************************************************************************* PROCEDURE : readColName
* 返回列名
******************************************************************************/
char * readColName (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer) {
  strcpy(buffer, sqldaPointer->sd_column[sd_columnIndex].sd_datafmt.name);
  return buffer;
}
/******************************************************************************* PROCEDURE : readCol
* 返回列数据。
******************************************************************************/
char * readCol (SQLDA *sqldaPointer, short sd_columnIndex, char * buffer){
short numBytes;
  short idx, ind ;            /* Array idx variables */
  /* Variables for decoding packed decimal data */
  char tmpstr[1024];
  short collen;
  char *dataptr;
   /* 检查是否为NULL */
  if ( sqldaPointer->sd_column[sd_columnIndex].sd_sqlind )
     { buffer[0] = 0;
       return buffer;
     }
  /*返回列数据到buffer变量*/
  strcpy( buffer, (char *) sqldaPointer->sd_column[ sd_columnIndex ].sd_sqldata);
  return buffer;
}
/* COMMENT OUT OFF */


<!--[endif]-->
<!--[if !supportLineBreakNewLine]--><!--[endif]-->


http://rickya.bokee.com/viewdiary.14009093.html


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理