diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index d9a24a3448..8bbd1942eb 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.77 2003/12/18 20:21:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.78 2004/03/02 18:56:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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; +} diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 4f36602aa0..caae6e880e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.228 2004/01/22 02:23:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.229 2004/03/02 18:56:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -659,10 +659,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; @@ -683,18 +683,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: diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 39e07da8af..cc2e49efaf 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.106 2004/01/22 02:23:21 tgl Exp $ + * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.107 2004/03/02 18:56:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -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