From: Justin Pryzby Date: Tue, 1 Dec 2020 23:11:31 -0600 Subject: [PATCH v10 2/4] WIP: Check for volatile defaults We want to check if any column *uses* a volatile default value, but after parsing and rewriting, that information appears to be lost about which column values are defaults and which were specified. insertedcols doesn't appear to be useful for this. So add a field to track if a TargetEntry is planned with column default. --- src/backend/executor/nodeModifyTable.c | 64 ++++++++++++++++++++++++-- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/equalfuncs.c | 1 + src/backend/nodes/makefuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/nodes/readfuncs.c | 1 + src/backend/optimizer/util/tlist.c | 1 + src/backend/rewrite/rewriteHandler.c | 3 ++ src/include/nodes/primnodes.h | 2 + 9 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 0907b3ebd5..e018946c0e 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -50,6 +50,7 @@ #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "optimizer/optimizer.h" #include "rewrite/rewriteHandler.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" @@ -2401,6 +2402,61 @@ ExecModifyTable(PlanState *pstate) return NULL; } +/* + * Determine if a table has volatile column defaults which are used by a given + * planned statement (if the column is not specified or specified as DEFAULT). + * This works only for INSERT. + */ +static bool +has_volatile_defaults(ResultRelInfo *resultRelInfo, ModifyTable *node) +{ + TupleDesc tupDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); + Plan *plan; + + Assert(list_length(node->plans) == 1); + plan = linitial(node->plans); + + for (int attnum = 1; attnum <= tupDesc->natts; attnum++) + { + Form_pg_attribute att = TupleDescAttr(tupDesc, attnum - 1); + Expr *defexpr; + TargetEntry *tle; + + /* We don't need to check dropped/generated attributes */ + if (att->attisdropped || att->attgenerated) + continue; + + tle = list_nth(plan->targetlist, attnum - 1); + Assert(tle != NULL); + Assert(tle->resno == attnum); + + /* + * If the column was specified with a non-default value, then don't + * check the volatility of its default + */ + if (!tle->isdefault) + continue; + + /* Check the column's default value if one exists */ + defexpr = (Expr *) build_column_default(resultRelInfo->ri_RelationDesc, attnum); + if (defexpr == NULL) + continue; + + /* Run the expression through planner */ + // defexpr = expression_planner(defexpr); + // (void) ExecInitExpr(defexpr, NULL); + expression_planner(defexpr); + + if (contain_volatile_functions_not_nextval((Node *) defexpr)) + { + elog(DEBUG1, "found volatile att %d", attnum); + return true; + } + } + + return false; +} + /* ---------------------------------------------------------------- * ExecInitModifyTable * ---------------------------------------------------------------- @@ -2495,10 +2551,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * are any statement level insert triggers. */ mtstate->miinfo = NULL; - else if (mtstate->rootResultRelInfo->ri_FdwRoutine != NULL - /* || cstate->volatile_defexprs */ ) - // XXX contain_volatile_functions_not_nextval((Node *) defexpr); - /* Can't support multi-inserts to foreign tables or if there are any */ + else if (mtstate->rootResultRelInfo->ri_FdwRoutine != NULL || + has_volatile_defaults(mtstate->rootResultRelInfo, node)) + /* Can't support multi-inserts to foreign tables or if there are any + * volatile default expressions in the table. */ mtstate->miinfo = NULL; else { diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 65bbc18ecb..0088539df7 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2178,6 +2178,7 @@ _copyTargetEntry(const TargetEntry *from) COPY_SCALAR_FIELD(resorigtbl); COPY_SCALAR_FIELD(resorigcol); COPY_SCALAR_FIELD(resjunk); + COPY_SCALAR_FIELD(isdefault); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c2d73626fc..12260f803f 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -770,6 +770,7 @@ _equalTargetEntry(const TargetEntry *a, const TargetEntry *b) COMPARE_SCALAR_FIELD(resorigtbl); COMPARE_SCALAR_FIELD(resorigcol); COMPARE_SCALAR_FIELD(resjunk); + COMPARE_SCALAR_FIELD(isdefault); return true; } diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 01c110cd2f..aeeba7032f 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -254,6 +254,7 @@ makeTargetEntry(Expr *expr, tle->ressortgroupref = 0; tle->resorigtbl = InvalidOid; tle->resorigcol = 0; + tle->isdefault = false; tle->resjunk = resjunk; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index f5dcedf6e8..50f5bfda8e 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1661,6 +1661,7 @@ _outTargetEntry(StringInfo str, const TargetEntry *node) WRITE_OID_FIELD(resorigtbl); WRITE_INT_FIELD(resorigcol); WRITE_BOOL_FIELD(resjunk); + WRITE_BOOL_FIELD(isdefault); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 4388aae71d..973b558eec 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1315,6 +1315,7 @@ _readTargetEntry(void) READ_OID_FIELD(resorigtbl); READ_INT_FIELD(resorigcol); READ_BOOL_FIELD(resjunk); + READ_BOOL_FIELD(isdefault); READ_DONE(); } diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 89853a0630..7ef1517027 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -349,6 +349,7 @@ apply_tlist_labeling(List *dest_tlist, List *src_tlist) dest_tle->resorigtbl = src_tle->resorigtbl; dest_tle->resorigcol = src_tle->resorigcol; dest_tle->resjunk = src_tle->resjunk; + dest_tle->isdefault = src_tle->isdefault; } } diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 0672f497c6..3d93e24004 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -987,10 +987,13 @@ rewriteTargetListIU(List *targetList, } if (new_expr) + { new_tle = makeTargetEntry((Expr *) new_expr, attrno, pstrdup(NameStr(att_tup->attname)), false); + new_tle->isdefault = true; + } } /* diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index d4ce037088..888bd36a07 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1437,6 +1437,8 @@ typedef struct TargetEntry AttrNumber resorigcol; /* column's number in source table */ bool resjunk; /* set to true to eliminate the attribute from * final target list */ + bool isdefault; /* true if using the column default, either + * by "DEFAULT" or omission of the column */ } TargetEntry; -- 2.17.0 --8nsIa27JVQLqB7/C Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v10-0003-COPY-flush-multi-insert-buffer-based-on-accumula.patch"