Skip to content

Commit

Permalink
verionsing using oid
Browse files Browse the repository at this point in the history
This closes arkhipov#31
  • Loading branch information
mlt committed Aug 30, 2017
1 parent 1ad58a3 commit 2adcced
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 45 deletions.
2 changes: 1 addition & 1 deletion GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions temporal_tables--1.1.1.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
192 changes: 149 additions & 43 deletions versioning.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand All @@ -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]);
}
}

/*
Expand Down Expand Up @@ -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;
Expand All @@ -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),
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 2adcced

Please sign in to comment.