嵌入式程序的组成

3.嵌入式程序的组成

3.1一个简单的嵌入式程序结构分析

首先,从一个简单的程序入手来认识嵌入式程序的组成和基本语法。

例如,下面是一个名为test.pc的简单的嵌入式程序。这个程序查询并输出“人员信息”表中人员编号为“1”的人员姓名和联系电话。

/* test.pc */

##include <stdio.h>

/*宿主变量的定义 */

EXEC SQL BEGIN DECLARE SECTION;

char username[20],password[20],servername[20];

varchar person_name[50];

varchar person_phone[25];

EXEC SQL END DECLARE SECTION;

void main(void)

{

printf(" Please input username:");

scanf("%s",username);

printf(" Please input password :");

scanf("%s",password);

printf(" Please input servername :");

scanf("%s",servername);

/*登录数据库*/

EXEC SQL LOGIN :username PASSWORD :password SERVER :servername;

/*对数据库操作 */

EXEC SQL SELECT name, phone

INTO :person_name,:person_phone FROM person.person

WHERE personid = '1';

printf("\n 对应的人员姓名为: %s\n", person_name);

printf("\n 对应的联系电话为: %s\n", person_phone);

/*退出数据库*/

EXEC SQL LOGOUT;

}

一般说来,pc 文件由以下几个部分构成:

  1. 宿主变量定义

在上面的例子中,宿主变量定义为嵌入在如下语句中的部分:

EXEC SQL BEGIN DECLARE SECTION;

EXEC SQL END DECLARE SECTION;
  1. 登录数据库
EXEC SQL LOGIN :username PASSWORD :password SERVER :servername;
  1. 数据库操作

登录成功之后,就可以对数据库进行各种操作。

  1. 退出登录
EXEC SQL LOGOUT;

下面将对以上各部分加以详细说明。

3.2宿主变量的定义

嵌入的SQL语句可以使用主语言的变量来输入/输出数据。例如在SELECT语句中,需要将查找到的数据通过INTO子句送到某些变量中,或者WHERE子句需要用某些变量的值作条件。我们称SQL语句中使用到的变量为宿主变量。在SQL中所使用的宿主变量均必须在嵌入的SQL声明节中加以说明。

3.2.1声明节语句

  • 声明节开始语句

语法:

EXEC SQL BEGIN DECLARE SECTION;

功能:表示声明节的开始。

使用说明:后面必须有一个声明节结束语句与之相对应。

  • 声明节结束语句

语法:

EXEC SQL END DECLARE SECTION;

功能:表示声明节的结束。

使用说明:前面必须有一个声明节开始语句与之相对应。

  • 使用声明节应注意的问题

在声明节开始语句与结束语句中间,采用C语言定义变量的方式定义宿主变量。在声明节中定义的变量可以被C语句使用,而不必在声明节之外重新定义这些变量,否则会引起变量重复定义的错误。

一个程序模块中可以出现多个声明节,各个声明节可以出现在C程序的说明语句可出现的位置上。PRO*C规定,一个模块中所有的声明节中的宿主变量不得同名,而不论其实质是全局变量还是局部变量。预编译时,相应声明节的C语句是去掉了“EXECSQL BEGIN DECLARE SECTION”和“EXEC SQL END DECLARE SECTION”两个语句后的变量定义语句,其位置顺序均保持不变。

3.2.2常规数据类型变量的定义

在声明节中,可以使用的常规数据类型包括:char、short、int、long、float、double等C数据类型。定义这些类型变量的方法与C语言定义相应变量的方法基本上是相同的。由于DM采用的是一次一个元组的处理方式,因而限制了数组的定义和使用,即不能使用数组的某个元素。作为一种特例,在声明节中定义字符数组,作为字符串指针使用。如:char name[20]等价于char *name ,其中name是指向长度为20个字节空间的字符指针(等价于指针变量)。

在定义常规类型的变量时,与 C语言一样,可以加上存储类型修饰符,包括 extern、static和auto。

定义语句的语法如下:

<宿主变量定义>::=<常规数据类型变量定义>

<常规数据类型变量定义>::=[<存储类型>] <C 类型说明><变量声明>[{,<变量声明>}...]

<存储类型>::=extern|static|auto

<C类型说明>::= char | short | int| long | float | double 等

<变量声明>::= <变量名说明> [= <常量表达式>]

<变量名说明>::=<变量名> | *<变量名> | <变量名>[无符号整数]

例如:

常规数据类型变量定义。

......

EXEC SQL BEGIN DECLARE SECTION;

char shop_no[20],shop_name[20];

short flag;

EXEC SQL END DECLARE SECTION;

......

预编译后,产生的结果为:

char shop_no[20],shop_name[20];

short flag;

......

3.2.3宿主变量的使用

3.2.3.1常规使用方法

在SQL 语句中引用的宿主变量都应在声明节中定义,而且定义应先于引用。

