[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 數據庫 > 其它 > 正文
PostgreSQL7.0手冊-程序員手冊 -48. 服務器編程接口
編譯:何偉平 laser@zhengmai.com.cn (2001-04-21 23:10:29)
第四十八章.服務器編程接口
內容 
接口函數 
接口支持函數 
存儲器管理 
數據改變的可視性 
例子 
服務器編程接口(Server Programming Interface) (SPI)給我們在用戶定義的 C 函數裡面運行 SQL查詢的能力.可用的過程語言(PL)給我們一個實現這些功能的可選的手段. 
實際上,SPI 只是一套用訪問分析器,規劃器,優化器和執行器(Parser,Planner,Optimizer and Executor)的本機接口函數.SPI 同樣做一些存儲器管理工作. 

為了避免混淆,我們將使用 函數(function) 來代表 SPI 接口函數,用 過程(procedure) 代表用戶用 SPI定義 C 函數. 

SPI 過程總是被一些(上層)執行器和 SPI 管理器用執行器調用來運行你的查詢.其他過程可以通過執行器從你的過程裡運行查詢來調用. 

注意,如果在你的過程裡執行查詢時,事務退出了,那控制不會返回到你的過程中.相反,所有工作都將回卷並且服務器將等待客戶端的另一個命令.這一點將在以的版本中修正. 

其他限制是不能執行 BEGIN,END 和 ABORT (交易控制語句)和遊標操作.這些同樣在將來的版本中要被修改. 

如果執行成功了,SPI 函數返回一個非負結果(或者通過返回一個整數值或放在 SPI_result 全局變量,象下面描述的那樣).出錯時,返回一個負數或 NULL 結果. 

接口函數

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_connect
名稱
SPI_connect  把你的過程與 SPI 管理器連接起來. 

語法
int SPI_connect(void)
輸入
無 

輸出
int 
  
返回狀態 
SPI_OK_CONNECT 
如果聯接成功 
SPI_ERROR_CONNECT 
如果聯接失敗 

描述SPI_connect 打開一個與 Postgres 端的聯接.如果你需要執行查詢你就要調用這個函數.有些使用 SPI 函數的應用可以從非聯接的過程調用. 
如果試圖對一個已經聯接的過程調用 SPI_connect 你可能得到一個 SPI_ERROR_CONNECT 錯誤信息 - 例如,如果你直接從另一個已聯接的過程裡調用一個過程.實際上,因為子進程可能使用 SPI,子進程返回你的父進程將不能繼續使用 SPI (如果子進程調用了 SPI_finish).這是一個糟糕的方面.

用法
算法
SPI_connect 執行下面操作: 
• 
初始化 SPI 用查詢執行和存儲器管理的內部結構. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_finish
名稱
SPI_finish  把你的過程與 SPI 管理器斷開. 

語法
SPI_finish(void)
輸入
無 

輸出
int 
   
 SPI_OK_FINISH 如果正常斷開,返回此信息 
SPI_ERROR_UNCONNECTED 如果從一個未聯接過程調用,返回此信息 

描述
SPI_finish 關閉一個現有的與 Postgres 端的聯接.你應該在結束通過 SPI 管理器的操作調用此函數. 
如果 SPI_finish 是在當前沒有有效聯接的情況下被調用的,你可能會得到一個 SPI_ERROR_UNCONNECTED 的返回.這樣做沒有什根本性的錯誤,這意味著 SPI 管理器不做任何事情.

用法
SPI_finish 必須 作為一個已聯接的過程的最一步被調用,否則你可能得到不可預料的結果!注意:如果你從事務退出(通過 elog(ERROR)),你可以安全的忽略對 SPI_finish 的調用.
算法
SPI_finish 執行下列操作: 
• 
斷開你的過程與 SPI 管理器的連接並且釋放所有你的過程自 SPI_connect 起通過 palloc 分配的存儲器.這些存儲器不能再利用!請參考存儲器管理. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_exec
名稱
SPI_exec  創建一個執行規劃 (分析器+規劃器+優化器)並且執行一個查詢.
語法
SPI_exec(query, tcount)
輸入
char *query 
包含查詢規劃的字符串 
int tcount 
返回的最大記錄數 

輸出
int 
   
 SPI_OK_EXEC 如果正確斷開,返回此值 
SPI_ERROR_UNCONNECTED 如果從一個未聯接的過程調用,返回此值 
SPI_ERROR_ARGUMENT 如果查詢是 NULL(空)或 tcount < 0,返回此值 
SPI_ERROR_UNCONNECTED 如果過程未聯接,返回此值. 
SPI_ERROR_COPY 如果 COPY TO/FROM stdin(標準輸入),返回此值. 
SPI_ERROR_CURSOR 如果 DECLARE/CLOSE CURSOR,FETCH,返回此值 
SPI_ERROR_TRANSACTION 如果 BEGIN/ABORT/END,返回此值. 
SPI_ERROR_OPUNKNOWN 如果查詢類型未知(這種情況不應發生). 

如果你的查詢執行成功,那將返回下列非負數值之一: 
  
   
 SPI_OK_UTILITY 如果執行了某些應用(例如 CREATE TABLE ...) 
SPI_OK_SELECT 如果執行了 SELECT (但不是 SELECT ... INTO!) 
SPI_OK_SELINTO 如果執行了 SELECT ... INTO 
SPI_OK_INSERT 如果執行了 INSERT(或 INSERT ... SELECT) 
SPI_OK_DELETE 如果執行了 DELETE 
SPI_OK_UPDATE 如果執行了 UPDATE 


描述
SPI_exec 創建一個執行規劃(分析器+規劃器+優化器)並且執行查詢以獲取 tcount 條記錄.
用法
這個(函數)只能從一個以聯接的過程中調用.如果 tcount 是零則對查詢掃描返回的所有記錄都執行查詢.使用 tcount > 0 你可以限制查詢執行的記錄數.例如, 
SPI_exec ("insert into table select * from table", 5);
將最多允許 5 條記錄插入表中.如果你的查詢執行成功則返回一個非負數. 
注意:你可能在一個字符串裡傳遞許多查詢或一個查詢字符串可能被 RULE (規則)重寫.SPI_exec 返回最一個執行的查詢的結果.
最一個被執行的查詢的實際記錄數放在全局變量 SPI_processed 裡返回(如果不是 SPI_OK_UTILITY).如果返回了SPI_OK_SELECT 而且 SPI_processed > 0 那你可以通過全局指針 SPITupleTable *SPI_tuptable 來訪問這些選擇了的記錄:同樣要注意,SPI_finish 將釋放所有 SPITupleTable 並令所有 SPITupleTable 不可用!(參閱存儲器管理). 
SPI_exec 可能返回下面的(本地)值: 
   
 SPI_ERROR_ARGUMENT 如果查詢是 NULL (空)或 tcount < 0,返回此值. 
SPI_ERROR_UNCONNECTED 如果過程沒有聯接,返回此值. 
SPI_ERROR_COPY如果是 COPY TO/FROM stdin(標準輸入),返回此值. 
SPI_ERROR_CURSOR 如果是 DECLARE/CLOSE CURSOR,FETCH,返回此值. 
SPI_ERROR_TRANSACTION 如果 BEGIN/ABORT/END,返回此值. 
SPI_ERROR_OPUNKNOWN 如果查詢類型未知(不應發生這種情況),返回此值. 


算法
SPI_exec 執行下面操作: 
  
• 
斷開你的過程與 SPI 管理器的連接並且釋放所有你的過程自 SPI_connect 起通過 palloc 分配的存儲器.這些存儲器不能再利用!請參考存儲器管理。 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_prepare
名稱
SPI_prepare  將你的過程與 SPI 管理器連接. 

語法
SPI_prepare(query, nargs, argtypes)
輸入
query 
查詢字符串 
nargs 
輸入的參數個數($1 ... $nargs -象 SQL-函數裡一樣) 
argtypes 
指向輸入參數的類型為 OID 的指針數組 

輸出
void * 
指向一個執行規劃的指針(分析器+規劃器+優化器) 

描述
SPI_prepare 創建和返回一個執行規劃(分析器+規劃器+優化器)但是不執行查詢.應該只從一個已聯接的過程內部調用.
用法
nargs 是參數個數($1 ... $nargs - 象 SQL-函數裡一樣),並且 nargs 可以是 0 --只有在查詢裡沒有任何 $1 時是這樣. 
準備好的執行規劃的執行速度有時快很多,所以如果某個查詢會被執行多次時這個特性可能會很有用. 

SPI_prepare 返回的規劃可能只能被用目前的過程,因為 SPI_finish 將釋放為規劃分配的存儲器.參考 SPI_saveplan. 

如果成功,將返回一個非空的指針.否則,你會得到一個 NULL(空)的規劃.不管那種情況 SPI_result 都將象 SPI_exec 返回的值那樣被設置,除非它被設置為 SPI_ERROR_ARGUMENT --因為查詢是 NULL 或 nargs < 0 或 nargs > 0 && argtypes 是 NULL.


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_saveplan
名稱
SPI_saveplan  保存傳遞進來的分析器規劃 

語法
SPI_saveplan(plan)
輸入
void *query 
傳入的規劃 

輸出
void * 
執行規劃定位.如果成功返回 NULL. 
SPI_result 
   
 SPI_ERROR_ARGUMENT 如果規劃是 NULL,返回此值 
SPI_ERROR_UNCONNECTED 如果過程沒有聯接,返回此值 

描述
SPI_saveplan 把一個由 SPI_prepare 準備的規劃存儲在安全的存儲器中,以避免被 SPI_finish 或事務管理器釋放. 
目前的 Postgres 版本不能把計劃好的規劃存儲在系統表裡並從中獲取它們執行.這個(特性)將在未來的版本中實現.做為可選的方法,目前的版本可以在當前會話中隨擊活的過程中重新使用準備好的規劃.用 SPI_execp 執行這些保存了的規劃.

用法
SPI_saveplan 把一個傳入的規劃(由 SPI_prepare 準備)保存在存儲器中防止被 SPI_finish 和事務管理器釋放並且返回一個指向被保存的規劃的指針.你可以把返回的指針保存在一個局部變量裡.在準備一個規劃或在 SPI_execp (見下面)中使用已準備的規劃時,注意檢查這個指針是否為 NULL(空). 
注意:如果已準備的規劃參考的對象之一 (一個關系,函數,等.)在你的會話過程中被刪除(被你的端或其他過程)那對此規劃的 SPI_execp 執行結果將不可預料.

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_execp
名稱
SPI_execp  執行一個從 SPI_saveplan 來的規劃 

語法
SPI_execp(plan,
values,
nulls,
tcount)
輸入
void *plan 
執行規劃 
Datum *values 
實際參數值 
char *nulls 
  
  
  
  

描述哪個參數獲得 NULL 值的數組  
 'n' 表明允許 NULL  
' ' 表示不允許 NULL  


int tcount 
將被執行的規劃的記錄數 

輸出
int 
返回與 SPI_exec 一樣的值以及  
 SPI_ERROR_ARGUMENT 如果 plan 是 NULL 或 tcount < 0,返回此值 
SPI_ERROR_PARAM 如果 values 是 NULL 並且所準備的 plan 帶有一些參數. 

SPI_tuptable 
如果成功,則象 SPI_exec 一樣初始化 
SPI_processed 
如果成功,則象 SPI_exec 一樣初始化 

描述
SPI_execp 把一個由 SPI_prepare 準備的規劃存儲在安全存儲器中,以免被 SPI_finish 或事務管理器釋放. 
目前的 Postgres 版本不能把計劃好的規劃存儲在系統表裡並從中獲取它們執行.這個(特性)將在未來的版本中實現.做為繞開的方法,目前的版本可以在當前會話中隨擊活的過程中重新使用準備好的規劃.用 SPI_execp 執行這些保存了的規劃.

用法
如果 nulls 是 NULL 那 SPI_execp 假設所有值(如果有的話)都是 NOT NULL. 
注意:如果已準備的規劃參考的對象之一(一個關系,函數,等.)在你的會話過程中被刪除(被你的端或其他過程)那對此規劃的 SPI_execp 執行結果將不可預料.

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

接口支持函數
所有面描述的函數都可被聯接或未聯接的過程使用.

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_copytuple
名稱
SPI_copytuple  把記錄的拷貝放到上層執行器環境 

語法
SPI_copytuple(tuple)
輸入
HeapTuple tuple 
  
  
  
  

Input tuple to be copied

Outputs
HeapTuple 
輸入被拷貝的記錄 
   
 non-NULL 如果 tuple 為非空(not NULL)並且拷貝成功 
NULL 只有 tuple 是 NULL 

描述
SPI_copytuple 把一個記錄的拷貝放到上層高級執行器環境.參考存儲器管理章節.
用法
TBD

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_modifytuple
名稱
SPI_modifytuple  修改關系的記錄 

語法
SPI_modifytuple(rel, tuple , nattrs
, attnum , Values , Nulls)
輸入
Relation rel 
HeapTuple tuple 
要修改的輸入記錄 
int nattrs 
attnum 裡字段號的個數 
int * attnum 
將要修改的字段號的數組 
Datum * Values 
給聲明的屬性的新值 
char * Nulls 
若存在,哪個字段是 NULL. 

輸出
HeapTuple 

修改的新記錄  
 non-NULL 如果 tuple 為非空(not NULL)並且修改成功 
NULL 只有當 tuple 為 NULL(空) 


SPI_result 
   
 SPI_ERROR_ARGUMENT 如果 rel 是 NULL 或 tuple 是 NULL 或 natts &le(小)0 或 attnum 是 NULL 或 Values 是 NULL. 
SPI_ERROR_NOATTRIBUTE 如果在 attnum 裡有一個非法的數字 (attnum &le(小)0 或 > 記錄中字段數) 

 
描述
SPI_modifytuple 修改一個上層執行器環境的記錄.參考存儲器管理章節. 
 
用法
如果成功,返回一個指向新記錄的指針.新記錄在執行器上層環境分配(參見 存儲器管理).傳入的記錄沒有改變.

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_fnumber
名稱
SPI_fnumber  查找聲明的字段的字段號 

語法
SPI_fnumber(tupdesc, fname)
輸入
TupleDesc tupdesc 
輸入記錄的描述 
char * fname 
字段名 

輸出
int 

字段號  
 有效的以1為基的字段索引號 
SPI_ERROR_NOATTRIBUTE 如果命名的字段沒有找到 

 

描述
SPI_fnumber 返回 fname 指明的字段的字段號. 

用法
字段號是以1為基的. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_fname
名稱
SPI_fname  找出指明的字段的字段名 

語法
SPI_fname(tupdesc, fname)
輸入
TupleDesc tupdesc 
輸入的記錄描述 
char * fnumber 
字段號 

輸出
char * 

字段名  
 NULL -- 如果 fnumber 超出范圍 
出錯時,SPI_result 設置為 SPI_ERROR_NOATTRIBUTE 

 

描述
SPI_fname 返回指明的字段的字段名. 

用法
字段號是以 1 為基的. 

算法
返回一個新分配的字段名的拷貝. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_getvalue
名稱
SPI_getvalue  返回指明的字段的字符串值 

語法
SPI_getvalue(tuple, tupdesc, fnumber)
輸入
HeapTuple tuple 
輸入的待檢查的字段 
TupleDesc tupdesc 
輸入字段描述 
int fnumber 
字段號 

輸出
char * 

字段值或 NULL(空),如果 
   
 字段為 NULL(空) 
fnumber 超出范圍(這時 SPI_result 設置為 SPI_ERROR_NOATTRIBUTE) 
沒有可用的輸出函數(這時 SPI_result 設置為 SPI_ERROR_NOOUTFUNC) 

 

描述
SPI_getvalue 返回指明字段的一個外部(字符串)形式的值. 

用法
字段號是以 1 為基的. 

算法
根據數值的要求分配存儲器. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_getbinval
名稱
SPI_getbinval  返回指明的字段的二進制數值 

語法
SPI_getbinval(tuple, tupdesc, fnumber, isnull)
輸入
HeapTuple tuple 
要檢查的輸入記錄 
TupleDesc tupdesc 
輸入記錄描述 
int fnumber 
字段號 

輸出
Datum 
字段二進制數值 
bool * isnull 
字段是否為空的標志 
SPI_result 
   
 SPI_ERROR_NOATTRIBUTE 

 
描述
SPI_getbinval 返回指明字段的二進制數值 

用法
字段號是以1為基的. 

算法
不為二進制數值分配新空間. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_gettype
名稱
SPI_gettype  返回指明字段的類型名 

語法
SPI_gettype(tupdesc, fnumber)
輸入
TupleDesc tupdesc 
輸入字段描述 
int fnumber 
字段號 

輸出
char * 
指明字段號的類型名稱 
SPI_result 
   
 SPI_ERROR_NOATTRIBUTE 

 
描述
SPI_gettype 返回一個指明字段的類型名的拷貝. 

用法
字段號是以1為基的. 

算法
不為二進制數值分配新空間. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_gettypeid
名稱
SPI_gettypeid  返回指明字段的類型 OID 

語法
SPI_gettypeid(tupdesc, fnumber)
輸入
TupleDesc tupdesc 
輸入字段的描述 
int fnumber 
字段號 

輸出
OID 
指明字段號的類型 OID. 
SPI_result 
   
 SPI_ERROR_NOATTRIBUTE 

 
描述
SPI_gettypeid 返回指明的字段的類型 OID. 

用法
字段號是以 1 為基的. 

算法
TBD

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_getrelname
名稱
SPI_getrelname  返回指明關系的名稱 

語法
SPI_getrelname(rel)
輸入
Relation rel 
輸入的關系 

輸出
char * 
指定的關系的名稱 

描述
SPI_getrelname 返回指明關系的名稱. 

用法
TBD
算法
把關系名稱拷貝到新的存儲器中去. 

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_palloc
名稱
SPI_palloc  在上層執行器環境中分配存儲器 

語法
SPI_palloc(size)
輸入
Size size 
八進制的待分配存儲空間大小 

輸出
void * 
指明大小的新存儲區 

描述
SPI_palloc 在執行器上層環境分配存儲器.參閱存儲器管理章節. 

用法
TBD

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_repalloc
名稱
SPI_repalloc  重新在執行器上層環境中分配存儲器 

語法
SPI_repalloc(pointer, size)
輸入
void * pointer 
指向現有存儲器 
Size size 
八進制的要分配的存儲空間尺寸 

輸出
void * 
新分配的存儲空間, 帶有從現存區域拷貝來的內容. 

描述
SPI_repalloc 在上層執行器環境中重新分配存儲器.參考存儲器管理章節. 

用法
TBD

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

SPI_pfree
名稱
SPI_pfree  從上層執行器環境中釋放存儲器 

語法
SPI_pfree(pointer)
輸入
void * pointer 
指向現有存儲器(區)的指針 

輸出
無 
描述
SPI_pfree 釋放在上層執行器環境中的存儲器.參考存儲器管理章節. 

用法
TBD

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

存儲器管理
服務器在存儲器環境按這樣的方法分配存儲器:在某個環境分配的存儲器可以被環境析構器釋放而不會影響其他環境中分配的存儲器.所有存儲器分配(通過 palloc 等)都被當作在當前環境的區域中分配存儲器.如果你試圖釋放(或再分配)不在當前環境的存儲器,你將得到不可預料的結果. 
創建存儲器環境和切換存儲器環境是 SPI 管理器中存儲器管理器的任務. 

SPI 過程處理兩種存儲器環境:上層執行器存儲器環境和過程存儲器環境(如果已聯接). 

在一個過程與 SPI 管理器聯接之前,當前存儲器環境是上層執行器環境,所以所有由過程自身通過 palloc/repalloc 或通過 SPI 工具函數在聯接到 SPI 管理器之前分配的存儲器都在這個環境裡. 

在進行 SPI_connect 調用之,當前環境是過程自身所有的.通過 palloc/repalloc 或通過 SPI 應用函數分配的存儲器(除了 SPI_copytuple,SPI_modifytuple,SPI_palloc 和 SPI_repalloc 以外)都在這個環境中分配. 

當進程與 SPI 管理器斷開(通過調用 SPI_finish),當前環境恢復為上層執行器環境並且所有在過程存儲器環境分配的存儲器都被釋放,並且不可繼續使用! 

如果你想返回一些東西給上層執行器,那你必須為此在上層環境分配一片存儲器! 

SPI 不能自動釋放在上層執行器環境裡分配的存儲器! 

SPI 在查詢完成自動釋放查詢執行期間分配的存儲器!


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

數據改變的可視性
Postgres 數據修改的可視性規則:在查詢執行過程中,由查詢本身造成的數據修改(通過 SQL-函數, SPI-函數,觸發器)對查詢掃描而言是不可見的.例如,在查詢 INSERT INTO a SELECT * FROM a 裡,插入的記錄對 SELECT 的掃描是不可見的.實際上,這做在數據庫內部形成非遞歸的數據庫表的復制品(當然是要受到唯一索引規則的制約的嘍)。 
由查詢 Q 造成的改變可以為查詢 Q 以運行的查詢可見,不管這些查詢是在查詢 Q 內部開始運行(在 Q 運行期間)的還是Q運行完畢開始運行的.


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

例子
這個 SPI 使用的樣例演示了可視性規則.在 src/test/regress/regress.c 和 contrib/spi 裡有更復雜的例子. 
這是一個非常簡單的 SPI 使用的例子.過程 execq 在其第一個參數裡接收一個 SQL 查詢,第二個參數接收一個 tcount(譯注:記錄個數),用 SPI_exec 執行這個查詢並且返回查詢執行過的記錄個數: 

#include "executor/spi.h"       /* this is what you need to work with SPI(這個是你用SPI所要用的頭文件) */

int execq(text *sql, int cnt);

int
execq(text *sql, int cnt)
{
        int ret;
        int proc = 0;
        
        SPI_connect();
        
        ret = SPI_exec(textout(sql), cnt);
        
        proc = SPI_processed;
        /*
         * If this is SELECT and some tuple(s) fetched -
         * returns tuples to the caller via elog (NOTICE).
         */
        if ( ret == SPI_OK_SELECT && SPI_processed > 0 )
        {
                TupleDesc tupdesc = SPI_tuptable->tupdesc;
                SPITupleTable *tuptable = SPI_tuptable;
                char buf[8192];
                int i;
                
                for (ret = 0; ret < proc; ret++)
                {
                        HeapTuple tuple = tuptable->vals[ret];
                        
                        for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++)
                                sprintf(buf + strlen (buf), " %s%s",
                                        SPI_getvalue(tuple, tupdesc, i),
                                        (i == tupdesc->natts) ? " " : " |");
                        elog (NOTICE, "EXECQ: %s", buf);
                }
        }

        SPI_finish();

        return (proc);
}
然,編譯並創建函數: 
create function execq (text, int4) returns int4 as '...path_to_so' language 'c';
vac=> select execq('create table a (x int4)', 0);
execq
-----
    0
