第三节 IBM DB2嵌入SQL语言 DB2支持SQL嵌入到C/C++、JAVA、COBOL、FORTRAN和REXX等语言。本节以SQL嵌入C/C++为例子,讲解静态的嵌入SQL编程和动态的嵌入SQL编程。 静态SQL嵌入C语言编程是指,应用程序在书写时,每个SQL语句的大部分都已确定下来(如:查询的表、列和语句的格式等),唯一不确定的是查询语句中某些特定变量的值,这些值可以在执行时由变量传进去,但是,值的类型要事先确定。 3.1 一个简单示例 首先,我们来看一个嵌入静态SQL语句的C程序。 例1、连接到SAMPLE数据库,查询LASTNAME为JOHNSON的FIRSTNAME信息。 #include #include #include #include "util.h" #include EXEC SQL INCLUDE SQLCA; (1) #define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1; int check_error (char eString[], struct sqlca *caPointer) { char eBuffer[1024]; char sBuffer[1024]; short rc, Erc; if (caPointer->sqlcode != 0) { printf ("--- error report ---\n"); printf ("ERROR occured : %s.\nSQLCODE : %ld\n", eString, caPointer->sqlcode); } return 0; } int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; (2) char firstname[13]; char userid[9]; char passwd[19]; EXEC SQL END DECLARE SECTION; printf( "Sample C program: STATIC\n" ); if (argc == 1) { EXEC SQL CONNECT TO sample; CHECKERR ("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); EXEC SQL CONNECT TO sample USER :userid USING :passwd; (3) CHECKERR ("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: static [userid passwd]\n\n"); return 1; } /* endif */
EXEC SQL SELECT FIRSTNME INTO :firstname FROM employee WHERE LASTNAME = 'JOHNSON';(4) CHECKERR ("SELECT statement"); (5) printf( "First name = %s\n", firstname ); EXEC SQL CONNECT RESET; (6) CHECKERR ("CONNECT RESET"); return 0; } /* end of program : STATIC.SQC */
上面是一个简单的静态嵌入SQL语句的应用程序。它包括了静态嵌入SQL的主要部分。 (1)中的include SQLCA语句定义并描述了SQLCA的结构。SQLCA用于应用程序和数据库之间的通讯,其中的SQLCODE返回SQL语句执行后的结果状态。 (2)在BEGIN DECLARE SECTION和END DECLARE SECTION之间定义了主变量。主变量可被SQL语句引用,也可以被C语言语句引用。它用于将程序中的数据通过SQL语句传给数据库管理器,或从数据库管理器接收查询的结果。在SQL语句中,主变量前均有“:”标志以示区别。 (3)在每次访问数据库之前必须做CONNECT操作,以连接到某一个数据库上。这时,应该保证数据库实例已经启动。 (4)是一条选择语句。它将表employee中的LASTNAME为“JOHNSON”的行数据的FIRSTNAME查出,并将它放在firstname变量中。该语句返回一个结果。可以通过游标返回多个结果。 (5)在该程序中通过调用宏CHECKERR(即调用函数check_error)来返回SQL语句执行的结果。Check_error函数在下面讲解。 (6)最后断开数据库的连接。 从上例看出,每条嵌入式SQL语句都用EXEC SQL开始,表明它是一条SQL语句。这也是告诉预编译器在EXEC SQL和“;”之间是嵌入SQL语句。如果一条嵌入式SQL语句占用多行,在C程序中可以用续行符“\”。 3.2 嵌入SQL语句 3.2.1宿主变量 1)、声明方法 宿主变量就是在嵌入式SQL语句中引用主语言说明的程序变量(如上例中的firstname变量)。如: …………. EXEC SQL SELECT FIRSTNME INTO :firstname (4) FROM employee WHERE LASTNAME = 'JOHNSON'; …………. 在嵌入式SQL语句中使用宿主变量前,必须采用BEGIN DECLARE SECTION 和END DECLARE SECTION之间给宿主变量说明。这两条语句不是可执行语句,而是预编译程序的说明。宿主变量是标准的C程序变量。嵌入SQL语句使用宿主变量把数据库中查询到的值返回给应用程序(称为输出宿主变量),也用于将程序中给定的值传到SQL语句中(称为输入宿主变量)。显然,C程序和嵌入SQL语句都可以访问宿主变量。 在使用宿主变量前,请注意以下几点: l宿主变量的长度不能超过30字节。开始的字母不能是EXEC和SQL。 l宿主变量必须在被引用之前定义。 l一个源程序文件中可以有多个SQL说明段。 l宿主变量名在整个程序中必须是唯一的。 2)、宿主变量的数据类型 宿主变量是一个用程序设计语言的数据类型说明并用程序设计语言处理的程序变量;另外,在嵌入SQL语句中用宿主变量保存数据库数据。所以,在嵌入SQL语句中,必须映射C数据类型为合适的DB2数据类型。必须慎重选择宿主变量的数据类型。请看下面这个例子: EXEC SQL BEGIN DECLARE SECTION; short hostvar1 = 39; char *hostvar2 = "telescope"; EXEC SQL END DECLARE SECTION;
EXEC SQL UPDATE inventory SET department = :hostvar1 WHERE part_num = "4572-3";
EXEC SQL UPDATE inventory SET prod_descrip = :hostvar2 WHERE part_num = "4572-3"; 在第一个update语句中,department列为smallint数据类型,所以应该把hostvar1定义为short数据类型。这样的话,从C到DB2的hostvar1可以直接映射。在第二个update语句中,prod_descip列为varchar数据类型,所以应该把hostvar2定义为字符数组。这样的话,从C到DB2的hostvar2可以从字符数组映射为varchar数据类型。 下表列出了C的数据类型和DB2的数据类型的一些转换关系: DB2数据类型C数据类型 Smallintshort IntegerLong Decimal(p,s)无 DoubleDouble DateChar[11] TimeChar[9] TimestampChar[27] Char(X)Char[X+1] Varchar(X)Char[X+1] Graphic(X)Wchar_t[X+1] Vargraphic(X)Wchar_t[X+1] 因为C没有date或time数据类型,所以DB2的date或time列将被转换为字符。缺省情况下,使用以下转换格式:mm dd yyyy hh:mm:ss[am | pm]。你也可以使用字符数据格式将C的字符数据存放到DB2的date列上。对于DECIMAL数据类型,在C语言中也没有对应的数据类型。但可以使用char数据类型实现。 3)、宿主变量和NULL 大多数程序设计语言(如C)都不支持NULL。所以对NULL的处理,一定要在SQL中完成。我们可以使用主机指示符变量来解决这个问题。在嵌入式SQL语句中,宿主变量和指示符变量共同规定一个单独的SQL类型值。指示变量和前面宿主变量之间用一个空格相分隔。如: EXEC SQL SELECT price INTO :price :price_nullflag FROM titles WHERE au_id = "mc3026" 其中,price是宿主变量,price_nullflag是指示符变量。指示符变量的值为: l-1。表示宿主变量应该假设为NULL。(注意:宿主变量的实际值是一个无关值,不予考虑)。 l=0。表示宿主变量不是NULL。 l>0。表示宿主变量不是NULL。而且宿主变量对返回值作了截断,指示变量存放了截断数据的长度。 所以,上面这个例子的含义是:如果不存在mc3026写的书,那么price_nullflag为-1,表示price为NULL;如果存在,则price为实际的价格。 指示变量也是一种宿主变量,也需要在程序中定义,它对应数据库系统中的数据类型为SMALLINT。为了便于识别宿主变量,当嵌入式SQL语句中出现宿主变量时,必须在变量名称前标上冒号(:)。冒号的作用是,告诉预编译器,这是个宿主变量而不是表名或列名。 3.2.2单行查询 单行查询是通过SELECT INTO语句完成。当这条语句执行时,查询的结果送入INTO所标志的变量中。如果SQLCODE是100,或者SQLSTATE是02000,则说明没有查询到结果或返回结果为NULL,这时,宿主变量不改变,否则,宿主变量中将包含查询的结果。如: …………. EXEC SQL SELECT FIRSTNME INTO :firstname FROM employee WHERE LASTNAME = 'JOHNSON'; ………….
3.2.3多行查询 对于多行结果,必须使用游标来完成。游标是一个与SELECT语句相关联的符号名,它使用户可逐行访问由DB2返回的结果集。下面这个例子演示了游标的使用方法。这个例子的作用是,逐行打印出每个经理的名字和部门。 #include #include #include #include "util.h" EXEC SQL INCLUDE SQLCA; #define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1; int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; char pname[10]; short dept; char userid[9]; char passwd[19]; EXEC SQL END DECLARE SECTION; printf( "Sample C program: CURSOR \n" ); if (argc == 1) { EXEC SQL CONNECT TO sample; CHECKERR ("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); EXEC SQL CONNECT TO sample USER :userid USING :passwd; CHECKERR ("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: cursor [userid passwd]\n\n"); return 1; } /* endif */
EXEC SQL DECLARE c1 CURSOR FOR (1) SELECT name, dept FROM staff WHERE job='Mgr' FOR UPDATE OF job; EXEC SQL OPEN c1; (2) CHECKERR ("OPEN CURSOR"); do { EXEC SQL FETCH c1 INTO :pname, :dept; (3) if (SQLCODE != 0) break; printf( "%-10.10s in dept. %2d will be demoted to Clerk\n", pname, dept ); } while ( 1 ); EXEC SQL CLOSE c1; (4) CHECKERR ("CLOSE CURSOR"); EXEC SQL ROLLBACK; CHECKERR ("ROLLBACK"); printf( "\nOn second thought -- changes rolled back.\n" ); EXEC SQL CONNECT RESET; CHECKERR ("CONNECT RESET"); return 0; } /* end of program : CURSOR.SQC */ 在上面这个程序中, (1)定义了一个游标,并指明游标的名字为C1,同时给出了相对于游标的查询语句和游标类型(UPDATE)。 (2)打开游标。系统执行查询语句,建立结果表,将游标指针指向第一条记录之前。 (3)FETCH语句将指针的下一条记录取出,将记录中的数据存放在相应的宿主变量中。同时指针下移。 (4)用CLOSE关闭游标。
3.2.4插入、删除和修改操作 DB2中的插入、删除和修改操作同SQL语句中INSERT、DELETE和UPDATE语句类似。只需在相应的SQL语句前加上EXEC SQL即可。请看下面这个例子: 例、将staff表中所有工作为“Mgr”的职工的工作改变为“clerk”,并将staff表中所有工作为“sale”的职工信息删除。最后插入一新行。 #include #include #include #include #include "util.h" EXEC SQL INCLUDE SQLCA; (1) #define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1; int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; (2) char statement[256]; char userid[9]; char passwd[19]; char jobUpdate[6]; EXEC SQL END DECLARE SECTION; printf( "\nSample C program: UPDAT \n"); if (argc == 1) { EXEC SQL CONNECT TO sample; CHECKERR ("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); EXEC SQL CONNECT TO sample USER :userid USING :passwd; (3) CHECKERR ("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: updat [userid passwd]\n\n"); return 1; } /* endif */ strcpy (jobUpdate, "Clerk"); EXEC SQL UPDATE staff SET job = :jobUpdate WHERE job = 'Mgr'; (4) CHECKERR ("UPDATE STAFF"); printf ("All 'Mgr' have been demoted to 'Clerk'!\n" ); strcpy (jobUpdate, "Sales"); EXEC SQL DELETE FROM staff WHERE job = :jobUpdate; (5) CHECKERR ("DELETE FROM STAFF"); printf ("All 'Sales' people have been deleted!\n"); EXEC SQL INSERT INTO staff valueS (999, 'Testing', 99, :jobUpdate, 0, 0, 0); (6) CHECKERR ("INSERT INTO STAFF"); printf ("New data has been inserted\n"); EXEC SQL ROLLBACK; (7) CHECKERR ("ROLLBACK"); printf( "On second thought -- changes rolled back.\n" ); EXEC SQL CONNECT RESET; CHECKERR ("CONNECT RESET"); return 0; } /* end of program : UPDAT.SQC */ 上述语句: (1)包含SQLCA结构。该结构用于将SQL语句执行的结果信息返回给应用程序。 (2)宿主变量定义。 (3)连接到DB2的SAMPLE数据库。 (4)UPDATE语句将staff表中所有工作为“Mgr”的职工的工作改变为“clerk”。 (5)DELETE语句将staff表中所有工作为“sale”的职工信息删除。 (6)INSERT语句插入一新行。
指定位置的UPDATE语句和DELETE语句
游标操作除了可以将多行的查询结果返回给应用程序,它还可以与UPDATE语句和DELETE相结合,根据游标当前的位置,对指针所指的这个行数据执行UPDATE操作和DELETE操作。它的语法为: UPDATE…. WHERE CURRENT OF cursor_name DELETE [FROM] {table_name | view_name} WHERE CURRENT OF cursor_name 请看下面这个例子。这个例子的作用是:将部门大于40的员工的工作改变为“clerk”,部门小于等于40的员工信息删除。 #include #include #include #include "util.h" EXEC SQL INCLUDE SQLCA; #define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1; int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; char pname[10]; short dept; char userid[9]; char passwd[19]; EXEC SQL END DECLARE SECTION; printf( "Sample C program: OPENFTCH\n" ); if (argc == 1) { EXEC SQL CONNECT TO sample; CHECKERR ("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); EXEC SQL CONNECT TO sample USER :userid USING :passwd; CHECKERR ("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: openftch [userid passwd]\n\n"); return 1; } /* endif */ EXEC SQL DECLARE c1 CURSOR FOR SELECT name, dept FROM staff WHERE job='Mgr' FOR UPDATE OF job; EXEC SQL OPEN c1; CHECKERR ("OPEN CURSOR");
do { EXEC SQL FETCH c1 INTO :pname, :dept; if (SQLCODE != 0) break;
if (dept > 40) { printf( "%-10.10s in dept. %2d will be demoted to Clerk\n", pname, dept ); EXEC SQL UPDATE staff SET job = 'Clerk' WHERE CURRENT OF c1; CHECKERR ("UPDATE STAFF"); } else { printf ("%-10.10s in dept. %2d will be DELETED!\n", pname, dept); EXEC SQL DELETE FROM staff WHERE CURRENT OF c1; CHECKERR ("DELETE"); } /* endif */ } while ( 1 );
EXEC SQL CLOSE c1; CHECKERR ("CLOSE CURSOR"); EXEC SQL ROLLBACK; CHECKERR ("ROLLBACK"); printf( "\nOn second thought -- changes rolled back.\n" ); EXEC SQL CONNECT RESET; CHECKERR ("CONNECT RESET"); return 0; } /* end of program : OPENFTCH.SQC */
在通过游标进行UPDATE和DELETE操作时,相应的游标在定义时必须加上FOR UPDATE子句。操作时,游标必须打开,而且正指向一行数据。 3.2.5 SQLCA 应用程序执行时,每执行一条SQL语句,就返回一个状态符和一些附加信息。这些信息反映了SQL语句或API的执行情况,它有助于用户分析应用程序的错误所在。这些信息都存放在一个定义在sqlca.h的sqlca结构中。如果一个源文件中后SQL语句,则必须要在源程序中定义一个SQLCA结构,而且名为SQLCA。最简单的定义方法是在源文件中加入一些语句:EXEC SQL INCLUDE sqlca.h 下面,我们首先看看SQLCA的结构: SQL_STRUCTURE sqlca { _SQLOLDCHAR sqlcaid[8]; /* Eyecatcher = 'SQLCA ' */ long sqlcabc; /* SQLCA size in bytes = 136 */ #ifdef DB2_SQL92E long sqlcade; /* SQL return code */ #else long sqlcode; /* SQL return code */ #endif short sqlerrml; /* Length for SQLERRMC */ _SQLOLDCHAR sqlerrmc[70]; /* Error message tokens */ _SQLOLDCHAR sqlerrp[8]; /* Diagnostic information */ long sqlerrd[6]; /* Diagnostic information */ _SQLOLDCHAR sqlwarn[11]; /* Warning flags */ #ifdef DB2_SQL92E _SQLOLDCHAR sqlstat[5]; /* State corresponding to SQLCODE */ #else _SQLOLDCHAR sqlstate[5]; /* State corresponding to SQLCODE */ #endif }; 结构中各个字段的作用是: lSqlcaid:包含字符串“SQLCA”。 lSqlcabc:包含SQLCA结构的长度。 lSqlcode:该值反映了SQL语句执行后的状态,0表示SQL执行成功;<0表示SQL语句执行出错;>0反映了一些特殊情况(如:没有查询结果)。不同的数据库产品,该值代表的含义可能不同。 lSqlerrml:sqlerrmc域中数据的实际长度。 lSqlerrmc:由0个或多个字符串组成,它对返回的值给予一个更详细的解释。 lSqlerrp:包含一些对用户没有用的信息。 lSqlwarn:包含了一些警告信息。 lSqlstate:长度为5的字符串。它表示SQL语句执行的结果。它的每一个含义是遵循ANSI/SQL 92标准。各个数据库产品的sqlstate域的含义都是相同的。 为了更方便地读取sqlca中SQL语句执行后的结果或错误,DB2提供了一个函数——sqlaintp,它在sql.h中声明:sqlaintp(msgbuf,bufsize,linesize,sqlcaptr)。其中msgbuf中存放信息;bufsize中存放了msgbuf的长度;linesize中存放了两个执行符之间的字符长度。函数的返回值为正,代表信息的长度;为负代表没有信息返回。 下面这个例子解释了sqlca和sqlaintp的使用方法: int check_error (char eString[], struct sqlca *caPointer) { char eBuffer[1024]; char sBuffer[1024]; short rc, Erc; /*通过SQLCODE来判断是否出错*/ if (caPointer->sqlcode != 0) { printf ("--- error report ---\n"); printf ("ERROR occured : %s.\nSQLCODE : %ld\n", eString, caPointer->sqlcode); /* 获取SQLSTATE信息*/ rc = sqlogstt (sBuffer, 1024, 80, caPointer->sqlstate); /*获取调用API的错误信息*/ Erc = sqlaintp (eBuffer, 1024, 80, caPointer); /* Erc中存放了eBuffer 的长度*/ if (Erc > 0) printf ("%s", eBuffer); if (caPointer->sqlcode < 0) { /*错误信息*/ if (rc == 0) { printf ("\n%s", sBuffer); } printf ("--- end error report ---\n"); return 1; } else { /* 仅仅是警告信息 */ if (rc == 0) { printf ("\n%s", sBuffer); } printf ("--- end error report ---\n"); printf ("WARNING - CONTINUING PROGRAM WITH WARNINGS!\n"); return 0; } /* endif */ } /* endif */ return 0; } 在每条SQL语句执行后都返回一个SQLCA结构,SQLCA结构中记载了SQL语句执行后的结果信息。用户可以根据返回信息执行各种操作。DB2也提供了WHENEVER语句。具体可参见SQL SERVER中的WHENEVER。但是,在DB2中,没有WHENEVER…CALL这个处理。 3.2.6事务 所谓事务,就是一系列应用程序和数据库之间交互操作的集合。一旦一个事务开始执行,则事务中的操作要么全部执行,要么全部不执行。 l事务开始:DB2事务是隐式开始的,除了下列的一些语句,其他任何一个可执行的SQL语句都隐式地开始一个事务。 BEGIN DECLARE SECTION END DECLARE SECTION DECLARE CURSOR INCLUDE SQLCA INCLUDE SQLDA WHENEVER l事务结束:事务由一个可执行的SQL语句开始,后面执行的所有SQL语句都将属于同一个事务,该事务一直遇到COMMIT或ROLLBACK命令时才结束。 COMMIT操作的作用是,结束当前的事务,事务对数据库所做的修改永久化。ROLLBACK的作用是,结束当前的事务,将被修改的数据恢复到事务执行以前的状态,即取消事务执行产生的影响。当程序结束时,系统自动隐式地执行COMMIT操作,如果系统检测到死锁等故障,则隐式地执行ROLLBACK操作。 3.3 DB2的嵌入SQL程序处理过程 嵌入SQL程序处理,由一个源程序创建为一个可执行文件的过程。如下图所示:
图6-3 嵌入SQL处理过程 从上图看出,首先对源文件做预编译(precompiler),生成两个部分文件:一部分是纯的C程序源文件,它们和其他的C程序源文件一起,经过编译和连接生成可执行的程序(executable program);而另一部分是bind文件或package文件。Bind文件经过binder操作以后,也生成为package文件。所谓package,实际上是SQL语句的访问计划。所以,预编译器将源程序中的SQL语句提出来,生成他们的访问计划,并将访问计划存放在数据库管理器中。当执行程序并遇到访问数据库的命令时,它将到数据库管理器中寻找属于它的访问计划,然后按照访问计划中所设计的方法对数据库进行访问。具体来说: 第一步、预编译 源程序生成以后,在源程序中嵌入了许多SQL语句,而SQL语句是宿主语言编译器所不认识的,所以在用宿主语言编译器进行编译、连接之前必须将SQL语句分离出来,这就是预编译所做的工作。DB2中预编译操作是通过PREP命令执行的,PREP命令首先将源程序中的所有有关SQL语句全部注释起来,对它进行分析和语法检查。如果源程序中的SQL语句全部书写正确,则将这些SQL语句转换成C语言可以识别的一系列的API函数。这些函数可以在函数执行时访问数据库,然后将源文件中所有用于生成数据库管理器的PACKAGE的数据提出组合成一个BIND文件。也可以直接生成一个PACKAGE,但这相当于在预编译后又执行了一次BIND操作。在预编译时,对整个源程序文件中的所有变量做统一处理而不根据变量的生命期来处理,所以宿宿主变量在整个程序中是唯一的。下面讲解预编译的步骤: 1)、连接到一个数据库,该操作是为BIND做准备。操作如下: db2 connect to cicstest 2)、执行预编译命令,假设源文件为adhoc.sqc,则: db2 prep adhoc.sqc bindfile 下面我们对预编译的几种输出文件进行讨论。 l预编译后生成的C语言源文件:该文件中原有的SQL语句,已经全部加上注释并转换成了C语言可以识别的API调用。 lBIND文件:如果在预编译时使用BINDFILE选项,则生成BIND文件,BIND文件的后缀为.bnd,BIND文件可以在将来使用BIND命令来生成PACKAGE。 db2 bind adhoc.bnd 如果在预编译时,只生成BIND文件,那么即使在预编译时,不能访问某些数据库对象,系统也只是报警,而不会报错。如果使用PACKAGE选项,则生成PACKAGE。如果有MESSAGE选项,则生成信息文件,它包含了所有的返回信息,如:警报、错误等。它便于程序员对源程序做进一步的修改。 第二步、编译和连接 在预编译后,程序中只有C语言语句,它们都可以为C语言的编译器所识别。所以,可以按照一般的方法进行编译和连接,但在将SQL语句转换以后,在C语言程序中,又引入了许多一般的C语言系统所没有的INCLUDE文件和函数库,这些均在DB2的SDK中。所以,要生成可执行的程序,就必须安装DB2的SDK,并且做以下设置: set INCLUDE=$(DB2PATH)\include;%include% set LIB=$(DB2PATH)\lib;%LIB% 下面是编译和连接: cl -o adhoc.exe adhoc.c 生成的可执行文件必须与数据库管理器中的PACKAGE相结合,才能执行。 下面对BIND做进一步解释。 PACKAGE是DB2为SQL语句制定的访问计划。通过precompile之后,源程序中的SQL语句部分就被分离出来。PACKAGE就是根据具体的SQL语句和数据库中的信息生成的针对每条SQL语句的访问计划,它存放在DB2数据库服务器上。应用程序执行到SQL语句时,就到相应的服务器上去找它们的PACKAGE,数据库服务器根据PACKAGE执行具体的数据库操作。所以,如果一个应用程序访问了多个数据库服务器,则该应用程序应在它访问到的数据库服务器上均生成相应的PACKAGE。因此,当遇到这种情况时,推荐的方式是将源程序分成若干个文件,每个文件只访问一个服务器,然后分别进行预编译。 执行PREP命令时,加上选项PACKAGE或不注明BINDFILE、SYNTAX或SQLFLAG选项,这时BIND操作将自动进行。 直接使用BIND命令从BIND文件中生成PACKAGE存放在数据库管理器中。BIND完成的功能是,从数据库中找到SQL语句所涉及的表,查看SQL语句中提到的表名及属性是否与数据库中的表名和属性相匹配,以及应用程序开发者是否有权限查询或修改应用程序中所涉及到的表及属性。这就是为什么在预编译之前要连接到相应的数据库上的原因。在做BIND操作后,在数据库管理器中就生成一个PACKAGE,PACKAGE的名字与源程序的文件名字相同。 一个源文件在数据库中可有多个PACKAGE存在。为了区分不同的PACKAGE,DB2中引入了时间戳的概念。在PREP执行时,系统对生成的修改过的C语言程序和BIND文件以及PACKAGE中都加入了一个时间戳。BIND文件在生成PACKAGE时也将时间戳传递下来,修改过的C语言程序在生成可执行程序时,同样也将时间戳传递下去。当应用程序运行时,可执行程序是通过时间戳找到相应的PACKAGE,如果时间戳不匹配,则说明版本更新,需要做BIND。 本章第一个例子被编译后所生成的C程序: static char sqla_program_id[40] = {111,65,65,66,65,73,65,70,89,65,78,71,90,72,32,32,68,69,77,79, 68,66,50,32,67,65,51,54,75,75,67,81,48,49,49,49,49,50,32,32}; #include "sqladef.h" static struct sqla_runtime_info sqla_rtinfo = {{'S','Q','L','A','R','T','I','N'}, sizeof(wchar_t), 0, {' ',' ',' ',' '}}; #line 1 "demodb2.sqc" #include #include #include #include "util.h" #include /* EXEC SQL INCLUDE SQLCA; */ /* SQL Communication Area - SQLCA - structures and constants */ #include "sqlca.h" struct sqlca sqlca; #line 6 "demodb2.sqc" #define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1; int check_error (char eString[], struct sqlca *caPointer) { char eBuffer[1024]; char sBuffer[1024]; short rc, Erc; if (caPointer->sqlcode != 0) { printf ("--- error report ---\n"); printf ("ERROR occured : %s.\nSQLCODE : %ld\n", eString, caPointer->sqlcode); } return 0; } int main(int argc, char *argv[]) { /* EXEC SQL BEGIN DECLARE SECTION; */ #line 21 "demodb2.sqc" char firstname[13]; char userid[9]; char passwd[19]; /* EXEC SQL END DECLARE SECTION; */ #line 25 "demodb2.sqc"
printf( "Sample C program: STATIC\n" ); if (argc == 1) { /* EXEC SQL CONNECT TO sample; */ { #line 28 "demodb2.sqc" sqlastrt(sqla_program_id, &sqla_rtinfo, &sqlca); #line 28 "demodb2.sqc" sqlaaloc(2,1,1,0L); { struct sqla_setd_list sql_setdlist[1]; #line 28 "demodb2.sqc" sql_setdlist[0].sqltype = 460; sql_setdlist[0].sqllen = 7; #line 28 "demodb2.sqc" sql_setdlist[0].sqldata = (void*)"sample"; #line 28 "demodb2.sqc" sql_setdlist[0].sqlind = 0L; #line 28 "demodb2.sqc" sqlasetd(2,0,1,sql_setdlist,0L); } #line 28 "demodb2.sqc" sqlacall((unsigned short)29,4,2,0,0L); #line 28 "demodb2.sqc" sqlastop(0L); } #line 28 "demodb2.sqc" CHECKERR ("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); /* EXEC SQL CONNECT TO sample USER :userid USING :passwd; */ { #line 34 "demodb2.sqc" sqlastrt(sqla_program_id, &sqla_rtinfo, &sqlca); #line 34 "demodb2.sqc" sqlaaloc(2,3,2,0L); { struct sqla_setd_list sql_setdlist[3]; #line 34 "demodb2.sqc" sql_setdlist[0].sqltype = 460; sql_setdlist[0].sqllen = 7; #line 34 "demodb2.sqc" sql_setdlist[0].sqldata = (void*)"sample"; #line 34 "demodb2.sqc" sql_setdlist[0].sqlind = 0L; #line 34 "demodb2.sqc" sql_setdlist[1].sqltype = 460; sql_setdlist[1].sqllen = 9; #line 34 "demodb2.sqc" sql_setdlist[1].sqldata = (void*)userid; #line 34 "demodb2.sqc" sql_setdlist[1].sqlind = 0L; #line 34 "demodb2.sqc" sql_setdlist[2].sqltype = 460; sql_setdlist[2].sqllen = 19; #line 34 "demodb2.sqc" sql_setdlist[2].sqldata = (void*)passwd; #line 34 "demodb2.sqc" sql_setdlist[2].sqlind = 0L; #line 34 "demodb2.sqc" sqlasetd(2,0,3,sql_setdlist,0L); } #line 34 "demodb2.sqc" sqlacall((unsigned short)29,5,2,0,0L); #line 34 "demodb2.sqc" sqlastop(0L); }
#line 34 "demodb2.sqc" CHECKERR ("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: static [userid passwd]\n\n"); return 1; } /* endif */ /* EXEC SQL SELECT FIRSTNME INTO :firstname FROM employee WHERE LASTNAME = 'JOHNSON'; */ { #line 44 "demodb2.sqc" sqlastrt(sqla_program_id, &sqla_rtinfo, &sqlca); #line 44 "demodb2.sqc" sqlaaloc(3,1,3,0L); { struct sqla_setd_list sql_setdlist[1]; #line 44 "demodb2.sqc" sql_setdlist[0].sqltype = 460; sql_setdlist[0].sqllen = 13; #line 44 "demodb2.sqc" sql_setdlist[0].sqldata = (void*)firstname; #line 44 "demodb2.sqc" sql_setdlist[0].sqlind = 0L; #line 44 "demodb2.sqc" sqlasetd(3,0,1,sql_setdlist,0L); } #line 44 "demodb2.sqc" sqlacall((unsigned short)24,1,0,3,0L); #line 44 "demodb2.sqc" sqlastop(0L); }
#line 44 "demodb2.sqc"
CHECKERR ("SELECT statement"); printf( "First name = %s\n", firstname );
/* EXEC SQL CONNECT RESET; */ { #line 47 "demodb2.sqc" sqlastrt(sqla_program_id, &sqla_rtinfo, &sqlca); #line 47 "demodb2.sqc" sqlacall((unsigned short)29,3,0,0,0L); #line 47 "demodb2.sqc" sqlastop(0L); } #line 47 "demodb2.sqc" CHECKERR ("CONNECT RESET"); return 0; } /* end of program : STATIC.SQC */
生成的BIND文件为demodb2.bnd。 3.4 DB2的动态SQL嵌入语句 所谓静态SQL的编程方法,就是指在预编译时SQL语句已经基本确定,即访问的表或视图名、访问的列等信息已经确定。但是,有时整个SQL语句要到执行的时候才能确定下来,而且SQL语句所访问的对象也要到执行时才能确定。这就需要通过动态SQL语句完成。 3.4.1 基本方法 执行动态SQL语句的程序,主要有三条语句来完成: 1)、PREPARE语句。由于动态SQL语句在执行时才能确定,所以DB2中使用一个字符型的宿主变量来存放相应的SQL语句。但是,这条语句在预编译时不存在,因而在预编译时无法编译这个SQL语句。但是,存放在宿主变量中的语句必须转化为可执行的格式后才能执行。PREPARE命令的作用是完成编译的工作。即相当于静态SQL中的BIND操作,在数据库管理器中生成PACKAGE,并给出一个语句的名字。在后面的DESCRIBE、EXECUTE和OPEN等命令都使用这个名字来访问相应的SQL语句。SQL语句在做PREPARE操作时,不可使用宿主变量来表示参数,但可以在相应的位置上使用“?”来表示该位置上应该有一个参数。PREPARE还将生成相应的SQLDA结构。 2)、EXECUTE语句。该语句的作用是执行已经做过PREPARE操作的SQL语句。在EXECUTE语句中,将针对PREPARE语句中的每个参数标志给出相应的参数值,这些参数值可以有宿主变量传递,他们的类型应该匹配。EXECUTE命令不能做SELECT操作,这是因为EXECUTE语句无法返回结果,要执行SELECT操作,应该通过游标完成。 EXECUTE IMMEDIATE语句是PERPARE语句和EXECUTE语句的综合,它对语句做PREPARE,生成可执行模式,在执行。 3)、DESCRIBE语句。将执行过的PREPARE的SQL语句结果信息存放在SQLDA结构。 例、查询表名不是STAFF的表信息。 #include #include #include #include "util.h" EXEC SQL INCLUDE SQLCA; #define CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return 1; int main(int argc, char *argv[]) { EXEC SQL BEGIN DECLARE SECTION; char table_name[19]; /*st[80]宿主变量存放SQL语句*/ char st[80]; char parm_var[19]; char userid[9]; char passwd[19]; EXEC SQL END DECLARE SECTION; printf( "Sample C program: DYNAMIC\n" ); if (argc == 1) { EXEC SQL CONNECT TO sample; CHECKERR ("CONNECT TO SAMPLE"); } else if (argc == 3) { strcpy (userid, argv[1]); strcpy (passwd, argv[2]); EXEC SQL CONNECT TO sample USER :userid USING :passwd; CHECKERR ("CONNECT TO SAMPLE"); } else { printf ("\nUSAGE: dynamic [userid passwd]\n\n"); return 1; } /* endif */ strcpy( st, "SELECT tabname FROM syscat.tables" ); strcat( st, " WHERE tabname <> ?" ); /*对st做PREPARE操作,st中的“?”表示参数*/ EXEC SQL PREPARE s1 FROM :st; CHECKERR ("PREPARE"); /*定义游标*/ EXEC SQL DECLARE c1 CURSOR FOR s1; strcpy( parm_var, "STAFF" ); /*打开游标,并用parm_var代替SELECT语句中的“?”参数*/ EXEC SQL OPEN c1 USING :parm_var; CHECKERR ("OPEN"); do { /*用FETCH语句从结果集中取出结果*/ EXEC SQL FETCH c1 INTO :table_name; if (SQLCODE != 0) break; printf( "Table = %s\n", table_name ); } while ( 1 ); /*关闭游标*/ EXEC SQL CLOSE c1; CHECKERR ("CLOSE"); EXEC SQL COMMIT; CHECKERR ("COMMIT"); EXEC SQL CONNECT RESET; CHECKERR ("CONNECT RESET"); return 0; } /* end of program : DYNAMIC.SQC */ 从上面这个例子看出,动态SQL语句同静态SQL语句的不同之处在于,要使用PREPARE语句操作具体的SQL语句,然后使用EXECUTE或者游标等方式执行。 3.4.2 动态游标 1)、动态游标的DECLARE语句 动态游标对应的SQL语句应该是一个用PREPARE操作从文本形式转换趁个可执行形式的语句。在上例子中: EXEC SQL DECLARE c1 CURSOR FOR s1; 其中s1就是PREPARE操作后的可执行语句,c1是游标的名字。 2)、动态游标的OPEN语句 动态OPEN操作的作用是:将宿主变量或SQLDA结构中的值取出,填充到PREPARE操作后的SQL语句中标有“?”的参数的位置,并执行查询。宿主变量或SQLDA结构中的值应该与参数有一一对应的关系,而且数据类型符合。如上例子中: EXEC SQL OPEN c1 USING :parm_var; 3)、动态游标的FETCH语句 FETCH语句是从结果集中取出一行,将结果送入宿主变量列表或SQLDA结构中。关于SQLDA结构,见下节。如上例子中: EXEC SQL FETCH c1 INTO :table_name; 3.4.3 SQLDA SQLDA结构的作用与宿主变量相同,可用于应用程序与数据库之间的数据交换,它适用于:在编写程序时不确定要使用的变量个数和数据类型或不确定输出数据的列数,如:动态输入的SELECT语句,就必须使用SQLDA结构来获得查询的数据。SQLDA用于描述数据的类型、长度、变量的值和数据项的个数。在应用程序中使用SQLDA,必须在程序中定义SQLDA。即加入以下语句:EXEC SQL INCLUDE SQLDA。 1)、SQLDA结构 SQLDA的定义存放在sqlda.h中。具体为: SQL_STRUCTURE sqlname /* Variable Name */ { short length; /* Name length [1..30] */ _SQLOLDCHAR data[30]; /* Variable or Column name */ }; SQL_STRUCTURE sqlvar /* Variable Description */ { short sqltype; /* Variable data type */ short sqllen; /* Variable data length */ _SQLOLDCHAR *SQL_POINTER sqldata; /* Pointer to variable data value */ short *SQL_POINTER sqlind; /* Pointer to Null indicator */ struct sqlname sqlname; /* Variable name */ }; SQL_STRUCTURE sqlda { _SQLOLDCHAR sqldaid[8]; /* Eye catcher = 'SQLDA ' */ long sqldabc; /* SQLDA size in bytes=16+44*SQLN */ short sqln; /* Number of SQLVAR elements */ short sqld; /* # of columns or host vars. */ struct sqlvar sqlvar[1]; /* first SQLVAR element */ };
下图形象的描述了用SQLDA来存放两列数据。
SQLDA结构 Sqld=2 sqlvar ……
Sqltype=500 Sqllen sqldata …..
Sqltype=501 Sqllen Sqldata …..
图6-4 SQLDA结构示例
从上面这个定义看出,SQLDA是一种由两个不同部分组成的可变长数据结构。从位于SQLDA开端的sqldaid到sqld为固定部分,用于标志该SQLDA,并规定这一特定的SQLDA的长度。而后是一个或多个sqlvar结构 ,用于标志列数据。当用SQLDA把参数送到执行语句时,每一个参数都是一个sqlvar结构;当用SQLDA返回输出列信息时,每一列都是一个sqlvar结构。具体每个元素的含义为: lSqldaid。用于输入标志信息,如:“SQLDA”。 lSqldabc。SQLDA数据结果的长度。应该是16+44*SQLN。Sqldaid、sqldabc、sqln和sqld的总长度为16个字节。而sqlvar结构的长度为44个字节。 lSqln。分配的Sqlvar结构的个数。等价于输入参数的个数或输出列的个数。 lSqld。目前使用的sqlvar结构的个数。 lSqltype。代表参数或列的数据类型。它是一个整数数据类型代码。如:500代表smallint。具体每个整数的含义见下表:
SQL 列类型 SQLTYPE值 SQLTYPE 的字符名
DATE 384/385 SQL_TYP_DATE / SQL_TYP_NDATE TIME 388/389 SQL_TYP_TIME / SQL_TYP_NTIME TIMESTAMP 392/393 SQL_TYP_STAMP / SQL_TYP_NSTAMP n/a2 400/401 SQL_TYP_CGSTR / SQL_TYP_NCGSTR BLOB 404/405 SQL_TYP_BLOB / SQL_TYP_NBLOB CLOB 408/409 SQL_TYP_CLOB / SQL_TYP_NCLOB DBCLOB 412/413 SQL_TYP_DBCLOB / SQL_TYP_NDBCLOB VARCHAR 448/449 SQL_TYP_VARCHAR / SQL_TYP_NVARCHAR CHAR 452/453 SQL_TYP_CHAR / SQL_TYP_NCHAR LONG VARCHAR 456/457 SQL_TYP_LONG / SQL_TYP_NLONG n/a3 460/461 SQL_TYP_CSTR / SQL_TYP_NCSTR VARGRAPHIC 464/465 SQL_TYP_VARGRAPH / SQL_TYP_NVARGRAPH GRAPHIC 468/469 SQL_TYP_GRAPHIC / SQL_TYP_NGRAPHIC LONG VARGRAPHIC 472/473 SQL_TYP_LONGRAPH / SQL_TYP_NLONGRAPH FLOAT 480/481 SQL_TYP_FLOAT / SQL_TYP_NFLOAT REAL4 480/481 SQL_TYP_FLOAT / SQL_TYP_NFLOAT DECIMAL5 484/485 SQL_TYP_DECIMAL / SQL_TYP_DECIMAL INTEGER 496/497 SQL_TYP_INTEGER / SQL_TYP_NINTEGER SMALLINT 500/501 SQL_TYP_SMALL / SQL_TYP_NSMALL n/a 804/805 SQL_TYP_BLOB_FILE / SQL_TYPE_NBLOB_FILE n/a 808/809 SQL_TYP_CLOB_FILE / SQL_TYPE_NCLOB_FILE n/a 812/813 SQL_TYP_DBCLOB_FILE / SQL_TYPE_NDBCLOB_FILE n/a 960/961 SQL_TYP_BLOB_LOCATOR / SQL_TYP_NBLOB_LOCATOR n/a 964/965 SQL_TYP_CLOB_LOCATOR / SQL_TYP_NCLOB_LOCATOR n/a 968/969 SQL_TYP_DBCLOB_LOCATOR / SQL_TYP_NDBCLOB_LOCATOR
我们知道,SQLDA的作用是应用程序与数据库之间交换数据。对于从数据库向应用程序输出数据,则SQLDA存放了每列的信息,如:数据类型、长度和值。输出数据要同FETCH语句结合;对于从应用程序向数据库输入数据,则要同OPEN或EXECUTE操作结合。请看下面这个ADHOC例子,来理解SQLDA的作用。这个例子很经典,它的功能是处理任意输入的SQL语句,并返回结果。 #include #include #include #include #include /*包含SQLDA结构的定义*/ #include #include "util.h" /*包含SQLCA结构的定义*/ EXEC SQL INCLUDE SQLCA; /* 'check_error' is a function found in the util.c program */ #define CHECKERR(CE_STR) check_error (CE_STR, &sqlca) #define SQLSTATE sqlca.sqlstate /*初试化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); /*处理SQL语句*/ int process_statement (char[1000]); #define MAX_COLUMNS 255 int main(void) { int rc; char sqlInput[255]; char st[1000]=""; char Transaction; char tmpstr[1024]; /*定义宿主变量*/ EXEC SQL BEGIN DECLARE SECTION; (3) char server[9]; char userid[9]; char passwd[19]; EXEC SQL END DECLARE SECTION; printf("Sample C program : ADHOC interactive SQL\n"); /* 初试化与数据库的连接 */ do { printf("input the server (database) which you wish to attach to : \n"); gets (sqlInput); strcpy(server,sqlInput); printf("input your userid : \n"); gets (sqlInput); strcpy (userid, sqlInput); printf("input your passwd : \n"); gets (sqlInput); strcpy (passwd, sqlInput); printf("CONNECTING TO %s\n",server); /*连接数据库*/ EXEC SQL CONNECT TO :server USER :userid USING :passwd; /*检查连接是否成功*/ CHECKERR("CONNECT TO DATABASE"); } while (SQLCODE != 0); /* enddo */ printf("CONNECTED TO %s\n",server); /* Enter the continuous command line loop. */ while ( 1 ) { /*提示用户输入要操作的SQL语句*/ printf ("Enter an SQL statement or 'quit' to Quit :\n"); gets (sqlInput); if (strcmp(sqlInput, "quit") == 0) { break; } else if (strlen(sqlInput) == 0) { /* Don't process the statement */ printf ("\tNo characters entered.\n"); } else if (sqlInput[strlen(sqlInput) - 1] == '\\') { /* 查看是否有续行 */ strcpy (st, "\0"); do { strncat (st, sqlInput, strlen(sqlInput) -1); gets (sqlInput); } while (sqlInput[strlen(sqlInput) - 1] == '\\'); strcat (st, sqlInput); /* 处理SQL语句 */ rc = process_statement (st); } else { strcpy (st, sqlInput); /* 处理输入的SQL语句*/ rc = process_statement (st); } /* end if */ } /* end while */ printf ("Enter 'c' to COMMIT or Any Other key to ROLLBACK the transaction :\n"); Transaction = getc(stdin); if (Transaction == 'c') { printf("COMMITING the transactions.\n"); /提交结果*/ EXEC SQL COMMIT; CHECKERR ("COMMIT"); } else { /* 撤消语句的执行结果*/ printf("ROLLING BACK the transactions.\n"); EXEC SQL ROLLBACK; CHECKERR ("ROLLBACK"); }; /* endif */ /*断开数据库的连接*/ EXEC SQL CONNECT RESET; CHECKERR ("CONNECT RESET"); return 0; } /************************************************************************ * 函数 : process_statement * This function processes the inputted statement and then prepares the * procedural SQL implementation to take place. *************************************************************************/ int process_statement (char sqlInput[1000]) { int counter = 0; struct sqlda *sqldaPointer; short sqlda_d /* Total columns */ short idx; char buffer[4096]; char varname[1024]; char colnamelist[4096]; /*声明一个宿主变量,用于存放SQL语句*/ EXEC SQL BEGIN DECLARE SECTION; char st[1000]; EXEC SQL END DECLARE SECTION; /*向宿主变量中存放SQL语句*/ strcpy(st, sqlInput); /* 分配SQLDA空间,以存放查询结果 */ init_da (&sqldaPointer, 1); EXEC SQL PREPARE statement1 from :st; if (CHECKERR ("PREPARE") != 0) return SQLCODE; /*获得返回结果的描述信息,填入SQLDA结构*/ EXEC SQL DESCRIBE statement1 INTO :*sqldaPointer; /* 判断DESCRIBE是否正确执行*/ if (SQLCODE != 0 && SQLCODE != SQL_RC_W236 && SQLCODE != SQL_RC_W237 && SQLCODE != SQL_RC_W238 && SQLCODE != SQL_RC_W239) { /* An unexpected warning/error has occured. Check the SQLCA. */ if (CHECKERR ("DESCRIBE") != 0) return SQLCODE; } /* end if */ /*如果SQLDA结构中的sqld大于0,则表明是一个SELECT语句,sqld值是列的个数*/ if (sqldaPointer->sqld > 0) { /*判断是否有LOB列,若是,则需要双倍的SQLDA空间*/ if (strncmp(SQLSTATE, "01005", sizeof(SQLSTATE)) == 0) { /* this output contains columns that need a DOUBLED SQLDA */ SETSQLDOUBLED (sqldaPointer, SQLDOUBLED); init_da (&sqldaPointer, sqldaPointer->sqld * 2); } else { /*否则,只需要一个SQLDA */ init_da (&sqldaPointer, sqldaPointer->sqld); } /* end if */ /* 对SQLDA 重新赋值*/ EXEC SQL DESCRIBE statement1 INTO :*sqldaPointer; if (CHECKERR ("DESCRIBE") != 0) return SQLCODE; /* 给SQLDA分配合适的内存空间*/ alloc_host_vars (sqldaPointer); /* 声明游标*/ EXEC SQL DECLARE pcurs CURSOR FOR statement1; /*打开游标*/ EXEC SQL OPEN pcurs; if (CHECKERR ("OPEN") != 0) return SQLCODE; /*查询一行,存放在SQLDA中*/ EXEC SQL FETCH pcurs USING DEscriptOR :*sqldaPointer; if (CHECKERR ("FETCH") != 0) return SQLCODE; /* 从SQLDA中获得列标题信息,并显示之*/ colnamelist[0] = 0; for ( idx=0; idx< sqlda_d; idx++) { strcat(colnamelist, readColName(sqldaPointer, idx, buffer)); If (idx < sqlda_d -1) strcat(colnamelist, ","); } printf(“%s\n”,colnamelist); /*显示所有的行数据*/ while ( SQLCODE == 0 ) { counter++ for ( idx=0; idx< sqlda_d; idx++) printf(“%s”,readCol(sqldaPointer, idx, buffer)); EXEC SQL FETCH pcurs USING DEscriptOR :*sqldaPointer } /* endwhile */ /*关闭游标*/ EXEC SQL CLOSE pcurs; if (CHECKERR ("CLOSE CURSOR") != 0) return SQLCODE; printf ("\n %d record(s) selected\n\n", counter); /* 释放SQLDA申请的空间 */ free_da(sqldaPointer); } else { /*不是SELECT语句,则执行SQL语句 */ EXEC SQL EXECUTE statement1; if (CHECKERR ("executing the SQL statement") != 0) return SQLCODE; } /* end if */ return SQLCODE; } /* end of program : ADHOC.SQC */ /************************************************************************** * PROCEDURE : init_da *为SQLDA分配空间。其中SQLDASIZE的作用,是计算SQLDA的大小。返回-1, *表示无法分配空间。 **************************************************************************/ int init_da (struct sqlda **DAPointer, int DAsqln) { int idx; *DAPointer = (struct sqlda *) malloc (SQLDASIZE(DAsqln)); if (*DAPointer == NULL) return (-1); memset (*DAPointer, '\0', SQLDASIZE(DAsqln)); strncpy((*DAPointer)->sqldaid, "SQLDA ", sizeof ((*DAPointer)->sqldaid)); (*DAPointer)->sqldabc = (long)SQLDASIZE(DAsqln); (*DAPointer)->sqln = DAsqln; (*DAPointer)->sqld = 0; return 0; } /********************************************************************** * FUNCTION : alloc_host_vars *为sqlvar结构申请空间。返回-1表示申请失败。 ***********************************************************************/ int alloc_host_vars (struct sqlda *sqldaPointer) { short idx; unsigned int memsize =0; long longmemsize =0; int precision =0;
for (idx = 0; idx < sqldaPointer->sqld; idx++) { switch (sqldaPointer->sqlvar[idx].sqltype ) { case SQL_TYP_VARCHAR: case SQL_TYP_NVARCHAR: case SQL_TYP_LONG: case SQL_TYP_NLONG: case SQL_TYP_DATE: case SQL_TYP_NDATE: case SQL_TYP_TIME: case SQL_TYP_NTIME: case SQL_TYP_STAMP: case SQL_TYP_NSTAMP: sqldaPointer->sqlvar[idx].sqltype = SQL_TYP_NCSTR; sqldaPointer->sqlvar[idx].sqldata = (char *SQL_POINTER) malloc ((sqldaPointer->sqlvar[idx].sqllen)); memsize = (sqldaPointer->sqlvar[idx].sqllen); break; case SQL_TYP_DECIMAL: case SQL_TYP_NDECIMAL: precision = ((char *)&(sqldaPointer->sqlvar[idx].sqllen))[0]; sqldaPointer->sqlvar[idx].sqldata = (char *SQL_POINTER) malloc ((precision + 2) /2); memsize = (precision +2) /2; break; default: sqldaPointer->sqlvar[idx].sqldata = (char *SQL_POINTER) malloc (sqldaPointer->sqlvar[idx].sqllen); memsize = sqldaPointer->sqlvar[idx].sqllen; break; } /* endswitch */ if (sqldaPointer->sqlvar[idx].sqldata == NULL) { return (-1); } else { memset (sqldaPointer->sqlvar[idx].sqldata,'\0',memsize); } /* endif */ /*为sqlind申请空间*/ if ( sqldaPointer->sqlvar[idx].sqltype & 1 ) { /* Allocate storage for short int */ sqldaPointer->sqlvar[idx].sqlind = (short *)malloc(sizeof(short)); /* Detect memory allocation error */ if ( sqldaPointer->sqlvar[idx].sqlind == NULL ) { return(-1) } else { /* initialize memory to zero */ memset(sqldaPointer->sqlvar[idx].sqldata,'\0',sizeof(short)); } /* endif */ } /* endif */ } /* endfor */ return 0; } /************************************************************************** * FUNCTION : free_da * 释放SQLDA 申请的空间。 **************************************************************************/ void free_da (struct sqlda *sqldaPointer) { short idx; for (idx = 0; idx < sqldaPointer->sqld; idx++) { free (sqldaPointer->sqlvar[idx].sqldata); if (sqldaPointer->sqlvar[idx].sqltype & 1) { free (sqldaPointer->sqlvar[idx].sqlind); } } /* endfor */ free (sqldaPointer); } /************************************************************************** * PROCEDURE : readColName * 返回列名信息 **************************************************************************/ char * readColName (struct sqlda *sqldaPointer, short sqlvarIndex, char * buffer) { strncpy(buffer, sqldaPointer->sqlvar[sqlvarIndex].sqlname.data, sqldaPointer->sqlvar[sqlvarIndex].sqlname.length); return buffer; } /************************************************************************** * PROCEDURE : readCol * 返回一行数据 **************************************************************************/ char * readCol (struct sqlda *sqldaPointer, short sqlvarIndex, char * buffer){ short numBytes; short idx, ind /* Array idx variables */ /* Variables for decoding packed decimal data */ short bottom, point unsigned short top, precision, scale; char tmpstr[1024]; short pos; short collen; char *dataptr;
/* 检查是否为null */ if ( sqldaPointer->sqlvar[sqlvarIndex].sqltype & 1 &&\ *(sqldaPointer->sqlvar[sqlvarIndex].sqlind) < 0 ) { buffer[0] = 0; return buffer; } dataptr = (char *) sqldaPointer->sqlvar[ sqlvarIndex ].sqldata; collen = sqldaPointer->sqlvar[ sqlvarIndex ].sqllen;
switch ( sqldaPointer->sqlvar[ sqlvarIndex ].sqltype ) { case SQL_TYP_INTEGER: /* long */ case SQL_TYP_NINTEGER: /* long with null indicator */ sprintf(buffer, "%ld", * ( long *) dataptr ) break case SQL_TYP_SMALL: /* short */ case SQL_TYP_NSMALL: /* short with null indicator */ sprintf(buffer, "%d", * ( short *) dataptr ) break case SQL_TYP_DECIMAL: /* decimal */ case SQL_TYP_NDECIMAL: /* decimal with null indicator */ /* Determine the scale and precision */ precision = ((char *)&(collen))[0]; scale = ((char *)&(collen))[1];
/************************************************************************* *计算精度 *************************************************************************/ if ((precision %2) == 0) precision += 1;
/* Calculate the total number of bytes */ idx = ( short ) ( precision + 2 ) / 2 point = precision - scale
pos = 0; /* Determine the sign */ bottom = *(dataptr + idx -1) & 0x000F /* sign */ if ( (bottom == 0x000D) || (bottom == 0x000B) ) { buffer[pos++]='-'; } /* Decode and print the decimal number */ for (pos=0, ind=0; ind < idx; ind++) { top = *(dataptr + ind) & 0x00F0 top = (top >> 4 ) bottom = *(dataptr + ind) & 0x000F if ( point-- == 0 ) buffer[pos++]='.' buffer[pos++]='0' + top /*************************************************************************/ /*忽略最后一位(符号位) */ /*************************************************************************/ if ( ind < idx - 1 ) { /* sign half byte ? */ if ( point-- == 0 ) buffer[pos++] = '.'; buffer[pos++] = '0'+ bottom; } } buffer[pos] = 0; break case SQL_TYP_FLOAT: /* double */ case SQL_TYP_NFLOAT: /* double with null indicator */ sprintf(buffer, "%e", * (double *) dataptr) break case SQL_TYP_CHAR: /* fixed length character string */ case SQL_TYP_NCHAR: /* fixed length character string with null indicator */ strncpy(buffer, dataptr, collen); buffer[collen] = 0; collen--; while ((collen >=0) && (buffer[collen] == ' ')) buffer[collen--] = 0; break; case SQL_TYP_LSTR: /* varying length character string, 1-byte length */ case SQL_TYP_NLSTR: /* varying length character string, 1-byte length, with null indicator */
/* Initialize blen to the value the length field in the varchar data structure. */ collen = *dataptr; /* Advance the data pointer beyond the length field */ dataptr+=sizeof(char); strncpy(buffer, dataptr, collen); buffer[collen] = 0; break case SQL_TYP_CSTR: /* null terminated varying length character string */ case SQL_TYP_NCSTR: /* null terminate varying length character string with null indicator */ strcpy(buffer, dataptr); break default: buffer[0] = 0; }
return buffer; } /* COMMENT OUT OFF */
| | |