Speed up COPY into tables with DEFAULT nextval()
authorSimon Riggs <simon@2ndQuadrant.com>
Mon, 20 Jan 2014 17:22:38 +0000 (17:22 +0000)
committerSimon Riggs <simon@2ndQuadrant.com>
Mon, 20 Jan 2014 17:22:38 +0000 (17:22 +0000)
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
src/backend/optimizer/util/clauses.c
src/include/optimizer/clauses.h

index f91f1646dac28e474d30685c52d9f85d3ad09d22..7c4039cb7ff2d225248ef8bdacabd385d25cf540 100644 (file)
@@ -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);
                        }
                }
        }
index e0dbaf7e4e531924d7a799d44586438cefdbb0d5..201529b885a6211a16e5565695645e5d620d4a4b 100644 (file)
@@ -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
index 53484ff8c0604182a3e29de0e2e40c439c850770..dd991b16bce0da3ea5c8ff3d2c4bd25a952ea980 100644 (file)
@@ -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);