mirror of https://github.com/citusdata/citus.git
Copy & paste pg_get_triggerdef_worker from Postgres
parent
dca09e998f
commit
5af64084ea
|
@ -100,6 +100,7 @@
|
|||
/* Pretty flags */
|
||||
#define PRETTYFLAG_PAREN 0x0001
|
||||
#define PRETTYFLAG_INDENT 0x0002
|
||||
#define PRETTYFLAG_SCHEMA 0x0004
|
||||
|
||||
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
||||
#define WRAP_COLUMN_DEFAULT 0
|
||||
|
@ -107,6 +108,7 @@
|
|||
/* macros to test if pretty action needed */
|
||||
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
||||
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
||||
#define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
|
||||
|
||||
|
||||
/* ----------
|
||||
|
@ -424,6 +426,8 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
|
|||
deparse_context *context);
|
||||
static void get_tablesample_def(TableSampleClause *tablesample,
|
||||
deparse_context *context);
|
||||
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
|
||||
static void set_simple_column_names(deparse_namespace *dpns);
|
||||
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
||||
StringInfo buf);
|
||||
static Node *processIndirection(Node *node, deparse_context *context);
|
||||
|
@ -7481,6 +7485,295 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
|
|||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
pg_get_triggerdef_worker(Oid trigid, bool pretty)
|
||||
{
|
||||
HeapTuple ht_trig;
|
||||
Form_pg_trigger trigrec;
|
||||
StringInfoData buf;
|
||||
Relation tgrel;
|
||||
ScanKeyData skey[1];
|
||||
SysScanDesc tgscan;
|
||||
int findx = 0;
|
||||
char *tgname;
|
||||
char *tgoldtable;
|
||||
char *tgnewtable;
|
||||
Oid argtypes[1]; /* dummy */
|
||||
Datum value;
|
||||
bool isnull;
|
||||
|
||||
/*
|
||||
* Fetch the pg_trigger tuple by the Oid of the trigger
|
||||
*/
|
||||
tgrel = heap_open(TriggerRelationId, AccessShareLock);
|
||||
|
||||
ScanKeyInit(&skey[0],
|
||||
ObjectIdAttributeNumber,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(trigid));
|
||||
|
||||
tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
|
||||
NULL, 1, skey);
|
||||
|
||||
ht_trig = systable_getnext(tgscan);
|
||||
|
||||
if (!HeapTupleIsValid(ht_trig))
|
||||
{
|
||||
systable_endscan(tgscan);
|
||||
heap_close(tgrel, AccessShareLock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
|
||||
|
||||
/*
|
||||
* Start the trigger definition. Note that the trigger's name should never
|
||||
* be schema-qualified, but the trigger rel's name may be.
|
||||
*/
|
||||
initStringInfo(&buf);
|
||||
|
||||
tgname = NameStr(trigrec->tgname);
|
||||
appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
|
||||
OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
|
||||
quote_identifier(tgname));
|
||||
|
||||
if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "BEFORE");
|
||||
else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "AFTER");
|
||||
else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "INSTEAD OF");
|
||||
else
|
||||
elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
|
||||
|
||||
if (TRIGGER_FOR_INSERT(trigrec->tgtype))
|
||||
{
|
||||
appendStringInfoString(&buf, " INSERT");
|
||||
findx++;
|
||||
}
|
||||
if (TRIGGER_FOR_DELETE(trigrec->tgtype))
|
||||
{
|
||||
if (findx > 0)
|
||||
appendStringInfoString(&buf, " OR DELETE");
|
||||
else
|
||||
appendStringInfoString(&buf, " DELETE");
|
||||
findx++;
|
||||
}
|
||||
if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
|
||||
{
|
||||
if (findx > 0)
|
||||
appendStringInfoString(&buf, " OR UPDATE");
|
||||
else
|
||||
appendStringInfoString(&buf, " UPDATE");
|
||||
findx++;
|
||||
/* tgattr is first var-width field, so OK to access directly */
|
||||
if (trigrec->tgattr.dim1 > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
appendStringInfoString(&buf, " OF ");
|
||||
for (i = 0; i < trigrec->tgattr.dim1; i++)
|
||||
{
|
||||
char *attname;
|
||||
|
||||
if (i > 0)
|
||||
appendStringInfoString(&buf, ", ");
|
||||
attname = get_attname(trigrec->tgrelid,
|
||||
trigrec->tgattr.values[i], false);
|
||||
appendStringInfoString(&buf, quote_identifier(attname));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
|
||||
{
|
||||
if (findx > 0)
|
||||
appendStringInfoString(&buf, " OR TRUNCATE");
|
||||
else
|
||||
appendStringInfoString(&buf, " TRUNCATE");
|
||||
findx++;
|
||||
}
|
||||
|
||||
/*
|
||||
* In non-pretty mode, always schema-qualify the target table name for
|
||||
* safety. In pretty mode, schema-qualify only if not visible.
|
||||
*/
|
||||
appendStringInfo(&buf, " ON %s ",
|
||||
pretty ?
|
||||
generate_relation_name(trigrec->tgrelid, NIL) :
|
||||
generate_qualified_relation_name(trigrec->tgrelid));
|
||||
|
||||
if (OidIsValid(trigrec->tgconstraint))
|
||||
{
|
||||
if (OidIsValid(trigrec->tgconstrrelid))
|
||||
appendStringInfo(&buf, "FROM %s ",
|
||||
generate_relation_name(trigrec->tgconstrrelid, NIL));
|
||||
if (!trigrec->tgdeferrable)
|
||||
appendStringInfoString(&buf, "NOT ");
|
||||
appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
|
||||
if (trigrec->tginitdeferred)
|
||||
appendStringInfoString(&buf, "DEFERRED ");
|
||||
else
|
||||
appendStringInfoString(&buf, "IMMEDIATE ");
|
||||
}
|
||||
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (!isnull)
|
||||
tgoldtable = NameStr(*DatumGetName(value));
|
||||
else
|
||||
tgoldtable = NULL;
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (!isnull)
|
||||
tgnewtable = NameStr(*DatumGetName(value));
|
||||
else
|
||||
tgnewtable = NULL;
|
||||
if (tgoldtable != NULL || tgnewtable != NULL)
|
||||
{
|
||||
appendStringInfoString(&buf, "REFERENCING ");
|
||||
if (tgoldtable != NULL)
|
||||
appendStringInfo(&buf, "OLD TABLE AS %s ",
|
||||
quote_identifier(tgoldtable));
|
||||
if (tgnewtable != NULL)
|
||||
appendStringInfo(&buf, "NEW TABLE AS %s ",
|
||||
quote_identifier(tgnewtable));
|
||||
}
|
||||
|
||||
if (TRIGGER_FOR_ROW(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "FOR EACH ROW ");
|
||||
else
|
||||
appendStringInfoString(&buf, "FOR EACH STATEMENT ");
|
||||
|
||||
/* If the trigger has a WHEN qualification, add that */
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (!isnull)
|
||||
{
|
||||
Node *qual;
|
||||
char relkind;
|
||||
deparse_context context;
|
||||
deparse_namespace dpns;
|
||||
RangeTblEntry *oldrte;
|
||||
RangeTblEntry *newrte;
|
||||
|
||||
appendStringInfoString(&buf, "WHEN (");
|
||||
|
||||
qual = stringToNode(TextDatumGetCString(value));
|
||||
|
||||
relkind = get_rel_relkind(trigrec->tgrelid);
|
||||
|
||||
/* Build minimal OLD and NEW RTEs for the rel */
|
||||
oldrte = makeNode(RangeTblEntry);
|
||||
oldrte->rtekind = RTE_RELATION;
|
||||
oldrte->relid = trigrec->tgrelid;
|
||||
oldrte->relkind = relkind;
|
||||
oldrte->alias = makeAlias("old", NIL);
|
||||
oldrte->eref = oldrte->alias;
|
||||
oldrte->lateral = false;
|
||||
oldrte->inh = false;
|
||||
oldrte->inFromCl = true;
|
||||
|
||||
newrte = makeNode(RangeTblEntry);
|
||||
newrte->rtekind = RTE_RELATION;
|
||||
newrte->relid = trigrec->tgrelid;
|
||||
newrte->relkind = relkind;
|
||||
newrte->alias = makeAlias("new", NIL);
|
||||
newrte->eref = newrte->alias;
|
||||
newrte->lateral = false;
|
||||
newrte->inh = false;
|
||||
newrte->inFromCl = true;
|
||||
|
||||
/* Build two-element rtable */
|
||||
memset(&dpns, 0, sizeof(dpns));
|
||||
dpns.rtable = list_make2(oldrte, newrte);
|
||||
dpns.ctes = NIL;
|
||||
set_rtable_names(&dpns, NIL, NULL);
|
||||
set_simple_column_names(&dpns);
|
||||
|
||||
/* Set up context with one-deep namespace stack */
|
||||
context.buf = &buf;
|
||||
context.namespaces = list_make1(&dpns);
|
||||
context.windowClause = NIL;
|
||||
context.windowTList = NIL;
|
||||
context.varprefix = true;
|
||||
context.prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||
context.wrapColumn = WRAP_COLUMN_DEFAULT;
|
||||
context.indentLevel = PRETTYINDENT_STD;
|
||||
context.special_exprkind = EXPR_KIND_NONE;
|
||||
|
||||
get_rule_expr(qual, &context, false);
|
||||
|
||||
appendStringInfoString(&buf, ") ");
|
||||
}
|
||||
|
||||
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
|
||||
generate_function_name(trigrec->tgfoid, 0,
|
||||
NIL, argtypes,
|
||||
false, NULL, EXPR_KIND_NONE));
|
||||
|
||||
if (trigrec->tgnargs > 0)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (isnull)
|
||||
elog(ERROR, "tgargs is null for trigger %u", trigid);
|
||||
p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
|
||||
for (i = 0; i < trigrec->tgnargs; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
appendStringInfoString(&buf, ", ");
|
||||
simple_quote_literal(&buf, p);
|
||||
/* advance p to next string embedded in tgargs */
|
||||
while (*p)
|
||||
p++;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* We deliberately do not put semi-colon at end */
|
||||
appendStringInfoChar(&buf, ')');
|
||||
|
||||
/* Clean up */
|
||||
systable_endscan(tgscan);
|
||||
|
||||
heap_close(tgrel, AccessShareLock);
|
||||
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_simple_column_names: fill in column aliases for non-query situations
|
||||
*
|
||||
* This handles EXPLAIN and cases where we only have relation RTEs. Without
|
||||
* a join tree, we can't do anything smart about join RTEs, but we don't
|
||||
* need to (note that EXPLAIN should never see join alias Vars anyway).
|
||||
* If we do hit a join RTE we'll just process it like a non-table base RTE.
|
||||
*/
|
||||
static void
|
||||
set_simple_column_names(deparse_namespace *dpns)
|
||||
{
|
||||
ListCell *lc;
|
||||
ListCell *lc2;
|
||||
|
||||
/* Initialize dpns->rtable_columns to contain zeroed structs */
|
||||
dpns->rtable_columns = NIL;
|
||||
while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
|
||||
dpns->rtable_columns = lappend(dpns->rtable_columns,
|
||||
palloc0(sizeof(deparse_columns)));
|
||||
|
||||
/* Assign unique column aliases within each RTE */
|
||||
forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
||||
deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
|
||||
|
||||
set_relation_column_names(dpns, rte, colinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get_opclass_name - fetch name of an index operator class
|
||||
*
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
/* Pretty flags */
|
||||
#define PRETTYFLAG_PAREN 0x0001
|
||||
#define PRETTYFLAG_INDENT 0x0002
|
||||
#define PRETTYFLAG_SCHEMA 0x0004
|
||||
|
||||
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
||||
#define WRAP_COLUMN_DEFAULT 0
|
||||
|
@ -107,6 +108,7 @@
|
|||
/* macros to test if pretty action needed */
|
||||
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
||||
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
||||
#define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
|
||||
|
||||
|
||||
/* ----------
|
||||
|
@ -424,6 +426,8 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
|
|||
deparse_context *context);
|
||||
static void get_tablesample_def(TableSampleClause *tablesample,
|
||||
deparse_context *context);
|
||||
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
|
||||
static void set_simple_column_names(deparse_namespace *dpns);
|
||||
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
||||
StringInfo buf);
|
||||
static Node *processIndirection(Node *node, deparse_context *context);
|
||||
|
@ -7481,6 +7485,297 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
|
|||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
pg_get_triggerdef_worker(Oid trigid, bool pretty)
|
||||
{
|
||||
HeapTuple ht_trig;
|
||||
Form_pg_trigger trigrec;
|
||||
StringInfoData buf;
|
||||
Relation tgrel;
|
||||
ScanKeyData skey[1];
|
||||
SysScanDesc tgscan;
|
||||
int findx = 0;
|
||||
char *tgname;
|
||||
char *tgoldtable;
|
||||
char *tgnewtable;
|
||||
Oid argtypes[1]; /* dummy */
|
||||
Datum value;
|
||||
bool isnull;
|
||||
|
||||
/*
|
||||
* Fetch the pg_trigger tuple by the Oid of the trigger
|
||||
*/
|
||||
tgrel = table_open(TriggerRelationId, AccessShareLock);
|
||||
|
||||
ScanKeyInit(&skey[0],
|
||||
Anum_pg_trigger_oid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(trigid));
|
||||
|
||||
tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
|
||||
NULL, 1, skey);
|
||||
|
||||
ht_trig = systable_getnext(tgscan);
|
||||
|
||||
if (!HeapTupleIsValid(ht_trig))
|
||||
{
|
||||
systable_endscan(tgscan);
|
||||
table_close(tgrel, AccessShareLock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
|
||||
|
||||
/*
|
||||
* Start the trigger definition. Note that the trigger's name should never
|
||||
* be schema-qualified, but the trigger rel's name may be.
|
||||
*/
|
||||
initStringInfo(&buf);
|
||||
|
||||
tgname = NameStr(trigrec->tgname);
|
||||
appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
|
||||
OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
|
||||
quote_identifier(tgname));
|
||||
|
||||
if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "BEFORE");
|
||||
else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "AFTER");
|
||||
else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "INSTEAD OF");
|
||||
else
|
||||
elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
|
||||
|
||||
if (TRIGGER_FOR_INSERT(trigrec->tgtype))
|
||||
{
|
||||
appendStringInfoString(&buf, " INSERT");
|
||||
findx++;
|
||||
}
|
||||
if (TRIGGER_FOR_DELETE(trigrec->tgtype))
|
||||
{
|
||||
if (findx > 0)
|
||||
appendStringInfoString(&buf, " OR DELETE");
|
||||
else
|
||||
appendStringInfoString(&buf, " DELETE");
|
||||
findx++;
|
||||
}
|
||||
if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
|
||||
{
|
||||
if (findx > 0)
|
||||
appendStringInfoString(&buf, " OR UPDATE");
|
||||
else
|
||||
appendStringInfoString(&buf, " UPDATE");
|
||||
findx++;
|
||||
/* tgattr is first var-width field, so OK to access directly */
|
||||
if (trigrec->tgattr.dim1 > 0)
|
||||
{
|
||||
int i;
|
||||
|
||||
appendStringInfoString(&buf, " OF ");
|
||||
for (i = 0; i < trigrec->tgattr.dim1; i++)
|
||||
{
|
||||
char *attname;
|
||||
|
||||
if (i > 0)
|
||||
appendStringInfoString(&buf, ", ");
|
||||
attname = get_attname(trigrec->tgrelid,
|
||||
trigrec->tgattr.values[i], false);
|
||||
appendStringInfoString(&buf, quote_identifier(attname));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
|
||||
{
|
||||
if (findx > 0)
|
||||
appendStringInfoString(&buf, " OR TRUNCATE");
|
||||
else
|
||||
appendStringInfoString(&buf, " TRUNCATE");
|
||||
findx++;
|
||||
}
|
||||
|
||||
/*
|
||||
* In non-pretty mode, always schema-qualify the target table name for
|
||||
* safety. In pretty mode, schema-qualify only if not visible.
|
||||
*/
|
||||
appendStringInfo(&buf, " ON %s ",
|
||||
pretty ?
|
||||
generate_relation_name(trigrec->tgrelid, NIL) :
|
||||
generate_qualified_relation_name(trigrec->tgrelid));
|
||||
|
||||
if (OidIsValid(trigrec->tgconstraint))
|
||||
{
|
||||
if (OidIsValid(trigrec->tgconstrrelid))
|
||||
appendStringInfo(&buf, "FROM %s ",
|
||||
generate_relation_name(trigrec->tgconstrrelid, NIL));
|
||||
if (!trigrec->tgdeferrable)
|
||||
appendStringInfoString(&buf, "NOT ");
|
||||
appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
|
||||
if (trigrec->tginitdeferred)
|
||||
appendStringInfoString(&buf, "DEFERRED ");
|
||||
else
|
||||
appendStringInfoString(&buf, "IMMEDIATE ");
|
||||
}
|
||||
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (!isnull)
|
||||
tgoldtable = NameStr(*DatumGetName(value));
|
||||
else
|
||||
tgoldtable = NULL;
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (!isnull)
|
||||
tgnewtable = NameStr(*DatumGetName(value));
|
||||
else
|
||||
tgnewtable = NULL;
|
||||
if (tgoldtable != NULL || tgnewtable != NULL)
|
||||
{
|
||||
appendStringInfoString(&buf, "REFERENCING ");
|
||||
if (tgoldtable != NULL)
|
||||
appendStringInfo(&buf, "OLD TABLE AS %s ",
|
||||
quote_identifier(tgoldtable));
|
||||
if (tgnewtable != NULL)
|
||||
appendStringInfo(&buf, "NEW TABLE AS %s ",
|
||||
quote_identifier(tgnewtable));
|
||||
}
|
||||
|
||||
if (TRIGGER_FOR_ROW(trigrec->tgtype))
|
||||
appendStringInfoString(&buf, "FOR EACH ROW ");
|
||||
else
|
||||
appendStringInfoString(&buf, "FOR EACH STATEMENT ");
|
||||
|
||||
/* If the trigger has a WHEN qualification, add that */
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (!isnull)
|
||||
{
|
||||
Node *qual;
|
||||
char relkind;
|
||||
deparse_context context;
|
||||
deparse_namespace dpns;
|
||||
RangeTblEntry *oldrte;
|
||||
RangeTblEntry *newrte;
|
||||
|
||||
appendStringInfoString(&buf, "WHEN (");
|
||||
|
||||
qual = stringToNode(TextDatumGetCString(value));
|
||||
|
||||
relkind = get_rel_relkind(trigrec->tgrelid);
|
||||
|
||||
/* Build minimal OLD and NEW RTEs for the rel */
|
||||
oldrte = makeNode(RangeTblEntry);
|
||||
oldrte->rtekind = RTE_RELATION;
|
||||
oldrte->relid = trigrec->tgrelid;
|
||||
oldrte->relkind = relkind;
|
||||
oldrte->rellockmode = AccessShareLock;
|
||||
oldrte->alias = makeAlias("old", NIL);
|
||||
oldrte->eref = oldrte->alias;
|
||||
oldrte->lateral = false;
|
||||
oldrte->inh = false;
|
||||
oldrte->inFromCl = true;
|
||||
|
||||
newrte = makeNode(RangeTblEntry);
|
||||
newrte->rtekind = RTE_RELATION;
|
||||
newrte->relid = trigrec->tgrelid;
|
||||
newrte->relkind = relkind;
|
||||
newrte->rellockmode = AccessShareLock;
|
||||
newrte->alias = makeAlias("new", NIL);
|
||||
newrte->eref = newrte->alias;
|
||||
newrte->lateral = false;
|
||||
newrte->inh = false;
|
||||
newrte->inFromCl = true;
|
||||
|
||||
/* Build two-element rtable */
|
||||
memset(&dpns, 0, sizeof(dpns));
|
||||
dpns.rtable = list_make2(oldrte, newrte);
|
||||
dpns.ctes = NIL;
|
||||
set_rtable_names(&dpns, NIL, NULL);
|
||||
set_simple_column_names(&dpns);
|
||||
|
||||
/* Set up context with one-deep namespace stack */
|
||||
context.buf = &buf;
|
||||
context.namespaces = list_make1(&dpns);
|
||||
context.windowClause = NIL;
|
||||
context.windowTList = NIL;
|
||||
context.varprefix = true;
|
||||
context.prettyFlags = pretty ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) : PRETTYFLAG_INDENT;
|
||||
context.wrapColumn = WRAP_COLUMN_DEFAULT;
|
||||
context.indentLevel = PRETTYINDENT_STD;
|
||||
context.special_exprkind = EXPR_KIND_NONE;
|
||||
|
||||
get_rule_expr(qual, &context, false);
|
||||
|
||||
appendStringInfoString(&buf, ") ");
|
||||
}
|
||||
|
||||
appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
|
||||
generate_function_name(trigrec->tgfoid, 0,
|
||||
NIL, argtypes,
|
||||
false, NULL, EXPR_KIND_NONE));
|
||||
|
||||
if (trigrec->tgnargs > 0)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
|
||||
tgrel->rd_att, &isnull);
|
||||
if (isnull)
|
||||
elog(ERROR, "tgargs is null for trigger %u", trigid);
|
||||
p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
|
||||
for (i = 0; i < trigrec->tgnargs; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
appendStringInfoString(&buf, ", ");
|
||||
simple_quote_literal(&buf, p);
|
||||
/* advance p to next string embedded in tgargs */
|
||||
while (*p)
|
||||
p++;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* We deliberately do not put semi-colon at end */
|
||||
appendStringInfoChar(&buf, ')');
|
||||
|
||||
/* Clean up */
|
||||
systable_endscan(tgscan);
|
||||
|
||||
table_close(tgrel, AccessShareLock);
|
||||
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
/*
|
||||
* set_simple_column_names: fill in column aliases for non-query situations
|
||||
*
|
||||
* This handles EXPLAIN and cases where we only have relation RTEs. Without
|
||||
* a join tree, we can't do anything smart about join RTEs, but we don't
|
||||
* need to (note that EXPLAIN should never see join alias Vars anyway).
|
||||
* If we do hit a join RTE we'll just process it like a non-table base RTE.
|
||||
*/
|
||||
static void
|
||||
set_simple_column_names(deparse_namespace *dpns)
|
||||
{
|
||||
ListCell *lc;
|
||||
ListCell *lc2;
|
||||
|
||||
/* Initialize dpns->rtable_columns to contain zeroed structs */
|
||||
dpns->rtable_columns = NIL;
|
||||
while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
|
||||
dpns->rtable_columns = lappend(dpns->rtable_columns,
|
||||
palloc0(sizeof(deparse_columns)));
|
||||
|
||||
/* Assign unique column aliases within each RTE */
|
||||
forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
||||
deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
|
||||
|
||||
set_relation_column_names(dpns, rte, colinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get_opclass_name - fetch name of an index operator class
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue