我目前瞭解的PG插件大約有兩種,一種是利用hook,另一種是建立C函數,然後在數據庫中進行關聯。PG本身就是最好的老師,大家有興趣可以看一下contrib目錄下的插件。
下面將對兩種方式進行介紹:
1、利用hook建立插件,hook是PG中可以對PG運行機制進行修改的一種方式,大家可以看一下我之前對PG hook的介紹:
a.在contrib目錄下建立brother目錄,建立brother.c文件和Makefile文件。具體內容見下:
brother.c(在用戶brother登錄驗證成功後,顯示信息Welcome to the world of PostgreSQL!):
/*-------------------------------------------------------------------------
*
* brother.c
* welcome to the world of PostgreSQL
*
* contrib/brother/brother.c
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "libpq/auth.h"
#include "miscadmin.h"
#include "utils/guc.h"
PG_MODULE_MAGIC;
void _PG_init(void);
void _PG_fini(void);
static ClientAuthentication_hook_type pre_ClientAuthentication_hook = NULL;
static
void welcome_to_pg(Port *port, int status)
{
if(status == STATUS_OK && (strcmp(port->user_name, "brother") == 0))
printf("Welcome to the world of PostgreSQL!\n");
}
void
_PG_init(void)
{
pre_ClientAuthentication_hook = ClientAuthentication_hook;
ClientAuthentication_hook = welcome_to_pg;
}
void
_PG_fini(void)
{
ClientAuthentication_hook = pre_ClientAuthentication_hook;
}
Makefile文件:
# contrib/brother/Makefile
MODULE_big = brother
OBJS = brother.o
# uncomment the following two lines to enable cracklib support
# PG_CPPFLAGS = -DUSE_CRACKLIB '-DCRACKLIB_DICTPATH="/usr/lib/cracklib_dict"'
# SHLIB_LINK = -lcrack
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/brother
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
b.編譯並安裝
[postgres@localhost brother]$ make
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -g -O2 -fpic -I. -I. -I../../src/include -D_GNU_SOURCE -c -o brother.o brother.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -g -O2 -fpic -shared -o brother.so brother.o -L../../src/port -L../../src/common -Wl,--as-needed -Wl,-rpath,'/opt/hg3.0/lib',--enable-new-dtags
[postgres@localhost brother]$ make install
/bin/mkdir -p '/opt/hg3.0/lib/postgresql'
/usr/bin/install -c -m 755 brother.so '/opt/hg3.0/lib/postgresql/brother.so'
c.文件安裝位置
[postgres@localhost postgresql]$ pwd
/opt/hg3.0/lib/postgresql
[postgres@localhost postgresql]$ ll brother.so
-rwxr-xr-x. 1 postgres postgres 21523 Dec 23 06:27 brother.so
d.在postgresql.conf中配置
shared_preload_libraries = 'brother'
e.運行,查看效果
[postgres@localhost bin]$ ./pg_ctl -D ../data start
server starting
[postgres@localhost bin]$ LOG: database system was shut down at 2015-12-23 06:32:38 PST
LOG: MultiXact member wraparound protections are now enabled
LOG: database system is ready to accept connections
LOG: autovacuum launcher started
[postgres@localhost bin]$ ./psql
psql (9.4.5)
Type "help" for help.
postgres=# create user brother;
CREATE ROLE
[postgres@localhost bin]$ ./psql postgres brother
Welcome to the world of PostgreSQL!
psql (9.4.5)
Type "help" for help.
postgres=>
2、建立C函數,並建立關聯
a.在contrib下建立目錄userid,建立C文件userid.c,建立Makefile文件,建立兩個SQL文件,userid--1.0.sql,userid--unpackaged--1.0.sql,以及一個control文件,userid.control。
(1)C文件和Makefile就不介紹了,內容如下:
userid.c:
/*-------------------------------------------------------------------------
*
* userid.c
* display current session user oid
*
* contrib/userid/userid.c
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "libpq/auth.h"
#include "utils/guc.h"
#include "miscadmin.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(get_current_user_id);
Datum
get_current_user_id(PG_FUNCTION_ARGS)
{
PG_RETURN_OID(GetSessionUserId());
}
Makefile:
# contrib/userid/Makefile
MODULE_big = userid
OBJS = userid.o
EXTENSION = userid
DATA = userid--1.0.sql userid--unpackaged--1.0.sql
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/userid
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
(2)userid--1.0.sql是在create extension時加載的SQL文件,會執行SQL文件中的sql,內容如下:
/* contrib/userid/userid--1.0.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION userid" to load this file. \quit
-- Register the function.
CREATE SCHEMA brother;
CREATE FUNCTION get_current_userid()
RETURNS OID
AS 'MODULE_PATHNAME', 'get_current_user_id'
LANGUAGE C;
(3)
userid--unpackaged--1.0.sql是CREATE EXTENSION userid FROM unpackaged時調用的SQL文件,這個文件是在更新9.1之前版本的插件函數時使用的,後面會介紹用法。/* contrib/userid/userid--unpackaged--1.0.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION userid FROM unpackaged" to load this file. \quit
ALTER EXTENSION userid ADD schema brother;
ALTER EXTENSION userid ADD function get_current_userid();
(4)control文件是在create extension加載的文件,根據這個文件,PG去尋找對應的sql文件,內容如下:
# userid extension
comment = 'display current session user oid'
default_version = '1.0'
module_pathname = '$libdir/userid'
relocatable = true
b.編譯安裝
[postgres@localhost userid]$ make
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -g -O2 -fpic -I. -I. -I../../src/include -D_GNU_SOURCE -c -o userid.o userid.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -g -O2 -fpic -shared -o userid.so userid.o -L../../src/port -L../../src/common -Wl,--as-needed -Wl,-rpath,'/opt/hg3.0/lib',--enable-new-dtags
[postgres@localhost userid]$ make install
/bin/mkdir -p '/opt/hg3.0/lib/postgresql'
/bin/mkdir -p '/opt/hg3.0/share/postgresql/extension'
/bin/mkdir -p '/opt/hg3.0/share/postgresql/extension'
/usr/bin/install -c -m 755 userid.so '/opt/hg3.0/lib/postgresql/userid.so'
/usr/bin/install -c -m 644 userid.control '/opt/hg3.0/share/postgresql/extension/'
/usr/bin/install -c -m 644 userid--1.0.sql userid--unpackaged--1.0.sql '/opt/hg3.0/share/postgresql/extension/'
c.文件安裝位置
[postgres@localhost postgresql]$ pwd
/opt/hg3.0/lib/postgresql
[postgres@localhost postgresql]$ ll userid.so
-rwxr-xr-x. 1 postgres postgres 18665 Dec 23 06:48 userid.so
[postgres@localhost extension]$ pwd
/opt/hg3.0/share/postgresql/extension
[postgres@localhost extension]$ ll userid*
-rw-r--r--. 1 postgres postgres 329 Dec 23 06:48 userid--1.0.sql
-rw-r--r--. 1 postgres postgres 142 Dec 23 06:48 userid.control
-rw-r--r--. 1 postgres postgres 304 Dec 23 06:48 userid--unpackaged--1.0.sql
d.配置
[postgres@localhost bin]$ ./psql
psql (9.4.5)
Type "help" for help.
postgres=# create extension userid;
CREATE EXTENSION
postgres=# select * from pg_extension ;
extname | extowner | extnamespace | extrelocatable | extversion | ex
tconfig | extcondition
--------------------+----------+--------------+----------------+------------+---
--------+--------------
plpgsql | 10 | 11 | f | 1.0 |
|
pgfincore | 10 | 2200 | t | 1.1.1 |
|
pg_stat_statements | 10 | 2200 | t | 1.2 |
|
pg_freespacemap | 10 | 2200 | t | 1.0 |
|
pgstattuple | 10 | 2200 | t | 1.2 |
|
userid | 10 | 2200 | t | 1.0 |
|
(6 rows)
e.運行,查看效果
postgres=# select SESSION_USER;
session_user
--------------
postgres
(1 row)
postgres=# select oid from pg_authid where rolname = 'postgres';
oid
-----
10
(1 row)
postgres=# select get_current_userid();
get_current_userid
--------------------
10
(1 row)
postgres=# \dn
List of schemas
Name | Owner
---------+----------
brother | postgres
public | postgres
(2 rows)
postgres=#
f.使用unpackaged.sql,這裏是更新9.1之前的插件使用的,現在來模擬一下:
postgres=# create schema brother;
CREATE SCHEMA
postgres=# CREATE FUNCTION get_current_userid()
RETURNS OID
AS '$libdir/userid', 'get_current_user_id'
LANGUAGE C;
CREATE FUNCTION
postgres=# create extension userid with schema public from unpackaged;
CREATE EXTENSION
因爲在9.1之前都是利用SQL安裝的插件,沒有和extension進行關聯,so,在這裏可以關聯一下。那什麼是關聯呢?
g.extension關聯的意思,首先大家需要知道PG的表,pg_depend。這張表是當我們進行drop時,添加參數CASCADE會調用查詢的一張表,這張表主要存儲對象oid依賴的對象oid等等。下面來看一下:
postgres=# select oid from pg_proc where proname = 'get_current_userid';
oid
-------
24715
(1 row)
postgres=# select oid from pg_namespace where nspname = 'brother';
oid
-------
24714
(1 row)
postgres=# select oid from pg_extension where extname = 'userid';
oid
-------
24716
(1 row)
postgres=# select * from pg_depend where refobjid = 24716;
classid | objid | objsubid | refclassid | refobjid | refobjsubid | deptype
---------+-------+----------+------------+----------+-------------+---------
2615 | 24714 | 0 | 3079 | 24716 | 0 | e
1255 | 24715 | 0 | 3079 | 24716 | 0 | e
(2 rows)
這個地方就是當你drop extension時,同時會刪除你插件中SQL文件建立的對象的原因。那當create extension時,又是什麼地方去寫pg_depend信息的呢?
h.當你進行create extension時,下面這段代碼就是自動建立關聯的代碼:
/*
* If we are executing a CREATE EXTENSION operation, mark the given object
* as being a member of the extension. Otherwise, do nothing.
*
* This must be called during creation of any user-definable object type
* that could be a member of an extension.
*
* If isReplace is true, the object already existed (or might have already
* existed), so we must check for a pre-existing extension membership entry.
* Passing false is a guarantee that the object is newly created, and so
* could not already be a member of any extension.
*/
void
recordDependencyOnCurrentExtension(const ObjectAddress *object,
bool isReplace)
{
/* Only whole objects can be extension members */
Assert(object->objectSubId == 0);
if (creating_extension)
{
ObjectAddress extension;
/* Only need to check for existing membership if isReplace */
if (isReplace)
{
Oid oldext;
oldext = getExtensionOfObject(object->classId, object->objectId);
if (OidIsValid(oldext))
{
/* If already a member of this extension, nothing to do */
if (oldext == CurrentExtensionObject)
return;
/* Already a member of some other extension, so reject */
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("%s is already a member of extension \"%s\"",
getObjectDescription(object),
get_extension_name(oldext))));
}
}
/* OK, record it as a member of CurrentExtensionObject */
extension.classId = ExtensionRelationId;
extension.objectId = CurrentExtensionObject;
extension.objectSubId = 0;
recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
}
}
至此,就是我在學習PG源碼中學習到的知識。這裏介紹的還比較簡單,請大家多多關注,我後續還會繼續整理的。