在第 34.3 節中,您看到了如何從嵌入式 SQL 程式執行 SQL 陳述式。這些陳述式中,有些僅使用固定值,而且沒有提供方法將使用者提供的值插入陳述式中,或是讓程式處理查詢傳回的值。這些種類的陳述式在實際應用中並不真正有用。本節詳細說明如何使用稱為主機變數的簡單機制,在 C 程式和嵌入式 SQL 陳述式之間傳遞資料。在嵌入式 SQL 程式中,我們將 SQL 陳述式視為 C 程式碼中的訪客,而 C 程式碼是主機語言。因此,C 程式的變數稱為主機變數。
在 PostgreSQL 後端和 ECPG 應用程式之間交換值的另一種方式是使用 SQL 描述器,如第 34.7 節中所述。
在嵌入式 SQL 中,在 C 程式和 SQL 陳述式之間傳遞資料特別簡單。您可以簡單地將 C 變數的名稱寫入 SQL 陳述式中,並以冒號作為前綴,而無需讓程式將資料貼到陳述式中,這會導致各種複雜情況,例如正確地引用值。例如
EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2);
此陳述式引用了兩個名為 v1
和 v2
的 C 變數,並且還使用了常規 SQL 字串文字,以說明您不限於使用一種資料或其他資料。
這種在 SQL 陳述式中插入 C 變數的樣式,適用於 SQL 陳述式中預期出現值運算式的位置。
若要將資料從程式傳遞到資料庫,例如作為查詢中的參數,或將資料從資料庫傳遞回程式,則需要在特殊標記的區段中宣告預期包含此資料的 C 變數,以便嵌入式 SQL 前置處理器可以知道它們。
此區段以
EXEC SQL BEGIN DECLARE SECTION;
結尾於
EXEC SQL END DECLARE SECTION;
在這些行之間,必須有正常的 C 變數宣告,例如
int x = 4; char foo[16], bar[16];
如您所見,您可以選擇性地為變數指派初始值。變數的範圍由其宣告區段在程式中的位置決定。您也可以使用以下語法宣告變數,這會隱式建立宣告區段
EXEC SQL int i = 4;
您可以在程式中擁有任意數量的宣告區段。
宣告也會以正常的 C 變數形式回顯到輸出檔案,因此無需再次宣告它們。不打算在 SQL 指令中使用的變數可以在這些特殊區段之外正常宣告。
結構或聯集的定義也必須列在 DECLARE
區段內。否則,前置處理器無法處理這些型別,因為它不知道定義。
現在您應該能夠將程式產生的資料傳遞到 SQL 指令中。但是,您如何擷取查詢的結果?為此,嵌入式 SQL 提供了常用指令 SELECT
和 FETCH
的特殊變體。這些指令具有特殊的 INTO
子句,用於指定要將擷取的值儲存在哪個主機變數中。SELECT
用於僅傳回單一列的查詢,而 FETCH
用於使用游標傳回多個列的查詢。
這是一個範例
/* * assume this table: * 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
子句出現在所有正常子句之後。
當 ECPG 應用程式在 PostgreSQL 伺服器和 C 應用程式之間交換值時,例如從伺服器擷取查詢結果或使用輸入參數執行 SQL 陳述式時,需要在 PostgreSQL 資料型別和主機語言變數型別(具體來說,是 C 語言資料型別)之間轉換這些值。ECPG 的主要優點之一是,它可以在大多數情況下自動處理此問題。
在這方面,有兩種資料類型:一些簡單的 PostgreSQL 資料類型,例如 integer
和 text
,可以直接由應用程式讀取和寫入。其他 PostgreSQL 資料類型,例如 timestamp
和 numeric
,只能透過特殊的函式庫函數存取;請參閱第 34.4.4.2 節。
表 34.1 顯示了哪些 PostgreSQL 資料類型對應於哪些 C 資料類型。 當您希望發送或接收給定 PostgreSQL 資料類型的值時,您應該在宣告區段中宣告對應 C 資料類型的 C 變數。
表 34.1. PostgreSQL 資料類型和 C 變數類型之間的映射
PostgreSQL 資料類型 | 主變數類型 |
---|---|
smallint |
short |
integer |
int |
bigint |
long long int |
decimal |
decimal [a] |
numeric |
numeric [a] |
real |
float |
double precision |
double |
smallserial |
short |
serial |
int |
bigserial |
long long int |
oid |
unsigned int |
character( , varchar( , text |
char[ , VARCHAR[ |
name |
char[NAMEDATALEN] |
timestamp |
timestamp [a] |
interval |
interval [a] |
date |
date [a] |
boolean |
bool [b] |
bytea |
char * , bytea[ |
[a] 這種資料類型只能透過特殊的函式庫函數存取;請參閱第 34.4.4.2 節。 [b] 如果不是原生型別,則在 |
為了處理 SQL 字串資料類型,例如 varchar
和 text
,有兩種方法可以宣告主機變數。
一種方法是使用 char[]
,一個 char
的陣列,這是 C 語言中最常見的字串資料處理方式。
EXEC SQL BEGIN DECLARE SECTION; char str[50]; EXEC SQL END DECLARE SECTION;
請注意,您必須自己處理長度。 如果您將此主機變數用作查詢的目標變數,該查詢傳回的字串超過 49 個字元,則會發生緩衝區溢位。
另一種方法是使用 VARCHAR
類型,這是 ECPG 提供的一種特殊類型。 類型 VARCHAR
的陣列定義會轉換為每個變數的命名 struct
。 像這樣的宣告
VARCHAR var[180];
會轉換成
struct varchar_var { int len; char arr[180]; } var;
成員 arr
儲存包含終止零位元組的字串。 因此,若要在 VARCHAR
主機變數中儲存字串,必須宣告包含零位元組終止符的長度。 成員 len
儲存儲存在 arr
中的字串長度,不包含終止零位元組。 當主機變數用作查詢的輸入時,如果 strlen(arr)
和 len
不同,則會使用較短的一個。
VARCHAR
可以用大寫或小寫表示,但不能混用。
char
和 VARCHAR
主機變數也可以儲存其他 SQL 類型的值,這些值將以字串形式儲存。
ECPG 包含一些特殊類型,可協助您輕鬆地與 PostgreSQL 伺服器中的一些特殊資料類型互動。 特別是,它已經實作了對 numeric
、decimal
、date
、timestamp
和 interval
類型的支援。 這些資料類型無法有效地對應到基本主機變數類型(例如 int
、long long int
或 char[]
),因為它們具有複雜的內部結構。 應用程式透過宣告特殊類型的主機變數並使用 pgtypes 函式庫中的函數來存取它們來處理這些類型。 pgtypes 函式庫在第 34.6 節中詳細介紹,包含處理這些類型的基本函數,因此您不需要僅為了將間隔新增到時間戳記而向 SQL 伺服器發送查詢。
以下小節描述了這些特殊資料類型。 有關 pgtypes 函式庫函數的更多詳細資訊,請參閱第 34.6 節。
以下是在 ECPG 主機應用程式中處理 timestamp
變數的模式。
首先,程式必須包含 timestamp
類型的標頭檔
#include <pgtypes_timestamp.h>
接下來,在宣告區段中宣告一個類型為 timestamp
的主機變數
EXEC SQL BEGIN DECLARE SECTION; timestamp ts; EXEC SQL END DECLARE SECTION;
讀取數值到主機變數後,使用 pgtypes 函式庫函數處理它。 在以下範例中,timestamp
值會使用 PGTYPEStimestamp_to_asc()
函數轉換為文字 (ASCII) 形式
EXEC SQL SELECT now()::timestamp INTO :ts; printf("ts = %s\n", PGTYPEStimestamp_to_asc(ts));
此範例將顯示如下結果
ts = 2010-06-27 18:03:56.949343
此外,DATE 類型可以用相同的方式處理。 程式必須包含 pgtypes_date.h
,宣告一個 date 類型的主機變數,並使用 PGTYPESdate_to_asc()
函數將 DATE 值轉換為文字形式。 有關 pgtypes 函式庫函數的更多詳細資訊,請參閱第 34.6 節。
處理 interval
類型也類似於 timestamp
和 date
類型。 然而,需要為 interval
類型值顯式分配記憶體。 換句話說,變數的記憶體空間必須在堆積記憶體中分配,而不是在堆疊記憶體中分配。
這是一個範例程式
#include <stdio.h> #include <stdlib.h> #include <pgtypes_interval.h> int main(void) { EXEC SQL BEGIN DECLARE SECTION; interval *in; EXEC SQL END DECLARE SECTION; EXEC SQL CONNECT TO testdb; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; in = PGTYPESinterval_new(); EXEC SQL SELECT '1 min'::interval INTO :in; printf("interval = %s\n", PGTYPESinterval_to_asc(in)); PGTYPESinterval_free(in); EXEC SQL COMMIT; EXEC SQL DISCONNECT ALL; return 0; }
處理 numeric
和 decimal
型別的方式與 interval
型別相似:都需要定義指標、在堆積上分配一些記憶體空間,然後使用 pgtypes 函式庫函式來存取變數。 有關 pgtypes 函式庫函式的更多詳細資訊,請參閱第 34.6 節。
沒有提供專門用於 decimal
型別的函式。 應用程式必須使用 pgtypes 函式庫函式將其轉換為 numeric
變數才能進行進一步的處理。
以下是一個處理 numeric
和 decimal
型別變數的範例程式。
#include <stdio.h> #include <stdlib.h> #include <pgtypes_numeric.h> EXEC SQL WHENEVER SQLERROR STOP; int main(void) { EXEC SQL BEGIN DECLARE SECTION; numeric *num; numeric *num2; decimal *dec; EXEC SQL END DECLARE SECTION; EXEC SQL CONNECT TO testdb; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; num = PGTYPESnumeric_new(); dec = PGTYPESdecimal_new(); EXEC SQL SELECT 12.345::numeric(4,2), 23.456::decimal(4,2) INTO :num, :dec; printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 0)); printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 1)); printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 2)); /* Convert decimal to numeric to show a decimal value. */ num2 = PGTYPESnumeric_new(); PGTYPESnumeric_from_decimal(dec, num2); printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 0)); printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 1)); printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 2)); PGTYPESnumeric_free(num2); PGTYPESdecimal_free(dec); PGTYPESnumeric_free(num); EXEC SQL COMMIT; EXEC SQL DISCONNECT ALL; return 0; }
處理 bytea
型別的方式與 VARCHAR
類似。 bytea
型別陣列的定義會轉換為每個變數的具名結構。 例如宣告像這樣:
bytea var[180];
會轉換成
struct bytea_var { int len; char arr[180]; } var;
成員 arr
儲存二進位格式的資料。 與 VARCHAR
不同,它也可以處理作為資料一部分的 '\0'
。 這些資料會在 ecpglib 中轉換為十六進位格式以及從十六進位格式轉換出來並傳送/接收。
只有在 bytea_output 設定為 hex
時,才能使用 bytea
變數。
作為主機變數,您也可以使用陣列、typedef、結構和指標。
陣列作為主機變數有兩種使用情況。 第一種方法是在 char[]
或 VARCHAR[]
中儲存一些文字字串,如第 34.4.4.1 節中所述。 第二種使用情況是從查詢結果中檢索多個列,而無需使用游標。 如果沒有陣列,要處理由多個列組成的查詢結果,則需要使用游標和 FETCH
命令。 但是使用陣列主機變數,可以一次接收多個列。 陣列的長度必須定義為能夠容納所有列,否則很可能會發生緩衝區溢位。
以下範例掃描 pg_database
系統表,並顯示可用資料庫的所有 OID 和名稱
int main(void) { EXEC SQL BEGIN DECLARE SECTION; int dbid[8]; char dbname[8][16]; int i; EXEC SQL END DECLARE SECTION; memset(dbname, 0, sizeof(char)* 16 * 8); memset(dbid, 0, sizeof(int) * 8); EXEC SQL CONNECT TO testdb; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; /* Retrieve multiple rows into arrays at once. */ EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database; for (i = 0; i < 8; i++) printf("oid=%d, dbname=%s\n", dbid[i], dbname[i]); EXEC SQL COMMIT; EXEC SQL DISCONNECT ALL; return 0; }
此範例顯示以下結果。 (確切值取決於當地情況。)
oid=1, dbname=template1 oid=11510, dbname=template0 oid=11511, dbname=postgres oid=313780, dbname=testdb oid=0, dbname= oid=0, dbname= oid=0, dbname=
成員名稱與查詢結果的欄位名稱相符的結構,可以用於一次檢索多個欄位。 結構可讓您在單一主機變數中處理多個欄位值。
以下範例從 pg_database
系統表中使用 pg_database_size()
函式檢索可用資料庫的 OID、名稱和大小。 在此範例中,具有成員的結構變數 dbinfo_t
,其名稱與 SELECT
結果中的每個欄位相符,用於檢索一個結果列,而無需在 FETCH
陳述式中放入多個主機變數。
EXEC SQL BEGIN DECLARE SECTION; typedef struct { int oid; char datname[65]; long long int size; } dbinfo_t; dbinfo_t dbval; EXEC SQL END DECLARE SECTION; memset(&dbval, 0, sizeof(dbinfo_t)); EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database; EXEC SQL OPEN cur1; /* when end of result set reached, break out of while loop */ EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /* Fetch multiple columns into one structure. */ EXEC SQL FETCH FROM cur1 INTO :dbval; /* Print members of the structure. */ printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, dbval.size); } EXEC SQL CLOSE cur1;
此範例顯示以下結果。 (確切值取決於當地情況。)
oid=1, datname=template1, size=4324580 oid=11510, datname=template0, size=4243460 oid=11511, datname=postgres, size=4324580 oid=313780, datname=testdb, size=8183012
結構主機變數會「吸收」結構作為欄位的盡可能多的欄位。 其他欄位可以指派給其他主機變數。 例如,上面的程式也可以像這樣重新架構,其中 size
變數在結構外部
EXEC SQL BEGIN DECLARE SECTION; typedef struct { int oid; char datname[65]; } dbinfo_t; dbinfo_t dbval; long long int size; EXEC SQL END DECLARE SECTION; memset(&dbval, 0, sizeof(dbinfo_t)); EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database; EXEC SQL OPEN cur1; /* when end of result set reached, break out of while loop */ EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /* Fetch multiple columns into one structure. */ EXEC SQL FETCH FROM cur1 INTO :dbval, :size; /* Print members of the structure. */ printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, size); } EXEC SQL CLOSE cur1;
使用 typedef
關鍵字將新類型對應到現有類型。
EXEC SQL BEGIN DECLARE SECTION; typedef char mychartype[40]; typedef long serial_t; EXEC SQL END DECLARE SECTION;
請注意,您也可以使用
EXEC SQL TYPE serial_t IS long;
此宣告不需要是宣告區段的一部分; 也就是說,您也可以將 typedefs 編寫為一般的 C 陳述式。
您宣告為 typedef
的任何單字都不能在同一程式後面的 EXEC SQL
命令中用作 SQL 關鍵字。 例如,這樣做將不起作用
EXEC SQL BEGIN DECLARE SECTION; typedef int start; EXEC SQL END DECLARE SECTION; ... EXEC SQL START TRANSACTION;
ECPG 將報告 START TRANSACTION
的語法錯誤,因為它不再將 START
識別為 SQL 關鍵字,而僅識別為 typedef。 (如果您遇到這種衝突,並且重新命名 typedef 似乎不切實際,則可以使用動態 SQL編寫 SQL 命令。)
在 v16 之前的 PostgreSQL 版本中,將 SQL 關鍵字用作 typedef 名稱可能會導致與 typedef 本身使用相關的語法錯誤,而不是將名稱用作 SQL 關鍵字。 當現有的 ECPG 應用程式在新 PostgreSQL 版本中使用新關鍵字重新編譯時,新行為不太可能導致問題。
本節包含有關如何在 ECPG 應用程式中處理非純量和使用者定義的 SQL 層級資料類型 的資訊。 請注意,這與上一節中描述的非基本類型的主機變數處理不同。
ECPG 中不直接支援多維 SQL 層級陣列。 一維 SQL 層級陣列可以對應到 C 陣列主機變數,反之亦然。 但是,建立陳述式時,ecpg 不知道欄位的型別,因此它無法檢查是否將 C 陣列輸入到對應的 SQL 層級陣列中。 處理 SQL 陳述式的輸出時,ecpg 具有必要的資訊,因此會檢查兩者是否都是陣列。
如果查詢分別存取陣列的元素,則可以避免在 ECPG 中使用陣列。 然後,應使用具有可以對應到元素型別的型別的主機變數。 例如,如果欄位型別是 integer
陣列,則可以使用 int
型別的主機變數。 此外,如果元素型別是 varchar
或 text
,則可以使用 char[]
或 VARCHAR[]
型別的主機變數。
這是一個範例。 假設有以下表格
CREATE TABLE t3 ( ii integer[] ); testdb=> SELECT * FROM t3; ii ------------- {1,2,3,4,5} (1 row)
以下範例程式檢索陣列的第 4 個元素,並將其儲存到 int
型別的主機變數中
EXEC SQL BEGIN DECLARE SECTION; int ii; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[4] FROM t3; EXEC SQL OPEN cur1; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { EXEC SQL FETCH FROM cur1 INTO :ii ; printf("ii=%d\n", ii); } EXEC SQL CLOSE cur1;
此範例顯示以下結果
ii=4
若要將多個陣列元素對應到陣列型別主機變數中的多個元素,則必須分別管理陣列欄位的每個元素和主機變數陣列的每個元素,例如
EXEC SQL BEGIN DECLARE SECTION; int ii_a[8]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii[1], ii[2], ii[3], ii[4] FROM t3; EXEC SQL OPEN cur1; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { EXEC SQL FETCH FROM cur1 INTO :ii_a[0], :ii_a[1], :ii_a[2], :ii_a[3]; ... }
再次注意
EXEC SQL BEGIN DECLARE SECTION; int ii_a[8]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE cur1 CURSOR FOR SELECT ii FROM t3; EXEC SQL OPEN cur1; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /* WRONG */ EXEC SQL FETCH FROM cur1 INTO :ii_a; ... }
在這種情況下,不會正確地運作,因為您無法將陣列型別欄位直接對應到陣列主機變數。
另一種解決方案是將陣列以其外部字串表示法儲存在 char[]
或 VARCHAR[]
型別的主機變數中。 有關此表示法的更多詳細資訊,請參閱第 8.15.2 節。 請注意,這表示無法在主機程式中自然地以陣列形式存取陣列(除非經過進一步處理以剖析文字表示法)。
ECPG 不直接支援複合型別,但有一個簡單的解決方法。可用的解決方法與上述陣列的描述類似:可以分別存取每個屬性,或使用外部字串表示法。
在以下範例中,假設有以下型別和表格
CREATE TYPE comp_t AS (intval integer, textval varchar(32)); CREATE TABLE t4 (compval comp_t); INSERT INTO t4 VALUES ( (256, 'PostgreSQL') );
最明顯的解決方案是分別存取每個屬性。以下程式透過分別選擇 comp_t
型別的每個屬性,從範例表格中檢索資料。
EXEC SQL BEGIN DECLARE SECTION; int intval; varchar textval[33]; EXEC SQL END DECLARE SECTION; /* Put each element of the composite type column in the SELECT list. */ EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4; EXEC SQL OPEN cur1; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /* Fetch each element of the composite type column into host variables. */ EXEC SQL FETCH FROM cur1 INTO :intval, :textval; printf("intval=%d, textval=%s\n", intval, textval.arr); } EXEC SQL CLOSE cur1;
為了增強此範例,可以用一個結構來收集主機變數,以便在 FETCH
命令中儲存值。關於結構形式的主機變數的更多詳細資訊,請參閱第 34.4.4.3.2 節。為了切換到結構,可以如下修改範例。兩個主機變數 intval
和 textval
變成 comp_t
結構的成員,並且在 FETCH
命令中指定該結構。
EXEC SQL BEGIN DECLARE SECTION; typedef struct { int intval; varchar textval[33]; } comp_t; comp_t compval; EXEC SQL END DECLARE SECTION; /* Put each element of the composite type column in the SELECT list. */ EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).intval, (compval).textval FROM t4; EXEC SQL OPEN cur1; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /* Put all values in the SELECT list into one structure. */ EXEC SQL FETCH FROM cur1 INTO :compval; printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr); } EXEC SQL CLOSE cur1;
雖然 FETCH
命令中使用了一個結構,但 SELECT
子句中的屬性名稱是一個個指定的。可以透過使用 *
來要求複合型別值的所有屬性來增強此功能。
... EXEC SQL DECLARE cur1 CURSOR FOR SELECT (compval).* FROM t4; EXEC SQL OPEN cur1; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /* Put all values in the SELECT list into one structure. */ EXEC SQL FETCH FROM cur1 INTO :compval; printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr); } ...
這樣,複合型別幾乎可以無縫地對應到結構中,即使 ECPG 本身不理解複合型別。
最後,也可以將複合型別值以其外部字串表示法儲存在 char[]
或 VARCHAR[]
型別的主機變數中。但是,這樣就無法輕易地從主機程式存取該值的欄位。
ECPG 不直接支援新的使用者定義的基本型別。您可以使用外部字串表示法和 char[]
或 VARCHAR[]
型別的主機變數,而且這種解決方案對於許多型別而言實際上是適當且足夠的。
以下是使用 第 36.13 節中範例的 complex
資料型別的範例。該型別的外部字串表示法為 (%f,%f)
,該表示法定義在 第 36.13 節中的 complex_in()
和 complex_out()
函數中。以下範例將複合型別值 (1,1)
和 (3,3)
插入到欄位 a
和 b
中,然後從表格中選取它們。
EXEC SQL BEGIN DECLARE SECTION; varchar a[64]; varchar b[64]; EXEC SQL END DECLARE SECTION; EXEC SQL INSERT INTO test_complex VALUES ('(1,1)', '(3,3)'); EXEC SQL DECLARE cur1 CURSOR FOR SELECT a, b FROM test_complex; EXEC SQL OPEN cur1; EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { EXEC SQL FETCH FROM cur1 INTO :a, :b; printf("a=%s, b=%s\n", a.arr, b.arr); } EXEC SQL CLOSE cur1;
此範例顯示以下結果
a=(1,1), b=(3,3)
另一種解決方法是避免在 ECPG 中直接使用使用者定義的型別,而是建立一個函數或強制轉換,以在使用者定義的型別和 ECPG 可以處理的基本型別之間進行轉換。但是請注意,型別強制轉換,尤其是隱式強制轉換,應非常小心地引入到型別系統中。
例如,
CREATE FUNCTION create_complex(r double, i double) RETURNS complex LANGUAGE SQL IMMUTABLE AS $$ SELECT $1 * complex '(1,0')' + $2 * complex '(0,1)' $$;
在這個定義之後,以下
EXEC SQL BEGIN DECLARE SECTION; double a, b, c, d; EXEC SQL END DECLARE SECTION; a = 1; b = 2; c = 3; d = 4; EXEC SQL INSERT INTO test_complex VALUES (create_complex(:a, :b), create_complex(:c, :d));
與以下效果相同
EXEC SQL INSERT INTO test_complex VALUES ('(1,2)', '(3,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
將為零;如果該值為空值,則它將為負數。(請參閱第 34.16 節以啟用 Oracle 特定的行為。)
指示器還有另一個功能:如果指示器值為正數,則表示該值不是空值,但是在將其儲存在主機變數中時已被截斷。
如果將引數 -r no_indicator
傳遞給前置處理器 ecpg
,它將在“無指示器”模式下工作。在無指示器模式下,如果未指定指示器變數,則對於字元串型別,空值將以空字串表示,對於整數型別,空值將以該型別的最小值表示(例如,int
的 INT_MIN
)。
如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。