29.6. 使用宿主變量

Section 29.4 裡您看到了如何從嵌入的 SQL 程序裡執行 SQL 語句。 那些語句有些只使用了固定的數值,並沒有提供一個插入用戶提供的數值到語句中的方法, 也沒有提供讓程序訪問查詢返回的數值的方法。這種類型的語句在真正的應用中並不是很有用。 本節詳細解釋如何在您的 C 程序和嵌入的 SQL 語句之間使用一種被稱作宿主變量的機制傳遞資料。

29.6.1. 概述

在 C 程序和 SQL 語句之間傳遞資料在嵌入的 SQL 裡特別簡單。我們不用把資料貼到語句中, 這樣必然會有各種複雜事情需要處理,比如正確給數值加引號等等,我們只需要在 SQL 語句裡寫上 C 變量的名字,前綴一個冒號即可。 比如:

EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2);

這個語句引用了兩個 C 變量,一個叫 v1,另一個叫 v2, 並且也使用一個普通的 SQL 字串文本,這樣資料表明您並不局限於只使用某一種資料。

這種在 SQL 語句裡插入 C 變量的方式在 SQL 語句裡任何需要資料表達式的地方都可以用。 在 SQL 語句裡,我們把引用的 C 變量叫做 宿主變量

29.6.2. 聲明段

要從程序中向資料庫傳遞資料,比如,查詢中的參數,或者從資料庫裡向程序傳回的資料, 想包含這類資料的 C 變量必須在一個特殊的標記段裡面聲明,這樣嵌入的 SQL 預處理器就會明白要做什麼。

這個段以下面的代碼開頭

EXEC SQL BEGIN DECLARE SECTION;

以下面的代碼結束

EXEC SQL END DECLARE SECTION;

在這兩行之間,是普通的 C 變量聲明,比如

int   x;
char  foo[16], bar[16];

在程序裡您可以有任意多個聲明段。

這些聲明也同時以普通 C 變量的形式回顯到輸出文件中, 因此,我們不必再次聲明他們。那些不準備在 SQL 命令裡使用的變量可以像通常一樣在這些特殊的段外面聲明。

結構或者聯合的定義也必須在 DECLARE 段中列出。 否則,預處理器就無法處理這些類型,因為它不知道定義。

特殊的類型 varchar 對每個變量都轉化成一個叫 struct 的名字。像下面這樣的聲明:

varchar var[180];

轉化成

struct varchar_var { int len; char arr[180]; } var;

這個結構適合於和 SQL 資料類型 varchar 的資料交互。

29.6.3. SELECT INTOFETCH INTO

現在您應該能把您的程序生成的資料傳遞到 SQL 命令裡面去了。但是您如何檢索一個查詢的結果呢? 為了這個目的,嵌入的 SQL 提供了常用命令 SELECTFETCH 的特殊變體。 這些命令有了特殊的 INTO 子句,聲明檢索出來的數值儲存在哪個宿主變量裡。

下面是一些例子:

/*
 * 假設資料表是這個:
 * CREATE TABLE test1 (a int, b varchar(50));
 */

EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;

所以,INTO 子句出現在選擇列資料表和 FROM 子句之間。選擇列資料表和 INTO 後面的列資料表的元素(也叫目標列資料表)個數必須相同。

下面是使用 FETCH 命令的例子:

EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test;

 ...

do {
    ...
    EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
    ...
} while (...);

這裡的 INTO 子句出現在所有正常的子句後面。

這些方法只能一次檢索一行。如果您需要處理可能多於一行的結果集, 那麼您需要使用游標,就想我們第二個例子演示的那樣。

29.6.4. 指示器

上面的例子不能處理空值。實際上,如果從資料庫中抓到一條空值,那麼上面的檢索例子會拋出一個錯誤。 要能夠向資料庫中傳遞空值,或者從資料庫中檢索空值,您需要給每個包含資料的宿主變量後面附加一個額外的宿主變量。 這第二個宿主變量叫指示器,裡面包含一個標誌, 告訴我們資料是否為空,如果為空,那麼真正的宿主變量的數值就可以忽略。 下面是一個能正確檢索空值的例子:

EXEC SQL BEGIN DECLARE SECTION;
VARCHAR val;
int val_ind;
EXEC SQL END DECLARE SECTION:

 ...

EXEC SQL SELECT b INTO :val :val_ind FROM test1;

如果數值不是空,那麼指示器變量 val_ind 將是零, 如果值是空,那麼它將是負數。

指示器還有另外一個用途:如果它是正數,那麼它標識數值不是空, 但是在數值儲存到宿主變量裡的時候被截斷了。