PostgreSQL Source Code  git master
timetravel.c File Reference
#include "postgres.h"
#include <ctype.h>
#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/nabstime.h"
#include "utils/rel.h"
Include dependency graph for timetravel.c:

Go to the source code of this file.

Data Structures

struct  EPlan
 
struct  _TTOffList
 

Macros

#define MaxAttrNum   5
 
#define MinAttrNum   2
 
#define a_time_on   0
 
#define a_time_off   1
 
#define a_ins_user   2
 
#define a_upd_user   3
 
#define a_del_user   4
 

Typedefs

typedef struct _TTOffList TTOffList
 

Functions

static int findTTStatus (char *name)
 
static EPlanfind_plan (char *ident, EPlan **eplan, int *nplans)
 
 PG_FUNCTION_INFO_V1 (timetravel)
 
Datum timetravel (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (set_timetravel)
 
Datum set_timetravel (PG_FUNCTION_ARGS)
 
 PG_FUNCTION_INFO_V1 (get_timetravel)
 
Datum get_timetravel (PG_FUNCTION_ARGS)
 

Variables

 PG_MODULE_MAGIC
 
static EPlanPlans = NULL
 
static int nPlans = 0
 
static TTOffListTTOff = NULL
 

Macro Definition Documentation

◆ a_del_user

#define a_del_user   4

Definition at line 77 of file timetravel.c.

Referenced by timetravel().

◆ a_ins_user

#define a_ins_user   2

Definition at line 75 of file timetravel.c.

Referenced by timetravel().

◆ a_time_off

#define a_time_off   1

Definition at line 74 of file timetravel.c.

Referenced by timetravel().

◆ a_time_on

#define a_time_on   0

Definition at line 73 of file timetravel.c.

Referenced by timetravel().

◆ a_upd_user

#define a_upd_user   3

Definition at line 76 of file timetravel.c.

Referenced by timetravel().

◆ MaxAttrNum

#define MaxAttrNum   5

Definition at line 70 of file timetravel.c.

Referenced by timetravel().

◆ MinAttrNum

#define MinAttrNum   2

Definition at line 71 of file timetravel.c.

Referenced by timetravel().

Typedef Documentation

◆ TTOffList

Function Documentation

◆ find_plan()

static EPlan * find_plan ( char *  ident,
EPlan **  eplan,
int *  nplans 
)
static

Definition at line 525 of file timetravel.c.

References i, EPlan::ident, malloc, realloc, and EPlan::splan.

Referenced by timetravel().

526 {
527  EPlan *newp;
528  int i;
529 
530  if (*nplans > 0)
531  {
532  for (i = 0; i < *nplans; i++)
533  {
534  if (strcmp((*eplan)[i].ident, ident) == 0)
535  break;
536  }
537  if (i != *nplans)
538  return (*eplan + i);
539  *eplan = (EPlan *) realloc(*eplan, (i + 1) * sizeof(EPlan));
540  newp = *eplan + i;
541  }
542  else
543  {
544  newp = *eplan = (EPlan *) malloc(sizeof(EPlan));
545  (*nplans) = i = 0;
546  }
547 
548  newp->ident = strdup(ident);
549  newp->splan = NULL;
550  (*nplans)++;
551 
552  return newp;
553 }
#define malloc(a)
Definition: header.h:50
SPIPlanPtr * splan
Definition: refint.c:23
char * ident
Definition: refint.c:21
Definition: refint.c:19
#define realloc(a, b)
Definition: header.h:60
int i

◆ findTTStatus()

static int findTTStatus ( char *  name)
static

Definition at line 506 of file timetravel.c.

References _TTOffList::name, _TTOffList::next, and pg_strcasecmp().

Referenced by timetravel().

507 {
508  TTOffList *pp;
509 
510  for (pp = TTOff; pp; pp = pp->next)
511  if (pg_strcasecmp(name, pp->name) == 0)
512  return 0;
513  return 1;
514 }
static TTOffList * TTOff
Definition: timetravel.c:42
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
struct _TTOffList * next
Definition: timetravel.c:38
const char * name
Definition: encode.c:521
char name[FLEXIBLE_ARRAY_MEMBER]
Definition: timetravel.c:39

◆ get_timetravel()

Datum get_timetravel ( PG_FUNCTION_ARGS  )

Definition at line 492 of file timetravel.c.

References _TTOffList::name, namestrcmp(), _TTOffList::next, PG_GETARG_NAME, and PG_RETURN_INT32.

Referenced by set_timetravel().

493 {
494  Name relname = PG_GETARG_NAME(0);
495  TTOffList *pp;
496 
497  for (pp = TTOff; pp; pp = pp->next)
498  {
499  if (namestrcmp(relname, pp->name) == 0)
500  PG_RETURN_INT32(0);
501  }
502  PG_RETURN_INT32(1);
503 }
static TTOffList * TTOff
Definition: timetravel.c:42
#define PG_RETURN_INT32(x)
Definition: fmgr.h:314
int namestrcmp(Name name, const char *str)
Definition: name.c:247
Definition: c.h:541
struct _TTOffList * next
Definition: timetravel.c:38
char name[FLEXIBLE_ARRAY_MEMBER]
Definition: timetravel.c:39
#define PG_GETARG_NAME(n)
Definition: fmgr.h:243

◆ PG_FUNCTION_INFO_V1() [1/3]

PG_FUNCTION_INFO_V1 ( timetravel  )

Referenced by set_timetravel(), and timetravel().

◆ PG_FUNCTION_INFO_V1() [2/3]

PG_FUNCTION_INFO_V1 ( set_timetravel  )

◆ PG_FUNCTION_INFO_V1() [3/3]

PG_FUNCTION_INFO_V1 ( get_timetravel  )

◆ set_timetravel()

Datum set_timetravel ( PG_FUNCTION_ARGS  )

Definition at line 424 of file timetravel.c.

References DatumGetCString, DirectFunctionCall1, free, get_timetravel(), malloc, _TTOffList::name, NameGetDatum, nameout(), namestrcmp(), _TTOffList::next, offsetof, pfree(), PG_FUNCTION_INFO_V1(), PG_GETARG_INT32, PG_GETARG_NAME, and PG_RETURN_INT32.

Referenced by timetravel().

425 {
426  Name relname = PG_GETARG_NAME(0);
427  int32 on = PG_GETARG_INT32(1);
428  char *rname;
429  char *d;
430  char *s;
431  int32 ret;
432  TTOffList *prev,
433  *pp;
434 
435  prev = NULL;
436  for (pp = TTOff; pp; prev = pp, pp = pp->next)
437  {
438  if (namestrcmp(relname, pp->name) == 0)
439  break;
440  }
441  if (pp)
442  {
443  /* OFF currently */
444  if (on != 0)
445  {
446  /* turn ON */
447  if (prev)
448  prev->next = pp->next;
449  else
450  TTOff = pp->next;
451  free(pp);
452  }
453  ret = 0;
454  }
455  else
456  {
457  /* ON currently */
458  if (on == 0)
459  {
460  /* turn OFF */
462  if (s)
463  {
464  pp = malloc(offsetof(TTOffList, name) + strlen(rname) + 1);
465  if (pp)
466  {
467  pp->next = NULL;
468  d = pp->name;
469  while (*s)
470  *d++ = tolower((unsigned char) *s++);
471  *d = '\0';
472  if (prev)
473  prev->next = pp;
474  else
475  TTOff = pp;
476  }
477  pfree(rname);
478  }
479  }
480  ret = 1;
481  }
482  PG_RETURN_INT32(ret);
483 }
#define PG_GETARG_INT32(n)
Definition: fmgr.h:234
#define NameGetDatum(X)
Definition: postgres.h:601
static TTOffList * TTOff
Definition: timetravel.c:42
#define PG_RETURN_INT32(x)
Definition: fmgr.h:314
int namestrcmp(Name name, const char *str)
Definition: name.c:247
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:585
signed int int32
Definition: c.h:284
#define malloc(a)
Definition: header.h:50
void pfree(void *pointer)
Definition: mcxt.c:949
#define DatumGetCString(X)
Definition: postgres.h:572
Definition: c.h:541
struct _TTOffList * next
Definition: timetravel.c:38
#define free(a)
Definition: header.h:65
const char * name
Definition: encode.c:521
Datum nameout(PG_FUNCTION_ARGS)
Definition: name.c:69
#define offsetof(type, field)
Definition: c.h:593
#define PG_GETARG_NAME(n)
Definition: fmgr.h:243

◆ timetravel()

Datum timetravel ( PG_FUNCTION_ARGS  )

Definition at line 82 of file timetravel.c.

References a_del_user, a_ins_user, a_time_off, a_time_on, a_upd_user, ABSTIMEOID, generate_unaccent_rules::args, CALLED_AS_TRIGGER, CStringGetTextDatum, DatumGetInt32, DEBUG4, elog, ERROR, find_plan(), findTTStatus(), GetCurrentAbsoluteTime(), GetUserId(), GetUserNameFromId(), heap_modify_tuple_by_cols(), i, MaxAttrNum, MinAttrNum, NAMEDATALEN, NOEND_ABSTIME, nPlans, palloc(), pfree(), PG_FUNCTION_INFO_V1(), PointerGetDatum, set_timetravel(), snprintf(), SPI_connect(), SPI_execp(), SPI_finish(), SPI_fnumber(), SPI_getbinval(), SPI_getrelname(), SPI_gettypeid(), SPI_keepplan(), SPI_modifytuple(), SPI_prepare(), SPI_result, SPI_result_code_string(), TEXTOID, TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgnargs, TRIGGER_FIRED_BEFORE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, and TupleDescAttr.

83 {
84  TriggerData *trigdata = (TriggerData *) fcinfo->context;
85  Trigger *trigger; /* to get trigger name */
86  int argc;
87  char **args; /* arguments */
88  int attnum[MaxAttrNum]; /* fnumbers of start/stop columns */
89  Datum oldtimeon,
90  oldtimeoff;
91  Datum newtimeon,
92  newtimeoff,
93  newuser,
94  nulltext;
95  Datum *cvals; /* column values */
96  char *cnulls; /* column nulls */
97  char *relname; /* triggered relation name */
98  Relation rel; /* triggered relation */
99  HeapTuple trigtuple;
100  HeapTuple newtuple = NULL;
101  HeapTuple rettuple;
102  TupleDesc tupdesc; /* tuple description */
103  int natts; /* # of attributes */
104  EPlan *plan; /* prepared plan */
105  char ident[2 * NAMEDATALEN];
106  bool isnull; /* to know is some column NULL or not */
107  bool isinsert = false;
108  int ret;
109  int i;
110 
111  /*
112  * Some checks first...
113  */
114 
115  /* Called by trigger manager ? */
116  if (!CALLED_AS_TRIGGER(fcinfo))
117  elog(ERROR, "timetravel: not fired by trigger manager");
118 
119  /* Should be called for ROW trigger */
120  if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
121  elog(ERROR, "timetravel: must be fired for row");
122 
123  /* Should be called BEFORE */
124  if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
125  elog(ERROR, "timetravel: must be fired before event");
126 
127  /* INSERT ? */
128  if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
129  isinsert = true;
130 
131  if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
132  newtuple = trigdata->tg_newtuple;
133 
134  trigtuple = trigdata->tg_trigtuple;
135 
136  rel = trigdata->tg_relation;
137  relname = SPI_getrelname(rel);
138 
139  /* check if TT is OFF for this relation */
140  if (0 == findTTStatus(relname))
141  {
142  /* OFF - nothing to do */
143  pfree(relname);
144  return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple);
145  }
146 
147  trigger = trigdata->tg_trigger;
148 
149  argc = trigger->tgnargs;
150  if (argc != MinAttrNum && argc != MaxAttrNum)
151  elog(ERROR, "timetravel (%s): invalid (!= %d or %d) number of arguments %d",
152  relname, MinAttrNum, MaxAttrNum, trigger->tgnargs);
153 
154  args = trigger->tgargs;
155  tupdesc = rel->rd_att;
156  natts = tupdesc->natts;
157 
158  for (i = 0; i < MinAttrNum; i++)
159  {
160  attnum[i] = SPI_fnumber(tupdesc, args[i]);
161  if (attnum[i] <= 0)
162  elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]);
163  if (SPI_gettypeid(tupdesc, attnum[i]) != ABSTIMEOID)
164  elog(ERROR, "timetravel (%s): attribute %s must be of abstime type",
165  relname, args[i]);
166  }
167  for (; i < argc; i++)
168  {
169  attnum[i] = SPI_fnumber(tupdesc, args[i]);
170  if (attnum[i] <= 0)
171  elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]);
172  if (SPI_gettypeid(tupdesc, attnum[i]) != TEXTOID)
173  elog(ERROR, "timetravel (%s): attribute %s must be of text type",
174  relname, args[i]);
175  }
176 
177  /* create fields containing name */
178  newuser = CStringGetTextDatum(GetUserNameFromId(GetUserId(), false));
179 
180  nulltext = (Datum) NULL;
181 
182  if (isinsert)
183  { /* INSERT */
184  int chnattrs = 0;
185  int chattrs[MaxAttrNum];
186  Datum newvals[MaxAttrNum];
187  bool newnulls[MaxAttrNum];
188 
189  oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull);
190  if (isnull)
191  {
192  newvals[chnattrs] = GetCurrentAbsoluteTime();
193  newnulls[chnattrs] = false;
194  chattrs[chnattrs] = attnum[a_time_on];
195  chnattrs++;
196  }
197 
198  oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull);
199  if (isnull)
200  {
201  if ((chnattrs == 0 && DatumGetInt32(oldtimeon) >= NOEND_ABSTIME) ||
202  (chnattrs > 0 && DatumGetInt32(newvals[a_time_on]) >= NOEND_ABSTIME))
203  elog(ERROR, "timetravel (%s): %s is infinity", relname, args[a_time_on]);
204  newvals[chnattrs] = NOEND_ABSTIME;
205  newnulls[chnattrs] = false;
206  chattrs[chnattrs] = attnum[a_time_off];
207  chnattrs++;
208  }
209  else
210  {
211  if ((chnattrs == 0 && DatumGetInt32(oldtimeon) > DatumGetInt32(oldtimeoff)) ||
212  (chnattrs > 0 && DatumGetInt32(newvals[a_time_on]) > DatumGetInt32(oldtimeoff)))
213  elog(ERROR, "timetravel (%s): %s gt %s", relname, args[a_time_on], args[a_time_off]);
214  }
215 
216  pfree(relname);
217  if (chnattrs <= 0)
218  return PointerGetDatum(trigtuple);
219 
220  if (argc == MaxAttrNum)
221  {
222  /* clear update_user value */
223  newvals[chnattrs] = nulltext;
224  newnulls[chnattrs] = true;
225  chattrs[chnattrs] = attnum[a_upd_user];
226  chnattrs++;
227  /* clear delete_user value */
228  newvals[chnattrs] = nulltext;
229  newnulls[chnattrs] = true;
230  chattrs[chnattrs] = attnum[a_del_user];
231  chnattrs++;
232  /* set insert_user value */
233  newvals[chnattrs] = newuser;
234  newnulls[chnattrs] = false;
235  chattrs[chnattrs] = attnum[a_ins_user];
236  chnattrs++;
237  }
238  rettuple = heap_modify_tuple_by_cols(trigtuple, tupdesc,
239  chnattrs, chattrs,
240  newvals, newnulls);
241  return PointerGetDatum(rettuple);
242  /* end of INSERT */
243  }
244 
245  /* UPDATE/DELETE: */
246  oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull);
247  if (isnull)
248  elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]);
249 
250  oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull);
251  if (isnull)
252  elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]);
253 
254  /*
255  * If DELETE/UPDATE of tuple with stop_date neq INFINITY then say upper
256  * Executor to skip operation for this tuple
257  */
258  if (newtuple != NULL)
259  { /* UPDATE */
260  newtimeon = SPI_getbinval(newtuple, tupdesc, attnum[a_time_on], &isnull);
261  if (isnull)
262  elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]);
263 
264  newtimeoff = SPI_getbinval(newtuple, tupdesc, attnum[a_time_off], &isnull);
265  if (isnull)
266  elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]);
267 
268  if (oldtimeon != newtimeon || oldtimeoff != newtimeoff)
269  elog(ERROR, "timetravel (%s): you cannot change %s and/or %s columns (use set_timetravel)",
270  relname, args[a_time_on], args[a_time_off]);
271  }
272  if (oldtimeoff != NOEND_ABSTIME)
273  { /* current record is a deleted/updated record */
274  pfree(relname);
275  return PointerGetDatum(NULL);
276  }
277 
278  newtimeoff = GetCurrentAbsoluteTime();
279 
280  /* Connect to SPI manager */
281  if ((ret = SPI_connect()) < 0)
282  elog(ERROR, "timetravel (%s): SPI_connect returned %d", relname, ret);
283 
284  /* Fetch tuple values and nulls */
285  cvals = (Datum *) palloc(natts * sizeof(Datum));
286  cnulls = (char *) palloc(natts * sizeof(char));
287  for (i = 0; i < natts; i++)
288  {
289  cvals[i] = SPI_getbinval(trigtuple, tupdesc, i + 1, &isnull);
290  cnulls[i] = (isnull) ? 'n' : ' ';
291  }
292 
293  /* change date column(s) */
294  cvals[attnum[a_time_off] - 1] = newtimeoff; /* stop_date eq current date */
295  cnulls[attnum[a_time_off] - 1] = ' ';
296 
297  if (!newtuple)
298  { /* DELETE */
299  if (argc == MaxAttrNum)
300  {
301  cvals[attnum[a_del_user] - 1] = newuser; /* set delete user */
302  cnulls[attnum[a_del_user] - 1] = ' ';
303  }
304  }
305 
306  /*
307  * Construct ident string as TriggerName $ TriggeredRelationId and try to
308  * find prepared execution plan.
309  */
310  snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id);
311  plan = find_plan(ident, &Plans, &nPlans);
312 
313  /* if there is no plan ... */
314  if (plan->splan == NULL)
315  {
316  SPIPlanPtr pplan;
317  Oid *ctypes;
318  char sql[8192];
319  char separ = ' ';
320 
321  /* allocate ctypes for preparation */
322  ctypes = (Oid *) palloc(natts * sizeof(Oid));
323 
324  /*
325  * Construct query: INSERT INTO _relation_ VALUES ($1, ...)
326  */
327  snprintf(sql, sizeof(sql), "INSERT INTO %s VALUES (", relname);
328  for (i = 1; i <= natts; i++)
329  {
330  ctypes[i - 1] = SPI_gettypeid(tupdesc, i);
331  if (!(TupleDescAttr(tupdesc, i - 1)->attisdropped)) /* skip dropped columns */
332  {
333  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%c$%d", separ, i);
334  separ = ',';
335  }
336  }
337  snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ")");
338 
339  elog(DEBUG4, "timetravel (%s) update: sql: %s", relname, sql);
340 
341  /* Prepare plan for query */
342  pplan = SPI_prepare(sql, natts, ctypes);
343  if (pplan == NULL)
344  elog(ERROR, "timetravel (%s): SPI_prepare returned %s", relname, SPI_result_code_string(SPI_result));
345 
346  /*
347  * Remember that SPI_prepare places plan in current memory context -
348  * so, we have to save plan in Top memory context for later use.
349  */
350  if (SPI_keepplan(pplan))
351  elog(ERROR, "timetravel (%s): SPI_keepplan failed", relname);
352 
353  plan->splan = pplan;
354  }
355 
356  /*
357  * Ok, execute prepared plan.
358  */
359  ret = SPI_execp(plan->splan, cvals, cnulls, 0);
360 
361  if (ret < 0)
362  elog(ERROR, "timetravel (%s): SPI_execp returned %d", relname, ret);
363 
364  /* Tuple to return to upper Executor ... */
365  if (newtuple)
366  { /* UPDATE */
367  int chnattrs = 0;
368  int chattrs[MaxAttrNum];
369  Datum newvals[MaxAttrNum];
370  char newnulls[MaxAttrNum];
371 
372  newvals[chnattrs] = newtimeoff;
373  newnulls[chnattrs] = ' ';
374  chattrs[chnattrs] = attnum[a_time_on];
375  chnattrs++;
376 
377  newvals[chnattrs] = NOEND_ABSTIME;
378  newnulls[chnattrs] = ' ';
379  chattrs[chnattrs] = attnum[a_time_off];
380  chnattrs++;
381 
382  if (argc == MaxAttrNum)
383  {
384  /* set update_user value */
385  newvals[chnattrs] = newuser;
386  newnulls[chnattrs] = ' ';
387  chattrs[chnattrs] = attnum[a_upd_user];
388  chnattrs++;
389  /* clear delete_user value */
390  newvals[chnattrs] = nulltext;
391  newnulls[chnattrs] = 'n';
392  chattrs[chnattrs] = attnum[a_del_user];
393  chnattrs++;
394  /* set insert_user value */
395  newvals[chnattrs] = nulltext;
396  newnulls[chnattrs] = 'n';
397  chattrs[chnattrs] = attnum[a_ins_user];
398  chnattrs++;
399  }
400 
401  /*
402  * Use SPI_modifytuple() here because we are inside SPI environment
403  * but rettuple must be allocated in caller's context.
404  */
405  rettuple = SPI_modifytuple(rel, newtuple, chnattrs, chattrs, newvals, newnulls);
406  }
407  else
408  /* DELETE case */
409  rettuple = trigtuple;
410 
411  SPI_finish(); /* don't forget say Bye to SPI mgr */
412 
413  pfree(relname);
414  return PointerGetDatum(rettuple);
415 }
int SPI_fnumber(TupleDesc tupdesc, const char *fname)
Definition: spi.c:767
#define a_del_user
Definition: timetravel.c:77
#define MaxAttrNum
Definition: timetravel.c:70
Oid SPI_gettypeid(TupleDesc tupdesc, int fnumber)
Definition: spi.c:900
#define MinAttrNum
Definition: timetravel.c:71
#define DatumGetInt32(X)
Definition: postgres.h:478
Oid GetUserId(void)
Definition: miscinit.c:284
int SPI_connect(void)
Definition: spi.c:84
#define TEXTOID
Definition: pg_type.h:324
#define PointerGetDatum(X)
Definition: postgres.h:562
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes)
Definition: spi.c:488
int SPI_finish(void)
Definition: spi.c:149
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
static int nPlans
Definition: timetravel.c:34
unsigned int Oid
Definition: postgres_ext.h:31
#define DEBUG4
Definition: elog.h:22
#define a_upd_user
Definition: timetravel.c:76
HeapTuple tg_trigtuple
Definition: trigger.h:35
#define NAMEDATALEN
int SPI_result
Definition: spi.c:42
void pfree(void *pointer)
Definition: mcxt.c:949
#define ERROR
Definition: elog.h:43
Datum SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
Definition: spi.c:844
const char * SPI_result_code_string(int code)
Definition: spi.c:1521
int SPI_keepplan(SPIPlanPtr plan)
Definition: spi.c:566
AbsoluteTime GetCurrentAbsoluteTime(void)
Definition: nabstime.c:89
#define a_ins_user
Definition: timetravel.c:75
#define a_time_on
Definition: timetravel.c:73
HeapTuple SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, Datum *Values, const char *Nulls)
Definition: spi.c:696
uintptr_t Datum
Definition: postgres.h:372
Trigger * tg_trigger
Definition: trigger.h:37
HeapTuple tg_newtuple
Definition: trigger.h:36
char * SPI_getrelname(Relation rel)
Definition: spi.c:918
static EPlan * Plans
Definition: timetravel.c:33
#define CALLED_AS_TRIGGER(fcinfo)
Definition: trigger.h:25
char * GetUserNameFromId(Oid roleid, bool noerr)
Definition: miscinit.c:692
TriggerEvent tg_event
Definition: trigger.h:33
Definition: refint.c:19
static EPlan * find_plan(char *ident, EPlan **eplan, int *nplans)
Definition: timetravel.c:525
#define a_time_off
Definition: timetravel.c:74
HeapTuple heap_modify_tuple_by_cols(HeapTuple tuple, TupleDesc tupleDesc, int nCols, int *replCols, Datum *replValues, bool *replIsnull)
Definition: heaptuple.c:865
#define TRIGGER_FIRED_BEFORE(event)
Definition: trigger.h:134
#define TRIGGER_FIRED_BY_INSERT(event)
Definition: trigger.h:116
void * palloc(Size size)
Definition: mcxt.c:848
int i
int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls, long tcount)
Definition: spi.c:372
int16 tgnargs
Definition: reltrigger.h:37
#define CStringGetTextDatum(s)
Definition: builtins.h:91
#define elog
Definition: elog.h:219
#define TRIGGER_FIRED_FOR_ROW(event)
Definition: trigger.h:128
#define ABSTIMEOID
Definition: pg_type.h:422
#define NOEND_ABSTIME
Definition: nabstime.h:77
#define TRIGGER_FIRED_BY_UPDATE(event)
Definition: trigger.h:122
static int findTTStatus(char *name)
Definition: timetravel.c:506
Relation tg_relation
Definition: trigger.h:34

Variable Documentation

◆ nPlans

int nPlans = 0
static

Definition at line 34 of file timetravel.c.

Referenced by timetravel().

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 23 of file timetravel.c.

◆ Plans

EPlan* Plans = NULL
static

Definition at line 33 of file timetravel.c.

◆ TTOff

TTOffList* TTOff = NULL
static

Definition at line 42 of file timetravel.c.