PostgreSQL 内核开发练习 - 实现删除表时记录表信息

代码版本: postgresql-10.1

一、直接写入内核代码
1.创建系统表

修改postgres.bki文件,添加创建系统表语句

create pg_subscription_rel 6102 without_oids
 (
 srsubid = oid ,
 srrelid = oid ,
 srsubstate = char ,
 srsublsn = pg_lsn
 )
open pg_subscription_rel
close pg_subscription_rel
# by sunbeau
create sunbeau_droprecord 88889
 (
 tableiod = oid ,
 tablename = name ,
 namespaceid = oid ,
 ownerid = oid
 )
open sunbeau_droprecord
close sunbeau_droprecord
declare toast 2830 2831 on pg_attrdef
declare toast 2832 2833 on pg_constraint
declare toast 2834 2835 on pg_description
2. 修改 tablecmd.c 文件

1)添加头文件

#include "libpq-fe.h" //直接添加头文件编译会找不到,可以使用 /xxx/src/interfaces/libpq/libpq-fe.h 绝对路径

或者修改 src/backend/commands/Makefile
添加

override CPPFLAGS := -I. -I$(libpq_srcdir) $(CPPFLAGS)

2)添加两个函数

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);
	return ownerId;
}

static
void Write_info(Oid tableOid,char * tableName,Oid NameSpaceId,Oid Ownid)
{
	char conninfo[256];
	char SQL[256];

	PGconn *conn = NULL;
	PGresult   *res;
	char * DBname;
	char * Username; 

	DBname = get_database_name(MyDatabaseId);
	// Username = GetUserNameFromId(GetUserId(), true);
	Username = GetUserNameFromId(10, true); // 10 speruser
	sprintf(conninfo, "dbname = %s user = %s", DBname, Username);
	sprintf(SQL, "insert into sunbeau_droprecord values(%d,\'%s\',%d,%d);", tableOid, tableName, NameSpaceId, Ownid);
	conn = PQconnectdb(conninfo);
	if (PQstatus(conn) != CONNECTION_OK)
	{
		printf("Connection to database failed");
	}
	res = PQexec(conn,SQL);
	PQclear(res);
	PQfinish(conn);
}

3)修改RemoveRelations函数

/*
 * RemoveRelations
 *		Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
 *		DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
 */
void
RemoveRelations(DropStmt *drop)
{
	ObjectAddresses *objects;
	char		relkind;
	ListCell   *cell;
	int			flags = 0;
	LOCKMODE	lockmode = AccessExclusiveLock;

	/* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
	if (drop->concurrent)
	{
		flags |= PERFORM_DELETION_CONCURRENTLY;
		lockmode = ShareUpdateExclusiveLock;
		Assert(drop->removeType == OBJECT_INDEX);
		if (list_length(drop->objects) != 1)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
		if (drop->behavior == DROP_CASCADE)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
	}

	/*
	 * First we identify all the relations, then we delete them in a single
	 * performMultipleDeletions() call.  This is to avoid unwanted DROP
	 * RESTRICT errors if one of the relations depends on another.
	 */

	/* Determine required relkind */
	switch (drop->removeType)
	{
		case OBJECT_TABLE:
			relkind = RELKIND_RELATION;
			break;

		case OBJECT_INDEX:
			relkind = RELKIND_INDEX;
			break;

		case OBJECT_SEQUENCE:
			relkind = RELKIND_SEQUENCE;
			break;

		case OBJECT_VIEW:
			relkind = RELKIND_VIEW;
			break;

		case OBJECT_MATVIEW:
			relkind = RELKIND_MATVIEW;
			break;

		case OBJECT_FOREIGN_TABLE:
			relkind = RELKIND_FOREIGN_TABLE;
			break;

		default:
			elog(ERROR, "unrecognized drop object type: %d",
				 (int) drop->removeType);
			relkind = 0;		/* keep compiler quiet */
			break;
	}

	/* Lock and validate each relation; build a list of object addresses */
	objects = new_object_addresses();

	foreach(cell, drop->objects)
	{
		RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
		Oid			relOid;
		ObjectAddress obj;
		struct DropRelationCallbackState state;

		/*
		 * These next few steps are a great deal like relation_openrv, but we
		 * don't bother building a relcache entry since we don't need it.
		 *
		 * Check for shared-cache-inval messages before trying to access the
		 * relation.  This is needed to cover the case where the name
		 * identifies a rel that has been dropped and recreated since the
		 * start of our transaction: if we don't flush the old syscache entry,
		 * then we'll latch onto that entry and suffer an error later.
		 */
		AcceptInvalidationMessages();

		/* Look up the appropriate relation using namespace search. */
		state.relkind = relkind;
		state.heapOid = InvalidOid;
		state.partParentOid = InvalidOid;
		state.concurrent = drop->concurrent;
		relOid = RangeVarGetRelidExtended(rel, lockmode, true,
										  false,
										  RangeVarCallbackForDropRelation,
										  (void *) &state);


		/* Not there? */
		if (!OidIsValid(relOid))
		{
			DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
			continue;
		}

		/* OK, we're ready to delete this one */
		obj.classId = RelationRelationId;
		obj.objectId = relOid;
		obj.objectSubId = 0;

		add_exact_object_address(&obj, objects);
		Write_info(relOid,rel->relname,RangeVarGetCreationNamespace(rel),MyGetOwnerId(relOid)); // add by sunbeau

	}
	
	
	performMultipleDeletions(objects, drop->behavior, flags);

	free_object_addresses(objects);
}

