天天看點

PostgreSQL 資料庫擴充語言程式設計 之 plpgsql - 1

postgresql , plpgsql , server programming , 存儲過程 , 函數 , udf , create language , plpython , pljava , plr , plperl , pltcl

postgresql是一個開放的資料庫,開發性表現在支援自定義資料類型、索引方法、索引、操作符、聚合、視窗、服務端程式設計語言等等。

是以我們可以看到在postgresql的生态中有很多貼近業務的用法,比如在postgresql中存儲和處理化學分子、存儲和處理圖像、存儲和處理基因資料、存儲和處理文本(包括正則、模糊、全文檢索等技術)、通路外部任意資料源等。

接下來我會花一些時間給大家介紹一下postgresql的服務端程式設計,這也是postgresql開放性的表現之一。

通常我們的用法可能是這樣的,程式設計語言,通過對應的資料庫驅動,連接配接到資料庫,如果要實作一些資料的處理時,需要将資料下拉到用戶端,在用戶端的語言中進行處理。

PostgreSQL 資料庫擴充語言程式設計 之 plpgsql - 1

而postgresql還可以這樣使用,把程式設計語言和資料庫融為一體,你可以把程式放到資料庫裡面去執行,這樣的話資料庫幾乎可以做任何事情(隻要程式能做的事情,資料庫都可以做)。

PostgreSQL 資料庫擴充語言程式設計 之 plpgsql - 1

它的好處是非常明顯的,你可以把任何程式設計語言(隻要是地球語言),作為它的服務端語言,你可以用它在資料庫中做任何事情,以前你可能總覺得資料庫就是select, update, insert, delete,現在你要改變一下你的想法了。

比如你需要處理一大批的資料,原來從資料庫拉取到程式端處理,可能把程式跑挂(比如記憶體溢出),又或者網絡傳輸需要花費太多的時間。

又比如你原來的一次業務邏輯需要與資料庫互動幾十次,總體開銷可能達到了幾秒(線上業務的使用者完全忍受不了),現在,你把業務邏輯和資料結合在一起,不需要互動幾十次,一次就夠了,延遲一下就降低了。

那麼postgresql是如何支援這麼多的語言的呢?

<a href="https://www.postgresql.org/docs/devel/static/xplang.html">https://www.postgresql.org/docs/devel/static/xplang.html</a>

安裝擴充語言很簡單,通常隻需要 create extension language_name 即可,目前系統自帶的語言包括python, perl, tcl, plpgsql.

編譯時帶上對應的參數

這樣的話,軟體安裝好後,就會帶對應的擴充語言,例如

在需要使用該語言的資料庫中建立擴充語言,也可以在模闆庫中建立,這樣的話以後通過模闆庫建立的資料庫就預設帶有這個擴充語言了

擴充語言包括如下幾個部分,其中language_handler為必須的

1. language_handler,必須為擴充語言建立一個對應的language_handler

2. 當這個擴充語言需要使用do的文法(即不建立函數,直接跑代碼)時,則需要實作一個對應的inline_function

3. 如果需要讓資料庫支援這個語言的函數,在建立函數時檢查内部的代碼,則需要實作對應的validator

postgresql 參數 check_function_bodies = on 時,如果這個擴充語言實作了validator,那麼使用這個擴充語言create function時會自動檢查函數體的内容,比如文法是否正确。

4. 建立擴充語言

例子

the optional key word trusted specifies that the language does not grant access to data that the user would not otherwise have.

trusted languages are designed for ordinary database users (those without superuser privilege) and allows them to safely create functions and trigger procedures.

since pl functions are executed inside the database server, the trusted flag should only be given for languages that do not allow access to database server internals or the file system.

the languages pl/pgsql, pl/tcl, and pl/perl are considered trusted;

the languages pl/tclu, pl/perlu, and pl/pythonu are designed to provide unlimited functionality and should not be marked trusted.

trusted 指 這個語言不能通路越權的資料(如資料庫伺服器的檔案、資料庫内部(包括直接通路共享記憶體))

untrusted 指 這個語言沒有任何限制,允許通路任何資料(包括檔案,網絡,共享lib庫等,危害性較大),但是功能更加強大。

編寫擴充語言主要就是實作前面講的幾個内容,包括call handler, inline handler, validator。

範例可以參考postgresql内置的幾個語言的代碼,比如python

postgresql-9.6.1/src/pl/plpython

<a href="https://www.postgresql.org/docs/devel/static/sql-createtransform.html">https://www.postgresql.org/docs/devel/static/sql-createtransform.html</a>

因為資料庫有自己的類型定義和表達方法,而擴充語言有自己的類型支援。

