Tweak ExecIndexEvalRuntimeKeys to forcibly detoast any toasted comparison
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 23 Aug 2009 18:26:08 +0000 (18:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 23 Aug 2009 18:26:08 +0000 (18:26 +0000)
values before they get passed to the index access method.  This avoids
repeated detoastings that will otherwise ensue as the comparison value
is examined by various index support functions.  We have seen a couple of
reports of cases where repeated detoastings result in an order-of-magnitude
slowdown, so it seems worth adding a bit of extra logic to prevent this.

I had previously proposed trying to avoid duplicate detoastings in general,
but this fix takes care of what seems the most important case in practice
with very little effort or risk.

Back-patch to 8.4 so that the PostGIS folk won't have to wait a year to
have this fix in a production release.  (The issue exists further back,
of course, but the code's diverged enough to make backpatching further a
higher-risk action.  Also it appears that the possible gains may be limited
in prior releases because of different handling of lossy operators.)

src/backend/executor/nodeIndexscan.c
src/include/nodes/execnodes.h

index e2ad067049ce7dd259278ba84100aab138f367f4..d8ec8b34f5d1583ec5beb04f90c07ee1790b5e3f 100644 (file)
@@ -238,6 +238,10 @@ ExecIndexEvalRuntimeKeys(ExprContext *econtext,
                                                 IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys)
 {
        int                     j;
+       MemoryContext oldContext;
+
+       /* We want to keep the key values in per-tuple memory */
+       oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
 
        for (j = 0; j < numRuntimeKeys; j++)
        {
@@ -256,18 +260,32 @@ ExecIndexEvalRuntimeKeys(ExprContext *econtext,
                 * econtext->ecxt_per_tuple_memory.  We assume that the outer tuple
                 * will stay put throughout our scan.  If this is wrong, we could copy
                 * the result into our context explicitly, but I think that's not
-                * necessary...
+                * necessary.
+                *
+                * It's also entirely possible that the result of the eval is a
+                * toasted value.  In this case we should forcibly detoast it,
+                * to avoid repeat detoastings each time the value is examined
+                * by an index support function.
                 */
-               scanvalue = ExecEvalExprSwitchContext(key_expr,
-                                                                                         econtext,
-                                                                                         &isNull,
-                                                                                         NULL);
-               scan_key->sk_argument = scanvalue;
+               scanvalue = ExecEvalExpr(key_expr,
+                                                                econtext,
+                                                                &isNull,
+                                                                NULL);
                if (isNull)
+               {
+                       scan_key->sk_argument = scanvalue;
                        scan_key->sk_flags |= SK_ISNULL;
+               }
                else
+               {
+                       if (runtimeKeys[j].key_toastable)
+                               scanvalue = PointerGetDatum(PG_DETOAST_DATUM(scanvalue));
+                       scan_key->sk_argument = scanvalue;
                        scan_key->sk_flags &= ~SK_ISNULL;
+               }
        }
+
+       MemoryContextSwitchTo(oldContext);
 }
 
 /*
@@ -795,6 +813,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
                                runtime_keys[n_runtime_keys].scan_key = this_scan_key;
                                runtime_keys[n_runtime_keys].key_expr =
                                        ExecInitExpr(rightop, planstate);
+                               runtime_keys[n_runtime_keys].key_toastable =
+                                       TypeIsToastable(op_righttype);
                                n_runtime_keys++;
                                scanvalue = (Datum) 0;
                        }
@@ -843,6 +863,31 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
 
                                varattno = ((Var *) leftop)->varattno;
 
+                               /*
+                                * We have to look up the operator's associated btree support
+                                * function
+                                */
+                               opno = lfirst_oid(opnos_cell);
+                               opnos_cell = lnext(opnos_cell);
+
+                               if (index->rd_rel->relam != BTREE_AM_OID ||
+                                       varattno < 1 || varattno > index->rd_index->indnatts)
+                                       elog(ERROR, "bogus RowCompare index qualification");
+                               opfamily = index->rd_opfamily[varattno - 1];
+
+                               get_op_opfamily_properties(opno, opfamily,
+                                                                                  &op_strategy,
+                                                                                  &op_lefttype,
+                                                                                  &op_righttype);
+
+                               if (op_strategy != rc->rctype)
+                                       elog(ERROR, "RowCompare index qualification contains wrong operator");
+
+                               opfuncid = get_opfamily_proc(opfamily,
+                                                                                        op_lefttype,
+                                                                                        op_righttype,
+                                                                                        BTORDER_PROC);
+
                                /*
                                 * rightop is the constant or variable comparison value
                                 */
@@ -867,35 +912,12 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
                                        runtime_keys[n_runtime_keys].scan_key = this_sub_key;
                                        runtime_keys[n_runtime_keys].key_expr =
                                                ExecInitExpr(rightop, planstate);
+                                       runtime_keys[n_runtime_keys].key_toastable =
+                                               TypeIsToastable(op_righttype);
                                        n_runtime_keys++;
                                        scanvalue = (Datum) 0;
                                }
 
-                               /*
-                                * We have to look up the operator's associated btree support
-                                * function
-                                */
-                               opno = lfirst_oid(opnos_cell);
-                               opnos_cell = lnext(opnos_cell);
-
-                               if (index->rd_rel->relam != BTREE_AM_OID ||
-                                       varattno < 1 || varattno > index->rd_index->indnatts)
-                                       elog(ERROR, "bogus RowCompare index qualification");
-                               opfamily = index->rd_opfamily[varattno - 1];
-
-                               get_op_opfamily_properties(opno, opfamily,
-                                                                                  &op_strategy,
-                                                                                  &op_lefttype,
-                                                                                  &op_righttype);
-
-                               if (op_strategy != rc->rctype)
-                                       elog(ERROR, "RowCompare index qualification contains wrong operator");
-
-                               opfuncid = get_opfamily_proc(opfamily,
-                                                                                        op_lefttype,
-                                                                                        op_righttype,
-                                                                                        BTORDER_PROC);
-
                                /*
                                 * initialize the subsidiary scan key's fields appropriately
                                 */
index 48beaa47095b790fd7d32b6f07d3f66a11a5695e..34fcf6899a042b836acc6aaaf53870c0ab871c67 100644 (file)
@@ -1084,6 +1084,7 @@ typedef struct
 {
        ScanKey         scan_key;               /* scankey to put value into */
        ExprState  *key_expr;           /* expr to evaluate to get value */
+       bool            key_toastable;  /* is expr's result a toastable datatype? */
 } IndexRuntimeKeyInfo;
 
 typedef struct