在SQL语句中使用宿主变量时,在引用的宿主变量名前必须加上并且只能加上冒号“:”,而不论说明该变量时变量名前有无“*”,或者是否采用数组形式。在C语句中使用宿主变量时,不能够加上“:”。

例如:

查询人员编号为“1”的人员姓名及联系电话。

EXEC SQL BEGIN DECLARE SECTION;

varchar person_name[50];

varchar person_phone[25];

varchar person_id[10];

EXEC SQL END DECLARE SECTION;

......

strcpy(person_id,"1");

EXEC SQL SELECT name, phone

INTO :person_name, :person_phone FROM person.person

WHERE personid =:person_id;

printf("%s %s ", person_name, person_phone);

执行结果:

李丽02788548562
3.2.3.2指示符的使用

在SQL 语句中,在一个宿主变量之后,可以再跟一个数据类型为整型 (short,long,int)的被称为指示变量的宿主变量,其作用是指明该变量之前的一个宿主变量的取值情况。具体地说,它有如下两个作用,分别举例说明:

  • 指示SQL 语句中输入变量的值是否为空值。

例如:

使用指示变量插入空值。利用预置指示变量的值为-1,表明插入一个空值。

EXEC SQL BEGIN DECLARE SECTION;

varchar person_name[50];

varchar person_phone[25];

varchar person_id[10];

short value_indicator;

EXEC SQL END DECLARE SECTION;

......

strcpy(person_name,"李丽");

person_phone ='02788548562';

value_indicator=-1;

EXEC SQL INSERT INTO person.person (name, phone)

VALUES(:person_name,:person_phone INDICATOR :value_indicator);

/*该语句等价于:

EXEC SQL INSERT INTO person.person (name, phone)

VALUES('李丽', NULL); */

在前一种插入语句中,person_phone带指示变量value_indicator,由于value_indicator的值为-1,尽管person_phone的值为02788548562,系统仍不使用该值作插入值,而认为person_phone的插入值为空。如果希望person_phone的插入值为02788548562,只需改变value_indicator的值为0即可。

在后一个插入语句中,person_phone未使用指示变量,它的插入值在插入语句中固定为NULL,如果希望person_phone的插入值为02788548562,则要修改该插入语句。因此,前一种使用指示变量的方法比后一种方法灵活,同时也给用户编程带来方便。

  • 指示执行 SQL语句后,返回结果是否为空值或有截取等情况。

在INTO子句中,使用指示变量表明该SQL语句执行时,对被指示的宿主变量的赋值情况。

一个指示变量的取值有三种:取值为0,说明返回值非空且赋给宿主变量时不发生截舍;取值为-1,说明返回的值为空值;取值>0,说明返回的值为非空值,但在赋给宿主变量时,字符串的值被截舍,这时指示变量的值为截断之前的长度。

3.2.3.3使用宿主变量应注意的问题

宿主变量的命名是有大小写区别的。字符序列相同,大小写不同的变量名代表不同的变量。

宿主变量的命名不应是C语句的关键字,以免产生误用或错误。使用宿主变量时应注意:SQL语句引用一个宿主变量时,宿主变量名前加一个冒号;C语句中使用宿主变量时,不加冒号;引用宿主变量时,可以使用一个相关的指示变量。

另外,由于C语言所允许的数据类型与SQL语言所采用的数据类型有一定的差别,因而在输入输出数据时,在两种数据类型之间要做一定的转换工作。DM支持BYTE、NUMERIC与short、int、float、double之间的转换,但应注意数据转换时产生的溢出问题。

3.2.4VARCHAR宿主变量的使用

可以使用VARCHAR类型声明可变长度的字符串,如果需要处理VARCHAR类型列的数据的输入输出,使用VARCHAR类型变量比使用C字符串类型会更加方便。“VARCHAR”可以是大写也可以是小写,但是不能大小写混用。

VARCHAR类型经过预编译被转换为C的结构。

例如:

VARCHAR username[20];

经过预编译后转换成:

struct

{

    unsigned short len;

    unsigned char arr[20];

} username;

其中len是数据的实际长度,arr存放的是字符串的数据。在SQL语句中使用VARCHAR宿主变量与使用C类型宿主变量一样。

例如:

EXEC SQL SELECT NAME INTO :username FROM PERSON.PERSON;

3.2.5游标变量的使用

游标在PRO*C中作为一种对象变量使用,具体的使用方法见第6.4节介绍。

3.2.6CONTEXT变量

运行上下文CONTEXT变量实际上是一个句柄,指向客户端内存的一段区域,对应一个DM8数据库连接,包含0个或多个游标的状态与信息等。

使用CONTEXT变量一般包括以下几个步骤:

  1. 使用sql_context定义上下文宿主变量

例如:

sql_context my_context ;
  1. 使用ALLOCATE语句初始化context变量

例如:

