PG版本:PostgreSQL 9.6.5node
因爲版本不一樣代碼會有所差異,本例在PostgreSQL 9.6.5編寫而來,其餘版本未經測試(PostgreSQL 10beta3測試不能直接用)。git
sun@sun:~/PG/PG9.6.5/bin$ ./createdb testdb sun@sun:~/PG/PG9.6.5/bin$ ./createuser testuser sun@sun:~/PG/PG9.6.5/bin$ ./psql -U testuser testdb psql (9.6.5) Type "help" for help. testdb=> create table testtable(a int); CREATE TABLE testdb=> create table testtable2(a int); CREATE TABLE testdb=> \q sun@sun:~/PG/PG9.6.5/bin$ ./psql -U sun testdb psql (9.6.5) Type "help" for help. testdb=# \d List of relations Schema | Name | Type | Owner --------+------------+-------+---------- public | testtable | table | testuser public | testtable2 | table | testuser (2 rows) testdb=# drop table testtable2; DROP TABLE testdb=# \d List of relations Schema | Name | Type | Owner --------+-----------+-------+---------- public | testtable | table | testuser (1 row)
sun@sun:~/PG/PG9.6.5/bin$ ./psql -U sun testdb psql (9.6.5) Type "help" for help. testdb=# \d List of relations Schema | Name | Type | Owner --------+-----------+-------+---------- public | testtable | table | testuser (1 row) testdb=# drop table testtable; ERROR: Please drop with User: testuser STATEMENT: drop table testtable; ERROR: Please drop with User: testuser
在源碼contrib下創建目錄:limitsuperuserdrop,將C代碼文件和Makefile放入其中,編譯安裝,以下:sql
sun@sun:~/Data/postgresql-9.6.5/contrib/limitsuperuserdrop$ ll 總用量 80 -rw------- 1 sun sun 76898 9月 13 21:04 limitsuperuserdrop.c -rw------- 1 sun sun 394 9月 13 19:57 Makefile sun@sun:~/Data/postgresql-9.6.5/contrib/limitsuperuserdrop$ make ; make install
安裝完成後修改配置文件, 修改Data目錄下的postgresql.conf中的shared_preload_librariesshell
shared_preload_libraries = 'limitsuperuserdrop' # (change requires restart)
重啓數據庫。安裝完成。數據庫
本插件使用PostgreSQL的hook機制,實現對PG數據庫的超級用戶的刪除操做的權限限制。bash
經常使用hook:async
Hook | 初始版本 | 說明 |
check_password_hook | 9.0 | 處理用戶密碼時調用的hook,能夠對用戶的密碼進行限制,增長密碼的規範。 |
ClientAuthentication_hook | 9.1 | 處理鏈接時調用的hook,能夠對鏈接進行管理。 |
ExecutorStart_hook | 8.4 | 處理查詢執行開始時調用的hook |
ExecutorRun_hook | 8.4 | 處理查詢執行時調用的hook |
ExecutorFinish_hook | 8.4 | 處理查詢結束時調用的hook |
ExecutorEnd_hook | 8.4 | 處理查詢完成後調用的hook |
ExecutorCheckPerms_hook | 9.1 | 處理訪問權限時調用的hook |
ProcessUtility_hook | 9.0 | 通用hook,能夠處理不少的過程 |
本例使用ProcessUtility_hook函數
當數據庫調用ProcessUtility_hook時,首先會檢測是否爲NULL,不爲NULL的話,則調用由咱們編寫的函數,不然執行標準函數。 也就是說在咱們不使用插件的時候,數據庫老是在調用standard_ProcessUtility。所以咱們的插件代碼,就是在standard_ProcessUtility中執行drop操做的地方判斷要刪除的表是否屬於進行操做的用戶,不是的話就報錯。所以,本插件代碼絕大部分是由src/backend/tcop/utility.c文件複製而來。post
if (ProcessUtility_hook) (*ProcessUtility_hook) (parsetree, queryString, context, params, dest, completionTag); else standard_ProcessUtility(parsetree, queryString, context, params, dest, completionTag);
在此提供兩套代碼,完整版和精簡版,精簡版刪除了大多數的代碼。 點擊下載學習
Makefile代碼:
# contrib/limitsuperuserdrop/Makefile MODULES = limitsuperuserdrop OBJS = limitsuperuserdrop.o $(WIN32RES) PGFILEDESC = "limitsuperuserdrop" ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) else subdir = contrib/limitsuperuserdrop top_builddir = ../.. include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif
如下是精簡版的C文件代碼:
/* * limitsuperuserdrop.c * 說明:限制超級用戶刪除表的操做。 */ #include "postgres.h" #include "miscadmin.h" #include "nodes/parsenodes.h" #include "nodes/pg_list.h" #include "catalog/pg_class.h" #include "executor/executor.h" #include "tcop/utility.h" #include "postgres.h" #include "access/htup_details.h" #include "access/reloptions.h" #include "access/twophase.h" #include "access/xact.h" #include "access/xlog.h" #include "catalog/catalog.h" #include "catalog/namespace.h" #include "catalog/toasting.h" #include "commands/alter.h" #include "commands/async.h" #include "commands/cluster.h" #include "commands/comment.h" #include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/copy.h" #include "commands/createas.h" #include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/discard.h" #include "commands/event_trigger.h" #include "commands/explain.h" #include "commands/extension.h" #include "commands/matview.h" #include "commands/lockcmds.h" #include "commands/policy.h" #include "commands/portalcmds.h" #include "commands/prepare.h" #include "commands/proclang.h" #include "commands/schemacmds.h" #include "commands/seclabel.h" #include "commands/sequence.h" #include "commands/tablecmds.h" #include "commands/tablespace.h" #include "commands/trigger.h" #include "commands/typecmds.h" #include "commands/user.h" #include "commands/vacuum.h" #include "commands/view.h" #include "miscadmin.h" #include "parser/parse_utilcmd.h" #include "postmaster/bgwriter.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteRemove.h" #include "storage/fd.h" #include "tcop/pquery.h" #include "tcop/utility.h" #include "utils/acl.h" #include "utils/guc.h" #include "utils/syscache.h" PG_MODULE_MAGIC; void _PG_init(void); void _PG_fini(void); static ProcessUtility_hook_type My_ProcessUtility_hook_type = NULL; static void ExecDropStmt(DropStmt *stmt, bool isTopLevel); Oid MyGetOwnerId(Oid class_oid); Oid MyGetrelOid(DropStmt *drop); void My_ProcessUtility(Node *parsetree, const char *queryString, ProcessUtilityContext context, ParamListInfo params, DestReceiver *dest, char *completionTag) { bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL); switch (nodeTag(parsetree)) { case T_DropStmt: { DropStmt *stmt = (DropStmt *) parsetree; if( MyGetOwnerId(MyGetrelOid(stmt)) != GetUserId() ) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("Please drop with User: %s ", GetUserNameFromId(MyGetOwnerId(MyGetrelOid(stmt)),false)) ) ); else ExecDropStmt(stmt, isTopLevel); } break; } } static void ExecDropStmt(DropStmt *stmt, bool isTopLevel) { switch (stmt->removeType) { case OBJECT_INDEX: if (stmt->concurrent) PreventTransactionChain(isTopLevel, "DROP INDEX CONCURRENTLY"); /* fall through */ case OBJECT_TABLE: case OBJECT_SEQUENCE: case OBJECT_VIEW: case OBJECT_MATVIEW: case OBJECT_FOREIGN_TABLE: RemoveRelations(stmt); break; default: RemoveObjects(stmt); break; } } Oid MyGetOwnerId(Oid class_oid) { HeapTuple tuple; Oid ownerId; tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(class_oid)); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation with OID %u does not exist", class_oid))); ownerId = ((Form_pg_class) GETSTRUCT(tuple))->relowner; ReleaseSysCache(tuple); // printf("User = %d\n",ownerId); return ownerId; } Oid MyGetrelOid(DropStmt *drop) { ListCell *cell; LOCKMODE lockmode = AccessExclusiveLock; Oid relOid; foreach(cell, drop->objects) { RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell)); relOid = RangeVarGetRelidExtended(rel, lockmode, true, false, NULL, NULL); } // printf("relOid = %d\n",relOid); return relOid; } // Install Hook void _PG_init(void) { My_ProcessUtility_hook_type = ProcessUtility_hook; ProcessUtility_hook = My_ProcessUtility; } // Uninstall Hook void _PG_fini(void) { ProcessUtility_hook = My_ProcessUtility_hook_type; }
精簡後的代碼運行drop操做效果同樣,便於閱讀學習。可是因爲其餘的操做(好比建立表)也會通過咱們編寫的代碼,然而精簡後的對其餘操做並無相應的實現代碼,因此並不會發生真實的操做(以下,create table testtable2(a int),並無成功建立testtable2表,所以使用精簡代碼進行實驗的話,請在安裝插件前先建立幾張表備用)。
sun@sun:~/PG/PG9.6.5/bin$ ./psql -U sun testdb psql (9.6.5) Type "help" for help. testdb=# \d List of relations Schema | Name | Type | Owner --------+-----------+-------+---------- public | testtable | table | testuser (1 row) testdb=# create table testtable2(a int); CREATE TABLE testdb=# \d List of relations Schema | Name | Type | Owner --------+-----------+-------+---------- public | testtable | table | testuser (1 row)