嵌入式SQL

嵌入式SQL（英文: Embedded SQL）是一種將SQL語句直接寫入C語言、COBOL、FORTRAN及Ada等程式語言原始碼中的方法。藉此方法，可使得應用程式能夠存取以及處理資料。在這一方法中，將SQL文嵌入的目標源碼的語言稱為宿主語言。

在1986年發布的SQL86標準中定義了對於COBOL、FORTRAN及PI/L等語言的嵌入式SQL的規範。在1989年發布的SQL89規範中，定義了對於C語言的嵌入式SQL的規範。一些大型的資料庫廠商發布的資料庫產品中，都提供了對於嵌入式SQL的支持。比如Oracle、DB2等。

## 嵌入式SQL的工作原理

提供對於嵌入式SQL的支持，需要資料庫廠商除了提供DBMS之外，還必須提供一些工具。為了實現對於嵌入式SQL的支持，技術上必須解決以下4個問題：

- 宿主語言的編譯器不可能識別和接受SQL文，需要解決如何將SQL的宿主語言原始碼編譯成可執行碼。
- 宿主語言的應用程式如何與DBMS之間傳遞數據和消息。
- 如何把對數據的查詢結果逐次賦值給宿主語言程序中的變量以供其處理。
- 資料庫的數據類型與宿主語言的數據類型有時不完全對應或等價，如何解決必要的數據類型轉換問題。

<image>

為了解決上述這些問題，資料庫廠商需要提供一個嵌入式SQL的預編譯器，把包含有嵌入式SQL文的宿主語言源碼轉換成純宿主語言的代碼。這樣一來，源碼即可使用宿主語言對應的編譯器進行編譯。通常情況下，經過嵌入式SQL的預編譯之後，原有的嵌入式SQL會被轉換成一系列函數調用。因此，資料庫廠商還需要提供一些列函數庫，以確保連結器能夠把代碼中的函數調用與對應的實現連結起來。

## 嵌入式SQL的擴展語法

嵌入式SQL中除了可以執行標準SQL文之外，為了對應嵌入的需要，還增加了一些額外的語法成分。主要包含以下內容：

- 宿主變量使用聲明的語法
- 資料庫訪問的語法
- 事務控制的語法
- 游標操作的語法

### 示例代碼

以下展示了用於連接PostgreSQL資料庫並執行一次數據查詢的嵌入式SQL源碼（宿主語言為C語言）：

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

EXEC SQL BEGIN DECLARE SECTION;
int var_c1;
char var_c2[21] = { 0x00 };
EXEC SQL END DECLARE SECTION;

int main(int argc, char* argv[])
{
    /* 步骤1: 建立连接 */
    EXEC SQL WHENEVER SQLERROR SQLPRINT;   /* 声明异常发生时的处理动作统一为打印消息 */
    EXEC SQL CONNECT TO postgres@localhost:5432 USER postgres/xxxx;   /* 指定连接的目标数据库，用户名，密码 */  
     
    /* 步骤2: 利用游标执行查询 */
    EXEC SQL DECLARE foo_bar CURSOR FOR SELECT c1, c2 FROM tb1;    /* 声明一个游标使之用于执行SELECT文 */
    EXEC SQL OPEN foo_bar;                                         /* 打开游标从而使SELECT文被执行 */
    EXEC SQL FETCH foo_bar INTO :var_c1, :var_c2;                  /* 获取一行数据 */
    
    printf("C1: %d, C2: %s\n", var_c1, var_c2);
 
    EXEC SQL CLOSE foo_bar;                                        /* 关闭所打开的游标 */

    /* 步骤3: 关闭连接 */
    EXEC SQL DISCONNECT CURRENT;                   

    return 0;
}
```

在Cygwin平台上，利用PostgreSQL 7.4.5所自帶的嵌入式SQL的預編譯器ECPG（版本號 3.1.1）進行預編譯，生成的純C語言代碼如下所示：

```c
/* Processed by ecpg (3.1.1) */
/* These include files are added by the preprocessor */
#include <ecpgtype.h>
#include <ecpglib.h>
#include <ecpgerrno.h>
#include <sqlca.h>
#line 1 "ESQL_CONNECT.pgc"
/* End of automatic include section */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* exec sql begin declare section */