EXEC SQL CONTEXT ALLOCATE :my_context ;
  1. 使用context变量,context使用后直到下一个CONTEXT USE才会切换到新的上下文。

例如:

EXEC SQL CONTEXT USE :my_context ;
  1. 使用FREE语句释放context变量

例如:

EXEC SQL CONTEXT FREE :my_context ;

CONTEXT变量的具体使用可参看后续“多线程支持”章节。

3.2.7结构宿主变量

可以在嵌入式程序中定义C结构宿主变量,然后在SQL语句中引用结构变量,需要注意结构中的成员必须与查询或插入SQL语句中表达式的顺序一致。

例1:使用结构进行数据插入。

typedef struct

{

    charsex[2];

    char name[51];

    char email[51];

    char phone[26];

} person_record;

person_record new_person;

/* 为new_person赋值 */

......

EXEC SQL INSERT INTO PERSON.PERSON(SEX, NAME, EMAIL, PHONE)

	VALUES (:new_person);

结构中的成员也可以是数组,这样用来批量执行,可同时操作多行。

例2:一次向PERSON表插入三行数据。

typedef struct

{

    char sex[3][2];

    char name[3][51];

    char email[3][51];

    char phone[3][26];

} person_record;

person_record new_person;

/* 为new_person赋值 */

......

EXEC SQL INSERT INTO PERSON.PERSON(SEX, NAME, EMAIL, PHONE)

	VALUES (:new_person);

当需要使用指示符变量时,由于宿主变量都包含在结构里,所以必须再定义一个结构包含各个宿主变量对应的指示符变量,并且成员顺序必须与宿主变量的结构一致。

例3:

struct

{

    short sex_ind;

    short name_ind;

    short email_ind;

    short phone_ind;

} person_record_ind;

/* 为person_record_ind赋值 */

......

EXEC SQL INSERT INTO PERSON.PERSON(SEX, NAME, EMAIL, PHONE)

	VALUES (:person_record_ind);

例4:一个完整的使用结构的示例。

/*

* This program connects to DM, declares and opens a cursor,

* fetches the name, email, and phone of all

* people, displays the results, then closes the cursor.

*/

##include <stdio.h>

##define UNAME_LEN 20

##define PWD_LEN 40

EXEC SQL INCLUDE SQLCA;

typedef struct

{

    char name[50];

    char email[50];

    char phone[50];

}person_info;

person_info person_rec_ptr;

EXEC SQL BEGIN DECLARE SECTION;

char username[50];

char password[50];

char servername[50];

EXEC SQL END DECLARE SECTION;

/* Declare function to handle unrecoverable errors. */

void sql_error();

main()

{

/* Connect to DM. */

    strcpy(username, "SYSDBA");

    strcpy(password, "SYSDBA");

    strcpy(servername, "192.168.0.89:5289");

    EXEC SQL WHENEVER SQLERROR DO sql_error("DM error--");

    EXEC SQL CONNECT :username IDENTIFIED BY :password USING :servername;

    printf("\nConnected to dm as user: %s\n", username);

/* Declare the cursor. All static SQL explicit cursors

* contain SELECT commands. 'salespeople' is a SQL identifier,

* not a (C) host variable.

*/

    EXEC SQL DECLARE salespeople CURSOR FOR

    	SELECT NAME, EMAIL, PHONE FROM PERSON.PERSON;

/* Open the cursor. */

	EXEC SQL OPEN salespeople;

/* Get ready to print results. */

    printf("\n\nThe company's salespeople are--\n\n");

    printf("Salesperson EMAIL PHONE\n");

    printf("----------- ------ ----------\n");

/* Loop, fetching all salesperson's statistics.

* Cause the program to break the loop when no more

* data can be retrieved on the cursor.

*/

	EXEC SQL WHENEVER NOT FOUND DO break;

    for (;;)

    {

        EXEC SQL FETCH salespeople INTO :person_rec_ptr;

        printf("%s %s %s\n", person_rec_ptr.name,

        	person_rec_ptr.email, person_rec_ptr.phone);

    }

/* Close the cursor. */

    EXEC SQL CLOSE salespeople;

    printf("\nArrivederci.\n\n");

    EXEC SQL COMMIT WORK RELEASE;

    exit(0);

}

void

sql_error(msg)

char *msg;

{

	char err_msg[512];

size_t buf_len, msg_len;

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    printf("\n%s\n", msg);

/* Call sqlglm() to get the complete text of the error message.*/

    buf_len = sizeof (err_msg);

    sqlglm(err_msg, &buf_len, &msg_len);

    printf("%.*s\n", msg_len, err_msg);

    EXEC SQL ROLLBACK RELEASE;

    exit(1);

}

3.2.8指针变量

也可以在嵌入式SQL中使用指针类型的变量。

例如:

char *int_ptr;

EXEC SQL SELECT NAME INTO :int_ptr FROM PERSON.PERSON;

3.3可执行的SQL语句

