语法详解"/>
Pro*C入门手册+语法详解
Pro*C入门手册+语法详解
概述
嵌入式 SQL 是一种结合 C/C++ 等高级语言的计算能力和 SQL 的数据库操作能力的方法。它允许您从应用程序执行任何 SQL 语句。Oracle 的嵌入式 SQL 环境称为 ProC。
ProC 程序分两步编译。首先,Pro*C预编译器 识别程序中嵌入的 SQL 语句,并将它们替换为对 SQL 运行时库中函数的适当调用。输出是纯 C/C++ 代码,所有纯 C/C++ 部分都完好无损。然后,使用常规 C/C++ 编译器编译代码并生成可执行文件。有关详细信息,请参阅演示程序部分。
Pro*C 语法
SQL
所有 SQL 语句都需要以EXEC SQL开头并以分号“ ; ”结尾。您可以将 SQL 语句放置在 C/C++ 块中的任何位置,但限制是声明性语句不在可执行语句之后。举个例子:
{int a;/* ... */EXEC SQL SELECT salary INTO :aFROM EmployeeWHERE SSN=876543210;/* ... */printf("The salary is %d\n", a);/* ... */}
预处理器指令
与 ProC 一起使用的 C/C++ 预处理器指令是#include 和#if。ProC 无法识别#define。例如,以下代码无效:
应该仅仅是#define 定义的宏不能用于SQL语句。
#define THE_SSN 876543210
/* ... */
EXEC SQL SELECT salary INTO :aFROM EmployeeWHERE SSN = THE_SSN; /* 无效的*/
声明标签
您可以将 C/C++ 标签与 SQL 连接起来,如下所示:
EXEC SQL WHENEVER SQLERROR GOTO error_in_SQL;/* ... */error_in_SQL:/* 进行错误处理 */
我们将在稍后的错误处理部分讨论WHENEVER的含义。
宿主变量
基本
宿主变量是宿主程序与数据库之间通信的关键。宿主变量表达式必须解析为左值 (即,它可以被赋值)。您可以根据 C 语法声明宿主变量,就像声明常规 C 变量一样。宿主变量声明可以放置在可以放置 C 变量声明的任何位置。(C++ 用户需要使用声明部分;请参阅C++ 用户部分。)可用于 Oracle 的 C 数据类型包括:
char
char[n]
int
short
long
float
double
VARCHAR[n] - 这是 Pro*C 预编译器识别的伪类型。它用于表示空白填充的可变长度字符串。Pro*C 预编译器会将其转换为具有 2 字节长度字段和n字节字符数组的结构。
您不能对宿主变量使用寄存器 存储类说明符。
宿主变量引用在 SQL 语句中必须以冒号“ : ”为前缀,但在 C 语句中不应以冒号为前缀。通过宿主变量指定字符串文字时,必须省略单引号;Pro*C 理解您正在根据宿主变量的声明类型指定字符串。C 函数调用和大多数指针算术表达式不能用作宿主变量引用,即使它们确实可以解析为左值。以下代码说明了合法和非法的宿主变量引用:
int deptnos[3] = { 000, 111, 222 };
int get_deptno() { return deptnos[2]; }
int *get_deptnoptr() { return &(deptnos[2]); }
int main() {int x; char *y; int z;/* ... */EXEC SQL INSERT INTO emp(empno, ename, deptno)VALUES(:x, :y, :z); /* 合法的 */EXEC SQL INSERT INTO emp(empno, ename, deptno)VALUES(:x + 1, /* 合法的: 引用 x */'Big Shot', /* 合法的: 但不是真正的宿主变量 */:deptnos[2]); /* 合法:数组元素很好 */EXEC SQL INSERT INTO emp(empno, ename, deptno)VALUES(:x, :y,:(*(deptnos+2))); /* 非法:尽管它有一个左值 */EXEC SQL INSERT INTO emp(empno, ename, deptno)VALUES(:x, :y,:get_deptno()); /* 非法:不能函数调用 */EXEC SQL INSERT INTO emp(empno, ename, deptno)VALUES(:x, :y,:(*get_depnoptr())); /* 非法:尽管它有一个左值 *//* ... */
}
指针
您可以使用常规 C 语法定义指针,并在嵌入式 SQL 语句中使用它们。像往常一样,在它们前面加上一个冒号:
int *x;
/* ... */
EXEC SQL SELECT xyz INTO :x FROM ...;
此SELECT语句的结果将写入*x,而不是x。(x的值为地址,*x为x的值(地址)对应的值;写入x指针地址对应的值,而不是指针地址)
和上面注意区分:C 函数调用和大多数指针算术表达式不能用作宿主变量引用;意思是指针可以作为宿主变量,但是不能将指针进行算术表达式后作为宿主变量引用。
结构体
结构体可以用作宿主变量,如下例所示:
typedef struct {char name[21]; /* one greater than column length; for '\0' */int SSN;} Emp;/* ... */Emp bigshot;/* ... */EXEC SQL INSERT INTO emp (ename, eSSN)VALUES (:bigshot);
数组
宿主数组可以通过以下方式使用:
int emp_number[50];
char name[50][11];
/* ... */
EXEC SQL INSERT INTO emp(emp_number, name)VALUES (:emp_number, :emp_name);
这将一次性插入所有 50 个元组。
数组只能是一维的。示例char name[50][11]
似乎与该规则相矛盾。然而,Pro*C 实际上认为name 是一维字符串数组,而不是二维字符数组。您还可以拥有结构体数组。
使用数组存储查询结果时,如果宿主数组的大小(例如n)小于查询返回的实际元组数,则只有前n 个结果元组将输入宿主数组。
指示变量
指示变量本质上是附加到主变量的“ NULL flags”。您可以将每个宿主变量与可选的指示变量相关联。指示变量必须定义为 2 字节整数(使用类型short),并且在 SQL 语句中,必须以冒号为前缀;并紧跟其宿主变量。或者,您可以在宿主变量和指示变量之间使用关键字 INDICATOR 。这是一个例子:
short indicator_var;EXEC SQL SELECT xyz INTO :host_var:indicator_varFROM ...;/* ... */EXEC SQL INSERT INTO RVALUES(:host_var INDICATOR :indicator_var, ...);
您可以在SELECT语句的INTO子句中 使用指示变量 来检测输出宿主变量中的NULL或截断值。Oracle 可以分配给指示变量的值具有以下含义:
值 | 含义 |
---|---|
-1 | 列值为NULL,因此宿主变量的值是不确定的。 |
0 | Oracle 将完整的列值分配给宿主变量。 |
>0 | Oracle 将截断的列值分配给宿主变量。指示变量返回的整数是列值的原始长度。 |
-2 | Oracle 将截断的列变量分配给宿主变量,但无法确定原始列值。 |
您还可以在INSERT或UPDATE 语句 的VALUES和SET 子句中使用指示变量,将是否是NULL分配给输入宿主变量。您的程序可以分配给指示变量的值具有以下含义: | |
值 | 含义 |
– | – |
-1 | Oracle 将为该列分配NULL,而忽略宿主变量的值。 |
>=0 | Oracle 会将宿主变量的值分配给该列。 |
数据类型等效
Oracle 识别两种数据类型:内部和外部。内部数据类型指定 Oracle 如何在数据库表中存储列值。 外部数据类型指定用于在输入和输出宿主变量中存储值的格式。在预编译时,为每个宿主变量分配一个默认的 Oracle 外部数据类型。数据类型等效允许您覆盖此默认等效,并允许您控制 Oracle 解释输入数据和格式化输出数据的方式。
可以使用VAR语句在逐个变量的基础上进行等效。语法是:
EXEC SQL VAR <host_var> IS <type_name> [ (<length>) ];
例如,假设您想从emp 表中选择name,然后将它们传递给需要 C 风格’\0’终止字符串的例程。您不需要自己明确地用’\0’ 来终止name。只需将宿主变量等同于STRING外部数据类型,如下所示:
char emp_name[21];
EXEC SQL VAR emp_name IS STRING(21);
emp表中ename列 的长度为 20 个字符,因此您分配emp_name 21 个字符以容纳’\0’ -终止符。STRING是一种 Oracle 外部数据类型,专门设计用于与 C 样式字符串交互。当您从ename列中选择一个值到emp_name时,Oracle 将自动为您终止该值。
您还可以使用TYPE语句将用户定义的数据类型等同于 Oracle 外部数据类型。语法是:
EXEC SQL TYPE <user_type> IS <type_name> [ (<length>) ] [REFERENCE];
您可以将用户定义的类型声明为指针,可以显式声明为指向标量或结构的指针,也可以隐式声明为数组,然后在TYPE语句中使用此类型。在这些情况下,您需要在语句末尾使用REFERENCE子句,如下所示:
typedef unsigned char *my_raw;EXEC SQL TYPE my_raw IS VARRAW(4000) REFERENCE;my_raw buffer;/* ... */buffer = malloc(4004);
作用:说明自定义类型my_raw这个类型等价于数据库的VARRAW(4000)这个外部类型
这里我们分配了比类型长度(4000)更多的内存,因为预编译器也会返回长度,并且可能会在长度之后添加填充以满足您系统上的对齐要求。
总结:EXEC SQL VAR 是将宿主变量等同于数据库的外部变量类型 (当等同于STRING时,会自动以\0结束,注意宿主变量长度要多1,且数据库外部变量的类型指定的长度也要多1)
EXEC SQL TYPE 是将用户自定义的类型等同于数据库的外部变量的类型。(当定义一个字符指针类型char * 的变量时,可以等同于对应长度的VARRAW的数据库外部变量类型,注意宿主指针的空间要稍大于外部变量的长度)
动态 SQL
虽然嵌入式 SQL 适用于固定应用程序,但有时对于程序来说动态创建整个 SQL 语句很重要。使用动态 SQL,可以发布存储在字符串变量中的语句。PREPARE 将字符串转换为 SQL 语句,然后 EXECUTE执行该语句。考虑以下示例。
char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";EXEC SQL PREPARE q FROM :s;EXEC SQL EXECUTE q;
PREPARE和EXECUTE可以组合成一个语句或者EXECUTE IMMEDIATE:char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";EXEC SQL EXECUTE IMMEDIATE :s;
动态SQL结合USING使用
如果SQL语句要通过宿主变量赋值,输入SQL语句时要用占位符仅适用于非select语句
可包含虚拟输入宿主变量和指示器变量,其个数和类型在预编译时已知
语法
/*1.使用PREPARE命令准备SQL语句*/
/*statement_name: 标识符,host_string:含SQL语句的字符串*/
EXEC SQL PREPARE statement_name FROM :host_string;
/**2.使用EXECUTE命令执行SQL语句,使用USING使用宿主变量替代占位符*/
EXEC SQL EXECUTE statement_name [USING :host_variable]
示例
//准备动态sql
EXEC SQL PREPARE my_pre_sql FROM 'update dept set loc = :a where deptno = :b';
//执行动态sql
EXEC SQL EXECUTE my_pre_sql USING :loc, :deptno;
EXEC SQL COMMIT;
/*此处示例使用USING,感觉和下面的直接重写sql没什么区别*/
sprintf(sql, "update dept set loc='%s' where deptno=%d", loc, deptno );
EXEC SQL PREPARE my_pre_sql FROM :sql;
EXEC SQL EXECUTE my_pre_sql;
EXEC SQL COMMIT;
USING结合PREPARE和CURSOR一起使用的语法
/*1.使用PREPARE命令准备SQL语句*/
/*statement_name: 标识符,host_string:含SQL语句的字符串*/
EXEC SQL PREPARE statement_name FROM :host_string;
/*2.使用DECLARE命令定义游标,可以结合游标一块使用*/EXEC SQL DECLARE cursor_name CURSOR FOR statement_name; EXEC SQL OPEN cursor_name [using host_variable_list]EXEC SQL FETCH cursor_name INTO host_variable_listEXEC SQL CLOSE cursor_name
游标
查看多条记录时,通过游标进行数据的挨个读取。
结合动态SQL
int iCount=0;
EXEC SQL BEGIN DECLARE SECTION;char sSql[1024];char sData1[32+1];char sData2[32+1];
EXEC SQL END DECLARE SECTION;/*动态SQL*/
sprintf( sSql, "SELEECT col1, col2 FROM table1" );
EXEC SQL PREPARE qry_sql FROM :sSql;
/*声明游标 : qry_sql不需要冒号前缀是因为它是数据库变量而不是宿主变量*/
EXEC SQL DECLARE qry_cursor CURSOR WITH HOLD FOR qry_sql;
/*打开游标*/
EXEC SQL OPEN qry_cursor;
if(sqlca.sqlcode){printf("打开游标失败.\n");return;
}
iCount = 0;
while(1){memset(sData1, 0x00, sizeof(sData1) );memset(sData1, 0x00, sizeof(sData2) );EXEC SQL FETCH qry_cursor INTO :sData1, :sData2;if(sqlca.sqlcode){if(1403==sqlca.sqlcode){printf("已查询到最后一条.\n");printf("总记录:%d\n", iCount );break;}EXEC SQL CLOSE qry_cursor;printf("提取记录失败.\n");return;}printf("col1-val=%s\n", sData1);printf("col2-val=%s\n", sData2);iCount++;
}
/*关闭游标*/
EXEC SQL CLOSE qry_cursor;
如果CURSOR
不加WITH HOLD
;则此游标遇到COMMIT或者ROLLBACK
语句,会自动关闭。加上了WITH HOLD
则必须用EXEC SQL CLOSE cursor_name
关闭。
事务
Oracle PRO*C 支持 SQL 标准定义的事务。事务是 Oracle 将其视为单个工作单元的 SQL 语句序列。事务从您的第一个 SQL 语句开始。当您发出“ EXEC SQL COMMIT ”(在当前事务期间永久更改任何数据库)或“ EXEC SQL ROLLBACK ”(撤消自当前事务开始以来的任何更改)时,事务结束。在当前事务以您的 COMMIT或ROLLBACK语句结束后,下一个可执行 SQL 语句将自动开始一个新事务。
如果您的程序在没有调用EXEC SQL COMMIT的情况下退出,则所有数据库更改都将被丢弃。
EXEC SQL COMMIT; 和EXEC SQL COMMIT RELEASE;区别(ROLLBACK同理):
在Pro*C中 EXEC SQL COMMIT; 会提交事务并释放所有的锁定及其资源,而EXEC SQL COMMIT RELEASE会提交事务并释放所有的锁定及其资源
然后断开与数据库的连接,以后所有的与数据库操作的命令都会报ORA-01012错误,除非重新连接数据库。
错误处理
错误处理在每个可执行的 SQL 语句之后,您的程序可以通过显式检查 SQLCA 或通过使用 WHENEVER 语句的隐式检查来找到执行状态。下面将详细介绍这两种方式。
SQLCA
SQLCA (SQL Communications Area) 用于检测程序中的错误和状态变化。此结构包含由 Oracle 在运行时在每个可执行 SQL 语句之后填充的组件。
要使用 SQLCA,您需要使用#include指令包含头文件sqlca.h 。如果您需要在许多地方包含sqlca.h ,您需要首先使用 #undef SQLCA 取消定义宏SQLCA 。(因为sqlca.h中有SQLCA宏的判断)sqlca.h的相关块如下:
#ifndef SQLCA
#define SQLCA 1struct sqlca {/* ub1 */ char sqlcaid[8];/* b4 */ long sqlabc;/* b4 */ long sqlcode;struct {/* ub2 */ unsigned short sqlerrml;/* ub1 */ char sqlerrmc[70];} sqlerrm;/* ub1 */ char sqlerrp[8];/* b4 */ long sqlerrd[6];/* ub1 */ char sqlwarn[8];/* ub1 */ char sqlext[8];
};
/* ... */
sqlca中 的字段含义如下:
SQLCA 在其sqlerrm组件中只能容纳最多 70 个字符的错误消息。
要获取较长(或嵌套)错误消息的全文,您需要sqlglm()函数:
void sqlglm(char *msg_buf, size_t *buf_size, size_t *msg_length);
其中msg_buf是您希望 Oracle 存储错误消息的字符缓冲区;buf_size 以字节为单位指定msg_buf的大小;Oracle 将错误消息的实际长度存储在*msg_length中。Oracle 错误消息的最大长度为 512 字节。
WHENEVER 语句
WHENEVER语句允许您进行自动错误检查和处理。语法是:
EXEC SQL WHENEVER <condition> <action>;
Oracle 自动检查 SQLCA 的<condition>,如果检测到这样的条件,您的程序将自动执行<action>。
<condition>可以是以下任何一种:
值 | 含义 |
---|---|
SQLWARNING | -设置sqlwarn[0],因为 Oracle 返回了一个警告 |
SQLERROR | - sqlcode为负,因为 Oracle 返回了错误 |
NOT FOUND | - sqlcode为正,因为 Oracle 找不到满足WHERE条件的行,或者SELECT INTO或FETCH未返回任何 |
<action>可以是以下任何一种:
值 | 含义 |
---|---|
CONTINUE | - 如果可能,程序将尝试继续运行下一条语句 |
DO | - 程序将控制权转移到错误处理函数 |
GOTO <label> | - 程序分支到带标签的语句 |
STOP | - 程序通过exit()调用退出,未提交的工作被回滚 |
WHENEVER语句 的一些示例:
EXEC SQL WHENEVER SQLWARNING DO print_warning_msg();
EXEC SQL WHENEVER SQLERROR DO sql_error();
EXEC SQL WHENEVER NOT FOUND GOTO handle_empty;
这是一个更具体的例子:
/* code to find student name given id */
/* ... */
for (;;) {printf("Give student id number : ");scanf("%d", &id);EXEC SQL WHENEVER NOT FOUND GOTO notfound;EXEC SQL SELECT studentname INTO :st_nameFROM studentWHERE studentid = :id;printf("Name of student is %s.\n", st_name);continue;
notfound:printf("No record exists for id %d!\n", id);
}
/* ... */
请注意,WHENEVER 语句不遵循常规的 C 范围规则。范围对整个程序有效。例如,如果您在程序的某处(例如循环之前)有以下语句:
EXEC SQL WHENEVER NOT FOUND DO break;
文件中此行之后出现的所有 SQL语句 都会受到影响。确保在不再需要时(例如在循环之后)使用以下行来取消 WHENEVER 的效果:
EXEC SQL WHENEVER NOT FOUND CONTINUE;
/*先前设定的是NOT FOUND DO break;所以现在取消是NOT FOUND CONTINUE; SQLWARNING同理取消是SQLWARNING CONTINUE;*/
编译
#将嵌入式 SQL 代码转换为相应的库调用并输出 sample1.c
proc iname=sample1.pc oname=sample1.c
#生成可执行sample1
cc <a_number_of_flags_here> sample1.c
C++ 用户
要让预编译器生成适当的 C++ 代码,您需要注意以下问题:
预编译器的代码处理。要获取 C++ 代码,您需要 在执行proc时设置选项CODE=CPP
。C 用户不必担心这个选项;默认满足他们的需求。
解析能力。proc的PARSE选项可以采用以下值:
PARSE=NONE。C 预处理器指令只能在声明部分中理解,并且所有宿主变量都需要在声明部分中声明。
PARSE=PARTIAL。理解 C 预处理器指令;但是,所有宿主变量都需要在声明部分中声明。
PARSE=FULL。可以理解 C 预处理器指令,并且可以在任何地方声明宿主变量。当CODE 不是CPP时,这是默认设置; 使用CODE=CPP指定PARSE=FULL是错误的。
因此,C++ 用户必须指定PARSE=NONE或PARSE=PARTIAL。因此,他们失去了在代码中任意位置声明主变量的自由。相反,宿主变量必须封装在声明部分 中,如下所示:
EXEC SQL BEGIN DECLARE SECTION;// declarations...
EXEC SQL END DECLARE SECTION;
您需要遵循此例程在您这样做的所有地方声明宿主和指示变量。
文件扩展名。您需要指定选项CPP_SUFFIX=cc或 CPP_SUFFIX=C。
头文件的位置。默认情况下,proc在标准位置搜索诸如stdio.h 之类的头文件。但是,C++ 有自己的头文件,例如iostream.h,位于其他位置。所以你需要使用SYS_INCLUDE选项来指定proc
应该搜索头文件的路径。
Pro*C 支持的嵌入式 SQL 语句列表
声明性语法 | 说明 |
---|---|
EXEC SQL ARRAYLEN | 在 PL/SQL 中使用宿主数组 |
EXEC SQL BEGIN DECLARE SECTION | 开始声明宿主变量 |
EXEC SQL END DECLARE SECTION | 结束声明宿主变量 |
EXEC SQL DECLARE | 命名 Oracle 对象 |
EXEC SQL INCLUDE | 包含头文件(复制文件) |
EXEC SQL TYPE | 指定用户自定义数据类型等价数据库外部数据类型 |
EXEC SQL VAR | 指定用户定义的变量(应该是变量所属的类型)等价数据库外部数据类型 |
EXEC SQL WHENEVER | 处理运行时错误 |
可执行性语句 | 说明 |
---|---|
EXEC SQL ALLOCATE | 分配 |
EXEC SQL ALTER | 修改表 |
EXEC SQL ANALYZE | 定义和控制 Oracle 数据 |
EXEC SQL AUDIT | 定义和控制 Oracle 数据 |
EXEC SQL COMMENT | 注释 |
EXEC SQL CONNECT | 连接数据库 |
EXEC SQL CREATE | 创建表 |
EXEC SQL DROP | 删表 |
EXEC SQL GRANT | 授权 |
EXEC SQL NOAUDIT | |
EXEC SQL RENAME | 重命名 |
EXEC SQL REVOKE | 撤销 |
EXEC SQL TRUNCATE | 清空表或者说是截断表(与drop有点不同处) |
EXEC SQL CLOSE | 关闭 |
EXEC SQL DELETE | 删除数据 |
EXEC SQL EXPLAIN PLAN | 解释计划 |
EXEC SQL FETCH | 提取 |
EXEC SQL INSERT | 插入数据 |
EXEC SQL LOCK TABLE | 锁定表 |
EXEC SQL OPEN | 打开 |
EXEC SQL SELECT | 查询数据 |
EXEC SQL UPDATE | 更新数据 |
EXEC SQL COMMIT | 提交事务 |
EXEC SQL ROLLBACK | 回滚 |
EXEC SQL SAVEPOINT | 保存点 |
EXEC SQL SET TRANSACTION | 设置事务 |
EXEC SQL DESCRIBE | 动态 SQL描述 |
EXEC SQL EXECUTE | 动态SQL的执行 |
EXEC SQL PREPARE | 动态SQL的准备 |
EXEC SQL ALTER SESSION | 更改会话 |
EXEC SQL SET ROLE | 设置角色 |
EXEC SQL EXECUTE | 嵌入式PL/SQL块执行 |
END-EXEC | 嵌入式PL/SQL块 结束 |
附上:Proc*C开发示例
更多推荐
Pro*C入门手册+语法详解
发布评论