Skip to content

Commit

Permalink
Implement event triggers to identify index creation on encrypted tables.
Browse files Browse the repository at this point in the history
This commit implements ddl-start and ddl-end event triggers to identify index
creation operations on encrypted tables. Upon creating an index on an encrypted
table, the trigger function updates the global state, which can be accessed by
the storage manager (mgr) to decide if smgr_create needs to do encryption or not.

The start-ddl function analyzes the CREATE TABLE and CREATE INDEX statements
and identifies if the table uses the pg_tde access method. When the table is
created or the one on which the index is being created utilizes the
pg_tde access method, the start-ddl trigger function populates relevant
information about the encrypted table into a global structure.
This structure can be accessed using the GetCurrentTdeCreateEvent() function.
After the execution of the current DDL command finishes, the end-ddl
function clears out this structure.
  • Loading branch information
codeforall committed May 22, 2024
1 parent b447640 commit b849a2c
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ src/common/pg_tde_shmem.o \
src/common/pg_tde_utils.o \
src/smgr/pg_tde_smgr.o \
src/pg_tde_defs.o \
src/pg_tde_event_capture.o \
src/pg_tde.o

override PG_CPPFLAGS += @tde_CPPFLAGS@
Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pg_tde_sources = files(
'src/common/pg_tde_utils.c',
'src/pg_tde_defs.c',
'src/pg_tde.c',
'src/pg_tde_event_capture.c',
)

incdir = include_directories('src/include', '.')
Expand Down
18 changes: 18 additions & 0 deletions pg_tde--1.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,21 @@ COMMENT ON ACCESS METHOD pg_tde IS 'pg_tde table access method';

-- Per database extension initialization
SELECT pg_tde_extension_initialize();

CREATE OR REPLACE FUNCTION pg_tde_ddl_command_start_capture()
RETURNS event_trigger
AS 'MODULE_PATHNAME'
LANGUAGE C;

CREATE OR REPLACE FUNCTION pg_tde_ddl_command_end_capture()
RETURNS event_trigger
AS 'MODULE_PATHNAME'
LANGUAGE C;

CREATE EVENT TRIGGER trigger_create_index
ON ddl_command_start
EXECUTE FUNCTION pg_tde_ddl_command_start_capture();

CREATE EVENT TRIGGER trigger_create_index_2
ON ddl_command_end
EXECUTE FUNCTION pg_tde_ddl_command_end_capture();
31 changes: 31 additions & 0 deletions src/include/pg_tde_event_capture.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*-------------------------------------------------------------------------
*
* pg_tde_event_capture.h
*
*-------------------------------------------------------------------------
*/
#ifndef PG_TDE_EVENT_CAPTURE_H
#define PG_TDE_EVENT_CAPTURE_H

#include "postgres.h"
#include "nodes/parsenodes.h"

typedef enum TdeCreateEventType
{
TDE_UNKNOWN_CREATE_EVENT,
TDE_TABLE_CREATE_EVENT,
TDE_INDEX_CREATE_EVENT
}TdeCreateEventType;

typedef struct TdeCreateEvent
{
TdeCreateEventType eventType; /* DDL statement type */
bool encryptMode; /* true when the table uses encryption */
Oid baseTableOid; /* Oid of table on which index is being created on.
* For create table statement this contains InvalidOid
*/
RangeVar *relation; /* Reference to the parsed relation from create statement */
}TdeCreateEvent;

extern TdeCreateEvent* GetCurrentTdeCreateEvent(void);
#endif /*PG_TDE_EVENT_CAPTURE_H*/
146 changes: 146 additions & 0 deletions src/pg_tde_event_capture.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*-------------------------------------------------------------------------
*
* pg_tde_event_capture.c
* event trigger logic to identify if we are creating the encrypted table or not.
*
* IDENTIFICATION
* contrib/pg_tde/src/pg_tde_event_trigger.c
*
*-------------------------------------------------------------------------
*/

#include "postgres.h"
#include "funcapi.h"
#include "fmgr.h"
#include "utils/rel.h"
#include "utils/builtins.h"
#include "catalog/pg_class.h"
#include "access/table.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/namespace.h"
#include "commands/event_trigger.h"
#include "common/pg_tde_utils.h"
#include "pg_tde_event_capture.h"

/* Global variable that gets set at ddl start and cleard out at ddl end*/
TdeCreateEvent tdeCurrentCreateEvent = {.relation = NULL};


static void reset_current_tde_create_event(void);

PG_FUNCTION_INFO_V1(pg_tde_ddl_command_start_capture);
PG_FUNCTION_INFO_V1(pg_tde_ddl_command_end_capture);