在C程序中嵌入的可执行SQL语句包括普通的SQL语句和数据库登录/退出语句,其中数据库登录/退出不是SQL标准语句,为嵌入式程序扩展的可执行SQL语句。

3.3.1数据库登录语句

数据库登录语句的语法如下:

EXEC SQL LOGIN :username PASSWORD :pwd [AT [dbname | :dbname]] 

SERVER:servername;

或EXEC SQL CONNECT :username IDENTIFIED BY :pwd [AT [dbname | :dbname]] [USING :servername];

或EXEC SQL CONNECT :conninfo [AT [dbname | :dbname]];

功能:用于数据库的登录。

使用说明:

  • 语句中username是长度最多为128个字符的指针,其值为注册用户的名字;
  • pwd表示相应的用户口令,口令的长度最多为128个字符;
  • DM PROC支持多连接,dbname用于指定连接名,为长度最大为128个字符的标识符或宿主变量。在使用多连接时给每个连接命名(不命名时系统会自动给一个默认连接名),在执行其他嵌入SQL时通过指定连接名可以切换到不同连接进行操作;
  • servername为服务器的名字, 名字的长度最多为128个字符;
  • servername能带有端口号,例如“192.168.0.89:5236”,conninfo包含用户名密码以及IP和端口号,例如SYSDBA/SYSDBA@192.168.0.89:5236。
注意

在用户程序中,登录语句的逻辑位置必须先于所有可执行的 SQL语句,只有执行登录语句之后,才能执行其它可执行的SQL语句。如果登录失败,则终止程序的运行。

3.3.2数据库退出语句

数据库退出语句的语法如下:

EXEC SQL [AT [dbname | :dbname]] LOGOUT;

或EXEC SQL [AT [dbname | :dbname]] COMMIT RELEASE;

功能:断开与数据库的连接。

使用说明:在一个程序运行结束后,最好有一个LOGOUT语句,用于切断客户程序与DM数据库服务器之间的联系,有利于服务器释放相应的空间。在执行LOGOUT语句时,隐含有对最后一个事务的提交。

3.3.3普通SQL语句

能在C程序中嵌入的可执行普通SQL语句包括建表、建索引等DDL语句;SELECT、INSERT、DELETE、UPDATE等DML语句;RETURNING语句和游标操作语句等。

  • SELECT语句

查询是最常用的SQL操作之一,可以将从数据库中检索到的值赋给相应的目标变量。

常用语法格式如下,详细的SELECT语句语法介绍请参考《DM8_SQL语言使用手册》。

EXEC SQL [AT [dbname | :dbname]] SELECT [ALL|DISTINCT]<选择清单>INTO<选择目标清单> FROM <表引用> [,<表引用>...] WHERE<搜索条件>[<HAVING子句>];

例如下面的语句对PERSON.PERSON表进行查询。

EXEC SQL SELECT NAME,PHONE INTO :name,:phone FROM PERSON.PERSON WHERE
PERSONID=:personid;

查询语句的INTO、WHERE、CONNECT BY、START WITH、GROUP BY、HAVING、ORDER BY、FOR UPDATE等语法在嵌入SQL中也同样支持。

  • INSERT语句

使用INSERT语句向数据库插入数据,例如下面的语句通过宿主变量向PERSON.PERSON表插入数据。

EXEC SQL INSERT INTO PERSON.PERSON(NAME, PHONE) VALUES(:name, :phone);
  • DELETE语句

使用DELETE语句删除数据库表中的数据,例如下面的语句通过宿主变量删除PERSON.PERSON表中的特定数据。

EXEC SQL DELETE FROM PERSON.PERSON WHERE PERSONID=:personid;
  • UPDATE语句

使用UPDATE语句更新数据库表中指定列的值,例如下面的语句通过宿主变量更新PERSON.PERSON表中指定行的PHONE与EMAIL列的值。

EXEC SQL UPDATE PERSON.PERSON SET PHONE=:phone,EMAIL=:email WHERE
PERSONID=:PERSONID;
  • DML RETURNING语句

INSERT、DELETE、UPDATE语句后面可以跟RETURNING子句,RETURNING子句的语法如下:

<RETURNING | RETURN><expr {,expr}>

INTO <:hv [[INDICATOR]:iv] {, :hv [[INDICATOR]:iv]}>

其中hv表示宿主变量,iv表示指示符变量。

注意

RETURN后的表达式与INTO后的宿主变量数必须一致

  • DDL语句

可使用DDL语句进行数据定义,包括创建用户、创建表、修改表、创建索引等等。

例如下面的语句创建表T。

EXEC SQL CREATE TABLE T(C1 INT, C2 INT);

3.3.4游标语句

使用游标一般包括四个步骤:

  1. 声明游标:DECLARE CURSOR
  2. 打开游标:OPEN CURSOR
  3. 使用游标获取数据:FETCH
  4. 关闭游标:CLOSE CURSOR