(1 row)

vac=> insert into a values (execq('insert into a values (0)',0));
INSERT 167631 1
vac=> select execq('select * from a',0);
NOTICE:EXECQ:  0 <<< inserted by execq

NOTICE:EXECQ:  1 <<< value returned by execq and inserted by upper INSERT

execq
-----
    2
(1 row)

vac=> select execq('insert into a select x + 2 from a',1);
execq
-----
    1
(1 row)

vac=> select execq('select * from a', 10);
NOTICE:EXECQ:  0 

NOTICE:EXECQ:  1 

NOTICE:EXECQ:  2 <<< 0 + 2, only one tuple inserted - as specified

execq
-----
    3            <<< 10 is max value only, 3 is real # of tuples
(1 row)

vac=> delete from a;
DELETE 3
vac=> insert into a values (execq('select * from a', 0) + 1);
INSERT 167712 1
vac=> select * from a;
x
-
1                <<< no tuples in a (0) + 1
(1 row)

vac=> insert into a values (execq('select * from a', 0) + 1);
NOTICE:EXECQ:  0 
INSERT 167713 1
vac=> select * from a;
x
-
1
2                <<< there was single tuple in a + 1
(2 rows)

--   This demonstrates data changes visibility rule:

vac=> insert into a select execq('select * from a', 0) * x from a;
NOTICE:EXECQ:  1 
NOTICE:EXECQ:  2 
NOTICE:EXECQ:  1 
NOTICE:EXECQ:  2 
NOTICE:EXECQ:  2 
INSERT 0 2
vac=> select * from a;
x
-
1
2
2                <<< 2 tuples * 1 (x in first tuple)
6                <<< 3 tuples (2 + 1 just inserted) * 2 (x in second tuple)
(4 rows)             ^^^^^^^^ 
                     tuples visible to execq() in different invocations

--------------------------------------------------------------------------------

(http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
PostgreSQL7.0手冊-附錄-文檔 (2001-04-21 23:50:44)
PostgreSQL7.0手冊-附錄-日期/時間支持-CVS 倉庫 (2001-04-21 23:48:48)
PostgreSQL7.0手冊-教程 -73. Postgres SQL 高級特性 (2001-04-21 23:45:36)
PostgreSQL7.0手冊-教程 -72. 查詢語言 (2001-04-21 23:44:40)
PostgreSQL7.0手冊-教程 -71. 開始 (2001-04-21 23:42:54)
PostgreSQL7.0手冊-教程 -70. 體系結構 (2001-04-21 23:41:58)
PostgreSQL7.0手冊-教程 -69. SQL (2001-04-21 23:41:23)
PostgreSQL7.0手冊-開發者手冊 -68. 分頁文件 (2001-04-21 23:39:22)
PostgreSQL7.0手冊-開發者手冊 -67. 端接口 (2001-04-21 23:38:34)
PostgreSQL7.0手冊-開發者手冊 -66. gcc 缺省優化 (2001-04-21 23:37:20)

===更多相關===
 

★  樊強制作 歡迎分享  ★