#line 6 "ESQL_CONNECT.pgc"
 int  var_c1   ;
 
#line 7 "ESQL_CONNECT.pgc"
 char  var_c2 [ 21 ]  = { 0x00 } ;
/* exec sql end declare section */
#line 8 "ESQL_CONNECT.pgc"

int main(int argc, char* argv[])
{
    /* 步骤1: 建立连接 */
    /* exec sql whenever sqlerror  sqlprint ; */

#line 13 "ESQL_CONNECT.pgc"
 
    { ECPGconnect(, 0, "postgres@localhost:5432" , "postgres" , "asdf1234" , NULL, 0); 
#line 14 "ESQL_CONNECT.pgc"

if (sqlca.sqlcode < 0) sqlprint();}
#line 14 "ESQL_CONNECT.pgc"    
    /* 步骤2: 利用游标执行查询 */
    /* declare foo_bar  cursor  for select  c1  , c2   from tb1    */
#line 17 "ESQL_CONNECT.pgc"

    { ECPGdo(, 0, 1, NULL, "declare foo_bar  cursor  for select  c1  , c2   from tb1   ", ECPGt_EOIT, ECPGt_EORT);
#line 18 "ESQL_CONNECT.pgc"
if (sqlca.sqlcode < 0) sqlprint();}
#line 18 "ESQL_CONNECT.pgc"

    { ECPGdo(, 0, 1, NULL, "fetch foo_bar", ECPGt_EOIT, 
    ECPGt_int,&(var_c1),(long)1,(long)1,sizeof(int), 
    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
    ECPGt_char,(var_c2),(long)21,(long)1,21*sizeof(char), 
    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
#line 19 "ESQL_CONNECT.pgc"

if (sqlca.sqlcode < 0) sqlprint();}
#line 19 "ESQL_CONNECT.pgc"

    printf("C1: %d, C2: %s\n", var_c1, var_c2);
 
    { ECPGdo(, 0, 1, NULL, "close foo_bar", ECPGt_EOIT, ECPGt_EORT);
#line 23 "ESQL_CONNECT.pgc"

if (sqlca.sqlcode < 0) sqlprint();}
#line 23 "ESQL_CONNECT.pgc"

    /* 步骤3: 关闭连接 */
    { ECPGdisconnect(, "CURRENT");
#line 26 "ESQL_CONNECT.pgc"

if (sqlca.sqlcode < 0) sqlprint();}
#line 26 "ESQL_CONNECT.pgc"
    return 0;
}
```

生成後的代碼中所調用的ECPGconnect等函數，由安裝PostgreSQL所自帶的ECPG庫（本例中為`libecpg.so`）提供具體的實現，供連結器將其與上述C語言代碼編譯生成的目標文件進行連結。

## 支持嵌入式SQL的資料庫產品

以下列出支持嵌入式SQL的資料庫產品以及各自支持的宿主語言

### Oracle Database

Ada : Pro*Ada在Oracle 7.3的版本中被加入產品族，並且在Oracle 8中被替換為SQL*Module。但在此之後就一直沒有更新。SQL*Module支持Ada 83.

C/C++ : Pro*C 在Oracle 8 時被替換成了Pro*C/C++。之後Pro*C/C++ 到Oracle Database 11g仍都在被支持。

COBOL : Pro*COBOL到Oracle Database 11g仍都在被支持。

Fortran : Pro*FORTRAN 在Oracle 8之後的Oracle版本中就不再被更新，但Bug修正仍在維護中。

Pascal : Pro*Pascal在Oracle 8之後的Oracle版本中就不再被更新。

PI/L : Pro*PL/I 自Oracle 8之後就不再被更新，但文檔中仍然有記述。

### IBM DB2

IBM DB2的版本9中提供了對於C/C++，COBOL，Java等宿主語言的嵌入式SQL的支持。

### PostgreSQL

C/C++ : PostgreSQL 自版本6.3起就提供了對於C/C++的嵌入式SQL的支持，以ECPG組件的形式存在。