使用游标必须先声明,声明游标实际上是定义了一个游标工作区,并给该工作区分配了一个指定名字的指针(即游标)。游标工作区用以存放满足查询条件行的集合,因此它是一说明性语句,是不可执行的。在打开游标时,就可从指定的基表中取出所有满足查询条件的行送入游标工作区并根据需要分组排序,同时将游标置于第一行的前面以备读出该工作区中的数据。当对行集合操作结束后,应关闭游标,释放与游标有关的资源。

3.3.4.1声明游标语句

声明一个游标,给它一个名称,同时也可定义与之相联系的SELECT语句。

语法格式为:

[AT [dbname | :dbname]] DECLARE 游标名 CURSOR [WITH HOLD] [FAST | NOFAST] FOR < <SELECT语句>|<SQL 语句名> >;

参数说明:

  • dbname:指明游标声明使用的连接名,游标其他操作只是语法上支持AT,所以游标操作完成前不支持再进行连接切换;
  • 游标名:指明声明的游标的名称;
  • <SELECT 语句>:被称为查询说明,指明对应于被定义的游标的SELECT语句,不能包括INTO子句;
  • <SQL 语句名>:PREPARE语句的语句名。

使用说明:

  • 用户必须在其他的嵌入式SQL语句引用该游标之前定义它。游标说明的范围在整个预编译单元内,并且每一个游标的名字必须在此范围内唯一;
  • Oracle兼容模式下默认提交时不关闭游标,非Oracle兼容模式下默认提交时关闭游标。WITH HOLD游标与兼容模式无关,提交时都不关闭;
  • FAST属性指定游标是否为快速游标。缺省时默认为NO FAST对应的普通游标。若定义游标时设置FAST属性将游标定义为快速游标,该游标在执行过程中会提前返回结果集,速度上提升明显,但是存在以下的使用约束:
  1. 使用快速游标的PLSQL语句块中不能修改快速游标所涉及的表;
  2. 禁止将快速游标赋值给其它游标对象;
  3. 快速游标上不能创建引用游标;
  4. 不支持快速游标更新和删除;
  5. 快速游标不支持next以外的fetch方向。

例如:声明一个游标cheap_book。

EXEC SQL DECLARE cheap_book CURSOR FOR

  SELECT NAME, AUTHOR, PUBLISHER, NOWPRICE

  FROM PRODUCTION.PRODUCT

  WHERE NOWPRICE < 15.0000;
3.3.4.2打开游标语句

打开一个已经声明过的游标。

语法格式为:

格式一:OPEN <游标名>

格式二:OPEN <游标变量> FOR <查询表达式> | <表达式子句>;

<表达式子句>::=<表达式> [USING <绑定参数> {, <绑定参数>}]
注意

格式一对应声明游标语句的格式一<br>格式二对应声明游标语句的格式二

参数说明:

  • 游标名:指明被打开的游标的名称

例如,打开游标cheap_book。

EXEC SQL OPEN cheap_book;
3.3.4.3拨动游标语句

使游标移动到指定的一行,若游标名后跟随有INTO子句,则将游标当前指示行的内容取出分别送入INTO后的各变量中。

语法格式为:

FETCH [[NEXT|PRIOR|FIRST|LAST|ABSOLUTE n|RELATIVE n] [FROM] ] <游标名>

[INTO <赋值对象>{,<赋值对象>>}];

参数说明:

  • NEXT:游标下移一行;
  • PRIOR:游标前移一行;
  • FIRST:游标移动到第一行;
  • LAST:游标移动到最后一行;
  • ABSOLUTE n:游标移动到第n行;
  • RELATIVE n:游标移动到当前指示行后的第n行。

使用说明:

  • 当游标被打开后,若不指定游标的移动位置,第一次执行FETCH语句时,游标下移,指向工作区中的第一行,以后每执行一次FETCH语句,游标均顺序下移一行,使这一行成为当前行;
  • INTO后的变量个数、类型必须与定义游标语句中、SELECT后各值表达式的个数、类型一一对应。

例1,对于上一节中打开的游标,若连续执行以下语句两次:

EXEC SQL FETCH cheap_book INTO :a, :b, :c, :d;

由于是两次连续执行FETCH语句且结果均放在变量:a、:b、:c、:d中,这样,第一次的结果已被第二次的结果冲掉,最后的结果为:

a='老人与海';b='海明威';c='上海出版社';d=6.1000

该例说明:当需要连续取出工作区的多行数据时,应将FETCH语句置入高级语言的循环结构中。

例2,当设置ABSOLUTE属性时,n值是从1开始的。如执行如下语句:

EXEC SQL FETCH ABSOLUTE 3 cheap_book INTO :a, :b, :c, :d;

结果为:

a='突破英文基础词汇'; b='刘毅'; c='外语教学与研究出版社';d=11.1000
3.3.4.4关闭游标语句

关闭指定的游标,收回它所占的资源。

语法格式为:

CLOSE <游标名>;

参数说明:

  • 游标名:指明被关闭的游标的名称

例如,关闭之前打开的cheap_book游标。

EXEC SQL CLOSE cheap_book;
3.3.4.5游标定位删除更新语句
  • 可更新游标

在嵌入式SQL中,通过游标对基表进行修改和删除时要求该游标表必须是可更新的。可更新游标的条件是:游标定义中给出的查询说明必须是可更新的。DM对查询说明是可更新的有这样的规定:

  • 查询说明的FROM后只带一个表名,且该表必须是基表或者是可更新视图;
  • 查询说明是单个基表或单个可更新视图的行列子集,SELECT后的每个值表达式只能是单纯的列名,如果基表上有聚集索引键,则必须包含所有聚集索引键;
  • 查询说明不能带GROUP BY子句、HAVING子句、ORDER BY子句;
  • 查询说明不能嵌套子查询。

不满足以上条件的游标表是不可更新的。3.3.4.1节例子中声明的游标cheap_book就是一个可更新游标。

  • 游标定位删除语句

游标定位删除语句的语法格式为:

[AT [dbname | :dbname]] DELETE FROM <表引用> [WHERE CURRENT OF <游标名>];

<表引用>::= [<模式名>.] <基表或视图名> | <外部连接表>

<基表或视图名>::=<基表名> | <视图名>

例如,使用之前声明的游标cheap_book,下面的语句将游标拨动到指定的位置,并进行游标定位删除。

EXEC SQL OPEN cheap_book;

EXEC SQL FETCH ABSOLUTE 2 cheap_book;

EXEC SQL DELETE FROM PRODUCTION.PRODUCT WHERE CURRENT OF cheap_book;
  • 游标定位更新语句

游标定位更新语句的语法格式为:

[AT [dbname | :dbname]] UPDATE <表引用>

SET <列名>=<值表达式>{,<列名>=<值表达式>}

[WHERE CURRENT OF <游标名>];

<表引用>::= [<模式名>.] <基表或视图名> | <外部连接表>

<基表或视图名>::=<基表名> | <视图名>

例如,使用之前声明的游标cheap_book,下面的语句将游标拨动到指定的位置,并进行游标定位更新。

EXEC SQL OPEN cheap_book;

EXEC SQL FETCH ABSOLUTE 3 cheap_book;

EXEC SQL UPDATE PRODUCTION.PRODUCT SET NOWPIRCE=13.0000 WHERE CURRENT OF cheap_book;

3.3.5嵌入式程序中的异常处理

3.3.5.1嵌入的异常声明语句

在C程序中,任何可出现说明语句的位置,皆可嵌入异常声明处理语句,其作用是指明在该异常声明处理语句的作用域内每个SQL操纵语句在发生操作异常时的处理方法。下面介绍异常声明语句的格式、作用域、异常动作的执行。

  1. 语法格式
<嵌入的异常声明>::= EXEC SQL WHENEVER <条件><异常动作>;

<条件>::= SQLERROR │ NOT FOUND

<异常动作>::= STOP│ CONTINUE│GOTO <标号> │GO TO <标号> │DO<用户代码>

其中:<标号>为标准的C语言的标号。

  1. WHENEVER语句的作用域

一个WHENEVER 语句的作用域是从该语句出现的位置开始,到下一个指明相同条件的WHENEVER语句出现(若没有下一个相同条件的 WHENEVER语句,则到文件结束)之前的所有SQL语句。这种起始、终止位置是指源程序的物理位置,与该程序逻辑执行顺序无关。

  1. 异常动作的说明

如果<异常动作>为STOP,则停止操作;

如果<异常动作>为CONTINUE,即使产生异常,也不需要作异常处理。使用CONTINUE的主要作用是取消前面与之具有相同条件的异常声明处理语句的作用;

如果动作为GOTO<标号>,则程序转移到标号处执行;

如果动作为DO<用户代码>,则程序继续执行用户的C代码。

  1. 动作的触发条件

当一个SQL 语句执行后返回的 SQLCODE < 0时,SQLERROR 为真;当一个SQL语句执行后返回的 SQLCODE = 100,NOT FOUND为真。

当嵌入的异常声明的条件得到满足时,则触发异常动作的执行。

  1. 预编译的处理及举例

在对含有异常声明处理语句的程序进行预编译时,所有的异常声明处理语句都被删除。根据其作用域,在相应的SQL语句的调用函数之后,生成根据SQLCODE作相应处理的语句。

例如,按人员编号查询人员姓名、联系电话。设该文件名为test.pc。

EXEC SQL BEGIN DECLARE SECTION;

long SQLCODE;

char SQLSTATE[6];

varchar person_name[50];

varchar person_phone[25];

varchar person_id[10];

EXEC SQL END DECLARE SECTION;