TdeCreateEvent*
GetCurrentTdeCreateEvent(void)
{
return &tdeCurrentCreateEvent;
}

/*
* pg_tde_ddl_command_start_capture is an event trigger function triggered
* at the start of any DDL command execution.
*
* The function specifically focuses on CREATE INDEX and CREATE TABLE statements,
* aiming to determine if the create table or the table on which an index is being created
* utilizes the pg_tde access method for encryption.
* Once it confirms the table's encryption requirement or usage,
* it updates the table information in the tdeCurrentCreateEvent global variable.
* This information can be accessed by SMGR or any other component
* during the execution of this DDL statement.
*/
Datum
pg_tde_ddl_command_start_capture(PG_FUNCTION_ARGS)
{
EventTriggerData *trigdata;
Node *parsetree;

/* Ensure this function is being called as an event trigger */
if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */
ereport(ERROR,
(errmsg("Function can only be fired by event trigger manager")));

trigdata = (EventTriggerData *)fcinfo->context;
parsetree = trigdata->parsetree;

elog(DEBUG2, "EVENT TRIGGER (%s) %s", trigdata->event, nodeToString(parsetree));
reset_current_tde_create_event();

if (IsA(parsetree, IndexStmt))
{
IndexStmt *stmt = (IndexStmt *)parsetree;
Oid relationId= RangeVarGetRelid(stmt->relation, NoLock, true);

tdeCurrentCreateEvent.eventType = TDE_INDEX_CREATE_EVENT;
tdeCurrentCreateEvent.baseTableOid = relationId;
tdeCurrentCreateEvent.relation = stmt->relation;

if (relationId != InvalidOid)
{
LOCKMODE lockmode = AccessShareLock; /* TODO. Verify lock mode? */
Relation rel = table_open(relationId, lockmode);
if (rel->rd_rel->relam == get_tde_table_am_oid())
{
/* We are creating the index on encrypted table */
ereport(NOTICE,
(errmsg("Creating index on **ENCRYPTED** relation:%s with Oid:%d",stmt->relation->relname,relationId)));
/* set the global state */
tdeCurrentCreateEvent.encryptMode = true;
}
else
ereport(DEBUG1,
(errmsg("Creating index on relation:%s with Oid:%d",stmt->relation->relname,relationId)));
table_close(rel, lockmode);
}
else
ereport(DEBUG1,(errmsg("Failed to get relation Oid for relation:%s",stmt->relation->relname)));

}
else if (IsA(parsetree, CreateStmt))
{
CreateStmt *stmt = (CreateStmt *)parsetree;

tdeCurrentCreateEvent.eventType = TDE_TABLE_CREATE_EVENT;
tdeCurrentCreateEvent.relation = stmt->relation;

elog(DEBUG1, "CREATING TABLE %s Using Access Method %s", stmt->relation->relname, stmt->accessMethod);
if (stmt->accessMethod && !strcmp(stmt->accessMethod,"pg_tde"))
{
tdeCurrentCreateEvent.encryptMode = true;
}
}
PG_RETURN_NULL();
}

/*
* trigger function called at the end of DDL statement execution.
* It just clears the tdeCurrentCreateEvent global variable.
*/
Datum
pg_tde_ddl_command_end_capture(PG_FUNCTION_ARGS)
{
/* Ensure this function is being called as an event trigger */
if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */
ereport(ERROR,
(errmsg("Function can only be fired by event trigger manager")));

elog(DEBUG1,"Type:%s EncryptMode:%s, Oid:%d, Relation:%s ",
(tdeCurrentCreateEvent.eventType == TDE_INDEX_CREATE_EVENT) ?"CREATE INDEX":
(tdeCurrentCreateEvent.eventType == TDE_TABLE_CREATE_EVENT) ?"CREATE TABLE":"UNKNOWN",
tdeCurrentCreateEvent.encryptMode ?"true":"false",
tdeCurrentCreateEvent.baseTableOid,
tdeCurrentCreateEvent.relation?tdeCurrentCreateEvent.relation->relname:"UNKNOWN");

/* All we need to do is to clear the event state */
reset_current_tde_create_event();
PG_RETURN_NULL();
}

static void
reset_current_tde_create_event(void)
{
tdeCurrentCreateEvent.encryptMode = false;
tdeCurrentCreateEvent.eventType = TDE_UNKNOWN_CREATE_EVENT;
tdeCurrentCreateEvent.baseTableOid = InvalidOid;
tdeCurrentCreateEvent.relation = NULL;
}

0 comments on commit b849a2c

Please sign in to comment.