亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > PostgreSQL > 正文

PostgreSQL7.0手冊-程序員手冊 -48. 服務器編程接口

2019-09-08 23:34:17
字體:
來源:轉載
供稿:網友
第四十八章.服務器編程接口
內容 
接口函數 
接口支持函數 
存儲器管理 
數據改變的可視性 
例子 
服務器編程接口(Server Programming Interface)?。⊿PI)給我們在用戶定義的 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?。ǖ皇恰ELECT ... 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?。眨┗颉count < 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?。蟆QL-函數里一樣) 
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?。驗椴樵兪恰ULL 或 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

--------------------------------------------------------------------------------
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97视频免费在线观看| 九九视频这里只有精品| 国产精品video| 久久这里有精品视频| 欧美综合国产精品久久丁香| 欧美日韩人人澡狠狠躁视频| 国产亚洲视频在线| 亚洲国产精品中文| 98视频在线噜噜噜国产| 欧美成人免费网| 亚洲美女中文字幕| 日韩av电影在线播放| 欧美性色视频在线| 欧美视频精品一区| 欧美在线观看视频| 久久久久一本一区二区青青蜜月| 丁香五六月婷婷久久激情| 国产美女久久精品香蕉69| 日韩有码视频在线| 少妇高潮久久久久久潘金莲| 欧美中文在线字幕| 最新中文字幕亚洲| 亚洲视频在线观看网站| 丝袜美腿亚洲一区二区| 2019av中文字幕| 成人免费在线视频网站| 欧美成年人视频网站欧美| 国产亚洲综合久久| 国产91网红主播在线观看| 92版电视剧仙鹤神针在线观看| 亚洲男人天堂九九视频| 午夜精品久久久久久久久久久久久| 亚洲精品久久久久中文字幕欢迎你| 国产91免费看片| 韩国v欧美v日本v亚洲| 日韩电影免费在线观看中文字幕| 亚洲人成伊人成综合网久久久| 日韩美女福利视频| 日韩中文字幕不卡视频| 国产精品大片wwwwww| 91亚洲精品久久久久久久久久久久| 成人国产精品一区| 美女久久久久久久| 国产成人一区二区在线| www.午夜精品| 成人免费大片黄在线播放| 亚洲国内精品视频| 91在线|亚洲| 欧美日韩中国免费专区在线看| 亚洲国产精品视频在线观看| 国产69精品久久久| 日韩欧美极品在线观看| 777国产偷窥盗摄精品视频| 欧美精品videos性欧美| 亚洲欧美激情视频| 亚洲一区美女视频在线观看免费| 成人国产亚洲精品a区天堂华泰| 成人欧美一区二区三区黑人| 欧美精品做受xxx性少妇| 国产美女久久精品| 色综合天天综合网国产成人网| 亚洲国内高清视频| 91地址最新发布| 九九精品在线播放| 9.1国产丝袜在线观看| 久久久久久久电影一区| 国产精品入口夜色视频大尺度| 黄色精品在线看| 国产精品久久久久久久久久久新郎| 亚洲成人免费在线视频| 亚洲第五色综合网| 色婷婷**av毛片一区| 欧美成人午夜激情视频| 97精品伊人久久久大香线蕉| 亚洲欧洲日产国码av系列天堂| 亚洲第一天堂无码专区| 欧美性xxxxhd| 欧美日韩成人黄色| 国产精品99久久久久久人| 91精品国产自产在线观看永久| 国产精品久久久久91| 日韩电影大片中文字幕| 久久影视电视剧免费网站清宫辞电视| 国产盗摄xxxx视频xxx69| 97视频免费在线看| 久久精品国产91精品亚洲| 欧美一区二区三区免费观看| 国产激情999| 国产91成人在在线播放| 国内精品久久久| 国产亚洲精品激情久久| 国产精品久久9| 国产精品免费观看在线| 国产不卡一区二区在线播放| 国产精品国内视频| 日韩中文字幕久久| 91av在线国产| 国产激情视频一区| 亚洲欧美在线免费观看| 免费不卡在线观看av| 91av网站在线播放| 国产日韩精品一区二区| 亚洲欧美国产制服动漫| 日本亚洲欧美成人| 亚洲国产精彩中文乱码av在线播放| 亚洲国产精品99| 亚洲一区二区三区成人在线视频精品| 亚洲人成网站在线播| 欧美久久精品一级黑人c片| 欧美激情视频三区| 精品国产福利在线| 亚洲天堂男人天堂女人天堂| 欧美综合在线第二页| 国产成人综合亚洲| 成人福利网站在线观看11| 国产精品中文字幕久久久| 欧美精品成人91久久久久久久| 欧美视频一二三| 亚洲自拍欧美另类| 成人精品视频99在线观看免费| 日韩免费黄色av| 日韩av在线免播放器| 国产亚洲精品一区二555| 欧美成人合集magnet| 亚洲成avwww人| 国产精品久久综合av爱欲tv| 久久久噜噜噜久久中文字免| 国产不卡av在线| 伊人久久久久久久久久久| 国产精品海角社区在线观看| 国产精品免费观看在线| 日韩亚洲第一页| 美女扒开尿口让男人操亚洲视频网站| 国产精品永久在线| 国产精品极品美女在线观看免费| 久久综合电影一区| 欧美激情在线观看视频| 欧美国产极速在线| 亚洲成人免费网站| 2019亚洲男人天堂| 亚洲精品免费在线视频| 日韩男女性生活视频| 一区二区三区四区视频| 亚洲成人久久网| 亚洲精品久久久久中文字幕欢迎你| 不卡在线观看电视剧完整版| 色视频www在线播放国产成人| 成人av.网址在线网站| 亚洲女成人图区| 91九色国产视频| 最好看的2019年中文视频| 成人av在线天堂| 欧美激情久久久久| 97超级碰在线看视频免费在线看| 欧洲精品在线视频| 亚洲精品视频免费在线观看| 国产欧美一区二区白浆黑人| 欧美高清自拍一区| 亚洲精品99久久久久中文字幕| 精品成人69xx.xyz| 久久精品久久精品亚洲人| 成人网欧美在线视频| 少妇av一区二区三区| 精品久久香蕉国产线看观看亚洲|