mirror of https://github.com/citusdata/citus.git
Merge pull request #5636 from citusdata/drop-tg-utils
Drop ruleutils copied for triggers & statistics While reading trigger related parts of our code-base, realized that we actually don't need to copy & paste underlying worker functions from pg/ruleutils.c since higher level functions for those two are anyway exposed as SQL callables, so we can delete more than ~1k lines of code from our ruleutils_x.c files.pull/5639/head
commit
0244d3f206
|
@ -40,6 +40,7 @@
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/fmgrprotos.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/relcache.h"
|
#include "utils/relcache.h"
|
||||||
#include "utils/ruleutils.h"
|
#include "utils/ruleutils.h"
|
||||||
|
@ -447,8 +448,20 @@ GetExplicitStatisticsCommandList(Oid relationId)
|
||||||
foreach_oid(statisticsId, statisticsIdList)
|
foreach_oid(statisticsId, statisticsIdList)
|
||||||
{
|
{
|
||||||
/* we need create commands for already created stats before distribution */
|
/* we need create commands for already created stats before distribution */
|
||||||
char *createStatisticsCommand = pg_get_statisticsobj_worker_compat(statisticsId,
|
Datum commandText = DirectFunctionCall1(pg_get_statisticsobjdef,
|
||||||
false, false);
|
ObjectIdGetDatum(statisticsId));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pg_get_statisticsobjdef doesn't throw an error if there is no such
|
||||||
|
* statistics object, be on the safe side.
|
||||||
|
*/
|
||||||
|
if (DatumGetPointer(commandText) == NULL)
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("statistics with oid %u does not exist",
|
||||||
|
statisticsId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *createStatisticsCommand = TextDatumGetCString(commandText);
|
||||||
|
|
||||||
explicitStatisticsCommandList =
|
explicitStatisticsCommandList =
|
||||||
lappend(explicitStatisticsCommandList,
|
lappend(explicitStatisticsCommandList,
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "distributed/citus_ruleutils.h"
|
|
||||||
#include "distributed/commands.h"
|
#include "distributed/commands.h"
|
||||||
#include "distributed/commands/utility_hook.h"
|
#include "distributed/commands/utility_hook.h"
|
||||||
#include "distributed/coordinator_protocol.h"
|
#include "distributed/coordinator_protocol.h"
|
||||||
|
@ -27,6 +26,8 @@
|
||||||
#include "distributed/namespace_utils.h"
|
#include "distributed/namespace_utils.h"
|
||||||
#include "distributed/shard_utils.h"
|
#include "distributed/shard_utils.h"
|
||||||
#include "distributed/worker_protocol.h"
|
#include "distributed/worker_protocol.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/fmgrprotos.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
@ -68,7 +69,22 @@ GetExplicitTriggerCommandList(Oid relationId)
|
||||||
Oid triggerId = InvalidOid;
|
Oid triggerId = InvalidOid;
|
||||||
foreach_oid(triggerId, triggerIdList)
|
foreach_oid(triggerId, triggerIdList)
|
||||||
{
|
{
|
||||||
char *createTriggerCommand = pg_get_triggerdef_command(triggerId);
|
bool prettyOutput = false;
|
||||||
|
Datum commandText = DirectFunctionCall2(pg_get_triggerdef_ext,
|
||||||
|
ObjectIdGetDatum(triggerId),
|
||||||
|
BoolGetDatum(prettyOutput));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pg_get_triggerdef_ext doesn't throw an error if there is no such
|
||||||
|
* trigger, be on the safe side.
|
||||||
|
*/
|
||||||
|
if (DatumGetPointer(commandText) == NULL)
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("trigger with oid %u does not exist",
|
||||||
|
triggerId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *createTriggerCommand = TextDatumGetCString(commandText);
|
||||||
|
|
||||||
createTriggerCommandList = lappend(
|
createTriggerCommandList = lappend(
|
||||||
createTriggerCommandList,
|
createTriggerCommandList,
|
||||||
|
|
|
@ -103,7 +103,6 @@
|
||||||
/* Pretty flags */
|
/* Pretty flags */
|
||||||
#define PRETTYFLAG_PAREN 0x0001
|
#define PRETTYFLAG_PAREN 0x0001
|
||||||
#define PRETTYFLAG_INDENT 0x0002
|
#define PRETTYFLAG_INDENT 0x0002
|
||||||
#define PRETTYFLAG_SCHEMA 0x0004
|
|
||||||
|
|
||||||
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
||||||
#define WRAP_COLUMN_DEFAULT 0
|
#define WRAP_COLUMN_DEFAULT 0
|
||||||
|
@ -111,7 +110,6 @@
|
||||||
/* macros to test if pretty action needed */
|
/* macros to test if pretty action needed */
|
||||||
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
||||||
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
||||||
#define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
|
@ -429,9 +427,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
static void get_tablesample_def(TableSampleClause *tablesample,
|
static void get_tablesample_def(TableSampleClause *tablesample,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
char *pg_get_statisticsobj_worker(Oid statextid, bool missing_ok);
|
|
||||||
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,
|
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
||||||
StringInfo buf);
|
StringInfo buf);
|
||||||
static Node *processIndirection(Node *node, deparse_context *context);
|
static Node *processIndirection(Node *node, deparse_context *context);
|
||||||
|
@ -7519,428 +7514,6 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
|
||||||
pg_get_triggerdef_command(Oid triggerId)
|
|
||||||
{
|
|
||||||
Assert(OidIsValid(triggerId));
|
|
||||||
|
|
||||||
/* no need to have pretty SQL command */
|
|
||||||
bool prettyOutput = false;
|
|
||||||
return pg_get_triggerdef_worker(triggerId, prettyOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char *
|
|
||||||
pg_get_statisticsobj_worker(Oid statextid, bool missing_ok)
|
|
||||||
{
|
|
||||||
StringInfoData buf;
|
|
||||||
int colno;
|
|
||||||
bool isnull;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
HeapTuple statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
|
|
||||||
|
|
||||||
if (!HeapTupleIsValid(statexttup))
|
|
||||||
{
|
|
||||||
if (missing_ok)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
elog(ERROR, "cache lookup failed for statistics object %u", statextid);
|
|
||||||
}
|
|
||||||
|
|
||||||
Form_pg_statistic_ext statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
|
|
||||||
|
|
||||||
initStringInfo(&buf);
|
|
||||||
|
|
||||||
char *nsp = get_namespace_name(statextrec->stxnamespace);
|
|
||||||
appendStringInfo(&buf, "CREATE STATISTICS %s",
|
|
||||||
quote_qualified_identifier(nsp,
|
|
||||||
NameStr(statextrec->stxname)));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decode the stxkind column so that we know which stats types to print.
|
|
||||||
*/
|
|
||||||
Datum datum = SysCacheGetAttr(STATEXTOID, statexttup,
|
|
||||||
Anum_pg_statistic_ext_stxkind, &isnull);
|
|
||||||
Assert(!isnull);
|
|
||||||
ArrayType *arr = DatumGetArrayTypeP(datum);
|
|
||||||
if (ARR_NDIM(arr) != 1 ||
|
|
||||||
ARR_HASNULL(arr) ||
|
|
||||||
ARR_ELEMTYPE(arr) != CHAROID)
|
|
||||||
{
|
|
||||||
elog(ERROR, "stxkind is not a 1-D char array");
|
|
||||||
}
|
|
||||||
char *enabled = (char *) ARR_DATA_PTR(arr);
|
|
||||||
|
|
||||||
bool ndistinct_enabled = false;
|
|
||||||
bool dependencies_enabled = false;
|
|
||||||
bool mcv_enabled = false;
|
|
||||||
|
|
||||||
for (i = 0; i < ARR_DIMS(arr)[0]; i++)
|
|
||||||
{
|
|
||||||
if (enabled[i] == STATS_EXT_NDISTINCT)
|
|
||||||
{
|
|
||||||
ndistinct_enabled = true;
|
|
||||||
}
|
|
||||||
if (enabled[i] == STATS_EXT_DEPENDENCIES)
|
|
||||||
{
|
|
||||||
dependencies_enabled = true;
|
|
||||||
}
|
|
||||||
if (enabled[i] == STATS_EXT_MCV)
|
|
||||||
{
|
|
||||||
mcv_enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any option is disabled, then we'll need to append the types clause
|
|
||||||
* to show which options are enabled. We omit the types clause on purpose
|
|
||||||
* when all options are enabled, so a pg_dump/pg_restore will create all
|
|
||||||
* statistics types on a newer postgres version, if the statistics had all
|
|
||||||
* options enabled on the original version.
|
|
||||||
*/
|
|
||||||
if (!ndistinct_enabled || !dependencies_enabled || !mcv_enabled)
|
|
||||||
{
|
|
||||||
bool gotone = false;
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, " (");
|
|
||||||
|
|
||||||
if (ndistinct_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfoString(&buf, "ndistinct");
|
|
||||||
gotone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dependencies_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
|
|
||||||
gotone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mcv_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoChar(&buf, ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, " ON ");
|
|
||||||
|
|
||||||
for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
|
|
||||||
{
|
|
||||||
AttrNumber attnum = statextrec->stxkeys.values[colno];
|
|
||||||
|
|
||||||
if (colno > 0)
|
|
||||||
{
|
|
||||||
appendStringInfoString(&buf, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
char *attname = get_attname(statextrec->stxrelid, attnum, false);
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, quote_identifier(attname));
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfo(&buf, " FROM %s",
|
|
||||||
generate_relation_name(statextrec->stxrelid, NIL));
|
|
||||||
|
|
||||||
ReleaseSysCache(statexttup);
|
|
||||||
|
|
||||||
return buf.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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
|
* get_opclass_name - fetch name of an index operator class
|
||||||
|
|
|
@ -103,7 +103,6 @@
|
||||||
/* Pretty flags */
|
/* Pretty flags */
|
||||||
#define PRETTYFLAG_PAREN 0x0001
|
#define PRETTYFLAG_PAREN 0x0001
|
||||||
#define PRETTYFLAG_INDENT 0x0002
|
#define PRETTYFLAG_INDENT 0x0002
|
||||||
#define PRETTYFLAG_SCHEMA 0x0004
|
|
||||||
|
|
||||||
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
||||||
#define WRAP_COLUMN_DEFAULT 0
|
#define WRAP_COLUMN_DEFAULT 0
|
||||||
|
@ -111,7 +110,6 @@
|
||||||
/* macros to test if pretty action needed */
|
/* macros to test if pretty action needed */
|
||||||
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
||||||
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
||||||
#define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
|
@ -434,9 +432,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
static void get_tablesample_def(TableSampleClause *tablesample,
|
static void get_tablesample_def(TableSampleClause *tablesample,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
char *pg_get_statisticsobj_worker(Oid statextid, bool missing_ok);
|
|
||||||
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,
|
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
||||||
StringInfo buf);
|
StringInfo buf);
|
||||||
static Node *processIndirection(Node *node, deparse_context *context);
|
static Node *processIndirection(Node *node, deparse_context *context);
|
||||||
|
@ -7577,429 +7572,6 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
|
||||||
pg_get_triggerdef_command(Oid triggerId)
|
|
||||||
{
|
|
||||||
Assert(OidIsValid(triggerId));
|
|
||||||
|
|
||||||
/* no need to have pretty SQL command */
|
|
||||||
bool prettyOutput = false;
|
|
||||||
return pg_get_triggerdef_worker(triggerId, prettyOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char *
|
|
||||||
pg_get_statisticsobj_worker(Oid statextid, bool missing_ok)
|
|
||||||
{
|
|
||||||
StringInfoData buf;
|
|
||||||
int colno;
|
|
||||||
bool isnull;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
HeapTuple statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
|
|
||||||
|
|
||||||
if (!HeapTupleIsValid(statexttup))
|
|
||||||
{
|
|
||||||
if (missing_ok)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
elog(ERROR, "cache lookup failed for statistics object %u", statextid);
|
|
||||||
}
|
|
||||||
|
|
||||||
Form_pg_statistic_ext statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
|
|
||||||
|
|
||||||
initStringInfo(&buf);
|
|
||||||
|
|
||||||
char *nsp = get_namespace_name(statextrec->stxnamespace);
|
|
||||||
appendStringInfo(&buf, "CREATE STATISTICS %s",
|
|
||||||
quote_qualified_identifier(nsp,
|
|
||||||
NameStr(statextrec->stxname)));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decode the stxkind column so that we know which stats types to print.
|
|
||||||
*/
|
|
||||||
Datum datum = SysCacheGetAttr(STATEXTOID, statexttup,
|
|
||||||
Anum_pg_statistic_ext_stxkind, &isnull);
|
|
||||||
Assert(!isnull);
|
|
||||||
ArrayType *arr = DatumGetArrayTypeP(datum);
|
|
||||||
if (ARR_NDIM(arr) != 1 ||
|
|
||||||
ARR_HASNULL(arr) ||
|
|
||||||
ARR_ELEMTYPE(arr) != CHAROID)
|
|
||||||
{
|
|
||||||
elog(ERROR, "stxkind is not a 1-D char array");
|
|
||||||
}
|
|
||||||
char *enabled = (char *) ARR_DATA_PTR(arr);
|
|
||||||
|
|
||||||
bool ndistinct_enabled = false;
|
|
||||||
bool dependencies_enabled = false;
|
|
||||||
bool mcv_enabled = false;
|
|
||||||
|
|
||||||
for (i = 0; i < ARR_DIMS(arr)[0]; i++)
|
|
||||||
{
|
|
||||||
if (enabled[i] == STATS_EXT_NDISTINCT)
|
|
||||||
{
|
|
||||||
ndistinct_enabled = true;
|
|
||||||
}
|
|
||||||
if (enabled[i] == STATS_EXT_DEPENDENCIES)
|
|
||||||
{
|
|
||||||
dependencies_enabled = true;
|
|
||||||
}
|
|
||||||
if (enabled[i] == STATS_EXT_MCV)
|
|
||||||
{
|
|
||||||
mcv_enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any option is disabled, then we'll need to append the types clause
|
|
||||||
* to show which options are enabled. We omit the types clause on purpose
|
|
||||||
* when all options are enabled, so a pg_dump/pg_restore will create all
|
|
||||||
* statistics types on a newer postgres version, if the statistics had all
|
|
||||||
* options enabled on the original version.
|
|
||||||
*/
|
|
||||||
if (!ndistinct_enabled || !dependencies_enabled || !mcv_enabled)
|
|
||||||
{
|
|
||||||
bool gotone = false;
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, " (");
|
|
||||||
|
|
||||||
if (ndistinct_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfoString(&buf, "ndistinct");
|
|
||||||
gotone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dependencies_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
|
|
||||||
gotone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mcv_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoChar(&buf, ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, " ON ");
|
|
||||||
|
|
||||||
for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
|
|
||||||
{
|
|
||||||
AttrNumber attnum = statextrec->stxkeys.values[colno];
|
|
||||||
|
|
||||||
if (colno > 0)
|
|
||||||
{
|
|
||||||
appendStringInfoString(&buf, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
char *attname = get_attname(statextrec->stxrelid, attnum, false);
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, quote_identifier(attname));
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfo(&buf, " FROM %s",
|
|
||||||
generate_relation_name(statextrec->stxrelid, NIL));
|
|
||||||
|
|
||||||
ReleaseSysCache(statexttup);
|
|
||||||
|
|
||||||
return buf.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
context.appendparents = NULL;
|
|
||||||
|
|
||||||
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
|
* get_opclass_name - fetch name of an index operator class
|
||||||
|
|
|
@ -101,7 +101,6 @@
|
||||||
/* Pretty flags */
|
/* Pretty flags */
|
||||||
#define PRETTYFLAG_PAREN 0x0001
|
#define PRETTYFLAG_PAREN 0x0001
|
||||||
#define PRETTYFLAG_INDENT 0x0002
|
#define PRETTYFLAG_INDENT 0x0002
|
||||||
#define PRETTYFLAG_SCHEMA 0x0004
|
|
||||||
|
|
||||||
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
/* Default line length for pretty-print wrapping: 0 means wrap always */
|
||||||
#define WRAP_COLUMN_DEFAULT 0
|
#define WRAP_COLUMN_DEFAULT 0
|
||||||
|
@ -109,7 +108,6 @@
|
||||||
/* macros to test if pretty action needed */
|
/* macros to test if pretty action needed */
|
||||||
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN)
|
||||||
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
|
||||||
#define PRETTY_SCHEMA(context) ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
|
@ -322,9 +320,6 @@ typedef struct
|
||||||
* as a parameter, and append their text output to its contents.
|
* as a parameter, and append their text output to its contents.
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
static char *deparse_expression_pretty(Node *expr, List *dpcontext,
|
|
||||||
bool forceprefix, bool showimplicit,
|
|
||||||
int prettyFlags, int startIndent);
|
|
||||||
static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
|
||||||
Bitmapset *rels_used);
|
Bitmapset *rels_used);
|
||||||
static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
|
static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
|
||||||
|
@ -442,10 +437,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
static void get_tablesample_def(TableSampleClause *tablesample,
|
static void get_tablesample_def(TableSampleClause *tablesample,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
|
|
||||||
bool missing_ok);
|
|
||||||
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,
|
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
||||||
StringInfo buf);
|
StringInfo buf);
|
||||||
static Node *processIndirection(Node *node, deparse_context *context);
|
static Node *processIndirection(Node *node, deparse_context *context);
|
||||||
|
@ -652,51 +643,6 @@ pg_get_rule_expr(Node *expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* deparse_expression_pretty - General utility for deparsing expressions
|
|
||||||
*
|
|
||||||
* expr is the node tree to be deparsed. It must be a transformed expression
|
|
||||||
* tree (ie, not the raw output of gram.y).
|
|
||||||
*
|
|
||||||
* dpcontext is a list of deparse_namespace nodes representing the context
|
|
||||||
* for interpreting Vars in the node tree. It can be NIL if no Vars are
|
|
||||||
* expected.
|
|
||||||
*
|
|
||||||
* forceprefix is true to force all Vars to be prefixed with their table names.
|
|
||||||
*
|
|
||||||
* showimplicit is true to force all implicit casts to be shown explicitly.
|
|
||||||
*
|
|
||||||
* Tries to pretty up the output according to prettyFlags and startIndent.
|
|
||||||
*
|
|
||||||
* The result is a palloc'd string.
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
static char *
|
|
||||||
deparse_expression_pretty(Node *expr, List *dpcontext,
|
|
||||||
bool forceprefix, bool showimplicit,
|
|
||||||
int prettyFlags, int startIndent)
|
|
||||||
{
|
|
||||||
StringInfoData buf;
|
|
||||||
deparse_context context;
|
|
||||||
|
|
||||||
initStringInfo(&buf);
|
|
||||||
context.buf = &buf;
|
|
||||||
context.namespaces = dpcontext;
|
|
||||||
context.windowClause = NIL;
|
|
||||||
context.windowTList = NIL;
|
|
||||||
context.varprefix = forceprefix;
|
|
||||||
context.prettyFlags = prettyFlags;
|
|
||||||
context.wrapColumn = WRAP_COLUMN_DEFAULT;
|
|
||||||
context.indentLevel = startIndent;
|
|
||||||
context.special_exprkind = EXPR_KIND_NONE;
|
|
||||||
context.appendparents = NULL;
|
|
||||||
|
|
||||||
get_rule_expr(expr, &context, showimplicit);
|
|
||||||
|
|
||||||
return buf.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set_rtable_names: select RTE aliases to be used in printing a query
|
* set_rtable_names: select RTE aliases to be used in printing a query
|
||||||
*
|
*
|
||||||
|
@ -8133,497 +8079,6 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
|
||||||
pg_get_triggerdef_command(Oid triggerId)
|
|
||||||
{
|
|
||||||
Assert(OidIsValid(triggerId));
|
|
||||||
|
|
||||||
/* no need to have pretty SQL command */
|
|
||||||
bool prettyOutput = false;
|
|
||||||
return pg_get_triggerdef_worker(triggerId, prettyOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char *
|
|
||||||
pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
|
|
||||||
{
|
|
||||||
StringInfoData buf;
|
|
||||||
int colno;
|
|
||||||
char *nsp;
|
|
||||||
ArrayType *arr;
|
|
||||||
char *enabled;
|
|
||||||
Datum datum;
|
|
||||||
bool isnull;
|
|
||||||
bool ndistinct_enabled;
|
|
||||||
bool dependencies_enabled;
|
|
||||||
bool mcv_enabled;
|
|
||||||
int i;
|
|
||||||
List *context;
|
|
||||||
ListCell *lc;
|
|
||||||
List *exprs = NIL;
|
|
||||||
bool has_exprs;
|
|
||||||
int ncolumns;
|
|
||||||
|
|
||||||
HeapTuple statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
|
|
||||||
|
|
||||||
if (!HeapTupleIsValid(statexttup))
|
|
||||||
{
|
|
||||||
if (missing_ok)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
elog(ERROR, "cache lookup failed for statistics object %u", statextid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* has the statistics expressions? */
|
|
||||||
has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
|
|
||||||
|
|
||||||
Form_pg_statistic_ext statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the statistics expressions, if any. (NOTE: we do not use the
|
|
||||||
* relcache versions of the expressions, because we want to display
|
|
||||||
* non-const-folded expressions.)
|
|
||||||
*/
|
|
||||||
if (has_exprs)
|
|
||||||
{
|
|
||||||
Datum exprsDatum;
|
|
||||||
bool isNull;
|
|
||||||
char *exprsString;
|
|
||||||
|
|
||||||
exprsDatum = SysCacheGetAttr(STATEXTOID, statexttup,
|
|
||||||
Anum_pg_statistic_ext_stxexprs, &isNull);
|
|
||||||
Assert(!isNull);
|
|
||||||
exprsString = TextDatumGetCString(exprsDatum);
|
|
||||||
exprs = (List *) stringToNode(exprsString);
|
|
||||||
pfree(exprsString);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
exprs = NIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* count the number of columns (attributes and expressions) */
|
|
||||||
ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
|
|
||||||
|
|
||||||
initStringInfo(&buf);
|
|
||||||
|
|
||||||
if (!columns_only)
|
|
||||||
{
|
|
||||||
nsp = get_namespace_name(statextrec->stxnamespace);
|
|
||||||
appendStringInfo(&buf, "CREATE STATISTICS %s",
|
|
||||||
quote_qualified_identifier(nsp,
|
|
||||||
NameStr(statextrec->stxname)));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Decode the stxkind column so that we know which stats types to
|
|
||||||
* print.
|
|
||||||
*/
|
|
||||||
datum = SysCacheGetAttr(STATEXTOID, statexttup,
|
|
||||||
Anum_pg_statistic_ext_stxkind, &isnull);
|
|
||||||
Assert(!isnull);
|
|
||||||
arr = DatumGetArrayTypeP(datum);
|
|
||||||
if (ARR_NDIM(arr) != 1 ||
|
|
||||||
ARR_HASNULL(arr) ||
|
|
||||||
ARR_ELEMTYPE(arr) != CHAROID)
|
|
||||||
elog(ERROR, "stxkind is not a 1-D char array");
|
|
||||||
enabled = (char *) ARR_DATA_PTR(arr);
|
|
||||||
|
|
||||||
ndistinct_enabled = false;
|
|
||||||
dependencies_enabled = false;
|
|
||||||
mcv_enabled = false;
|
|
||||||
|
|
||||||
for (i = 0; i < ARR_DIMS(arr)[0]; i++)
|
|
||||||
{
|
|
||||||
if (enabled[i] == STATS_EXT_NDISTINCT)
|
|
||||||
ndistinct_enabled = true;
|
|
||||||
else if (enabled[i] == STATS_EXT_DEPENDENCIES)
|
|
||||||
dependencies_enabled = true;
|
|
||||||
else if (enabled[i] == STATS_EXT_MCV)
|
|
||||||
mcv_enabled = true;
|
|
||||||
|
|
||||||
/* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any option is disabled, then we'll need to append the types
|
|
||||||
* clause to show which options are enabled. We omit the types clause
|
|
||||||
* on purpose when all options are enabled, so a pg_dump/pg_restore
|
|
||||||
* will create all statistics types on a newer postgres version, if
|
|
||||||
* the statistics had all options enabled on the original version.
|
|
||||||
*
|
|
||||||
* But if the statistics is defined on just a single column, it has to
|
|
||||||
* be an expression statistics. In that case we don't need to specify
|
|
||||||
* kinds.
|
|
||||||
*/
|
|
||||||
if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
|
|
||||||
(ncolumns > 1))
|
|
||||||
{
|
|
||||||
bool gotone = false;
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, " (");
|
|
||||||
|
|
||||||
if (ndistinct_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfoString(&buf, "ndistinct");
|
|
||||||
gotone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dependencies_enabled)
|
|
||||||
{
|
|
||||||
appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
|
|
||||||
gotone = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mcv_enabled)
|
|
||||||
appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
|
|
||||||
|
|
||||||
appendStringInfoChar(&buf, ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, " ON ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* decode simple column references */
|
|
||||||
for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
|
|
||||||
{
|
|
||||||
AttrNumber attnum = statextrec->stxkeys.values[colno];
|
|
||||||
|
|
||||||
if (colno > 0)
|
|
||||||
{
|
|
||||||
appendStringInfoString(&buf, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
char *attname = get_attname(statextrec->stxrelid, attnum, false);
|
|
||||||
|
|
||||||
appendStringInfoString(&buf, quote_identifier(attname));
|
|
||||||
}
|
|
||||||
|
|
||||||
context = deparse_context_for(get_relation_name(statextrec->stxrelid),
|
|
||||||
statextrec->stxrelid);
|
|
||||||
|
|
||||||
foreach(lc, exprs)
|
|
||||||
{
|
|
||||||
Node *expr = (Node *) lfirst(lc);
|
|
||||||
char *str;
|
|
||||||
int prettyFlags = PRETTYFLAG_INDENT;
|
|
||||||
|
|
||||||
str = deparse_expression_pretty(expr, context, false, false,
|
|
||||||
prettyFlags, 0);
|
|
||||||
|
|
||||||
if (colno > 0)
|
|
||||||
appendStringInfoString(&buf, ", ");
|
|
||||||
|
|
||||||
/* Need parens if it's not a bare function call */
|
|
||||||
if (looks_like_function(expr))
|
|
||||||
appendStringInfoString(&buf, str);
|
|
||||||
else
|
|
||||||
appendStringInfo(&buf, "(%s)", str);
|
|
||||||
|
|
||||||
colno++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!columns_only)
|
|
||||||
appendStringInfo(&buf, " FROM %s",
|
|
||||||
generate_relation_name(statextrec->stxrelid, NIL));
|
|
||||||
|
|
||||||
ReleaseSysCache(statexttup);
|
|
||||||
|
|
||||||
return buf.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
context.appendparents = NULL;
|
|
||||||
|
|
||||||
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
|
* get_opclass_name - fetch name of an index operator class
|
||||||
|
|
|
@ -54,13 +54,6 @@ bool get_merged_argument_list(CallStmt *stmt, List **mergedNamedArgList,
|
||||||
char * pg_get_rule_expr(Node *expression);
|
char * pg_get_rule_expr(Node *expression);
|
||||||
extern void deparse_shard_query(Query *query, Oid distrelid, int64 shardid,
|
extern void deparse_shard_query(Query *query, Oid distrelid, int64 shardid,
|
||||||
StringInfo buffer);
|
StringInfo buffer);
|
||||||
extern char * pg_get_triggerdef_command(Oid triggerId);
|
|
||||||
#if PG_VERSION_NUM >= PG_VERSION_14
|
|
||||||
extern char * pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
|
|
||||||
bool missing_ok);
|
|
||||||
#else
|
|
||||||
extern char * pg_get_statisticsobj_worker(Oid statextid, bool missing_ok);
|
|
||||||
#endif
|
|
||||||
extern char * generate_relation_name(Oid relid, List *namespaces);
|
extern char * generate_relation_name(Oid relid, List *namespaces);
|
||||||
extern char * generate_qualified_relation_name(Oid relid);
|
extern char * generate_qualified_relation_name(Oid relid);
|
||||||
extern char * generate_operator_name(Oid operid, Oid arg1, Oid arg2);
|
extern char * generate_operator_name(Oid operid, Oid arg1, Oid arg2);
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
#define RelationGetPartitionDesc_compat(a, b) RelationGetPartitionDesc(a, b)
|
#define RelationGetPartitionDesc_compat(a, b) RelationGetPartitionDesc(a, b)
|
||||||
#define make_simple_restrictinfo_compat(a, b) make_simple_restrictinfo(a, b)
|
#define make_simple_restrictinfo_compat(a, b) make_simple_restrictinfo(a, b)
|
||||||
#define pull_varnos_compat(a, b) pull_varnos(a, b)
|
#define pull_varnos_compat(a, b) pull_varnos(a, b)
|
||||||
#define pg_get_statisticsobj_worker_compat(a, b, c) pg_get_statisticsobj_worker(a, b, c)
|
|
||||||
#else
|
#else
|
||||||
#define AlterTableStmtObjType_compat(a) ((a)->relkind)
|
#define AlterTableStmtObjType_compat(a) ((a)->relkind)
|
||||||
#define F_NEXTVAL F_NEXTVAL_OID
|
#define F_NEXTVAL F_NEXTVAL_OID
|
||||||
|
@ -68,7 +67,6 @@
|
||||||
#define PQ_LARGE_MESSAGE_LIMIT 0
|
#define PQ_LARGE_MESSAGE_LIMIT 0
|
||||||
#define make_simple_restrictinfo_compat(a, b) make_simple_restrictinfo(b)
|
#define make_simple_restrictinfo_compat(a, b) make_simple_restrictinfo(b)
|
||||||
#define pull_varnos_compat(a, b) pull_varnos(b)
|
#define pull_varnos_compat(a, b) pull_varnos(b)
|
||||||
#define pg_get_statisticsobj_worker_compat(a, b, c) pg_get_statisticsobj_worker(a, c)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= PG_VERSION_13
|
#if PG_VERSION_NUM >= PG_VERSION_13
|
||||||
|
|
|
@ -146,7 +146,7 @@ SELECT master_get_table_ddl_events('test_table');
|
||||||
CREATE TABLE table_triggers_schema.test_table (id integer, text_number text, text_col text)
|
CREATE TABLE table_triggers_schema.test_table (id integer, text_number text, text_col text)
|
||||||
ALTER TABLE table_triggers_schema.test_table OWNER TO postgres
|
ALTER TABLE table_triggers_schema.test_table OWNER TO postgres
|
||||||
CREATE TRIGGER test_table_delete AFTER DELETE ON table_triggers_schema.test_table FOR EACH STATEMENT EXECUTE FUNCTION table_triggers_schema.test_table_trigger_function()
|
CREATE TRIGGER test_table_delete AFTER DELETE ON table_triggers_schema.test_table FOR EACH STATEMENT EXECUTE FUNCTION table_triggers_schema.test_table_trigger_function()
|
||||||
CREATE CONSTRAINT TRIGGER test_table_insert AFTER INSERT ON table_triggers_schema.test_table DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW WHEN (((new.id OPERATOR(pg_catalog.>) 5) OR ((new.text_col IS NOT NULL) AND ((new.id)::numeric OPERATOR(pg_catalog.<) to_number(new.text_number, '9999'::text))))) EXECUTE FUNCTION table_triggers_schema.test_table_trigger_function()
|
CREATE CONSTRAINT TRIGGER test_table_insert AFTER INSERT ON table_triggers_schema.test_table DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW WHEN (((new.id > 5) OR ((new.text_col IS NOT NULL) AND ((new.id)::numeric < to_number(new.text_number, '9999'::text))))) EXECUTE FUNCTION table_triggers_schema.test_table_trigger_function()
|
||||||
CREATE CONSTRAINT TRIGGER test_table_update AFTER UPDATE OF id ON table_triggers_schema.test_table NOT DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW WHEN (((NOT (old.* IS DISTINCT FROM new.*)) AND (old.text_number IS NOT NULL))) EXECUTE FUNCTION table_triggers_schema.test_table_trigger_function()
|
CREATE CONSTRAINT TRIGGER test_table_update AFTER UPDATE OF id ON table_triggers_schema.test_table NOT DEFERRABLE INITIALLY IMMEDIATE FOR EACH ROW WHEN (((NOT (old.* IS DISTINCT FROM new.*)) AND (old.text_number IS NOT NULL))) EXECUTE FUNCTION table_triggers_schema.test_table_trigger_function()
|
||||||
(5 rows)
|
(5 rows)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue