From 4d1e2aeb1a162770683a8d1e13fc13ac2d95d810 Mon Sep 17 00:00:00 2001 From: Simon Riggs Date: Mon, 20 Jan 2014 17:22:38 +0000 Subject: [PATCH] Speed up COPY into tables with DEFAULT nextval() Previously the presence of a nextval() prevented the use of batch-mode COPY. This patch introduces a special case just for nextval() functions. In future we will introduce a general case solution for labelling volatile functions as safe for use. --- src/backend/commands/copy.c | 15 +++- src/backend/optimizer/util/clauses.c | 116 +++++++++++++++++++++++++++ src/include/optimizer/clauses.h | 1 + 3 files changed, 130 insertions(+), 2 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index f91f1646dac..7c4039cb7ff 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2519,9 +2519,20 @@ BeginCopyFrom(Relation rel, defmap[num_defaults] = attnum - 1; num_defaults++; - /* Check to see if we have any volatile expressions */ + /* + * If a default expression looks at the table being loaded, then + * it could give the wrong answer when using multi-insert. Since + * database access can be dynamic this is hard to test for + * exactly, so we use the much wider test of whether the + * default expression is volatile. We allow for the special case + * of when the default expression is the nextval() of a sequence + * which in this specific case is known to be safe for use with + * the multi-insert optimisation. Hence we use this special case + * function checker rather than the standard check for + * contain_volatile_functions(). + */ if (!volatile_defexprs) - volatile_defexprs = contain_volatile_functions((Node *) defexpr); + volatile_defexprs = contain_volatile_functions_not_nextval((Node *)defexpr); } } } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index e0dbaf7e4e5..201529b885a 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -45,6 +45,7 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/syscache.h" @@ -94,6 +95,7 @@ static bool expression_returns_set_rows_walker(Node *node, double *count); static bool contain_subplans_walker(Node *node, void *context); static bool contain_mutable_functions_walker(Node *node, void *context); static bool contain_volatile_functions_walker(Node *node, void *context); +static bool contain_volatile_functions_not_nextval_walker(Node *node, void *context); static bool contain_nonstrict_functions_walker(Node *node, void *context); static bool contain_leaky_functions_walker(Node *node, void *context); static Relids find_nonnullable_rels_walker(Node *node, bool top_level); @@ -971,6 +973,19 @@ contain_volatile_functions(Node *clause) return contain_volatile_functions_walker(clause, NULL); } +bool +contain_volatile_functions_not_nextval(Node *clause) +{ + return contain_volatile_functions_not_nextval_walker(clause, NULL); +} + +/* + * General purpose code for checking expression volatility. + * + * Special purpose code for use in COPY is almost identical to this, + * so any changes here may also be needed in other contain_volatile... + * functions. + */ static bool contain_volatile_functions_walker(Node *node, void *context) { @@ -1072,6 +1087,107 @@ contain_volatile_functions_walker(Node *node, void *context) context); } +/* + * Special purpose version of contain_volatile_functions for use in COPY + */ +static bool +contain_volatile_functions_not_nextval_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, FuncExpr)) + { + FuncExpr *expr = (FuncExpr *) node; + + /* + * For this case only, we want to ignore the volatility of the + * nextval() function, since some callers want this. + */ + if (expr->funcid != F_NEXTVAL_OID && + func_volatile(expr->funcid) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, OpExpr)) + { + OpExpr *expr = (OpExpr *) node; + + set_opfuncid(expr); + if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, DistinctExpr)) + { + DistinctExpr *expr = (DistinctExpr *) node; + + set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ + if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, NullIfExpr)) + { + NullIfExpr *expr = (NullIfExpr *) node; + + set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ + if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, ScalarArrayOpExpr)) + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + set_sa_opfuncid(expr); + if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, CoerceViaIO)) + { + CoerceViaIO *expr = (CoerceViaIO *) node; + Oid iofunc; + Oid typioparam; + bool typisvarlena; + + /* check the result type's input function */ + getTypeInputInfo(expr->resulttype, + &iofunc, &typioparam); + if (func_volatile(iofunc) == PROVOLATILE_VOLATILE) + return true; + /* check the input type's output function */ + getTypeOutputInfo(exprType((Node *) expr->arg), + &iofunc, &typisvarlena); + if (func_volatile(iofunc) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; + + if (OidIsValid(expr->elemfuncid) && + func_volatile(expr->elemfuncid) == PROVOLATILE_VOLATILE) + return true; + /* else fall through to check args */ + } + else if (IsA(node, RowCompareExpr)) + { + /* RowCompare probably can't have volatile ops, but check anyway */ + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + ListCell *opid; + + foreach(opid, rcexpr->opnos) + { + if (op_volatile(lfirst_oid(opid)) == PROVOLATILE_VOLATILE) + return true; + } + /* else fall through to check args */ + } + return expression_tree_walker(node, contain_volatile_functions_not_nextval_walker, + context); +} /***************************************************************************** * Check clauses for nonstrict functions diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 53484ff8c06..dd991b16bce 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -61,6 +61,7 @@ extern bool contain_subplans(Node *clause); extern bool contain_mutable_functions(Node *clause); extern bool contain_volatile_functions(Node *clause); +extern bool contain_volatile_functions_not_nextval(Node *clause); extern bool contain_nonstrict_functions(Node *clause); extern bool contain_leaky_functions(Node *clause); -- 2.39.5