//①

若打开游标出错,则转至标号err_proc处执行。同样在取值过程中出错,也要转至err_proc处执行。若游标C1移至游标表的最后一行之后,则返回的SQLCODE为100,程序将跳转到标号aaa处的语句处执行。

在对该程序段预编译后,生成对应C文件test.c。程序test.c内容为:

##include "dpc_dll.h"

/* Thread Safety */

typedef void * sql_context;

typedef void * SQL_CONTEXT;

int SQLCODE;

char SQLSTATE[6];

char person_name[50];

char person_phone[25];

char person_id[10];

//①定义游标

/*EXEC SQL DECLARE C1 CURSOR FOR

SELECT name, phone

FROM person.person WHERE personid=:person_id;*/

{

    void* handle = NULL;

    char curname[128];

    char* sqlstmt = NULL;

    sqlstmt = "SELECT name, phone FROM person.person WHERE personid=\n\

    ?;";

    dpc_alloc_bind_items(1, &handle);

    dpc_bind_item(handle, 1, "person_id", (void*)person_id, DPC_C_VARCHAR, 10, 10,
    10, NULL, 0, -1);

    strcpy(curname, "C1");

    dpc_declare_cursor_ex(curname, sqlstmt, handle);

    dpc_free_bind_items(handle);

    dpc_get_rt_info(&SQLCODE, SQLSTATE);

}

strcpy(person_id,"1");

// ②打开游标

/*EXEC SQL OPEN C1;*/

{

    dpc_open_cursor_ex("C1",NULL, 1);

    dpc_get_rt_info(&SQLCODE, SQLSTATE);

    if (dpc_get_rt_info(&SQLCODE, SQLSTATE) < 0)

    goto err_proc;

}

for(;;)

{//③拨动游标

/*EXEC SQL FETCH C1 INTO :person_name,:person_phone;*/

{

    void* handle_using = NULL;

    void* handle_into = NULL;

    dpc_alloc_bind_items(2, &handle_into);

    dpc_bind_item(handle_into, 1, "person_name", (void*)person_name, DPC_C_VARCHAR,
50, 50, 50, NULL, 0, -1);

    dpc_bind_item(handle_into, 2, "person_phone", (void*)person_phone,
DPC_C_VARCHAR, 25, 25, 25, NULL, 0, -1);

    dpc_fetch("C1", handle_into, handle_using, "", "", 1, DPC_FETCH_NEXT, DPC_C_INT,
NULL, 4, 1);

    dpc_free_bind_items(handle_using);

    dpc_free_bind_items(handle_into);

    dpc_get_rt_info(&SQLCODE, SQLSTATE);

    if (dpc_get_rt_info(&SQLCODE, SQLSTATE) < 0)

    	goto err_proc;

    if (dpc_get_rt_info(&SQLCODE, SQLSTATE) == dpc_get_data_not_found())

    	goto aaa;

}

printf("%s,%s ", person_name, person_phone);

}

......

aaa:

//④关闭游标

/*EXEC SQL CLOSE C1;*/

{

    dpc_close_cursor_by_name("C1");

    dpc_get_rt_info(&SQLCODE, SQLSTATE);

    if (dpc_get_rt_info(&SQLCODE, SQLSTATE) < 0)

    	goto err_proc;

}

exit(0);

err_proc:

printf("error = %d",SQLCODE);

exit(1);

如果预编译出错,则给出错误信息说明。

3.3.5.2通过 SQLCODE和 SQLSTATE获取 SQL语句执行的信息

在C文件中可以定义两个特殊的变量SQLSTATE和SQLCODE:

long SQLCODE;

char SQLSTATE[6];

这两个变量记录了当前SQL语句执行情况的信息。也就是说执行了一个SQL语句之后,可以通过返回的SQLCODE和SQLSTATE来判断语句执行是否发生异常;如果发生了异常,那么具体是哪一种异常。这些信息对于了解程序运行情况和程序流程的控制是很有用的。

在DM嵌入式程序中 SQLCODE是一个long 型变量,可能的值有:

  • SQLCODE < 0,SQL语句执行发生异常;
  • SQLCODE = 0,SQL语句执行成功;
  • SQLCODE > 0,SQL语句执行中产生警告。如100表示找不到数据(如删除一个空表中的记录等)。

DM嵌入式程序中,SQLSTATE是一个char[6]型的变量。其前两个字符表示类别代码,随后的三个字符表示子类的代码。例如当SQLSTATE = "22012"时,22是类别代码,表示SQL语句发生了数据异常;012是该类别下属的子类代码,表示发生的是被零除的数据异常。同样是数据异常,当SQLSTATE = "22008"时表示的则是发生了日期时间数据溢出。DM遵循SQL92标准,所以SQLSTATE中代码所表示的具体含义可以参考SQL92标准中的SQLSTATE部分。

3.3.6数据类型支持

DM嵌入式编程支持的数据类型见下表。