3. 修改 src/backend/Makefile 文件
LIBS += -L$(top_builddir)/src/interfaces/libpq -lpq

PostgreSQL 内核开发练习 - 实现删除表时记录表信息_第1张图片

4.进入src/interfaces/libpq 编译文件

在编译链接src/backend/下的postgres程序时,libpq库文件还没有编译会报错,所以需要先进行本步骤先编译

cd src/interfaces/libpq
make
5.编译安装数据库

返回源代码根目录编译安装

make ; make install
6.测试

PostgreSQL 内核开发练习 - 实现删除表时记录表信息_第2张图片

二、使用插件
1.创建系统表

同一 略

2.添加自定义hook

1)修改 tablecmd.h 文件

typedef void (*TableDropRecord_hook_type) (Oid tableOid,char * tableName,Oid NameSpaceId,Oid Ownid);

extern PGDLLIMPORT TableDropRecord_hook_type TableDropRecord_hook;

2)修改 tablecmd.c 文件

#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
#include "utils/typcache.h"


TableDropRecord_hook_type TableDropRecord_hook = NULL; // add by sunbeau

/*
 * ON COMMIT action list
 */
typedef struct OnCommitItem
{
        Oid                     relid;                  /* relid of relation */
        OnCommitAction oncommit;        /* what to do at end of xact */

【1】添加MyGetOwnerId函数,同一 。
【2】修改RemoveRelations函数,将一中的Write_info调用函数去掉

// Write_info(relOid,rel->relname,RangeVarGetCreationNamespace(rel),MyGetOwnerId(relOid)); // add by sunbeau
if (TableDropRecord_hook) // add by sunbeau
{
    (*TableDropRecord_hook)(relOid,rel->relname,RangeVarGetCreationNamespace(rel),MyGetOwnerId(relOid));
}
3.编写插件

1)postgresql-10.1/contrib 文件目录下创建 droprecord 目录
2)创建Makefile

# contrib/droprecord/Makefile

#MODULES = droprecord
MODULE_big = droprecord
OBJS = droprecord.o $(WIN32RES)
PGFILEDESC = "droprecord"
PG_CPPFLAGS = -I$(libpq_srcdir)
SHLIB_LINK = $(libpq)

ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/droprecord
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

3)创建droprecord.c

#include   
#include   
#include  

#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"
#include "libpq-fe.h"

PG_MODULE_MAGIC;

void _PG_init(void);
void _PG_fini(void);

static TableDropRecord_hook_type prev_TableDropRecord_hook = NULL;

static
void
TableDropRecord(Oid tableOid,char * tableName,Oid NameSpaceId,Oid Ownid)
{
	char conninfo[256];
	char SQL[256];

	PGconn *conn = NULL;
	PGresult   *res;
	char * DBname;
	char * Username; 

	DBname = get_database_name(MyDatabaseId);
	// Username = GetUserNameFromId(GetUserId(), true);
	Username = GetUserNameFromId(10, true); // 10 speruser
	sprintf(conninfo, "dbname = %s user = %s", DBname, Username);
	sprintf(SQL, "insert into sunbeau_droprecord values(%d,\'%s\',%d,%d);", tableOid, tableName, NameSpaceId, Ownid);
	conn = PQconnectdb(conninfo);
	if (PQstatus(conn) != CONNECTION_OK)
	{
		printf("Connection to database failed");
	}
	res = PQexec(conn,SQL);
	PQclear(res);
	PQfinish(conn);
}

// Install Hook
void
_PG_init(void)
{
        prev_TableDropRecord_hook = TableDropRecord_hook;
        TableDropRecord_hook = TableDropRecord;
}


// Uninstall Hook
void
_PG_fini(void)
{
        TableDropRecord_hook = prev_TableDropRecord_hook;
}

4)编译安装插件

[sunbeau@centos7 ~/postgresql-10.1/contrib/droprecord]$ ll
total 8
-rw-rw-r--. 1 sunbiao sunbiao 2887 May 17 10:13 droprecord.c
-rw-rw-r--. 1 sunbiao sunbiao  433 May 17 15:03 Makefile
[sunbeau@centos7 ~/postgresql-10.1/contrib/droprecord]$ make ; make install

初始化数据库修改 postgresql.conf 加载插件

shared_preload_libraries = 'droprecord'         # (change requires restart)

5)启动数据库测试

转载于:https://my.oschina.net/u/3618133/blog/1813896

你可能感兴趣的:(数据库,shell)