比如postgresql支援hstore資料類型,而python沒有這個類型,那麼預設情況下python就隻能使用text來表達資料庫的hstore.

postgresql允許使用者自定義資料類型轉換方法,将資料庫支援的類型與擴充語言支援的類型橋梁建立起來。

文法如下

varchar_transform(internal) : 将資料庫類型轉換為sql類型。

int4recv(internal) : 将sql語言類型轉換為資料庫類型。

假設我們使用sql語言建立了一個資料庫函數

當函數參數類型為int時,那麼調用varchar_transform,将使用者傳入的資料庫int值 轉換為 sql函數支援的類型,在sql函數内部處理轉換後的值。

當函數傳回類型為int時,那麼在sql函數内部,傳回結果前,需要調用int4recv,将sql函數内的return result值 轉換為 資料庫int類型輸出。

<a href="https://www.postgresql.org/docs/9.6/static/xfunc-c.html#xfunc-c-basetype">https://www.postgresql.org/docs/9.6/static/xfunc-c.html#xfunc-c-basetype</a>

c擴充語言的資料庫類型與c語言類型映射關系如下

sql type

c type

defined in

abstime

absolutetime

utils/nabstime.h

bigint (int8)

int64

postgres.h

boolean

bool

postgres.h (maybe compiler built-in)

box

box*

utils/geo_decls.h

bytea

bytea*

"char"

char

(compiler built-in)

character

bpchar*

cid

commandid

date

dateadt

utils/date.h

smallint (int2)

int16

int2vector

int2vector*

integer (int4)

int32

real (float4)

float4*

double precision (float8)

float8*

interval

interval*

datatype/timestamp.h

lseg

lseg*

name

oid

oidvector

oidvector*

path

path*

point

point*

regproc

reltime

relativetime

text

text*

tid

itempointer

storage/itemptr.h

time

timeadt

time with time zone

timetzadt

timestamp

timestamp*

tinterval

timeinterval

varchar

varchar*

xid

transactionid

下面是一個transform的例子

varchar_transform@src/backend/utils/adt/varchar.c

int4recv@src/backend/utils/adt/int.c

函數是資料庫的擴充基礎,比如你要在postgresql上擴充新的資料類型,以及新的資料庫索引方法、操作符等,必須先建立function。

<a href="https://www.postgresql.org/docs/devel/static/sql-createfunction.html">https://www.postgresql.org/docs/devel/static/sql-createfunction.html</a>

當我們使用create extension plxx;建立好了資料庫擴充語言後,就可以使用對應的擴充語言建立對應的資料庫函數了。

目前postgresql内置的包括c, plpgsql, sql, plpython, plperl, pltcl。

固定的建立函數格式如下

建立函數的完整文法如下

視窗函數可用于支援視窗查詢,視窗查詢的例子可以參考

<a href="https://github.com/digoal/blog/blob/master/201612/20161201_01.md">《用postgresql找回618秒逝去的青春 - 遞歸收斂優化》</a>

src/backend/utils/adt/windowfuncs.c

聚合在資料分析,統計的應用場景中非常場景,比如count, avg, sum, ...。

聚合函數開發的例子

<a href="https://github.com/digoal/blog/blob/master/201212/20121217_01.md">《performance tuning about multi-rows query aggregated to single-row query》</a>

<a href="https://github.com/digoal/blog/blob/master/201212/20121218_02.md">《postgresql aggregate function customize》</a>

<a href="https://github.com/digoal/blog/blob/master/201608/20160825_02.md">《hll插件在greenplum中的使用 以及 分布式聚合函數優化思路》</a>

<a href="https://github.com/digoal/blog/blob/master/201305/20130502_01.md">《postgres-xc customized aggregate introduction》</a>

postgresql代碼中的自定義聚合例子

其中sfunc int4_avg_accum和finalfunc int8_avg函數如下

int4_avg_accum用于累加(包括計數和sum),int8_avg則在最後一步時做了一次除法得到avg

src/backend/utils/adt/numeric.c

使用擴充語言,除了可以用來編寫普通函數,聚合、視窗函數,還可以用于編寫觸發器函數,事件觸發器函數。

下面是使用plpgsql編寫觸發器函數的例子

<a href="https://github.com/digoal/blog/blob/master/201303/20130311_01.md">《postgresql 觸發器 用法詳解 1》</a>

<a href="https://github.com/digoal/blog/blob/master/201303/20130311_02.md">《postgresql 觸發器 用法詳解 2》</a>

下面是使用plpgsql編寫事件觸發器的例子

<a href="https://github.com/digoal/blog/blob/master/201303/20130313_01.md">《postgresql 事件觸發器 - postgresql 9.3 event trigger》</a>

下一篇文章開始介紹plpgsql的使用