
INFORMIX ESQL/C编程要求开发人员能够熟练使用C语言编程,这样就能够充分利用C语言的强大功能开发出满足各种需求的应用程序。
2.1 嵌入SQL语句
ESQL/C程序是把SQL语句嵌入到C程序中。可以嵌入到C程序中的SQL语句除了交互式的SQL语句之外,还有一些是专用于编程用的。交互式的SQL语句是指数据库操作语句和定义语句,它们能在数据库SQL语句界面中使用。而INFORMIX ESQL/C程序出现的PREPARE语句则属于非交互SQL的SQL语句。对于所有用于ESQL/C程序的SQL语句,需要用特殊的符号把它们标识出来。INFORMIX ESQL/C嵌入SQL语句的方法有两种:
(1)SQL语句用“$”符号作引导符,用“;”作为SQL语句的结束符。这种方式是INFORMIX
ESQL/C的独特用法,其它的数据库厂商不一定支持。
(2)SQL语句用“EXEC SQL”为前导,“;”表示SQL语句的结束。这种方式是ANSI制
定的标准方式,所有的数据库厂商都支持。考虑到程序的可移植性,采用这种嵌入
方式比较好。
在ESQL/C程序中,SQL语句与其它C语句具有同等的地位,也就是说,SQL语句能出现在C语句能出现的任何地方。
2.2 前导文件、蕴含文件
2.2.1 前导文件
ESQL/C提供了以下四个前导文件以定义存放所需信息的数据结构:
sqlca.h: 包含了存放出错信息的结构
sqlda.h: 包含含有动态定义变量的值指针和描述信息的结构
sqlstype.h:包含SQL语句中用到的常整数
sqltypes.h:包含与C语言和SQL中的数据类型相当的串定义
在ESQL/C程序中根据特定的需要包含不同的前导文件。为了检查ESQL/C语句执行成功与否,程序中总要包含sqlca.h,其它三个前导文件则根据需要而定。
包含这些文件的语法如下:
$include文件名; /* 文件名不带后缀 */
2.2.2 蕴含文件
在ESQL/C程序中,若要包含除前导文件之外的其它ESQL/C文件,语法为:
$include文件名;
或EXEC SQL INCLUDE文件名;
由此可见,前导文件,蕴含文件与标准C程序头文件不同的地方有两点,一是语法形式不同,前导文件,蕴含文件在include前加上符号“$”或EXEC SQL;二是引起的处理不同,ESQL/C预处理程序将C程序的头文件直接传给C编译器,而对前导文件,蕴含文件,则要先将其处理成C程序的文件,然后再传给C编译器。
2.3 C语句与SQL语句的数据传递
2.3.1 数据类型对照关系
两种语句之间通过变量来进行数据传递,这类变量称为宿主变量。出现在SQL语句中的宿主变量必须与SQL的数据类型相对应,才能保证操作的正确性,而宿主变量是说明成C语言的数据类型的,为此ESQL/C提供了两种数据类型之间的对应关系,如下表所示。
| SQL | C Language |
| CHAR(n) | char[n+1] |
| CHARACTER(n) | |
| SMALLINT | short int |
| INTEGER | long int |
| INT | |
| DECIMAL | dec_t or struct decimal |
| DEC | |
| NUMERIC | |
| SMALLFLOAT | float |
| REAL | |
| FLOAT | Double |
| DOUBLE PRECISION | |
| MONEY SERIAL | dec_t or struct decimalz long int Long int |
| DATE | |
| DATETIME | Dtime_t or struct dtime |
| INTERVAL | Intrvl_t or struct intrvl |
如果没有说明一个类型相容的宿主变量,有时系统会自动转换数据类型。当类型无法互换时,系统会报错:如果接收域太小,就会发生丢失数据的情况:当丢失的是有效数字时系统报错,当丢失的是有效字符时系统发出警告。
INFORMIX ESQL/C程序中的数据类型分为简单数据类型和复杂数据类型,它们的含义和使用方法在这章中介绍。对于每种数据类型,在ESQL/C程序中都有与之对应的类型,类型对应是基于各类型所占用的存储空间。另外,INFORMIX ESQL/C提供若干数据操作、类型转换的库函数,可以很方便的被开发人员调用。
●简单数据类型
简单数据类型包括:CHAR、SMALLINT、INTEGER、SMALLFLOAT、FLOAT、SERIAL和DATE类型。
对于SQL字符类型CHAR(n),为了字符不被截断,定义宿主变量的字符串长度一定是n+1,因为C语言中的字符串类型的数据在存储时有一个字节放空结束符(“\\0”)。INFORMIX ESQL/C提供了许多处理字符串类型的库函数,如rstod()、rstoi()等等。
对于数值类型的数据,在算术表达式中都当作DECIMAL数据类型对待。DECIMAL数据类型的格式是这样的:DECIMAL (m[,n])其中m表示数据的有效数位,n表示小数点后面的数位。当省略n时,表示小数点的位置不固定。
各数值类型与DECIMAL类型的对应关系为:
FLOAT → DECIMAL(16)
SMALLFLOAT → DECIMAL(8)
INTEGER → DECIMAL(10,0)
SMALLINT → DECIMAL(5,0)
DATE类型对应C的long类型。这是因为在数据库, DATE类型的数据存储是这样来规定的:19年12月31日0,1900年1月1日为1,依此类推。DATE类型与字符串类型可相互转换,参见INFORMIX ESQL/C的库函数。
●复杂数据类型
对每种复杂的数据类型,在这里只介绍它的用法。
(1)DECIMAL数据类型
DECIMAL数据类型是可表示一切数值型的数据,所表示的数据范围为:10×10-128-10×10+126;在ESQL/C程序中,DECIMAL值存放在decimal.h的dec_t结构中,需要包含文件decimal.h。
(2)DATETIME和INTERVAL数据类型
DATETIME数据类型用来表示时间,INTERVAL数据类型用来表示时间间隔。在ESQL/C程序中,DATETIME数据存放在dtime_t结构中,INTERVAL数据存放在intrvl_t结构中,这需要包含datetime.h文件。
在程序中,定义DATETIME的数据变量用下面的语句形式:
dtime_t 最大修饰粒度 to 最小修饰粒度 变量名
其中最大修饰粒度和最小修饰粒度从以下几个关键字中选取: YEAR MONTH DAY HOUR MINITE SECOND FACTION (n) 例如:$dtime_t year to minute dtvar;这条语句定义了变量dtvar的表示形式是从年到秒。 在程序中,定义INTERVAL的数据变量用下面的语句形式: intrvl_t 最大修饰粒度 to 最小修饰粒度 变量名
其中最大修饰粒度和最小修饰粒度必须同时从以下两个类之一的关键字中选取:
year-month类 Day-Time类
YEAR DAY
MONTH HOUR
MINUTE
SECOND
FRACTION
例如:$intrvl_t day to minute itvar;这条语句定义了变量itvar的表示形式是从天到秒。
(3)VARCHAR数据类型
VARCHAR数据类型用来定义变长字符串变量。VARCHAR类型定义的形式一般为:
VARCHAR str(n,m),n表示str的最大长度,m表示str的最小长度。
在ESQL/C程序中可以使用char (n+1), string(n+1), varchar(n+1), fixchar(n)等数据类型来存放VARCHAR类型的数据。
2.3.2 宿主变量的定义
宿主变量是用于SQL语句的普通C变量,ESQL/C程序通过宿主变量在C语句和SQL语句之间进行数据传递,如用SQL中的Select语句,将数据从数据库中查找出来,放到宿主变量中,C语句就能对该数据进行处理。
ESQL/C可在使用常数的任何地方使用宿主变量和指示变量。如果在SQL语句中使用了宿主变量,必须在它前面加上美元符($)或冒号(:),如宿主变量hostvar以$hostvar或:hostvar的形式出现在SQL语句中,其中:hostvar形式是符合ANSI标准的。
宿主变量被说明成通常的C变量,然后再加上如下的任一种标识:
(1)在说明语句前放入美元符“$”
(2)将说明语句放在以下两条语句之间
EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL END DECLARE SECTION;
例子:
/* 指向字符串的指针 */
$ char * hostvar;
/* 整数 */
$ int hostint;
/* 长整数 */
$ long hostlong;
/* 双精度浮点数 */
$ double hostdbl;
/* 字符数组 */
$ char hostarr[80];
/* 结构类型 */
$ struct {int svarl;
int svarz;
}host struct;
或按以下ANSI标准进行说明
EXEC SQL BEGIN DECLARE SECTION;
char * hostvar;
int hostint;
long hostlong;
double hostdbl;
char hostarr[80];
EXEC SQL END DECLARE SECTION;
EXEC SQL BEGIN DECLARE SECTION;
struct{
int svar1;
int svar2;
...
}hoststruct;
EXEC SQL END DECLARE SECTION;
在说明宿主变量时,可以包含通常的C初始化表达式,只是在包含字符串的初始化中,不能含有分号“;”或ESQL/C的关键字,下面是两个初始化例子:
$ int varname=12;
$ long cust_nos[8]=(0,0,0,0,0,0,0,999);
可以给结构类型定义结构标识,说明宿主变量时直接引用这一标识,如:
$ stract crstomer_t
{
int c_no;
char fname[32];
char lname[32];
} crst_rec;
$ struct customer_t custz_rec;
在SQL语句中,可以使用结构变量做为包含所有元素的整体,也可以使用结构的单个元素,结构可以嵌套,按前面的说明,语句:
$ insert $ cust_rec into customer;
等价于:
$ insert into customer
values($ cust_rec.c_no,$cust_rec.fname,$cust_rec.lname);
在说明宿主变量时,其作用域与普通C变量的作用域有所不同,用户应加以注意。如果在一个函数里说明了一个宿主变量,而在另一个函数里又重新说明了它,这就会引起预处理程序的警告,因为预处理程序总是使用最近识别到的变量。当上述宿主变量都是函数内的局部变量时,可以不理会警告,程序执行正确。当有全局变量时,就有可能引起程序的错误执行,看下面的例示:
$ extern int hostvar;
...
func1()
{
$ long hostvar;
...
}
...
func2()
{
...
$ declare q_stock cursor for
select stock_num into $ hostvar from stock;
...
}
func2中的变量hostvar被ESQL/C的预处理程序认为是最近遇到的func1中的hostrar,而实际上程序员的本意是用全局变量hostvar,这样程序在运行时一定会出现错误的结果。为了避免这种错误,ESQL/C提供了配对符“${”和“$}”去打开和关闭函数,这样就可保证在函数中说明的宿主变量局部于该函数,也可以使用“${”和“$}”去打开和关闭程序块,使得在此程序块中说明的宿主变量局部于此程序块。习惯于C变量作用域的程序员尤其应加以注意。上段程序的正确写法为:
$ extern int hostvar;
...
func1()
$ {
$ long hostvar;
...
$ }
...
func2()
{
...
$ declare q_stock cursor for
select stock_num
into $ hostvar
from stock;
...
}
2.3.3 宿主变量的使用
在嵌入的SQL语句中,需要标识出宿主变量。为区分宿主变量与SQL语句中其它的名字(如列名、表名等),在宿主变量前加上一个冒号(:)或“$”符,ANSI标准使用的是“:”。让我们看看几个使用宿主变量的例子。
(1)宿主变量名可与数据库表中的列名相同;
EXEC SQL update customer
set zipcode = :zipcode
where customer_num = :customer_num;
(2)宿主变量名区分大小写;
EXEC SQL BEGIN DECLARE SECTION;
long customer_num;
long Customer_num;
EXEC SQL end declare section;
customer_num与Customer_num是两个不同的宿主变量。
(3)宿主变量可用于C语句中;
EXEC SQL begin declare section;
char dbname[11];
EXEC SQL end declare section;
gets(dbname);
EXEC SQL database :dbname;
(4)在定义宿主变量段可使用typedef语句(用户类型定义语句);
EXEC SQL begin declare section;
typedef short smallint;
typedef long date;
smallint stocknum;
data orderdate;
EXEC SQL end declare section;
(5)宿主变量定义为结构变量;
EXEC SQL begin declare section;
struct stock_t{
short stocknum;
char manucode[4];
}stockrec1;
struct stock_t stockrec2;
EXEC SQL end declare section;
结构型宿主变量在SQL语句中作整体赋值,如:
EXEC SQL insert into stock
VALUES (:stockrec2);
(6)宿主变量定义为数组变量;
EXEC SQL begin declare section;
char *buf[6];
static long unit[6]=(0,0,0,0,0,0);
EXEC SQL end declare section;
在数据类型定义时,可对宿主变量进行初始化,与C语言的变量定义一样。
2.3.4 指示变量
指示变量主要的用途是处理空值,即数据库中的Null值,空值在C变量中是不可预知的。Null值通常是不确定的,如果将空值返回给了宿主变量,在C程序中无法对其进行算术运算和其它操作,所以用户必须能够发现ESQL/C语句是否将空值返回给宿主变量。
如果一个宿主变量对应于允许Null值的数据库字段,则必须定义一宿主变量相对应的指示变量,对应的宿主变量称为指示变量的主变量。
当ESQL/C语句(select语句中的into子句或fetch语句)将空值返回给宿主变量,并且已为此宿主变量定义了指示变量,则指示变量的值是-1,表示宿主变量的实际值是没有意义的。当一个非空的SQL值返回给说明成字符串的宿主变量时,为了适应变量的长度,它的值可能被截短。当被截短时,指示变量的值被置为截短前的字符串的实际字节数,若没被截短时,指示变量的值被置成0,因此,在编写ESQL/C程序时,可以通过指示变量来判断空值。
在SQL语句中可以采用以下两种方式之一来使用指示变量:
(1)在宿主变量与指示变量之间放置美元符“$”或冒号“:”,格式如下:
$宿主变量:指示变量
或
$宿主变量$指示变量
使用冒号“:”使代码易读、易于理解。
(2)在宿主变量和指示变量之间放置关键字INDICATOR格式为:
$宿主变量INDICATOR指示变量
这种写法符合ANSI标准。使用指示变量的例子如下:
$ char name[16];
$ char comp[20];
$ short nameind;
$ short compind;
.
.
.
$ select lname, company
into $ name:nameind, $ comp:compind
from customer
where customer_num = 105;
如果表customer中的列lname的数据类型被定义成了15的字符串,那么查询语句的结果是:name中含有lname的前15个字符,nameind中存有数据库列lname的值的实际长度。如果客户号为105和客户的lname中的实际值比15个字符短,那么截掉的只是字符串尾部的空格。如果compnany含有空值,则compind的值是-1,表示comp的值是中可知的。
指示变量的又一用途是,往数据库中插入一空值的字段。在INSERT语句中,宿主变量带有一个具有负值的指示变量,将数据插入数据库后,相应字段的值为Null。
2.4 INFORMIX ESQL/C编译器的支持
INFORMIX ESQL/C的编译命令esql,除了处理嵌入的SQL语句,还处理以下几个内容:
(1)嵌入的SQL语句中的注释
在嵌入的SQL语句中可以加上程序注释。除了C语言中支持的注释方法:/*注释信息*/,还有一种注释方法,那就是用双减号(--)来标识该行在双减号之后的信息为注释。
如:
EXEC SQL delete --has no order
from customer;
(2)包含其它文件
在ESQL/C程序中,可以通过include语句包含其它的源文件。例如,在一个单独的文件中定义程序的全局变量,各个引用全局变量的源程序在文件头部将此文件包含进来。当源文件中没有SQL语句时,使用C语言的#include语句;当源文件中有SQL语句时,使用以下方式来包含文件:
$include filename;
或EXEC SQL include filename;
在$INFORMIXDIR/incl/esql目录下,有九个可用的头文件,它们是:
sqlca.h 用于存储SQL语句执行后的状态
decimal.h 使用DECIMAL类型时,包含该文件
datetime.h 使用DATETIME和INTERVAL类型时,包含该文件
varchar.h 使用VARCHAR类型时,包含该文件
locator.h 使用BYTE和TEXT类型时,包含该文件
sqlda.h 使用动态SQL语句编程时,包含该文件
sqlstypes.h 使用动态SQL语句编程时,包含该文件
sqltypes.h 使用C和SQL的数据类型常量值时,包含该文件,
多数情况是在用动态SQL语句时sqlxtype.h 在X/OPEN模式下使用C和SQL的数据类型常量值
时,包含该文件
INFORMIX ESQL/C的预编译器在遇到ESQL/C中的include语句时,如果文件名没有指定绝对路径时,查找包含文件的顺序是首先查找当前目录,然后在$INFORMIXDIR/incl/esql、目录下查找,最后到/usr/include目录下查找。一旦在上述三个目录下找到,则停止查找;否则程序编译出错。
(3)条件编译 INFORMIX ESQL/C中支持条件编译语句,语句的书写形式与C的条件编译类似。例如:
EXEC SQL define USERTRANSACTIONS;
EXCE SQL ifdef USERTRANSACTIONS;
EXEC SQL begin work;
EXEC SQL endif;
2.5 几个常用SQL语句的写法
(1)INSERT语句
INSERT语句的一般形式为:
INSERT INTO表名[(columnList)]
VALUES (valueList)
valueList可以是常量值或宿主变量。例如:
EXEC SQL insert into customer (customer_num, fname, lname, company)
values (0,:fname,:lname,"MPS Corp");
EXEC SQL insert into items
values (:itemrec);
其中列名和要插入的值一一对应。第一个例子的写法与数据库表中列名的顺序无关,第二个例子用结构作整体赋值,若数据库表中列的顺序改变,则程序也要进行相应的变化。
(2)SELECT语句
SELECT语句的一般形式为:
SELECT SelectList INTO宿主变量列
FROM TableList
[WHERE查询条件]
例子:
EXEC SQL select * into :custrec
from customer
where customer_num = : cust_num;
其中custrec的域与customer表中的列的结构一一对应。ESQL/C程序中的SELECT语句只能处理返回一行数据的情况。若SELECT语句返回多行数据,则要使用游标来处理。
当从数据库取来的数据出现异常时,这里的异常指某列返回空值或数据发生截断,在INFORMIX ESQL/C中可以定义指示变量来检测。
指示变量是宿主变量的一种,它用于检测SQL语句中数据出现的异常。通常定义为short类型。使用方式为:
a.要检测的宿主变量:指示变量
b.要检测的宿主变量INDICATOR指示变量
例如:
EXEC SQL begin declare section;
char fname[15], lname[15];
short fname_ind, lname_ind;
long cnum;
EXEC SQL end declare section;
cnum = 101;
EXEC SQL select fname, lname
into :fname :fname_ind, :lname :lname_ind
from customer
where customer_num = :cnum;
当数据发生截断时,如果是字符型数据,则指示变量的值为截断前的字符串长,如果是数值型转换成字符串型,则指示变量的值大于0,sqlca.sqlwarn0=w,表示警告。当传进一个NULL值时,指示变量的值小于0。没有异常发生,指示变量的值为0。根据指示变量的值,程序员可以作出相应的处理。
(3)UPDATE语句
UPDATE一般形式是:
UPDATE表名
SET {column-name=expr[,...]|
{(column-list)|*}=
(value-list)}
[WHERE{condition|
CURRENT OF cursor-name}]
例子:
EXEC SQL update customer
set (fname, lname)=(:fname, :lname)
where customer_num=:cust_num;
WHERE后接CURRENT OF是使用了游标的情况,语句的含义为:更新游标指向的当前行。
(4)DELETE语句
DELETE语句的一般形式为:
DELETE FROM表名
WHERE条件
例子:
EXEC SQL delete from orders
where customer_num = custemer_num;