表3.1 DM嵌入式程序支持的数据类型
数据类型 DM_TYPE
DSQL_CHAR 1
DSQL_VARCHAR 2
DSQL_BIT 3
DSQL_TINYINT 5
DSQL_SMALLINT 6
DSQL_INT 7
DSQL_BIGINT 8
DSQL_DEC 9
DSQL_FLOAT 10
DSQL_DOUBLE 11
DSQL_BLOB 12
DSQL_DATE 14
DSQL_TIME 15
DSQL_TIMESTAMP 16
DSQL_BINARY 17
DSQL_VARBINARY 18
DSQL_CLOB 19
DSQL_TIME_TZ 22
DSQL_TIMESTAMP_TZ 23
DSQL_INTERVAL_YEAR 100
DSQL_INTERVAL_MONTH 101
DSQL_INTERVAL_DAY 102
DSQL_INTERVAL_HOUR 103
DSQL_INTERVAL_MINUTE 104
DSQL_INTERVAL_SECOND 105
DSQL_INTERVAL_YEAR_TO_MONTH 106
DSQL_INTERVAL_DAY_TO_HOUR 107
DSQL_INTERVAL_DAY_TO_MINUTE 108
DSQL_INTERVAL_DAY_TO_SECOND 109
DSQL_INTERVAL_HOUR_TO_MINUTE 110
DSQL_INTERVAL_HOUR_TO_SECOND 111
DSQL_INTERVAL_MINUTE_TO_SECOND 112

关于数据类型的长度、精度与刻度,需要注意以下几点:

  • DSQL_CHAR、DSQL_VARCHAR的长度为其定义的长度。char、varchar2类型会被创建为DSQL_CHAR类型;
  • DSQL_BIT、DSQL_TINYINT、DSQL_SMALLINT、DSQL_INT、DSQL_BIGINT的精度分别为3、3、5、10、19,刻度为0;
  • DSQL_DEC的精度与刻度根据定义来确定。dec的精度与刻度都为0;当dm_svc.conf中的DEC2DOUB参数为TRUE时,转换为double类型,精度为53;
  • DSQL_CLOB、DSQL_BLOB类型的长度并不限制,默认返回-1;
  • 创建DSQL_FLOAT需要使用real关键字,精度为24;float与double类型都会被创建为DSQL_DOUBLE类型,精度为53。

3.4编写嵌入式程序的注意事项

PRO*C程序和C程序相类似,其编写也遵守标准C程序的规定。除此之外,还应注意下列问题:

  1. 编程者应该在程序中定义一个类型为长整型的全局变量SQLCODE。该变量可以在嵌入的变量声明节中定义,也可以在声明节之外定义。定义该变量的物理位置应先于所有可执行的SQL语句。SQLCODE将保存一条SQL语句执行后所产生的返回码。
  2. 应广泛使用嵌入的异常声明处理语句,以便及时根据返回结果进行相应的处理。异常声明语句可出现在程序中的任何语句可出现的位置上。
  3. 宿主变量分全局变量与局部变量,在程序开始处定义的为全局变量,在子函数内定义的为局部变量,其作用域范围与相应位置定义的主语言变量相同,检查作用域的工作由主语言编译程序承担,预编译系统未作此项检查。
  4. 最好将宿主变量定义为全局变量。
  5. 应广泛使用指示变量。当查询的结果为空值时,若相应的目标变量之后无指示变量,SQLCODE将被置为-5000。
  6. 游标声明是一种特殊的说明性语句。我们规定该语句只能出现在可执行的C语句出现的位置上。在一个程序中,不能够定义同名游标,游标也不能跨模块使用。游标声明语句的物理位置应先于使用该游标的打开、拨动与关闭语句。在一个程序中,可以多次打开、关闭同一个游标。
  7. 在一个程序中,第一条可执行的SQL语句必须是“LOGIN”语句。
  8. 在程序运行结束前,最好有一条语句“LOGOUT”,注销对数据库的使用。
  9. 最好能够根据SQL语句的执行结果进行错误处理。
  10. 在对一个PRO*C程序进行预编译时(例如原文件为test.pc),在生成的C文件中会自动生成如下语句:
##include "dpc_dll.h"

##include "sqlca.h"

static sqlca_t sqlca;
  1. 一个PRO*C程序可以含有多个事务。在DM中,一个事务是被当作单个实体处理的SQL语句序列。在进行嵌入式程序设计时,应该注意正确及时的使用数据库的提交语句或回滚语句,一个模块中的事务最好安排在本模块内提交或回滚。在一个事务中,要么每个SQL语句的操作结果都被提交使之变为长久生效,要么同时被回滚使其改变被取消。
  2. 如果多个用户进程因为争夺资源而发生死锁,DM服务器将取消某一个用户进程的工作,该进程的当前事务被终止。
微信扫码
分享文档
扫一扫
联系客服