📄 esql-c资料(完全版)二_unix_操作系统_网络学院_天新网.htm
字号:
<P>
<P>
<P>
<P>
<P>
<P>
<P>
<P>
<P><BR>图6-3
嵌入SQL处理过程<BR>从上图看出,首先对源文件做预编译(precompiler),生成两个部分文件:一部分是纯的C程序源文件,它们和其他的C程序源文件一起,经过编译和连接生成可执行的程序(executable
program);而另一部分是bind文件或package文件。Bind文件经过binder操作以后,也生成为package文件。所谓package,实际上是SQL语句的访问计划。所以,预编译器将源程序中的SQL语句提出来,生成他们的访问计划,并将访问计划存放在数据库管理器中。当执行程序并遇到访问数据库的命令时,它将到数据库管理器中寻找属于它的访问计划,然后按照访问计划中所设计的方法对数据库进行访问。具体来说:<BR>第一步、预编译<BR>源程序生成以后,在源程序中嵌入了许多SQL语句,而SQL语句是宿主语言编译器所不认识的,所以在用宿主语言编译器进行编译、连接之前必须将SQL语句分离出来,这就是预编译所做的工作。DB2中预编译操作是通过PREP命令执行的,PREP命令首先将源程序中的所有有关SQL语句全部注释起来,对它进行分析和语法检查。如果源程序中的SQL语句全部书写正确,则将这些SQL语句转换成C语言可以识别的一系列的API函数。这些函数可以在函数执行时访问数据库,然后将源文件中所有用于生成数据库管理器的PACKAGE的数据提出组合成一个BIND文件。也可以直接生成一个PACKAGE,但这相当于在预编译后又执行了一次BIND操作。在预编译时,对整个源程序文件中的所有变量做统一处理而不根据变量的生命期来处理,所以宿宿主变量在整个程序中是唯一的。下面讲解预编译的步骤:<BR>1)、连接到一个数据库,该操作是为BIND做准备。操作如下:<BR>db2
connect to cicstest<BR>2)、执行预编译命令,假设源文件为adhoc.sqc,则:<BR>db2 prep
adhoc.sqc
bindfile<BR>下面我们对预编译的几种输出文件进行讨论。<BR>l预编译后生成的C语言源文件:该文件中原有的SQL语句,已经全部加上注释并转换成了C语言可以识别的API调用。<BR>lBIND文件:如果在预编译时使用BINDFILE选项,则生成BIND文件,BIND文件的后缀为.bnd,BIND文件可以在将来使用BIND命令来生成PACKAGE。<BR>db2
bind
adhoc.bnd<BR>如果在预编译时,只生成BIND文件,那么即使在预编译时,不能访问某些数据库对象,系统也只是报警,而不会报错。如果使用PACKAGE选项,则生成PACKAGE。如果有MESSAGE选项,则生成信息文件,它包含了所有的返回信息,如:警报、错误等。它便于程序员对源程序做进一步的修改。<BR>第二步、编译和连接<BR>在预编译后,程序中只有C语言语句,它们都可以为C语言的编译器所识别。所以,可以按照一般的方法进行编译和连接,但在将SQL语句转换以后,在C语言程序中,又引入了许多一般的C语言系统所没有的INCLUDE文件和函数库,这些均在DB2的SDK中。所以,要生成可执行的程序,就必须安装DB2的SDK,并且做以下设置:<BR>set
INCLUDE=$(DB2PATH)\include;%include%<BR>set
LIB=$(DB2PATH)\lib;%LIB%<BR>下面是编译和连接:<BR>cl -o adhoc.exe
adhoc.c<BR>生成的可执行文件必须与数据库管理器中的PACKAGE相结合,才能执行。<BR>下面对BIND做进一步解释。<BR>PACKAGE是DB2为SQL语句制定的访问计划。通过precompile之后,源程序中的SQL语句部分就被分离出来。PACKAGE就是根据具体的SQL语句和数据库中的信息生成的针对每条SQL语句的访问计划,它存放在DB2数据库<A
href="http://www.21tx.com/server/"
target=_blank>服务器</A>上。应用程序执行到SQL语句时,就到相应的服务器上去找它们的PACKAGE,数据库服务器根据PACKAGE执行具体的数据库操作。所以,如果一个应用程序访问了多个数据库服务器,则该应用程序应在它访问到的数据库服务器上均生成相应的PACKAGE。因此,当遇到这种情况时,推荐的方式是将源程序分成若干个文件,每个文件只访问一个服务器,然后分别进行预编译。<BR>执行PREP命令时,加上选项PACKAGE或不注明BINDFILE、SYNTAX或SQLFLAG选项,这时BIND操作将自动进行。<BR>直接使用BIND命令从BIND文件中生成PACKAGE存放在数据库管理器中。BIND完成的功能是,从数据库中找到SQL语句所涉及的表,查看SQL语句中提到的表名及属性是否与数据库中的表名和属性相匹配,以及应用程序开发者是否有权限查询或修改应用程序中所涉及到的表及属性。这就是为什么在预编译之前要连接到相应的数据库上的原因。在做BIND操作后,在数据库管理器中就生成一个PACKAGE,PACKAGE的名字与源程序的文件名字相同。<BR>一个源文件在数据库中可有多个PACKAGE存在。为了区分不同的PACKAGE,DB2中引入了时间戳的概念。在PREP执行时,系统对生成的修改过的C语言程序和BIND文件以及PACKAGE中都加入了一个时间戳。BIND文件在生成PACKAGE时也将时间戳传递下来,修改过的C语言程序在生成可执行程序时,同样也将时间戳传递下去。当应用程序运行时,可执行程序是通过时间戳找到相应的PACKAGE,如果时间戳不匹配,则说明版本更新,需要做BIND。<BR>本章第一个例子被编译后所生成的C程序:<BR>static
char sqla_program_id[40] =
<BR>{111,65,65,66,65,73,65,70,89,65,78,71,90,72,32,32,68,69,77,79,<BR>68,66,50,32,67,65,51,54,75,75,67,81,48,49,49,49,49,50,32,32};<BR>#include
"sqladef.h"<BR>static struct sqla_runtime_info sqla_rtinfo =
<BR>{{'S','Q','L','A','R','T','I','N'}, sizeof(wchar_t), 0, {' ','
',' ',' '}};<BR>#line 1 "demodb2.sqc"<BR>#include
<stdio.h><BR>#include <stdlib.h><BR>#include
<string.h><BR>#include "util.h"<BR>#include
<sqlca.h><BR>/*<BR>EXEC SQL INCLUDE SQLCA;<BR>*/<BR>/* SQL
Communication Area - SQLCA - structures and constants */<BR>#include
"sqlca.h"<BR>struct sqlca sqlca;<BR>#line 6 "demodb2.sqc"<BR>#define
CHECKERR(CE_STR) if (check_error (CE_STR, &sqlca) != 0) return
1;<BR>int check_error (char eString[], struct sqlca *caPointer)
{<BR>char eBuffer[1024];<BR>char sBuffer[1024];<BR>short rc,
Erc;<BR>if (caPointer->sqlcode != 0) {<BR>printf ("--- error
report ---\n");<BR>printf ("ERROR occured : %s.\nSQLCODE : %ld\n",
eString,<BR>caPointer->sqlcode);<BR>}<BR>return 0;<BR>}<BR>int
main(int argc, char *argv[]) {<BR>/*<BR>EXEC SQL BEGIN DECLARE
SECTION;<BR>*/<BR>#line 21 "demodb2.sqc"<BR>char
firstname[13];<BR>char userid[9];<BR>char passwd[19];<BR>/*<BR>EXEC
SQL END DECLARE SECTION;<BR>*/<BR>#line 25 "demodb2.sqc"
<P>printf( "Sample C program: STATIC\n" );<BR>if (argc == 1)
{<BR>/*<BR>EXEC SQL CONNECT TO sample;<BR>*/<BR>{<BR>#line 28
"demodb2.sqc"<BR>sqlastrt(sqla_program_id, &sqla_rtinfo,
&sqlca);<BR>#line 28
"demodb2.sqc"<BR>sqlaaloc(2,1,1,0L);<BR>{<BR>struct sqla_setd_list
sql_setdlist[1];<BR>#line 28
"demodb2.sqc"<BR>sql_setdlist[0].sqltype = 460;
sql_setdlist[0].sqllen = 7;<BR>#line 28
"demodb2.sqc"<BR>sql_setdlist[0].sqldata = (void*)"sample";<BR>#line
28 "demodb2.sqc"<BR>sql_setdlist[0].sqlind = 0L;<BR>#line 28
"demodb2.sqc"<BR>sqlasetd(2,0,1,sql_setdlist,0L);<BR>}<BR>#line 28
"demodb2.sqc"<BR>sqlacall((unsigned short)29,4,2,0,0L);<BR>#line 28
"demodb2.sqc"<BR>sqlastop(0L);<BR>}<BR>#line 28
"demodb2.sqc"<BR>CHECKERR ("CONNECT TO SAMPLE");<BR>}<BR>else if
(argc == 3) { <BR>strcpy (userid, argv[1]);<BR>strcpy (passwd,
argv[2]);<BR>/*<BR>EXEC SQL CONNECT TO sample USER :userid USING
:passwd;<BR>*/<BR>{<BR>#line 34
"demodb2.sqc"<BR>sqlastrt(sqla_program_id, &sqla_rtinfo,
&sqlca);<BR>#line 34
"demodb2.sqc"<BR>sqlaaloc(2,3,2,0L);<BR>{<BR>struct sqla_setd_list
sql_setdlist[3];<BR>#line 34
"demodb2.sqc"<BR>sql_setdlist[0].sqltype = 460;
sql_setdlist[0].sqllen = 7;<BR>#line 34
"demodb2.sqc"<BR>sql_setdlist[0].sqldata = (void*)"sample";<BR>#line
34 "demodb2.sqc"<BR>sql_setdlist[0].sqlind = 0L;<BR>#line 34
"demodb2.sqc"<BR>sql_setdlist[1].sqltype = 460;
sql_setdlist[1].sqllen = 9;<BR>#line 34
"demodb2.sqc"<BR>sql_setdlist[1].sqldata = (void*)userid;<BR>#line
34 "demodb2.sqc"<BR>sql_setdlist[1].sqlind = 0L;<BR>#line 34
"demodb2.sqc"<BR>sql_setdlist[2].sqltype = 460;
sql_setdlist[2].sqllen = 19;<BR>#line 34
"demodb2.sqc"<BR>sql_setdlist[2].sqldata = (void*)passwd;<BR>#line
34 "demodb2.sqc"<BR>sql_setdlist[2].sqlind = 0L;<BR>#line 34
"demodb2.sqc"<BR>sqlasetd(2,0,3,sql_setdlist,0L);<BR>}<BR>#line 34
"demodb2.sqc"<BR>sqlacall((unsigned short)29,5,2,0,0L);<BR>#line 34
"demodb2.sqc"<BR>sqlastop(0L);<BR>}
<P>#line 34 "demodb2.sqc"<BR>CHECKERR ("CONNECT TO
SAMPLE");<BR>}<BR>else {<BR>printf ("\nUSAGE: static [userid
passwd]\n\n");<BR>return 1;<BR>} /* endif */<BR>/*<BR>EXEC SQL
SELECT FIRSTNME INTO :firstname <BR>FROM employee<BR>WHERE LASTNAME
= 'JOHNSON';<BR>*/<BR>{<BR>#line 44
"demodb2.sqc"<BR>sqlastrt(sqla_program_id, &sqla_rtinfo,
&sqlca);<BR>#line 44
"demodb2.sqc"<BR>sqlaaloc(3,1,3,0L);<BR>{<BR>struct sqla_setd_list
sql_setdlist[1];<BR>#line 44
"demodb2.sqc"<BR>sql_setdlist[0].sqltype = 460;
sql_setdlist[0].sqllen = 13;<BR>#line 44
"demodb2.sqc"<BR>sql_setdlist[0].sqldata =
(void*)firstname;<BR>#line 44
"demodb2.sqc"<BR>sql_setdlist[0].sqlind = 0L;<BR>#line 44
"demodb2.sqc"<BR>sqlasetd(3,0,1,sql_setdlist,0L);<BR>}<BR>#line 44
"demodb2.sqc"<BR>sqlacall((unsigned short)24,1,0,3,0L);<BR>#line 44
"demodb2.sqc"<BR>sqlastop(0L);<BR>}
<P>#line 44 "demodb2.sqc"
<P>CHECKERR ("SELECT statement"); <BR>printf( "First name = %s\n",
firstname );<BR><BR>/*<BR>EXEC SQL CONNECT
RESET;<BR>*/<BR>{<BR>#line 47
"demodb2.sqc"<BR>sqlastrt(sqla_program_id, &sqla_rtinfo,
&sqlca);<BR>#line 47 "demodb2.sqc"<BR>sqlacall((unsigned
short)29,3,0,0,0L);<BR>#line 47
"demodb2.sqc"<BR>sqlastop(0L);<BR>}<BR>#line 47
"demodb2.sqc"<BR>CHECKERR ("CONNECT RESET");<BR>return 0;<BR>}<BR>/*
end of program : STATIC.SQC */
<P><BR>生成的BIND文件为demodb2.bnd。<BR>3.4
DB2的动态SQL嵌入语句<BR>所谓静态SQL的编程方法,就是指在预编译时SQL语句已经基本确定,即访问的表或视图名、访问的列等信息已经确定。但是,有时整个SQL语句要到执行的时候才能确定下来,而且SQL语句所访问的对象也要到执行时才能确定。这就需要通过动态SQL语句完成。<BR>3.4.1
基本方法<BR>执行动态SQL语句的程序,主要有三条语句来完成:<BR>1)、PREPARE语句。由于动态SQL语句在执行时才能确定,所以DB2中使用一个字符型的宿主变量来存放相应的SQL语句。但是,这条语句在预编译时不存在,因而在预编译时无法编译这个SQL语句。但是,存放在宿主变量中的语句必须转化为可执行的格式后才能执行。PREPARE命令的作用是完成编译的工作。即相当于静态SQL中的BIND操作,在数据库管理器中生成PACKAGE,并给出一个语句的名字。在后面的DESCRIBE、EXECUTE和OPEN等命令都使用这个名字来访问相应的SQL语句。SQL语句在做PREPARE操作时,不可使用宿主变量来表示参数,但可以在相应的位置上使用“?”来表示该位置上应该有一个参数。PREPARE还将生成相应的SQLDA结构。<BR>2)、EXECUTE语句。该语句的作用是执行已经做过PREPARE操作的SQL语句。在EXECUTE语句中,将针对PREPARE语句中的每个参数标志给出相应的参数值,这些参数值可以有宿主变量传递,他们的类型应该匹配。EXECUTE命令不能做SELECT操作,这是因为EXECUTE语句无法返回结果,要执行SELECT操作,应该通过游标完成。<BR>EXECUTE
IMMEDIATE语句是PERPARE语句和EXECUTE语句的综合,它对语句做PREPARE,生成可执行模式,在执行。<BR>3)、DESCRIBE语句。将执行过的PREPARE的SQL语句结果信息存放在SQLDA结构。<BR>例、查询表名不是STAFF的表信息。<BR>#include
<stdio.h><BR>#include <stdlib.h><BR>#include
<string.h><BR>#include "util.h"<BR>EXEC SQL INCLUDE
SQLCA;<BR>#define CHECKERR(CE_STR) if (check_error (CE_STR,
&sqlca) != 0) return 1;<BR>int main(int argc, char *argv[])
{<BR>EXEC SQL BEGIN DECLARE SECTION;<BR>char
table_name[19];<BR>/*st[80]宿主变量存放SQL语句*/<BR>char st[80]; <BR>char
parm_var[19];<BR>char userid[9];<BR>char passwd[19];<BR>EXEC SQL END
DECLARE SECTION;<BR>printf( "Sample C program: DYNAMIC\n" );<BR>if
(argc == 1) {<BR>EXEC SQL CONNECT TO sample;<BR>CHECKERR ("CONNECT
TO SAMPLE");<BR>}<BR>else if (argc == 3) { <BR>strcpy (userid,
argv[1]);<BR>strcpy (passwd, argv[2]);<BR>EXEC SQL CONNECT TO sample
USER :userid USING :passwd;<BR>CHECKERR ("CONNECT TO
SAMPLE");<BR>}<BR>else {<BR>printf ("\nUSAGE: dynamic [userid
passwd]\n\n");<BR>return 1;<BR>} /* endif */<BR>strcpy( st, "SELECT
tabname FROM syscat.tables" );<BR>strcat( st, " WHERE tabname
<> ?" );<BR>/*对st做PREPARE操作,st中的“?”表示参数*/<BR>EXEC SQL PREPARE
s1 FROM :st; <BR>CHECKERR ("PREPARE");<BR>/*定义游标*/<BR>EXEC SQL
DECLARE c1 CURSOR FOR s1; <BR>strcpy( parm_var, "STAFF"
);<BR>/*打开游标,并用parm_var代替SELECT语句中的“?”参数*/<BR>EXEC SQL OPEN c1 USING
:parm_var; <BR>CHECKERR ("OPEN");<BR>do
{<BR>/*用FETCH语句从结果集中取出结果*/<BR>EXEC SQL FETCH c1 INTO :table_name;
<BR>if (SQLCODE != 0) break;<BR>printf( "Table = %s\n", table_name
);<BR>} while ( 1 );<BR>/*关闭游标*/<BR>EXEC SQL CLOSE c1; <BR>CHECKERR
("CLOSE");<BR>EXEC SQL COMMIT;<BR>CHECKERR ("COMMIT");<BR>EXEC SQL
CONNECT RESET;<BR>CHECKERR ("CONNECT RESET");<BR>return
0;<BR>}<BR>/* end of program : DYNAMIC.SQC
*/<BR>从上面这个例子看出,动态SQL语句同静态SQL语句的不同之处在于,要使用PREPARE语句操作具体的SQL语句,然后使用EXECUTE或者游标等方式执行。<BR>3.4.2
动态游标<BR>1)、动态游标的DECLARE语句<BR>动态游标对应的SQL语句应该是一个用PREPARE操作从文本形式转换趁个可执行形式的语句。在上例子中:
EXEC SQL DECLARE c1 CURSOR FOR s1;
其中s1就是PREPARE操作后的可执行语句,c1是游标的名字。<BR>2)、动态游标的OPEN语句<BR>动态OPEN操作的作用是:将宿主变量或SQLDA结构中的值取出,填充到PREPARE操作后的SQL语句中标有“?”的参数的位置,并执行查询。宿主变量或SQLDA结构中的值应该与参数有一一对应的关系,而且数据类型符合。如上例子中:<BR>EXEC
SQL OPEN c1 USING :parm_var;
<BR>3)、动态游标的FETCH语句<BR>FETCH语句是从结果集中取出一行,将结果送入宿主变量列表或SQLDA结构中。关于SQLDA结构,见下节。如上例子中:<BR>EXEC
SQL FETCH c1 INTO :table_name; <BR>3.4.3
SQLDA<BR>SQLDA结构的作用与宿主变量相同,可用于应用程序与数据库之间的数据交换,它适用于:在编写程序时不确定要使用的变量个数和数据类型或不确定输出数据的列数,如:动态输入的SELECT语句,就必须使用SQLDA结构来获得查询的数据。SQLDA用于描述数据的类型、长度、变量的值和数据项的个数。在应用程序中使用SQLDA,必须在程序中定义SQLDA。即加入以下语句:EXEC
SQL INCLUDE
SQLDA。<BR>1)、SQLDA结构<BR>SQLDA的定义存放在sqlda.h中。具体为:<BR>SQL_STRUCTURE
sqlname /* Variable Name */<BR>{<BR>short length; /* Name length
[1..30] */<BR>_SQLOLDCHAR data[30]; /* Variable or Column name
*/<BR>};<BR>SQL_STRUCTURE sqlvar /* Variable Description
*/<BR>{<BR>short sqltype; /* Variable data type */<BR>short sqllen;
/* Variable data length */<BR>_SQLOLDCHAR *SQL_POINTER sqldata; /*
Pointer to variable data value */<BR>short *SQL_POINTER sqlind; /*
Pointer to Null indicator */<BR>struct sqlname sqlname; /* Variable
name */<BR>};<BR>SQL_STRUCTURE sqlda<BR>{<BR>_SQLOLDCHAR sqldaid[8];
/* Eye catcher = 'SQLDA ' */<BR>long sqldabc; /* SQLDA size in
bytes=16+44*SQLN */<BR>short sqln; /* Number of SQLVAR elements
*/<BR>short sqld; /* # of columns or host vars. */<BR>struct sqlvar
sqlvar[1]; /* first SQLVAR element */<BR>};
<P>下图形象的描述了用SQLDA来存放两列数据。
<P>SQLDA结构<BR>Sqld=2<BR>sqlvar<BR>……
<P><BR><BR>Sqltype=500<BR>Sqllen<BR>sqldata<BR>…..<BR><BR>Sqltype=501<BR>Sqllen<BR>Sqldata<BR>…..
<P>
<P>图6-4 SQLDA结构示例
<P>从上面这个定义看出,SQLDA是一种由两个不同
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -