本节是写给那些希望开发 ecpg 接口的人的. 这里描述了这些接口是怎样工作的.本节的目的是给那些想认识一些 内部机制的人提供一些信息,而"如何使用"那一节应该描述了所有通常的问题. 所以,在深入 ecpg内部之前请先读一下本节. 如果你对 ecpg 如何工作不感兴趣,请略过本节.
这个版本的预编译器有一些缺陷:
to_date 等不存在。不过 Postgres 本身有一些很好的转换过程。所以你可能不会想要这些(函数)。
结构和联合必须在定义段里定义。
下面的语句到目前为止还没有实现:
一个 exec sql insert select from 语句的 "no data" 错误信息应该是 100。
如果一个 SET DESCRIPTOR 语句里声明的 PRECISION 或 SCALE 值被忽略, sqlwarn[6] 应该是 'W'.
首先写到输出的四行是 ecpg 的一贯做法. 这些是两行注释和两行用于库接口必须的包含行.
然后预编译器对文件处理一遍,一边读输入文件,一边输出到输出文件. 通常它只是把不加分析的把所有东西输出到输出文件里去.
当处理到EXEC SQL 语句时,预编译器对之进行处理并根据语句做相应的改变. EXEC SQL 语句可以是下列之一:
定义段以
exec sql begin declare section;开头,以
exec sql end declare section;结束.在定义段里只允许变量定义.这个段里定义的每个变量同时也 放到一个以变量名和对应类型为索引的变量列表里头.
尤其是结构(struct)或者联合(union)的定义同样必须在定义段里面列出。 否则 ecpg 就不能处理这些类型, 因为它不知道定义(是什么)。
定义同时也输出到文件里把这些变量作为通常的 C-变量.
特殊的类型 VARCHAR 和 VARCHAR2 的每个变量都被转换成一个命名结构. 一个下面这样的定义:
VARCHAR var[180];被转换成
struct varchar_var { int len; char arr[180]; } var;
一个包含语句看起来象:
exec sql include filename;注意这个与下面这行
#include <filename.h>
是不一样的。被包含的文件由 ecpg 本身分析。因此声明的头文件被包括在生成的 C 代码里。 这样你也能够在一个头文件里声明 EXEC SQL 语句。
一个联接语句看起来象:
exec sql connect to connection target;它创建与指定数据库的联接。
connection target (联接目标)可以用下面的方法声明:
也有不同的方法声明用户名:
最后的 userid 和 password。 每个都可以是一个文本常量,一个字符变量或者一个字符串。
一个断开语句看起来象:
exec sql disconnect [connection target];它关闭与指定数据库的联接。
connection target 可以用下面方法声明:
一个打开游标语句看起来象:
exec sql open cursor;它被忽略因而不拷贝到输出文件.
一个提交语句看起来象
exec sql commit;它被转换成输出
ECPGcommit(__LINE__);
一个回卷语句看起来象
exec sql rollback;它被转换成如下输出
ECPGrollback(__LINE__);
其他 SQL语句是其他以 exec sql 开头并且以 ; 结尾的语句.所有两者之间的东西都被认为是一个 SQL 语句并做变量替换分析.
当一个符号以冒号( :)开头时,就会发生变量替换. 然后就会到前面定义段里(生成)的变量列表里找出该名称的变量, 然后根据该变量是用于输入还是输出, 把指向该变量的指针写到输出里供函数访问使用.
对 SQL 请求里的每个变量,函数都得到另外十个参数:
作为特殊符号的类型。 |
指向数值的指针或指针的指针。 |
如果变量是 varchar 或者 char,变量的尺寸。 |
数组里的元素个数(对数组抓取)。 |
数组里下一个元素的偏移量(对数组抓取) |
做为特殊符号的标识器变量的类型。 |
一个指向标识器变量值或者标识器变量指针的指针。 |
0. |
标识器数组里的元素个数(对数组抓取)。 |
标识器数组里下一个元素的偏移量(对数组抓取)。 |
下面是一个完整的描述预编译器对文件 foo.pgc 的输出的例子:
exec sql begin declare section; int index; int result; exec sql end declare section; ... exec sql select res into :result from mytable where index = :index;被解释成:
/* Processed by ecpg (2.6.0) */ /* These two include files are added by the preprocessor */ #include <ecpgtype.h>; #include <ecpglib.h>; /* exec sql begin declare section */ #line 1 "foo.pgc" int index; int result; /* exec sql end declare section */ ... ECPGdo(__LINE__, NULL, "select res from mytable where index = ? ", ECPGt_int,&(index),1L,1L,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_int,&(result),1L,1L,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT); #line 147 "foo.pgc"(本手册里的缩进是为可读性追加的,可不是预编译器能干的事.)
在库里面最重要的函数是 ECPGdo 函数.它有可变的参数. 希望我们不会碰到那些对变参数个数的函数的参数个数有限制的机器. 这些参数个数很容易多达 50 个.
这些参数是:
这是一个只用于错误信息里的表明原始出错行的行号.
这是声明的 SQL 请求。这个请求将用输入变量修改, 也就是说用那些编译时未知但要输入到请求里的变量修改. 这里变量应该包含 “;” 放到字符串里.
象预编译器节里描述的那样,每个输入变量换成十个参数.
一个 enum (枚举)表明输入变量(列表)的结尾. 没有更多需要输入的变量了.
象预编译器节里描述的那样,每个输出变量换成十个参数. 这些变量由函数填充.
一个 enum (枚举)表明变量(列表)的结尾.
所有SQL 语句都在一次事务中执行, 除非你进行了一次事务提交(commit). 要获取这样的自动事务, 第一个语句和/或事务提交或回卷后的第一个(语句)总是打开一个事务. 要关闭这个缺省的特性,可以在命令行上使用 -t 选项。
待续:描述其他记录的位置.