From 2adcced2ac7614f3e023f606dbc916c7b8b18a15 Mon Sep 17 00:00:00 2001 From: Mikhail Titov Date: Wed, 30 Aug 2017 18:15:11 -0500 Subject: [PATCH] verionsing using oid This closes #31 --- GNUmakefile | 2 +- appveyor.yml | 2 +- temporal_tables--1.1.1.sql | 9 ++ versioning.c | 192 ++++++++++++++++++++++++++++--------- 4 files changed, 160 insertions(+), 45 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 1e6a99c..3660a3e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -13,7 +13,7 @@ DOCS = README.md REGRESS = install no_system_period invalid_system_period \ no_history_table no_history_system_period invalid_types \ - invalid_system_period_values \ + invalid_system_period_values renaming \ versioning versioning_custom_system_time combinations \ structure uninstall diff --git a/appveyor.yml b/appveyor.yml index 51af790..4223a18 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -68,7 +68,7 @@ test_script: - if %pgversion%==9.4 set psqlopt=--psqldir - pg_regress "%psqlopt%=%pgroot%\bin" install no_system_period invalid_system_period no_history_table - no_history_system_period invalid_types invalid_system_period_values + no_history_system_period invalid_types invalid_system_period_values renaming versioning versioning_custom_system_time structure uninstall - if ERRORLEVEL 1 (set Outcome=Failed) else set Outcome=Passed - perl -e "my @s=stat('regression.out'); print 1000*($s[9]-$s[10]);" > duration diff --git a/temporal_tables--1.1.1.sql b/temporal_tables--1.1.1.sql index 242c702..0f0903e 100644 --- a/temporal_tables--1.1.1.sql +++ b/temporal_tables--1.1.1.sql @@ -12,6 +12,15 @@ REVOKE ALL ON FUNCTION versioning() FROM PUBLIC; COMMENT ON FUNCTION versioning() IS 'System-period temporal table trigger'; +CREATE FUNCTION versioning2() +RETURNS TRIGGER +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +REVOKE ALL ON FUNCTION versioning2() FROM PUBLIC; + +COMMENT ON FUNCTION versioning2() IS 'System-period temporal table trigger, use OID as text for history relation'; + CREATE FUNCTION set_system_time(timestamptz) RETURNS VOID AS 'MODULE_PATHNAME' diff --git a/versioning.c b/versioning.c index 5d03492..6564eb7 100644 --- a/versioning.c +++ b/versioning.c @@ -38,9 +38,11 @@ #endif PGDLLEXPORT Datum versioning(PG_FUNCTION_ARGS); +PGDLLEXPORT Datum versioning2(PG_FUNCTION_ARGS); PGDLLEXPORT Datum set_system_time(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(versioning); +PG_FUNCTION_INFO_V1(versioning2); PG_FUNCTION_INFO_V1(set_system_time); /* Warning if system period was adjusted. */ @@ -102,7 +104,7 @@ static void fill_versioning_hash_entry(VersioningHashEntry *hash_entry, static void insert_history_row(HeapTuple tuple, Relation relation, - const char *history_relation_argument, + Oid history_relation_oid, const char *period_attname); static void deserialize_system_period(HeapTuple tuple, @@ -138,14 +140,14 @@ static Datum versioning_update(TriggerData *trigdata, TypeCacheEntry *typcache, int period_attnum, const char *period_attname, - const char *history_relation_argument, + Oid history_relation_oid, const char *adjust_argument); static Datum versioning_delete(TriggerData *trigdata, TypeCacheEntry *typcache, int period_attnum, const char *period_attname, - const char *history_relation_argument, + Oid history_relation, const char *adjust_argument); static void init_versioning_hash_table(); @@ -196,37 +198,37 @@ versioning(PG_FUNCTION_ARGS) Form_pg_attribute period_attr; TypeCacheEntry *typcache; - trigdata = (TriggerData *) fcinfo->context; + trigdata = (TriggerData *)fcinfo->context; /* Check that the trigger function was called in expected context. */ if (!CALLED_AS_TRIGGER(fcinfo)) ereport(ERROR, - (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), - errmsg("function \"versioning\" was not called by trigger manager"))); + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("function \"versioning\" was not called by trigger manager"))); /* Check proper event. */ if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event) || !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ereport(ERROR, - (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), - errmsg("function \"versioning\" must be fired BEFORE ROW"))); + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("function \"versioning\" must be fired BEFORE ROW"))); if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) && !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event) && !TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) ereport(ERROR, - (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), - errmsg("function \"versioning\" must be fired for INSERT or UPDATE or DELETE"))); + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("function \"versioning\" must be fired for INSERT or UPDATE or DELETE"))); trigger = trigdata->tg_trigger; /* Check number of arguments. */ if (trigger->tgnargs != 3) ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("wrong number of parameters for function \"versioning\""), - errdetail("expected 3 parameters but got %d", - trigger->tgnargs))); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("wrong number of parameters for function \"versioning\""), + errdetail("expected 3 parameters but got %d", + trigger->tgnargs))); args = trigger->tgargs; @@ -241,43 +243,150 @@ versioning(PG_FUNCTION_ARGS) if (period_attnum == SPI_ERROR_NOATTRIBUTE) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - period_attname, - RelationGetRelationName(relation)))); + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + period_attname, + RelationGetRelationName(relation)))); period_attr = tupdesc->attrs[period_attnum - 1]; /* Check that system period attribute is not dropped. */ if (period_attr->attisdropped) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - period_attname, - RelationGetRelationName(relation)))); + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + period_attname, + RelationGetRelationName(relation)))); /* Check that system period attribute is not an array. */ if (period_attr->attndims != 0) ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("system period column \"%s\" of relation \"%s\" is not a range but an array", - period_attname, - RelationGetRelationName(relation)))); + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("system period column \"%s\" of relation \"%s\" is not a range but an array", + period_attname, + RelationGetRelationName(relation)))); /* Locate the typcache entry for the type of system period attribute. */ typcache = get_period_typcache(fcinfo, period_attr, relation); if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) return versioning_insert(trigdata, typcache, period_attnum); - else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) - return versioning_update(trigdata, typcache, - period_attnum, period_attname, - args[1], args[2]); else - /* otherwise this is ON DELETE trigger */ - return versioning_delete(trigdata, typcache, - period_attnum, period_attname, - args[1], args[2]); + { + RangeVar *relrv = makeRangeVarFromNameList(stringToQualifiedNameList(args[1])); + Oid history_relation_oid = RangeVarGetRelid(relrv, AccessShareLock, false);; + + if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) + return versioning_update(trigdata, typcache, + period_attnum, period_attname, + history_relation_oid, args[2]); + else + /* otherwise this is ON DELETE trigger */ + return versioning_delete(trigdata, typcache, + period_attnum, period_attname, + history_relation_oid, args[2]); + } +} + +Datum +versioning2(PG_FUNCTION_ARGS) +{ + TriggerData *trigdata; + Trigger *trigger; + char **args; + Relation relation; + TupleDesc tupdesc; + char *period_attname; + int period_attnum; + Form_pg_attribute period_attr; + TypeCacheEntry *typcache; + + trigdata = (TriggerData *)fcinfo->context; + + /* Check that the trigger function was called in expected context. */ + if (!CALLED_AS_TRIGGER(fcinfo)) + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("function \"" PG_FUNCNAME_MACRO "\" was not called by trigger manager"))); + + /* Check proper event. */ + if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event) || + !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("function \"" PG_FUNCNAME_MACRO "\" must be fired BEFORE ROW"))); + + if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) && + !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event) && + !TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("function \"" PG_FUNCNAME_MACRO "\" must be fired for INSERT or UPDATE or DELETE"))); + + trigger = trigdata->tg_trigger; + + /* Check number of arguments. */ + if (trigger->tgnargs != 3) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("wrong number of parameters for function \"" PG_FUNCNAME_MACRO "\""), + errdetail("expected 3 parameters but got %d", + trigger->tgnargs))); + + args = trigger->tgargs; + + relation = trigdata->tg_relation; + + tupdesc = RelationGetDescr(relation); + + period_attname = args[0]; + + /* Check that system period attribute exists in the versioned relation. */ + period_attnum = SPI_fnumber(tupdesc, period_attname); + + if (period_attnum == SPI_ERROR_NOATTRIBUTE) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + period_attname, + RelationGetRelationName(relation)))); + + period_attr = tupdesc->attrs[period_attnum - 1]; + + /* Check that system period attribute is not dropped. */ + if (period_attr->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + period_attname, + RelationGetRelationName(relation)))); + + /* Check that system period attribute is not an array. */ + if (period_attr->attndims != 0) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("system period column \"%s\" of relation \"%s\" is not a range but an array", + period_attname, + RelationGetRelationName(relation)))); + + /* Locate the typcache entry for the type of system period attribute. */ + typcache = get_period_typcache(fcinfo, period_attr, relation); + + if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) + return versioning_insert(trigdata, typcache, period_attnum); + else + { + Oid oid = strtoul(args[1], NULL, 10); + if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) + return versioning_update(trigdata, typcache, + period_attnum, period_attname, + oid, args[2]); + else + /* otherwise this is ON DELETE trigger */ + return versioning_delete(trigdata, typcache, + period_attnum, period_attname, + oid, args[2]); + } } /* @@ -597,10 +706,9 @@ fill_versioning_hash_entry(VersioningHashEntry *hash_entry, static void insert_history_row(HeapTuple tuple, Relation relation, - const char *history_relation_name, + Oid history_relation_oid, const char *period_attname) { - RangeVar *relrv; Relation history_relation; VersioningHashEntry *hash_entry; bool found; @@ -609,9 +717,7 @@ insert_history_row(HeapTuple tuple, int natts; /* Open the history relation and obtain AccessShareLock on it. */ - relrv = makeRangeVarFromNameList(stringToQualifiedNameList(history_relation_name)); - - history_relation = heap_openrv(relrv, AccessShareLock); + history_relation = heap_open(history_relation_oid, AccessShareLock); /* Look up the cached data for the versioned relation OID. */ hash_entry = lookup_versioning_hash_entry(RelationGetRelid(relation), @@ -933,7 +1039,7 @@ versioning_update(TriggerData *trigdata, TypeCacheEntry *typcache, int period_attnum, const char *period_attname, - const char *history_relation_argument, + Oid history_relation_oid, const char *adjust_argument) { HeapTuple tuple; @@ -966,7 +1072,7 @@ versioning_update(TriggerData *trigdata, history_tuple = modify_tuple(relation, tuple, period_attnum, range); - insert_history_row(history_tuple, relation, history_relation_argument, + insert_history_row(history_tuple, relation, history_relation_oid, period_attname); /* Construct a period for the current row. */ @@ -995,7 +1101,7 @@ versioning_delete(TriggerData *trigdata, TypeCacheEntry *typcache, int period_attnum, const char *period_attname, - const char *history_relation_argument, + Oid history_relation_oid, const char *adjust_argument) { HeapTuple tuple; @@ -1028,7 +1134,7 @@ versioning_delete(TriggerData *trigdata, history_tuple = modify_tuple(relation, tuple, period_attnum, range); - insert_history_row(history_tuple, relation, history_relation_argument, + insert_history_row(history_tuple, relation, history_relation_oid, period_attname); return PointerGetDatum(tuple);