public inbox for [email protected]help / color / mirror / Atom feed
[PATCH v9 2/4] WIP: Check for volatile defaults 24+ messages / 2 participants [nested] [flat]
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v8 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 3428d9f48a..47a5271b91 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 910906f639..3bfa59f1a5 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2177,6 +2177,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 687609f59e..84530468b8 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -769,6 +769,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 ee033ae779..1cd14fed3d 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 9c73c605a4..ef6d14e072 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1660,6 +1660,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 169d5581b9..ba2288f7b0 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1274,6 +1274,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 02a3c6b165..1043ee7b66 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -354,6 +354,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 c25012f325..9998db2268 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 cdbe781c73..c5e626f175 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1428,6 +1428,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 --AZuoSAvZwvV/ife4 Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v8-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v9 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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 a53cbeeb2c..2059428e2a 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" @@ -2258,6 +2259,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 * ---------------------------------------------------------------- @@ -2352,10 +2408,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 ba3ccc712c..2008e8e5d6 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 a2ef853dc2..aa3cdf3729 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 8392be6d44..924ffda1c6 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 d2c8d58070..642b6c63c5 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1275,6 +1275,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 0c7508a0d8..7ab13e51e5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -986,10 +986,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 --3yNHWXBV/QO9xKNm Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v9-0003-COPY-flush-multi-insert-buffer-based-on-accumulat.patch" ^ permalink raw reply [nested|flat] 24+ messages in thread
* [PATCH v10 2/4] WIP: Check for volatile defaults @ 2020-12-02 05:11 Justin Pryzby <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Justin Pryzby @ 2020-12-02 05:11 UTC (permalink / raw) 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" ^ permalink raw reply [nested|flat] 24+ messages in thread
* Re: [PATCH] psql: Add tab-complete for optional view parameters @ 2023-08-07 18:49 Christoph Heiss <[email protected]> 0 siblings, 0 replies; 24+ messages in thread From: Christoph Heiss @ 2023-08-07 18:49 UTC (permalink / raw) To: Jim Jones <[email protected]>; Mikhail Gribkov <[email protected]>; +Cc: pgsql-hackers Hi all, sorry for the long delay. On Mon, Jan 09, 2023 at 04:32:09PM +0100, Jim Jones wrote: > However, an "ALTER TABLE <name> S<tab>" does not complete the open > parenthesis "(" from "SET (", as suggested in "ALTER VIEW <name> <tab>". > > postgres=# ALTER VIEW w SET > Display all 187 possibilities? (y or n) > > Is it intended to behave like this? If so, an "ALTER VIEW <name> > RES<tab>" does complete the open parenthesis -> "RESET (". On Sun, Jan 29, 2023 at 10:19:12AM +0000, Mikhail Gribkov wrote: > The patch have a potential, although I have to agree with Jim Jones, > it obviously have a problem with "alter view <name> set<tab>" > handling. > [..] > I think it may worth looking at "alter materialized view" completion > tree and making "alter view" the same way. Thank you both for reviewing/testing and the suggestions. Yeah, definitively, sounds very sensible. I've attached a new revision, rebased and addressing the above by aligning it with how "ALTER MATERIALIZED VIEW" works, such that "SET (" and "SET SCHEMA" won't compete anymore. So that should now work more like expected. postgres=# ALTER MATERIALIZED VIEW m ALTER COLUMN CLUSTER ON DEPENDS ON EXTENSION NO DEPENDS ON EXTENSION OWNER TO RENAME RESET ( SET postgres=# ALTER MATERIALIZED VIEW m SET ( ACCESS METHOD SCHEMA TABLESPACE WITHOUT CLUSTER postgres=# ALTER VIEW v ALTER COLUMN OWNER TO RENAME RESET ( SET postgres=# ALTER VIEW v SET ( SCHEMA postgres=# ALTER VIEW v SET ( CHECK_OPTION SECURITY_BARRIER SECURITY_INVOKER On Fri, Jan 06, 2023 at 12:18:44PM +0000, Dean Rasheed wrote: > Hmm, I don't think we should be offering "check_option" as a tab > completion for CREATE VIEW at all, since that would encourage users to > use non-SQL-standard syntax, rather than CREATE VIEW ... WITH > [CASCADED|LOCAL] CHECK OPTION. Left that part in for now. I would argue that it is a well-documented combination and as such users would expect it to turn up in the tab-complete as well. OTOH not against removing it either, if there are others voicing the same opinion .. Thanks, Christoph From 3663eb0b5008d632972d4b66a105fc08cfff13fb Mon Sep 17 00:00:00 2001 From: Christoph Heiss <[email protected]> Date: Mon, 7 Aug 2023 20:37:19 +0200 Subject: [PATCH v3] psql: Add tab-complete for optional view parameters This adds them in the same matter as it works for storage parameters of tables. Signed-off-by: Christoph Heiss <[email protected]> --- src/bin/psql/tab-complete.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 779fdc90cb..83ec1508bb 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1329,6 +1329,13 @@ static const char *const table_storage_parameters[] = { NULL }; +/* Optional parameters for CREATE VIEW and ALTER VIEW */ +static const char *const view_optional_parameters[] = { + "check_option", + "security_barrier", + "security_invoker", + NULL +}; /* Forward declaration of functions */ static char **psql_completion(const char *text, int start, int end); @@ -2216,8 +2223,7 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH("TO"); /* ALTER VIEW <name> */ else if (Matches("ALTER", "VIEW", MatchAny)) - COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME", - "SET SCHEMA"); + COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME", "RESET (", "SET"); /* ALTER VIEW xxx RENAME */ else if (Matches("ALTER", "VIEW", MatchAny, "RENAME")) COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO"); @@ -2233,6 +2239,16 @@ psql_completion(const char *text, int start, int end) /* ALTER VIEW xxx RENAME COLUMN yyy */ else if (Matches("ALTER", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO"))) COMPLETE_WITH("TO"); + /* ALTER VIEW xxx SET ( yyy [= zzz] ) */ + else if (Matches("ALTER", "VIEW", MatchAny, "SET")) + COMPLETE_WITH("(", "SCHEMA"); + /* ALTER VIEW xxx SET|RESET ( yyy [= zzz] ) */ + else if (Matches("ALTER", "VIEW", MatchAny, "SET|RESET", "(")) + COMPLETE_WITH_LIST(view_optional_parameters); + else if (Matches("ALTER", "VIEW", MatchAny, "SET", "(", "check_option", "=")) + COMPLETE_WITH("local", "cascaded"); + else if (Matches("ALTER", "VIEW", MatchAny, "SET", "(", "security_barrier|security_invoker", "=")) + COMPLETE_WITH("true", "false"); /* ALTER MATERIALIZED VIEW <name> */ else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny)) @@ -3525,13 +3541,23 @@ psql_completion(const char *text, int start, int end) } /* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */ - /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS */ + /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS or WITH ( */ else if (TailMatches("CREATE", "VIEW", MatchAny) || TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny)) + COMPLETE_WITH("AS", "WITH ("); + /* Complete CREATE [ OR REPLACE ] VIEW <name> WITH ( with supported options */ + else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(")) + COMPLETE_WITH_LIST(view_optional_parameters); + /* Complete CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) with "AS" */ + else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)")) COMPLETE_WITH("AS"); - /* Complete "CREATE [ OR REPLACE ] VIEW <sth> AS with "SELECT" */ + /* Complete "CREATE [ OR REPLACE ] VIEW <sth> [ WITH ( ... ) ] AS with "SELECT" */ else if (TailMatches("CREATE", "VIEW", MatchAny, "AS") || - TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS")) + TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)", "AS") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)", "AS")) COMPLETE_WITH("SELECT"); /* CREATE MATERIALIZED VIEW */ -- 2.41.0 Attachments: [text/plain] v3-0001-psql-Add-tab-complete-for-optional-view-parameter.patch (3.8K, ../../7j6mvoz2f5ehlgzjgchyep2e7lwaizjpo5mhhoo4edddlva6yz@z423bkbzuujs/2-v3-0001-psql-Add-tab-complete-for-optional-view-parameter.patch) download | inline diff: From 3663eb0b5008d632972d4b66a105fc08cfff13fb Mon Sep 17 00:00:00 2001 From: Christoph Heiss <[email protected]> Date: Mon, 7 Aug 2023 20:37:19 +0200 Subject: [PATCH v3] psql: Add tab-complete for optional view parameters This adds them in the same matter as it works for storage parameters of tables. Signed-off-by: Christoph Heiss <[email protected]> --- src/bin/psql/tab-complete.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 779fdc90cb..83ec1508bb 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -1329,6 +1329,13 @@ static const char *const table_storage_parameters[] = { NULL }; +/* Optional parameters for CREATE VIEW and ALTER VIEW */ +static const char *const view_optional_parameters[] = { + "check_option", + "security_barrier", + "security_invoker", + NULL +}; /* Forward declaration of functions */ static char **psql_completion(const char *text, int start, int end); @@ -2216,8 +2223,7 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH("TO"); /* ALTER VIEW <name> */ else if (Matches("ALTER", "VIEW", MatchAny)) - COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME", - "SET SCHEMA"); + COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME", "RESET (", "SET"); /* ALTER VIEW xxx RENAME */ else if (Matches("ALTER", "VIEW", MatchAny, "RENAME")) COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO"); @@ -2233,6 +2239,16 @@ psql_completion(const char *text, int start, int end) /* ALTER VIEW xxx RENAME COLUMN yyy */ else if (Matches("ALTER", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO"))) COMPLETE_WITH("TO"); + /* ALTER VIEW xxx SET ( yyy [= zzz] ) */ + else if (Matches("ALTER", "VIEW", MatchAny, "SET")) + COMPLETE_WITH("(", "SCHEMA"); + /* ALTER VIEW xxx SET|RESET ( yyy [= zzz] ) */ + else if (Matches("ALTER", "VIEW", MatchAny, "SET|RESET", "(")) + COMPLETE_WITH_LIST(view_optional_parameters); + else if (Matches("ALTER", "VIEW", MatchAny, "SET", "(", "check_option", "=")) + COMPLETE_WITH("local", "cascaded"); + else if (Matches("ALTER", "VIEW", MatchAny, "SET", "(", "security_barrier|security_invoker", "=")) + COMPLETE_WITH("true", "false"); /* ALTER MATERIALIZED VIEW <name> */ else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny)) @@ -3525,13 +3541,23 @@ psql_completion(const char *text, int start, int end) } /* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */ - /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS */ + /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS or WITH ( */ else if (TailMatches("CREATE", "VIEW", MatchAny) || TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny)) + COMPLETE_WITH("AS", "WITH ("); + /* Complete CREATE [ OR REPLACE ] VIEW <name> WITH ( with supported options */ + else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(")) + COMPLETE_WITH_LIST(view_optional_parameters); + /* Complete CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) with "AS" */ + else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)")) COMPLETE_WITH("AS"); - /* Complete "CREATE [ OR REPLACE ] VIEW <sth> AS with "SELECT" */ + /* Complete "CREATE [ OR REPLACE ] VIEW <sth> [ WITH ( ... ) ] AS with "SELECT" */ else if (TailMatches("CREATE", "VIEW", MatchAny, "AS") || - TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS")) + TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)", "AS") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS") || + TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)", "AS")) COMPLETE_WITH("SELECT"); /* CREATE MATERIALIZED VIEW */ -- 2.41.0 ^ permalink raw reply [nested|flat] 24+ messages in thread
end of thread, other threads:[~2023-08-07 18:49 UTC | newest] Thread overview: 24+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v8 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v10 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2020-12-02 05:11 [PATCH v9 2/4] WIP: Check for volatile defaults Justin Pryzby <[email protected]> 2023-08-07 18:49 Re: [PATCH] psql: Add tab-complete for optional view parameters Christoph Heiss <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox