- 基於 psql (PostgreSQL) 10.4
pg_language表定義了函式實現所使用的語言。主要支援了C語言和SQL語句。一些可選的語言包括pl/pgsql、tcl和perl。
ligang=# select lanname, lanispl, lanpltrusted, lanplcallfoid, laninline, lanvalidator from pg_language;
lanname | lanispl | lanpltrusted | lanplcallfoid | laninline | lanvalidator
----------+---------+--------------+---------------+-----------+--------------
internal | f | f | 0 | 0 | 2246
c | f | f | 0 | 0 | 2247
sql | f | t | 0 | 0 | 2248
plpgsql | t | t | 13198 | 13199 | 13200
pg_proc表對函式進行了定義。每一個函式在該表中都對應一個元組,包含函式名。輸入引數型別,返回型別以及對函式的定義(可能是文字,可能是一段編譯型語句,也可能是對可執行程式碼的引用)。編譯過的函式可以靜態地連結到伺服器上,或者在儲存在共享庫內,當第一次使用該庫時動態的載入。
ligang=# select proname,prolang, prorettype,proargtypes, prosrc,probin from pg_proc where proname like '%square%';
proname | prolang | prorettype | proargtypes | prosrc | probin
---------+---------+------------+-------------+----------------------------+--------------------
square | 13201 | 23 | 23 | begin return $1 * $1; end; |
squares | 13 | 23 | 23 | squares_return_int | $libdir/squares.so
檢視其資料型別
ligang=# select oid , typname from pg_type where oid = 23;
oid | typname
-----+---------
23 | int4
(1 row)
以下是示例函式:
C: 與內建SQL型別等效的C型別
int
square_int (int x)
{
return x * x;
}
把上面的函式編譯成共享庫檔案,這樣宣告:
CREATE FUNCTION square(int) RETURNS int
AS '/path/to/square.so', 'square_int'
LANGUAGE 'C';
PL/PGSQL:
ligang=# create function square(int) returns int as 'begin return $1 * $1; end;' LANGUAGE 'plpgsql';
CREATE FUNCTION
ligang=#
ligang=#
ligang=# select square(4);
square
--------
16
建立使用者函式動態庫
新建程式碼
#include "postgres.h"
#include "fmgr.h"int square_int(int x) { return x * x; }
編譯 - 新增共享庫
[ligang@yfslcentos71 include]$ gcc -I`pg_config --includedir-server` -c squares.c [ligang@yfslcentos71 include]$ gcc -shared squares.o -o squares.so [ligang@yfslcentos71 include]$ cp squares.so `pg_config --libdir`/
Pg資料庫裝載
ligang=# create function squares(int) returns int as '$libdir/squares.so', 'square_int' LANGUAGE 'c' STRICT;
關於PG_MODULE_MAGIC
為了確保不會錯誤載入共享庫檔案,從PostgreSQL 開始將檢查那個檔案的"magic block",這允許伺服器以檢查明顯的不相容性。
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
如果不打算相容8.2 PostgreSQL之前的版本, #ifdef測試也可以省略
原始碼修改為:
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
int
square_int(int x)
{
return x * x;
}
版本約定
版本0約定
版本-0方法中,此風格 C 函式的引數和結果用普通 C 風格宣告, 但是要小心使用上面顯示的 SQL 資料型別的 C 表現形式。 (以前版本;)
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
int
square_int(int x)
{
return x * x;
}
版本1約定 (應當使用該版本)
版本-1呼叫約定使用巨集消除大多數傳遞引數和結果的複雜性。版本-1風格函式的C定義總是下面這樣:
Datum funcname(PG_FUNCTION_ARGS);
另外,巨集呼叫:
PG_FUNCTION_INFO_V1(funcname);
也必須出現在同一個原始檔裡(通常就可以寫在函式自身前面)。 對那些internal語言函式而言,不需要呼叫這個巨集, 因為PostgreSQL目前假設內部函式都是版本-1。不過,對於動態載入的函式, 它是必須的。
每個實際引數都是用一個對應該引數的資料型別的 PG_GETARG_xxx()巨集抓取的, 用返回型別的PG_RETURN_xxx()巨集返回結果。 PG_GETARG_xxx()接受要抓取的函式引數的編號 (從 0 開始)作為其引數。PG_RETURN_xxx() 接受實際要返回的數值為自身的引數。
關於PG_GETARG_XXX 定義於 src/include/fmgr.h
/* Macros for fetching arguments of standard types */
#define PG_GETARG_DATUM(n) (fcinfo->arg[n])
#define PG_GETARG_INT32(n) DatumGetInt32(PG_GETARG_DATUM(n))
#define PG_GETARG_UINT32(n) DatumGetUInt32(PG_GETARG_DATUM(n))
#define PG_GETARG_INT16(n) DatumGetInt16(PG_GETARG_DATUM(n))
#define PG_GETARG_UINT16(n) DatumGetUInt16(PG_GETARG_DATUM(n))
#define PG_GETARG_CHAR(n) DatumGetChar(PG_GETARG_DATUM(n))
#define PG_GETARG_BOOL(n) DatumGetBool(PG_GETARG_DATUM(n))
#define PG_GETARG_OID(n) DatumGetObjectId(PG_GETARG_DATUM(n))
#define PG_GETARG_POINTER(n) DatumGetPointer(PG_GETARG_DATUM(n))
#define PG_GETARG_CSTRING(n) DatumGetCString(PG_GETARG_DATUM(n))
#define PG_GETARG_NAME(n) DatumGetName(PG_GETARG_DATUM(n))
/* these macros hide the pass-by-reference-ness of the datatype: */
#define PG_GETARG_FLOAT4(n) DatumGetFloat4(PG_GETARG_DATUM(n))
#define PG_GETARG_FLOAT8(n) DatumGetFloat8(PG_GETARG_DATUM(n))
#define PG_GETARG_INT64(n) DatumGetInt64(PG_GETARG_DATUM(n))
/* use this if you want the raw, possibly-toasted input datum: */
#define PG_GETARG_RAW_VARLENA_P(n) ((struct varlena *) PG_GETARG_POINTER(n))
/* use this if you want the input datum de-toasted: */
#define PG_GETARG_VARLENA_P(n) PG_DETOAST_DATUM(PG_GETARG_DATUM(n))
/* and this if you can handle 1-byte-header datums: */
#define PG_GETARG_VARLENA_PP(n) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(n))
程式碼
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
PG_FUNCTION_INFO_V1(squares_return_int);
Datum squares_return_int(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
PG_RETURN_INT32(arg * arg);
}
編譯
[ligang@yfslcentos71 include]$ gcc -I`pg_config --includedir-server` -c squares.c
[ligang@yfslcentos71 include]$ gcc -shared squares.o -o squares.so
/usr/bin/ld: squares.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
squares.o: could not read symbols: Bad value
[ligang@yfslcentos71 include]$ gcc -I`pg_config --includedir-server` -fPIC -c squares.c
[ligang@yfslcentos71 include]$ gcc -shared squares.o -o squares.so
[ligang@yfslcentos71 include]$
[ligang@yfslcentos71 include]$ cp squares.so `pg_config --libdir`/
SQL宣告函式
ligang=# create function squares(int) returns int as '$libdir/squares.so', 'squares_return_int' LANGUAGE 'c' STRICT;
CREATE FUNCTION
補充
- 函式宣告為"strict"(嚴格),意思是說如果任何輸入值為NULL, 那麼系統應該自動假設一個NULL的結果。這樣處理可以讓我們避免在函式程式碼裡面檢查 NULL輸入。如果不這樣處理,我們就得明確檢查NULL, 比如為每個傳遞引用的引數檢查空指標。對於傳值型別的引數,我們甚至沒有辦法檢查!
參考Postgresql 9.4手冊