
# ecpg预处理、编译、执行
#### 前提条件
已准备嵌入式SQL-C源程序，以.pgc为后缀名，ecpg负责将其转换成可被编译器编译的C语言程序。
生成的C语言程序使用gcc命令编译为可执行文件，运行该可执行文件实现客户端程序访问数据库。
#### 文件包含
嵌入式SQL-C程序开发中，一般需要包含以下几个头文件：
```
#include <stdio.h>
#include <stdlib.h>
EXEC SQL INCLUDE sqlca;
```
其中，sqlca是SQL通信区（SQL Communication Area），用于存储SQL语句执行后的状态信息（如成功、错误码）。关于sqlca详情请参考[sqlca](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/gaussdb-12-0339.html)。
如需将外部文件包含到嵌入SQL程序中，可使用如下语句：
```
EXEC SQL INCLUDE filename;
EXEC SQL INCLUDE <filename>;
EXEC SQL INCLUDE "filename";
```
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
- ecpg预处理器按照如下顺序搜索文件：
  1. 当前目录
  
  2. /usr/local/include
  
  3. GaussDB的目录，在编译时定义
  
  4. /usr/include
   
- EXEC SQL INCLUDE "filename"语句仅搜索当前目录。
- 在每一个目录中，预处理器首先按照给定的文件名搜索，若没找到则会追加.h到文件名后重试（除非指定的文件名已有该后缀）。
- 文件名是大小写敏感的。
 
如果使用C语法引入自定义头文件，且头文件中包含ecpg的语法时，可以按照以下示例方式进行预处理：
- a.pgc文件：
  ```
  #include "a.h"
  ```
  
- a.h文件：
  ```
  #ifndef _A_H
  #define _A_H
  EXEC SQL BEGIN DECLARE SECTION;
  char sDbName[50] ;
  char sUser[50];
  char sPwd[50];
  EXEC SQL END DECLARE SECTION;
  #endif
  ```
  
- 编译方式：
  1. 使用cpp预处理，进行头文件展开，生成新的pgc文件：
     ```
     cpp -I $GAUSSHOME/include/ecpg -I $GAUSSHOME/include -I $GAUSSHOME/include/gaussdb/server/ -E -C a.pgc a.tmp.d
     cpp -I $GAUSSHOME/include/ecpg -I $GAUSSHOME/include -I $GAUSSHOME/include/gaussdb/server/ -P a.tmp.d a.tmp.pgc
     ```
     
  
  2. ecpg预处理：
     ```
     ecpg -I $GAUSSHOME/include/ecpg  -o a.tmp.c a.tmp.pgc
     ```
     
  
  3. gcc编译：
     ```
     gcc -g -I $GAUSSHOME/include/ecpg -I $GAUSSHOME/include -I $GAUSSHOME/include/gaussdb/server/ -L $GAUSSHOME/lib a.tmp.c -o a -lecpg -lrt -lpq -lpgtypes -lpthread
     ```
     
   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
- ecpg引入自定义头文件时，不要同时使用c语言的include方式引入和ecpg语法引入，避免出现可能重复展开头文件的问题。
- 使用cpp命令时，连接串中不能直接使用tcp和unix协议；如果需要此两种形式的连接串，使用宿主变量（宿主变量详情请参考[数据类型和宿主变量](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/gaussdb-12-0322.html)）定义。
 
#### 条件编译
ecpg提供了ifdef、ifndef、else、elif和endif条件编译指令。在预处理时，按照不同的条件去编译程序的不同部分，使用时，需要添加EXEC SQL前缀关键字。
示例如下：
```
EXEC SQL ifndef TZVAR; 
EXEC SQL SET TIMEZONE TO 'GMT'; 
EXEC SQL elif TZNAME; 
EXEC SQL SET TIMEZONE TO TZNAME; 
EXEC SQL else; 
EXEC SQL SET TIMEZONE TO TZVAR; 
EXEC SQL endif;
```
#### define和undef指令
嵌入式SQL具有类似于C语言中#define的指令：
```
EXEC SQL DEFINE name;
EXEC SQL DEFINE name value;
EXEC SQL UNDEF name;
```
示例如下：
```
/* 定义名称 */
EXEC SQL DEFINE HAVE_FEATURE;
/* 定义常量 */
EXEC SQL DEFINE MYNUMBER 12;
EXEC SQL DEFINE MYSTRING 'abc';
/* 使用 UNDEF 移除定义 */
EXEC SQL UNDEF MYNUMBER;
```
在嵌入式SQL程序中，既可以使用C语言标准的#define和#undef，也可以使用专为SQL设计的EXEC SQL DEFINE。两者的主要区别在于定义值的计算时机：C语言的#define在编译阶段由C预处理器处理并进行文本替换；而EXEC SQL DEFINE则在ecpg预处理阶段被计算和替换。这意味着，EXEC SQL DEFINE的定义会在SQL代码被处理之前生效，从而影响后续的SQL语句生成。如下示例，ecpg对其进行替换并且编译器不会解析名为MYNUMBER的任何名称或标识符：
```
EXEC SQL DEFINE MYNUMBER 12; 
... 
EXEC SQL UPDATE Tbl SET col = MYNUMBER;
```
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
不能把#define用于一个将要在嵌入式SQL查询中使用的变量，因为在这种情况下嵌入式SQL预编译器无法识别该声明。
#### 预处理
ecpg预处理命令如下（以test.pgc文件为例）：
```
ecpg -I $GAUSSHOME/include/ecpg -o test.c test.pgc
```
ecpg预处理的参数选项如下：
```
ecpg [OPTION]...
```
其中OPTION参数选项如下：
- -o OUTFILE：预处理嵌入式SQL-C程序将结果写入OUTFILE文件，OUTFILE文件为C语言文件（test.c）。
- -I DIRECTORY：头文件的搜索路径。
- -c：预处理嵌入式SQL-C程序自动生成C语言文件。
- --version：查看ecpg当前版本。
- -C MODE：指定预处理兼容模式，例如传入"ORA"可开启ORACLE兼容模式。
- -r OPTION：指定运行时的行为。OPTION支持以下值：
  - no_indicator：允许使用特殊值来表示空值。
  
  - prepare：强制在执行所有语句之前先进行PREPARE操作。
  
  - questionmarks：允许使用问号"?"作为占位符。
  
  - with_hold：对于未指定HOLD关键字创建的游标，当指定该选项后，游标行为默认为WITH HOLD（该功能需要ecpg和内核版本一致）。
   
 
#### 编译
编译经过预处理的嵌入式SQL-C程序命令如下：
```
gcc -I $GAUSSHOME/include/ecpg -I  $GAUSSHOME/include -I $GAUSSHOME/include/gaussdb/server/ -L $GAUSSHOME/lib test.c -o test  -lecpg -lrt -lpq -lpgtypes -lpthread
```
#### 执行
执行命令如下：
```
./test
```
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
- ecpg作为编译预处理工具，若在预处理或编译过程中出现找不到头文件或者函数实现的报错信息，可以根据需要指定头文件，或者连接动态库。
- 使用ecpg开发应用程序时，所依赖的其他动态库和头文件通常位于$GAUSSHOME/include/libpq、$GAUSSHOME/include目录下。
- 编译过程中常见的动态库依赖：-lpq、-lpq_ce、-lpthread。若开发过程中需要使用libpq通信库，则需要连接-lpq和-lpq_ce。若开发过程中需要使用多线程连接，则需要连接-lpthread。
 
