Junkfilter logic to force a projection step during SELECT INTO was too
authorTom Lane <[email protected]>
Tue, 2 Mar 2004 18:56:28 +0000 (18:56 +0000)
committerTom Lane <[email protected]>
Tue, 2 Mar 2004 18:56:28 +0000 (18:56 +0000)
simplistic; it recognized SELECT * FROM but not SELECT * FROM LIMIT.
Per bug report from Jeff Bohmer.

src/backend/executor/execAmi.c
src/backend/executor/execMain.c
src/include/executor/executor.h

index ecaaaf3a76011455616f6ea037b7f9dff6c3e13f..86d09345106bb3e861c7ee27ec955c8c9e5f29f3 100644 (file)
@@ -348,3 +348,68 @@ ExecSupportsBackwardScan(Plan *node)
                        return false;
        }
 }
+
+/*
+ * ExecMayReturnRawTuples
+ *             Check whether a plan tree may return "raw" disk tuples (that is,
+ *             pointers to original data in disk buffers, as opposed to temporary
+ *             tuples constructed by projection steps).  In the case of Append,
+ *             some subplans may return raw tuples and others projected tuples;
+ *             we return "true" if any of the returned tuples could be raw.
+ *
+ * This must be passed an already-initialized planstate tree, because we
+ * need to look at the results of ExecAssignScanProjectionInfo().
+ */
+bool
+ExecMayReturnRawTuples(PlanState *node)
+{
+       /*
+        * At a table scan node, we check whether ExecAssignScanProjectionInfo
+        * decided to do projection or not.  Most non-scan nodes always project
+        * and so we can return "false" immediately.  For nodes that don't
+        * project but just pass up input tuples, we have to recursively
+        * examine the input plan node.
+        *
+        * Note: Hash and Material are listed here because they sometimes
+        * return an original input tuple, not a copy.  But Sort and SetOp
+        * never return an original tuple, so they can be treated like
+        * projecting nodes.
+        */
+       switch (nodeTag(node))
+       {
+               /* Table scan nodes */
+               case T_SeqScanState:
+               case T_IndexScanState:
+               case T_TidScanState:
+               case T_SubqueryScanState:
+               case T_FunctionScanState:
+                       if (node->ps_ProjInfo == NULL)
+                               return true;
+                       break;
+
+               /* Non-projecting nodes */
+               case T_HashState:
+               case T_MaterialState:
+               case T_UniqueState:
+               case T_LimitState:
+                       return ExecMayReturnRawTuples(node->lefttree);
+
+               case T_AppendState:
+               {
+                       AppendState *appendstate = (AppendState *) node;
+                       int                     j;
+
+                       for (j = 0; j < appendstate->as_nplans; j++)
+                       {
+                               if (ExecMayReturnRawTuples(appendstate->appendplans[j]))
+                                       return true;
+                       }
+                       break;
+               }
+
+               /* All projecting node types come here */
+               default:
+                       break;
+       }
+       return false;
+}
index 654bc4ae19ef50476d6f09d01cd64a62096b6b0c..c05fe77bfed863a53a8ed1f58a5ca4b9dea94ad2 100644 (file)
@@ -665,10 +665,10 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
        /*
         * Initialize the junk filter if needed.  SELECT and INSERT queries
         * need a filter if there are any junk attrs in the tlist.      INSERT and
-        * SELECT INTO also need a filter if the top plan node is a scan node
-        * that's not doing projection (else we'll be scribbling on the scan
-        * tuple!)      UPDATE and DELETE always need a filter, since there's
-        * always a junk 'ctid' attribute present --- no need to look first.
+        * SELECT INTO also need a filter if the plan may return raw disk tuples
+        * (else heap_insert will be scribbling on the source relation!).
+        * UPDATE and DELETE always need a filter, since there's always a junk
+        * 'ctid' attribute present --- no need to look first.
         */
        {
                bool            junk_filter_needed = false;
@@ -689,18 +689,9 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
                                        }
                                }
                                if (!junk_filter_needed &&
-                                       (operation == CMD_INSERT || do_select_into))
-                               {
-                                       if (IsA(planstate, SeqScanState) ||
-                                               IsA(planstate, IndexScanState) ||
-                                               IsA(planstate, TidScanState) ||
-                                               IsA(planstate, SubqueryScanState) ||
-                                               IsA(planstate, FunctionScanState))
-                                       {
-                                               if (planstate->ps_ProjInfo == NULL)
-                                                       junk_filter_needed = true;
-                                       }
-                               }
+                                       (operation == CMD_INSERT || do_select_into) &&
+                                       ExecMayReturnRawTuples(planstate))
+                                       junk_filter_needed = true;
                                break;
                        case CMD_UPDATE:
                        case CMD_DELETE:
index 7334d1dbf551676611900d296ef31094d239253c..8365f151b13c2e06769d132d1edd52b2124b2636 100644 (file)
@@ -36,6 +36,7 @@ extern void ExecMarkPos(PlanState *node);
 extern void ExecRestrPos(PlanState *node);
 extern bool ExecSupportsMarkRestore(NodeTag plantype);
 extern bool ExecSupportsBackwardScan(Plan *node);
+extern bool ExecMayReturnRawTuples(PlanState *node);
 
 /*
  * prototypes from functions in execGrouping.c