<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 33.4\. 使用宿主變量 在[Section 33.3](#calibre_link-1992) 里你看到了如何從嵌入的SQL程序里執行SQL語句。 那些語句有些只使用了固定的數值, 并沒有提供一個插入用戶提供的數值到語句中的方法, 也沒有提供讓程序訪問查詢返回的數值的方法。 這種類型的語句在實際應用中并不是很有用。 本節詳細解釋如何在你的C程序和嵌入的SQL語句之間使用 一種被稱作_宿主變量_的機制傳遞數據。在嵌入SQL程序中, 我們將SQL語句認為是_宿主語言_C程序編碼的_客人_。 因此C程序變量稱為_宿主變量_。 在PostgreSQL后端和ECPG應用程序之間改變值的另一種方式是使用SQL描述符, 參見[Section 33.7](#calibre_link-1086)中的描述。 ## 33.4.1\. 概述 在C程序和SQL語句之間傳遞數據在嵌入的 SQL 里是特別簡單的。 我們不用把數據粘貼到語句中,這樣必然會有各種復雜事情需要處理, 比如正確地給數值加引號等等,我們只需要在SQL語句里寫上C變量的名字, 前綴一個冒號即可。比如: ``` EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2); ``` 這個語句引用了兩個變量,一個叫`v1`,另一個叫`v2`, 并且也使用一個普通的SQL字串文本,這樣表明你并不局限于只使用某一種數據或者其他。 這種在SQL語句里插入C變量的方式在SQL語句里任何需要表達式的地方都可用。 ## 33.4.2\. 聲明段 要從程序向數據庫傳遞數據,比如,查詢中的參數, 或者從數據庫里向程序傳回的數據,想包含這類數據的 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`段中列出。 否則,預處理器就無法處理這些類型,因為它不知道定義。 ## 33.4.3\. 檢索查詢結果 現在你應該能把你的程序生成的數據傳遞到SQL命令里面去了。 但是你如何檢索一個查詢的結果呢?為了這個目的, 嵌入的SQL提供了常用命令`SELECT`和 `FETCH`的特殊變體。 這些命令有了特殊的`INTO`子句, 聲明檢索出來的數值存儲在哪個宿主變量里。 `SELECT`用于返回單行的查詢,同時`FETCH` 用于使用游標返回多行的查詢。 下面是一個例子: ``` /* * 假設表是這個: * 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`子句出現在所有正常的子句后面。 ## 33.4.4\. 類型映射 當ECPG應用程序改變PostgreSQL服務器和C應用程序之間的值的時候, 比如檢索來自服務器的查詢結果或者執行帶有輸入參數的SQL語句, 在PostgreSQL數據類型和宿主語言變量類型(具體地C語言數據 類型)之間需要改變值。ECPG的一個主要點之一是 在大多數情況下自動的關注這個。 在這方面,有兩種數據類型:一些簡單的 PostgreSQL數據類型,如`integer`和`text`, 可以直接通過應用程序讀取和寫入。 其他PostgreSQL數據類型,如 `timestamp`和`numeric`只能 通過特殊庫函數進行訪問;參閱[Section 33.4.4.2](#calibre_link-1998)。 [Table 33-1](#calibre_link-1999)顯示了哪個PostgreSQL 數據類型對應哪個C數據類型。當你希望 發送或接收一個給定PostgreSQL數據類型的值時,你應該 在聲明部分聲明一個對應C數據類型的C變量。 **Table 33-1\. PostgreSQL數據類型和C變量類型之間的映射** | PostgreSQL數據類型 | 宿主變量類型 | | --- | --- | | `smallint` | `short` | | `integer` | `int` | | `bigint` | `long long int` | | `decimal` | `decimal`[[a]](#calibre_link-2000) | | `numeric` | `numeric`[[a]](#calibre_link-2000) | | `real` | `float` | | `double precision` | `double` | | `smallserial` | `short` | | `serial` | `int` | | `bigserial` | `long long int` | | `oid` | `unsigned int` | | `character(``_n_`), `varchar(``_n_`), `text` | `char[``_n_`+1], `VARCHAR[``_n_`+1][[b]](#calibre_link-2001) | | `name` | `char[NAMEDATALEN]` | | `timestamp` | `timestamp`[[a]](#calibre_link-2000) | | `interval` | `interval`[[a]](#calibre_link-2000) | | `date` | `date`[[a]](#calibre_link-2000) | | `boolean` | `bool`[[c]](#calibre_link-2002) | | Notes: a. 這種類型可以通過特殊庫函數訪問;參閱[Section 33.4.4.2](#calibre_link-1998)。 b. 在`ecpglib.h`中聲明 c. 如果不是本地的,在`ecpglib.h`中聲明 | ### 33.4.4.1\. 處理字符串 為了處理SQL字符串數據類型,比如`varchar`和`text`, 有兩種可能方式聲明宿主變量。 一種方式是使用`char[]`,`char`數組是在C中處理字符數據 最常見方式。 ``` EXEC SQL BEGIN DECLARE SECTION; char str[50]; EXEC SQL END DECLARE SECTION; ``` 請注意,你必須關注自身長度。 如果你使用這個宿主變量 作為查詢返回一個具有多于49個字符的字符串的目標變量, 那么發生緩沖區溢出。 另一種方法是使用`VARCHAR`類型,這是一個由ECPG提供的 特殊類型。`VARCHAR`類型的數組定義被轉換為 每個變量的命名`結構`。聲明如: ``` VARCHAR var[180]; ``` 轉換成: ``` struct varchar_var { int len; char arr[180]; } var; ``` `arr`有一個終止零字節的字符串。 因此,為了在`VARCHAR`宿主變量中存儲字符串, 宿主變量必須聲明為包含零字節終結符的長度。 `len`持有 存儲在`arr`中而沒有 終止零字節的字符串長度。當一個宿主變量作為一個查詢輸入時 ,如果`strlen(arr)` 和`len`是不同的,那么使用稍短的。 兩個或以上`VARCHAR`宿主變量不能在單行語句中被聲明。 下面的代碼將混淆`ecpg`預處理程序: ``` VARCHAR v1[128], v2[128]; /* WRONG */ ``` 兩個變量應該像下面這樣在獨立語句中進行定義: ``` VARCHAR v1[128]; VARCHAR v2[128]; ``` `VARCHAR`可以使用大寫或小寫,但是在不混淆的情況下。 `char`和`VARCHAR`宿主變量可以持有其它SQL類型的值, 這將被存儲在它們的字符串形式中。 ### 33.4.4.2\. 訪問特定數據類型 ECPG含有一些特定類型幫助你 與來自PostgreSQL服務器的一些特殊數據類型進行輕松互動。 特別是,它已經實現支持`numeric`, `decimal`, `date`, `timestamp`和`interval`類型。 這些數據類型不能有效地映射到原始主機變量類型(例如 `int`, `long long int`或者`char[]`), 因為他們有一個復雜的內部結構。 應用程序通過聲明特殊類型的主機變量處理這些類型,并且在pgtypes庫中使用函數訪問他們。 該pgtypes庫包含處理這些類型的基本函數的 詳細描述參閱[Section 33.6](#calibre_link-2003), 這樣你就不需要發送一個查詢到SQL服務器,僅僅為了添加間隔時間戳例子。 以下小節描述了這些特殊數據類型。為了獲得關于pgtypes庫函數的更多細節, 參閱[Section 33.6](#calibre_link-2003)。 #### 33.4.4.2.1\. timestamp, date 這是在ECPG宿主應用程序中處理`timestamp`變量的模式。 首先,程序必須包含`timestamp`類型的頭文件: ``` #include <pgtypes_timestamp.h> ``` 接下來,在聲明部分聲明作為類型`timestamp`的宿主變量: ``` EXEC SQL BEGIN DECLARE SECTION; timestamp ts; EXEC SQL END DECLARE SECTION; ``` 并且讀取值到宿主變量之后, 使用pgtypes庫函數處理它。在下面的例子中, 使用`PGTYPEStimestamp_to_asc()`函數該 `timestamp`值轉換成文本(ASCII)形式: ``` EXEC SQL SELECT now()::timestamp INTO :ts; printf("ts = %s\n", PGTYPEStimestamp_to_asc(ts)); ``` 這個例子將顯示如下一些結果: ``` ts = 2010-06-27 18:03:56.949343 ``` 此外,日期類型可以用同樣的方式處理。 程序必須包括`pgtypes_date.h`, 作為日期類型聲明一個宿主變量并且使用 `PGTYPESdate_to_asc()`函數轉換日期值為文本形式。 關于pgtypes庫函數的更多詳情,請參閱[Section 33.6](#calibre_link-2003)。 #### 33.4.4.2.2\. interval `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; 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; } ``` #### 33.4.4.2.3\. numeric, decimal `numeric`和`decimal`類型的處理類似于 `interval`類型:它需要定義一個指針, 在堆上分配一些內存空間,并且使用pgtypes庫函數訪問 變量。關于pgtypes庫函數的更多細節,參閱[Section 33.6](#calibre_link-2003)。 對于`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; 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)); /*轉換十進制到數值型以顯示十進制值*/ 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; } ``` ### 33.4.4.3\. 使用非初級類型的宿主變量 作為一個宿主變量你也可以使用數組,typedefs,結構和指針。 #### 33.4.4.3.1\. Arrays 有兩個作為宿主變量的數組用例。 最先的一種方式是在`char[]` 或者`VARCHAR[]`中存儲一些文本字符串, 正如[Section 33.4.4.1](#calibre_link-2004)解釋的。 第二個用例是不使用游標從查詢結果檢索多行。 沒有一個數組處理包括多行的一個查詢結果, 它需要使用一個游標和`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 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= ``` #### 33.4.4.3.2\. 結構 一個成員名稱匹配查詢結果列名稱的結構, 可用于一次檢索多個列。該結構可以在單一的宿主變量中處理多個列的值。 下面的示例檢索OID,名稱,和 來自`pg_database` 系統表可用數據庫的大小,并且使用 `pg_database_size()`函數。在這個例子中, 一個結構變量`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; /*當結果集到達末尾時,打破while循環*/ EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /*抓取多列到一個結構中*/ EXEC SQL FETCH FROM cur1 INTO :dbval; /*打印結構成員*/ 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; /*當結果集到達末尾時,打破while循環*/ EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { /*抓取多列到一個結構中*/ EXEC SQL FETCH FROM cur1 INTO :dbval, :size; /*打印結構成員*/ printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, size); } EXEC SQL CLOSE cur1; ``` #### 33.4.4.3.3\. Typedefs 使用`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; ``` 這種聲明并不需要聲明部分。 #### 33.4.4.3.4\. 指針 你可以聲明最常見類型的指針。 然而注意你不能作為沒有自動分配的查詢目標變量 而使用指針。參閱[Section 33.7](#calibre_link-1086)獲取更多自動配置的信息。 ``` EXEC SQL BEGIN DECLARE SECTION; int *intp; char **charp; EXEC SQL END DECLARE SECTION; ``` ## 33.4.5\. 處理非初級的SQL數據類型 本節包含了如何處理nonscalar和ECPG應用程序中用戶自定義的SQL級別數據類型的相關信息。 請注意這不同于非初級類型宿主變量的處理,在前面的章節中有描述。 ### 33.4.5.1\. 數組 在ECPG中不直接支持SQL級別數組。不可能簡單的映射SQL數組到C數組宿主變量。 這將產生未定義操作。然而,存在一些解決方法。 如果查詢分別訪問數組_元素_,那么這可以避免在ECPG中使用數組。 然后,應該使用可以映射到元素類型的宿主變量。比如, 如果列類型是`integer`數組,那么使用`int`類型宿主變量。 如果元素類型是`varchar`或者`text`, 則可以使用`char[]`或者`VARCHAR[]`類型宿主變量。 下面是一個例子。假設下列表: ``` CREATE TABLE t3 ( ii integer[] ); testdb=> SELECT * FROM t3; ii ------------- {1,2,3,4,5} (1 row) ``` 下面示例程序檢索了數組的第四個元素,并且將它存儲在`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[]`類型宿主變量的外部字符串形式中存儲數組。 更多關于該形式的詳細信息,請參閱[Section 8.15.2](#calibre_link-1639)。 注意這意味著在主程序(沒有對分析文本表示的進一步處理)中數組自然不能作為數組被訪問。 ### 33.4.5.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; /*將復合類型列的每個元素放在SELECT列表中*/ 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) { /*抓取復合類型列的每個元素給宿主變量*/ EXEC SQL FETCH FROM cur1 INTO :intval, :textval; printf("intval=%d, textval=%s\n", intval, textval.arr); } EXEC SQL CLOSE cur1; ``` 為了加強這個例子,在`FETCH`命令中存儲值的宿主變量 可以聚集在一個結構中。 關于結構形式中宿主變量的更多細節,參閱[Section 33.4.4.3.2](#calibre_link-2005)。 為了切換到結構,例子可以做如下修改。 兩個宿主變量`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; /*將復合類型列的每個元素放在SELECT列表中*/ 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) { /*將SELECT列表中所有值放到結構中*/ 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) { /*將SELECT列表中的所有值放到結構中*/ EXEC SQL FETCH FROM cur1 INTO :compval; printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr); } ... ``` 這種方式,可以無縫的將復合類型映射到結構中,盡管ECPG并不了解該復合類型。 最后,在類型`char[]`或者`VARCHAR[]`宿主變量中的外部字符串表示形式中 存儲復合類型是可能的。但是那種方式,不容易從主程序中訪問值的字段。 ### 33.4.5.3\. 用戶自定義基本類型 ECPG不直接支持新用戶自定義基礎類型。你可以使用外部字符串表示形式和 類型`char[]`或者`VARCHAR[]`的宿主變量,并且該方法 對于許多類型的確是合適的并且充分的。 這是一個使用[Section 35.11](#calibre_link-837)中`復合`數據類型的例子。 該類型的外部字符串表示形式是`(%lf,%lf)`, 定義在[Section 35.11](#calibre_link-837)中的`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)'); ``` ## 33.4.6\. 指示器 上面的例子不能處理空值。實際上,如果從數據庫中抓到一條空值, 那么上面的檢索例子會拋出一個錯誤。 要能夠向數據庫中傳遞空值,或者從數據庫中檢索空值, 你需要給每個包含數據的宿主變量后面附加一個額外的宿主變量。 這第二個宿主變量叫_指示器_,里面包含一個標志, 告訴我們數據是否為空,如果為空,那么真正的宿主變量的數值就可以忽略。 下面是一個能正確檢索空值的例子: ``` 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`將是零, 如果值是空,那么它將是負數。 指示器還有另外的一個用途,如果指示器值是正數, 則意味著值不空,但是在數值存儲到宿主變量里的時候被截斷了。 如果參數`-r no_indicator`被傳遞給預處理器`ecpg`, 那么它在"no-indicator"模式下工作。在非指示器模式下, 如果沒有聲明可用指示器,那么為了將字符串類型作為空字符串以及 整數類型作為類型的最小可能值(比如,`int`最小為`INT_MIN`),則使用空值(在輸入和輸出上)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看