1998-02-13 04:26:53 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* nodeSubplan.c
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
* routines to support sub-selects appearing in expressions
|
|
|
|
*
|
|
|
|
* This module is concerned with executing SubPlan expression nodes, which
|
|
|
|
* should not be confused with sub-SELECTs appearing in FROM. SubPlans are
|
|
|
|
* divided into "initplans", which are those that need only one evaluation per
|
|
|
|
* query (among other restrictions, this requires that they don't use any
|
|
|
|
* direct correlation variables from the parent plan level), and "regular"
|
|
|
|
* subplans, which are re-evaluated every time their result is required.
|
|
|
|
*
|
1998-02-13 04:26:53 +01:00
|
|
|
*
|
2020-01-01 18:21:45 +01:00
|
|
|
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1999-11-15 03:00:15 +01:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/executor/nodeSubplan.c
|
1999-11-15 03:00:15 +01:00
|
|
|
*
|
1998-02-13 04:26:53 +01:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* INTERFACE ROUTINES
|
1998-02-26 05:46:47 +01:00
|
|
|
* ExecSubPlan - process a subselect
|
1998-02-13 04:26:53 +01:00
|
|
|
* ExecInitSubPlan - initialize a subselect
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2012-08-29 01:02:00 +02:00
|
|
|
#include <limits.h>
|
2006-07-14 07:28:29 +02:00
|
|
|
#include <math.h>
|
|
|
|
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
1998-02-13 04:26:53 +01:00
|
|
|
#include "executor/executor.h"
|
|
|
|
#include "executor/nodeSubplan.h"
|
2019-11-12 04:00:16 +01:00
|
|
|
#include "miscadmin.h"
|
2003-01-12 05:03:34 +01:00
|
|
|
#include "nodes/makefuncs.h"
|
2019-01-29 21:26:44 +01:00
|
|
|
#include "nodes/nodeFuncs.h"
|
2011-09-04 07:13:16 +02:00
|
|
|
#include "utils/array.h"
|
2003-04-09 01:20:04 +02:00
|
|
|
#include "utils/lsyscache.h"
|
2005-05-06 19:24:55 +02:00
|
|
|
#include "utils/memutils.h"
|
1998-02-13 04:26:53 +01:00
|
|
|
|
2003-08-08 23:42:59 +02:00
|
|
|
static Datum ExecHashSubPlan(SubPlanState *node,
|
2019-05-22 19:04:48 +02:00
|
|
|
ExprContext *econtext,
|
|
|
|
bool *isNull);
|
2003-08-08 23:42:59 +02:00
|
|
|
static Datum ExecScanSubPlan(SubPlanState *node,
|
2019-05-22 19:04:48 +02:00
|
|
|
ExprContext *econtext,
|
|
|
|
bool *isNull);
|
2007-02-27 02:11:26 +01:00
|
|
|
static void buildSubPlanHash(SubPlanState *node, ExprContext *econtext);
|
2012-10-11 18:20:56 +02:00
|
|
|
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
|
2019-05-22 19:04:48 +02:00
|
|
|
FmgrInfo *eqfunctions);
|
2005-03-16 22:38:10 +01:00
|
|
|
static bool slotAllNulls(TupleTableSlot *slot);
|
|
|
|
static bool slotNoNulls(TupleTableSlot *slot);
|
2003-06-25 23:30:34 +02:00
|
|
|
|
2003-01-12 05:03:34 +01:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
/* ----------------------------------------------------------------
|
2003-01-12 05:03:34 +01:00
|
|
|
* ExecSubPlan
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
*
|
|
|
|
* This is the main entry point for execution of a regular SubPlan.
|
1998-02-13 04:26:53 +01:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
Datum
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecSubPlan(SubPlanState *node,
|
2002-12-13 20:46:01 +01:00
|
|
|
ExprContext *econtext,
|
2017-01-19 23:12:38 +01:00
|
|
|
bool *isNull)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
SubPlan *subplan = node->subplan;
|
2018-08-17 16:04:26 +02:00
|
|
|
EState *estate = node->planstate->state;
|
|
|
|
ScanDirection dir = estate->es_direction;
|
|
|
|
Datum retval;
|
2003-01-12 05:03:34 +01:00
|
|
|
|
2017-07-26 02:37:17 +02:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
|
2017-01-19 23:12:38 +01:00
|
|
|
/* Set non-null as default */
|
2004-03-17 02:02:24 +01:00
|
|
|
*isNull = false;
|
|
|
|
|
2008-10-04 23:56:55 +02:00
|
|
|
/* Sanity checks */
|
|
|
|
if (subplan->subLinkType == CTE_SUBLINK)
|
|
|
|
elog(ERROR, "CTE subplans should not be executed via ExecSubPlan");
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
if (subplan->setParam != NIL && subplan->subLinkType != MULTIEXPR_SUBLINK)
|
2003-07-21 19:05:12 +02:00
|
|
|
elog(ERROR, "cannot set parent params from subquery");
|
2003-01-12 05:03:34 +01:00
|
|
|
|
2018-08-17 16:04:26 +02:00
|
|
|
/* Force forward-scan mode for evaluation */
|
|
|
|
estate->es_direction = ForwardScanDirection;
|
|
|
|
|
2008-10-04 23:56:55 +02:00
|
|
|
/* Select appropriate evaluation strategy */
|
2003-01-12 05:03:34 +01:00
|
|
|
if (subplan->useHashTable)
|
2018-08-17 16:04:26 +02:00
|
|
|
retval = ExecHashSubPlan(node, econtext, isNull);
|
2003-01-12 05:03:34 +01:00
|
|
|
else
|
2018-08-17 16:04:26 +02:00
|
|
|
retval = ExecScanSubPlan(node, econtext, isNull);
|
|
|
|
|
|
|
|
/* restore scan direction */
|
|
|
|
estate->es_direction = dir;
|
|
|
|
|
|
|
|
return retval;
|
2003-01-12 05:03:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ExecHashSubPlan: store subselect result in an in-memory hash table
|
|
|
|
*/
|
|
|
|
static Datum
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecHashSubPlan(SubPlanState *node,
|
2003-01-12 05:03:34 +01:00
|
|
|
ExprContext *econtext,
|
|
|
|
bool *isNull)
|
|
|
|
{
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
SubPlan *subplan = node->subplan;
|
2003-01-12 05:03:34 +01:00
|
|
|
PlanState *planstate = node->planstate;
|
|
|
|
TupleTableSlot *slot;
|
|
|
|
|
|
|
|
/* Shouldn't have any direct correlation Vars */
|
|
|
|
if (subplan->parParam != NIL || node->args != NIL)
|
2003-07-21 19:05:12 +02:00
|
|
|
elog(ERROR, "hashed subplan with direct correlation not supported");
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If first time through or we need to rescan the subplan, build the hash
|
|
|
|
* table.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
2003-02-09 01:30:41 +01:00
|
|
|
if (node->hashtable == NULL || planstate->chgParam != NULL)
|
2007-02-27 02:11:26 +01:00
|
|
|
buildSubPlanHash(node, econtext);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* The result for an empty subplan is always FALSE; no need to evaluate
|
|
|
|
* lefthand side.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
*isNull = false;
|
|
|
|
if (!node->havehashrows && !node->havenullrows)
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
|
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Evaluate lefthand expressions and form a projection tuple. First we
|
|
|
|
* have to set the econtext to use (hack alert!).
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
node->projLeft->pi_exprContext = econtext;
|
2017-01-19 23:12:38 +01:00
|
|
|
slot = ExecProject(node->projLeft);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Note: because we are typically called in a per-tuple context, we have
|
|
|
|
* to explicitly clear the projected tuple before returning. Otherwise,
|
|
|
|
* we'll have a double-free situation: the per-tuple context will probably
|
|
|
|
* be reset before we're called again, and then the tuple slot will think
|
|
|
|
* it still needs to free the tuple.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If the LHS is all non-null, probe for an exact match in the main hash
|
|
|
|
* table. If we find one, the result is TRUE. Otherwise, scan the
|
|
|
|
* partly-null table to see if there are any rows that aren't provably
|
|
|
|
* unequal to the LHS; if so, the result is UNKNOWN. (We skip that part
|
|
|
|
* if we don't care about UNKNOWN.) Otherwise, the result is FALSE.
|
2003-01-12 05:03:34 +01:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* Note: the reason we can avoid a full scan of the main hash table is
|
|
|
|
* that the combining operators are assumed never to yield NULL when both
|
2005-10-15 04:49:52 +02:00
|
|
|
* inputs are non-null. If they were to do so, we might need to produce
|
|
|
|
* UNKNOWN instead of FALSE because of an UNKNOWN result in comparing the
|
|
|
|
* LHS to some main-table entry --- which is a comparison we will not even
|
|
|
|
* make, unless there's a chance match of hash keys.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
2005-03-16 22:38:10 +01:00
|
|
|
if (slotNoNulls(slot))
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
|
|
|
if (node->havehashrows &&
|
2007-02-06 03:59:15 +01:00
|
|
|
FindTupleHashEntry(node->hashtable,
|
|
|
|
slot,
|
2018-02-16 06:55:31 +01:00
|
|
|
node->cur_eq_comp,
|
2007-02-06 03:59:15 +01:00
|
|
|
node->lhs_hash_funcs) != NULL)
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
return BoolGetDatum(true);
|
|
|
|
}
|
|
|
|
if (node->havenullrows &&
|
2012-10-11 18:20:56 +02:00
|
|
|
findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs))
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
*isNull = true;
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
}
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* When the LHS is partly or wholly NULL, we can never return TRUE. If we
|
|
|
|
* don't care about UNKNOWN, just return FALSE. Otherwise, if the LHS is
|
|
|
|
* wholly NULL, immediately return UNKNOWN. (Since the combining
|
|
|
|
* operators are strict, the result could only be FALSE if the sub-select
|
|
|
|
* were empty, but we already handled that case.) Otherwise, we must scan
|
|
|
|
* both the main and partly-null tables to see if there are any rows that
|
|
|
|
* aren't provably unequal to the LHS; if so, the result is UNKNOWN.
|
|
|
|
* Otherwise, the result is FALSE.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
if (node->hashnulls == NULL)
|
|
|
|
{
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
}
|
2005-03-16 22:38:10 +01:00
|
|
|
if (slotAllNulls(slot))
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
*isNull = true;
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
}
|
|
|
|
/* Scan partly-null table first, since more likely to get a match */
|
|
|
|
if (node->havenullrows &&
|
2012-10-11 18:20:56 +02:00
|
|
|
findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs))
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
*isNull = true;
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
}
|
|
|
|
if (node->havehashrows &&
|
2012-10-11 18:20:56 +02:00
|
|
|
findPartialMatch(node->hashtable, slot, node->cur_eq_funcs))
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
*isNull = true;
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
}
|
|
|
|
ExecClearTuple(slot);
|
|
|
|
return BoolGetDatum(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ExecScanSubPlan: default case where we have to rescan subplan each time
|
|
|
|
*/
|
|
|
|
static Datum
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecScanSubPlan(SubPlanState *node,
|
2003-01-12 05:03:34 +01:00
|
|
|
ExprContext *econtext,
|
|
|
|
bool *isNull)
|
|
|
|
{
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
SubPlan *subplan = node->subplan;
|
2002-12-05 16:50:39 +01:00
|
|
|
PlanState *planstate = node->planstate;
|
2002-12-14 01:17:59 +01:00
|
|
|
SubLinkType subLinkType = subplan->subLinkType;
|
2000-07-12 04:37:39 +02:00
|
|
|
MemoryContext oldcontext;
|
1998-02-13 04:26:53 +01:00
|
|
|
TupleTableSlot *slot;
|
1999-11-12 07:39:34 +01:00
|
|
|
Datum result;
|
2017-08-16 06:22:32 +02:00
|
|
|
bool found = false; /* true if got at least one subplan tuple */
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *pvar;
|
|
|
|
ListCell *l;
|
2014-11-25 18:21:22 +01:00
|
|
|
ArrayBuildStateAny *astate = NULL;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
/*
|
|
|
|
* MULTIEXPR subplans, when "executed", just return NULL; but first we
|
|
|
|
* mark the subplan's output parameters as needing recalculation. (This
|
|
|
|
* is a bit of a hack: it relies on the subplan appearing later in its
|
|
|
|
* targetlist than any of the referencing Params, so that all the Params
|
|
|
|
* have been evaluated before we re-mark them for the next evaluation
|
|
|
|
* cycle. But in general resjunk tlist items appear after non-resjunk
|
|
|
|
* ones, so this should be safe.) Unlike ExecReScanSetParamPlan, we do
|
|
|
|
* *not* set bits in the parent plan node's chgParam, because we don't
|
|
|
|
* want to cause a rescan of the parent.
|
|
|
|
*/
|
|
|
|
if (subLinkType == MULTIEXPR_SUBLINK)
|
|
|
|
{
|
|
|
|
EState *estate = node->parent->state;
|
|
|
|
|
|
|
|
foreach(l, subplan->setParam)
|
|
|
|
{
|
|
|
|
int paramid = lfirst_int(l);
|
|
|
|
ParamExecData *prm = &(estate->es_param_exec_vals[paramid]);
|
|
|
|
|
|
|
|
prm->execPlan = node;
|
|
|
|
}
|
|
|
|
*isNull = true;
|
|
|
|
return (Datum) 0;
|
|
|
|
}
|
|
|
|
|
2014-11-25 18:21:22 +01:00
|
|
|
/* Initialize ArrayBuildStateAny in caller's context, if needed */
|
|
|
|
if (subLinkType == ARRAY_SUBLINK)
|
|
|
|
astate = initArrayResultAny(subplan->firstColType,
|
2015-02-22 02:24:48 +01:00
|
|
|
CurrentMemoryContext, true);
|
2014-11-25 18:21:22 +01:00
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We are probably in a short-lived expression-evaluation context. Switch
|
2007-02-27 02:11:26 +01:00
|
|
|
* to the per-query context for manipulating the child plan's chgParam,
|
2005-10-15 04:49:52 +02:00
|
|
|
* calling ExecProcNode on it, etc.
|
2000-07-12 04:37:39 +02:00
|
|
|
*/
|
2007-02-27 02:11:26 +01:00
|
|
|
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
2000-07-12 04:37:39 +02:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
/*
|
2003-08-04 02:43:34 +02:00
|
|
|
* Set Params of this plan from parent plan correlation values. (Any
|
|
|
|
* calculation we have to do is done in the parent econtext, since the
|
|
|
|
* Param values don't need to have per-query lifetime.)
|
1998-02-13 04:26:53 +01:00
|
|
|
*/
|
2004-05-31 01:40:41 +02:00
|
|
|
Assert(list_length(subplan->parParam) == list_length(node->args));
|
2004-05-26 06:41:50 +02:00
|
|
|
|
|
|
|
forboth(l, subplan->parParam, pvar, node->args)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
int paramid = lfirst_int(l);
|
2003-02-09 01:30:41 +01:00
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
|
|
|
|
|
|
|
prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
|
|
|
|
econtext,
|
2017-01-19 23:12:38 +01:00
|
|
|
&(prm->isnull));
|
2003-02-09 01:30:41 +01:00
|
|
|
planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2007-05-17 21:35:08 +02:00
|
|
|
/*
|
|
|
|
* Now that we've set up its parameters, we can reset the subplan.
|
|
|
|
*/
|
2010-07-12 19:01:06 +02:00
|
|
|
ExecReScan(planstate);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-04-19 06:17:11 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* For all sublink types except EXPR_SUBLINK and ARRAY_SUBLINK, the result
|
|
|
|
* is boolean as are the results of the combining operators. We combine
|
|
|
|
* results across tuples (if the subplan produces more than one) using OR
|
|
|
|
* semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK.
|
2005-12-28 02:30:02 +01:00
|
|
|
* (ROWCOMPARE_SUBLINK doesn't allow multiple tuples from the subplan.)
|
2005-10-15 04:49:52 +02:00
|
|
|
* NULL results from the combining operators are handled according to the
|
2014-05-06 18:12:18 +02:00
|
|
|
* usual SQL semantics for OR and AND. The result for no input tuples is
|
2005-10-15 04:49:52 +02:00
|
|
|
* FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK, NULL for
|
2005-12-28 02:30:02 +01:00
|
|
|
* ROWCOMPARE_SUBLINK.
|
1999-04-19 06:17:11 +02:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* For EXPR_SUBLINK we require the subplan to produce no more than one
|
2014-05-06 18:12:18 +02:00
|
|
|
* tuple, else an error is raised. If zero tuples are produced, we return
|
2007-08-26 23:44:25 +02:00
|
|
|
* NULL. Assuming we get a tuple, we just use its first column (there can
|
|
|
|
* be only one non-junk column in this case).
|
|
|
|
*
|
|
|
|
* For ARRAY_SUBLINK we allow the subplan to produce any number of tuples,
|
|
|
|
* and form an array of the first column's values. Note in particular
|
2007-11-15 22:14:46 +01:00
|
|
|
* that we produce a zero-element array if no tuples are produced (this is
|
|
|
|
* a change from pre-8.3 behavior of returning NULL).
|
1999-04-19 06:17:11 +02:00
|
|
|
*/
|
2000-07-12 04:37:39 +02:00
|
|
|
result = BoolGetDatum(subLinkType == ALL_SUBLINK);
|
1999-11-12 07:39:34 +01:00
|
|
|
*isNull = false;
|
1999-04-19 06:17:11 +02:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
for (slot = ExecProcNode(planstate);
|
1998-02-26 05:46:47 +01:00
|
|
|
!TupIsNull(slot);
|
2002-12-05 16:50:39 +01:00
|
|
|
slot = ExecProcNode(planstate))
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2005-03-16 22:38:10 +01:00
|
|
|
TupleDesc tdesc = slot->tts_tupleDescriptor;
|
2005-12-28 02:30:02 +01:00
|
|
|
Datum rowresult;
|
|
|
|
bool rownull;
|
|
|
|
int col;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *plst;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-04-19 06:17:11 +02:00
|
|
|
if (subLinkType == EXISTS_SUBLINK)
|
2000-07-12 04:37:39 +02:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
result = BoolGetDatum(true);
|
|
|
|
break;
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1999-11-15 03:00:15 +01:00
|
|
|
if (subLinkType == EXPR_SUBLINK)
|
|
|
|
{
|
|
|
|
/* cannot allow multiple input tuples for EXPR sublink */
|
|
|
|
if (found)
|
2003-07-21 19:05:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_CARDINALITY_VIOLATION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("more than one row returned by a subquery used as an expression")));
|
1999-11-15 03:00:15 +01:00
|
|
|
found = true;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1999-11-15 04:28:07 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We need to copy the subplan's tuple in case the result is of
|
|
|
|
* pass-by-ref type --- our return value will point into this
|
|
|
|
* copied tuple! Can't use the subplan's instance of the tuple
|
|
|
|
* since it won't still be valid after next ExecProcNode() call.
|
|
|
|
* node->curTuple keeps track of the copied tuple for eventual
|
|
|
|
* freeing.
|
1999-11-15 04:28:07 +01:00
|
|
|
*/
|
|
|
|
if (node->curTuple)
|
1999-12-16 23:20:03 +01:00
|
|
|
heap_freetuple(node->curTuple);
|
Make TupleTableSlots extensible, finish split of existing slot type.
This commit completes the work prepared in 1a0586de36, splitting the
old TupleTableSlot implementation (which could store buffer, heap,
minimal and virtual slots) into four different slot types. As
described in the aforementioned commit, this is done with the goal of
making tuple table slots extensible, to allow for pluggable table
access methods.
To achieve runtime extensibility for TupleTableSlots, operations on
slots that can differ between types of slots are performed using the
TupleTableSlotOps struct provided at slot creation time. That
includes information from the size of TupleTableSlot struct to be
allocated, initialization, deforming etc. See the struct's definition
for more detailed information about callbacks TupleTableSlotOps.
I decided to rename TTSOpsBufferTuple to TTSOpsBufferHeapTuple and
ExecCopySlotTuple to ExecCopySlotHeapTuple, as that seems more
consistent with other naming introduced in recent patches.
There's plenty optimization potential in the slot implementation, but
according to benchmarking the state after this commit has similar
performance characteristics to before this set of changes, which seems
sufficient.
There's a few changes in execReplication.c that currently need to poke
through the slot abstraction, that'll be repaired once the pluggable
storage patchset provides the necessary infrastructure.
Author: Andres Freund and Ashutosh Bapat, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
2018-11-17 01:35:11 +01:00
|
|
|
node->curTuple = ExecCopySlotHeapTuple(slot);
|
2002-12-15 17:17:59 +01:00
|
|
|
|
2005-12-28 02:30:02 +01:00
|
|
|
result = heap_getattr(node->curTuple, 1, tdesc, isNull);
|
1999-11-15 03:00:15 +01:00
|
|
|
/* keep scanning subplan to make sure there's only one tuple */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2003-04-09 01:20:04 +02:00
|
|
|
if (subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
Datum dvalue;
|
|
|
|
bool disnull;
|
2003-06-25 23:30:34 +02:00
|
|
|
|
2003-04-09 01:20:04 +02:00
|
|
|
found = true;
|
|
|
|
/* stash away current value */
|
2017-08-20 20:19:07 +02:00
|
|
|
Assert(subplan->firstColType == TupleDescAttr(tdesc, 0)->atttypid);
|
2005-03-16 22:38:10 +01:00
|
|
|
dvalue = slot_getattr(slot, 1, &disnull);
|
2014-11-25 18:21:22 +01:00
|
|
|
astate = accumArrayResultAny(astate, dvalue, disnull,
|
|
|
|
subplan->firstColType, oldcontext);
|
2003-04-09 01:20:04 +02:00
|
|
|
/* keep scanning subplan to collect all values */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-12-28 02:30:02 +01:00
|
|
|
/* cannot allow multiple input tuples for ROWCOMPARE sublink either */
|
|
|
|
if (subLinkType == ROWCOMPARE_SUBLINK && found)
|
2003-07-21 19:05:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_CARDINALITY_VIOLATION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("more than one row returned by a subquery used as an expression")));
|
1999-11-12 07:39:34 +01:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
found = true;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2005-12-28 02:30:02 +01:00
|
|
|
* For ALL, ANY, and ROWCOMPARE sublinks, load up the Params
|
2006-10-04 02:30:14 +02:00
|
|
|
* representing the columns of the sub-select, and then evaluate the
|
|
|
|
* combining expression.
|
1999-11-15 03:00:15 +01:00
|
|
|
*/
|
2005-12-28 02:30:02 +01:00
|
|
|
col = 1;
|
|
|
|
foreach(plst, subplan->paramIds)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
int paramid = lfirst_int(plst);
|
2003-06-25 23:30:34 +02:00
|
|
|
ParamExecData *prmdata;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2003-06-25 23:30:34 +02:00
|
|
|
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
|
|
|
|
Assert(prmdata->execPlan == NULL);
|
2005-12-28 02:30:02 +01:00
|
|
|
prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
|
2003-06-25 23:30:34 +02:00
|
|
|
col++;
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2005-12-28 02:30:02 +01:00
|
|
|
rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
|
2017-01-19 23:12:38 +01:00
|
|
|
&rownull);
|
2005-12-28 02:30:02 +01:00
|
|
|
|
2003-06-25 23:30:34 +02:00
|
|
|
if (subLinkType == ANY_SUBLINK)
|
1999-11-12 07:39:34 +01:00
|
|
|
{
|
2003-06-25 23:30:34 +02:00
|
|
|
/* combine across rows per OR semantics */
|
|
|
|
if (rownull)
|
|
|
|
*isNull = true;
|
|
|
|
else if (DatumGetBool(rowresult))
|
1999-11-12 07:39:34 +01:00
|
|
|
{
|
2003-06-25 23:30:34 +02:00
|
|
|
result = BoolGetDatum(true);
|
|
|
|
*isNull = false;
|
|
|
|
break; /* needn't look at any more rows */
|
2003-06-25 01:14:49 +02:00
|
|
|
}
|
2003-06-25 23:30:34 +02:00
|
|
|
}
|
|
|
|
else if (subLinkType == ALL_SUBLINK)
|
|
|
|
{
|
|
|
|
/* combine across rows per AND semantics */
|
|
|
|
if (rownull)
|
|
|
|
*isNull = true;
|
|
|
|
else if (!DatumGetBool(rowresult))
|
2003-06-25 01:14:49 +02:00
|
|
|
{
|
2003-06-25 23:30:34 +02:00
|
|
|
result = BoolGetDatum(false);
|
|
|
|
*isNull = false;
|
|
|
|
break; /* needn't look at any more rows */
|
1999-11-12 07:39:34 +01:00
|
|
|
}
|
|
|
|
}
|
2003-06-25 23:30:34 +02:00
|
|
|
else
|
|
|
|
{
|
2005-12-28 02:30:02 +01:00
|
|
|
/* must be ROWCOMPARE_SUBLINK */
|
2003-06-25 23:30:34 +02:00
|
|
|
result = rowresult;
|
|
|
|
*isNull = rownull;
|
|
|
|
}
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2007-08-26 23:44:25 +02:00
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
|
|
|
|
if (subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
|
|
|
/* We return the result in the caller's context */
|
2014-11-25 18:21:22 +01:00
|
|
|
result = makeArrayResultAny(astate, oldcontext, true);
|
2007-08-26 23:44:25 +02:00
|
|
|
}
|
|
|
|
else if (!found)
|
1999-04-19 06:17:11 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
2014-05-06 18:12:18 +02:00
|
|
|
* deal with empty subplan result. result/isNull were previously
|
2007-08-26 23:44:25 +02:00
|
|
|
* initialized correctly for all sublink types except EXPR and
|
2005-12-28 02:30:02 +01:00
|
|
|
* ROWCOMPARE; for those, return NULL.
|
1999-11-12 07:39:34 +01:00
|
|
|
*/
|
2003-04-09 01:20:04 +02:00
|
|
|
if (subLinkType == EXPR_SUBLINK ||
|
2005-12-28 02:30:02 +01:00
|
|
|
subLinkType == ROWCOMPARE_SUBLINK)
|
1999-11-12 07:39:34 +01:00
|
|
|
{
|
2000-07-12 04:37:39 +02:00
|
|
|
result = (Datum) 0;
|
2000-04-12 19:17:23 +02:00
|
|
|
*isNull = true;
|
1999-11-12 07:39:34 +01:00
|
|
|
}
|
1999-04-19 06:17:11 +02:00
|
|
|
}
|
2000-07-12 04:37:39 +02:00
|
|
|
|
1999-04-19 06:17:11 +02:00
|
|
|
return result;
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
|
|
|
|
2003-01-12 05:03:34 +01:00
|
|
|
/*
|
|
|
|
* buildSubPlanHash: load hash table by scanning subplan output.
|
|
|
|
*/
|
|
|
|
static void
|
2007-02-27 02:11:26 +01:00
|
|
|
buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
SubPlan *subplan = node->subplan;
|
2003-01-12 05:03:34 +01:00
|
|
|
PlanState *planstate = node->planstate;
|
2005-12-28 02:30:02 +01:00
|
|
|
int ncols = list_length(subplan->paramIds);
|
2003-01-12 05:03:34 +01:00
|
|
|
ExprContext *innerecontext = node->innerecontext;
|
|
|
|
MemoryContext oldcontext;
|
Install defenses against overflow in BuildTupleHashTable().
The planner can sometimes compute very large values for numGroups, and in
cases where we have no alternative to building a hashtable, such a value
will get fed directly to BuildTupleHashTable as its nbuckets parameter.
There were two ways in which that could go bad. First, BuildTupleHashTable
declared the parameter as "int" but most callers were passing "long"s,
so on 64-bit machines undetected overflow could occur leading to a bogus
negative value. The obvious fix for that is to change the parameter to
"long", which is what I've done in HEAD. In the back branches that seems a
bit risky, though, since third-party code might be calling this function.
So for them, just put in a kluge to treat negative inputs as INT_MAX.
Second, hash_create can go nuts with extremely large requested table sizes
(notably, my_log2 becomes an infinite loop for inputs larger than
LONG_MAX/2). What seems most appropriate to avoid that is to bound the
initial table size request to work_mem.
This fixes bug #6035 reported by Daniel Schreiber. Although the reported
case only occurs back to 8.4 since it involves WITH RECURSIVE, I think
it's a good idea to install the defenses in all supported branches.
2011-05-23 18:52:46 +02:00
|
|
|
long nbuckets;
|
2003-01-12 05:03:34 +01:00
|
|
|
TupleTableSlot *slot;
|
|
|
|
|
|
|
|
Assert(subplan->subLinkType == ANY_SUBLINK);
|
|
|
|
|
|
|
|
/*
|
2019-02-09 09:35:57 +01:00
|
|
|
* If we already had any hash tables, reset 'em; otherwise create empty
|
|
|
|
* hash table(s).
|
2003-01-12 05:03:34 +01:00
|
|
|
*
|
2003-08-04 02:43:34 +02:00
|
|
|
* If we need to distinguish accurately between FALSE and UNKNOWN (i.e.,
|
2005-10-15 04:49:52 +02:00
|
|
|
* NULL) results of the IN operation, then we have to store subplan output
|
|
|
|
* rows that are partly or wholly NULL. We store such rows in a separate
|
2005-11-22 19:17:34 +01:00
|
|
|
* hash table that we expect will be much smaller than the main table. (We
|
2006-10-04 02:30:14 +02:00
|
|
|
* can use hashing to eliminate partly-null rows that are not distinct. We
|
|
|
|
* keep them separate to minimize the cost of the inevitable full-table
|
2005-11-22 19:17:34 +01:00
|
|
|
* searches; see findPartialMatch.)
|
2003-01-12 05:03:34 +01:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* If it's not necessary to distinguish FALSE and UNKNOWN, then we don't
|
|
|
|
* need to store subplan output rows that contain NULL.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
2010-07-28 06:50:50 +02:00
|
|
|
MemoryContextReset(node->hashtablecxt);
|
2003-01-12 05:03:34 +01:00
|
|
|
node->havehashrows = false;
|
|
|
|
node->havenullrows = false;
|
|
|
|
|
Install defenses against overflow in BuildTupleHashTable().
The planner can sometimes compute very large values for numGroups, and in
cases where we have no alternative to building a hashtable, such a value
will get fed directly to BuildTupleHashTable as its nbuckets parameter.
There were two ways in which that could go bad. First, BuildTupleHashTable
declared the parameter as "int" but most callers were passing "long"s,
so on 64-bit machines undetected overflow could occur leading to a bogus
negative value. The obvious fix for that is to change the parameter to
"long", which is what I've done in HEAD. In the back branches that seems a
bit risky, though, since third-party code might be calling this function.
So for them, just put in a kluge to treat negative inputs as INT_MAX.
Second, hash_create can go nuts with extremely large requested table sizes
(notably, my_log2 becomes an infinite loop for inputs larger than
LONG_MAX/2). What seems most appropriate to avoid that is to bound the
initial table size request to work_mem.
This fixes bug #6035 reported by Daniel Schreiber. Although the reported
case only occurs back to 8.4 since it involves WITH RECURSIVE, I think
it's a good idea to install the defenses in all supported branches.
2011-05-23 18:52:46 +02:00
|
|
|
nbuckets = (long) Min(planstate->plan->plan_rows, (double) LONG_MAX);
|
2003-01-12 05:03:34 +01:00
|
|
|
if (nbuckets < 1)
|
|
|
|
nbuckets = 1;
|
|
|
|
|
2019-02-09 09:35:57 +01:00
|
|
|
if (node->hashtable)
|
|
|
|
ResetTupleHashTable(node->hashtable);
|
|
|
|
else
|
|
|
|
node->hashtable = BuildTupleHashTableExt(node->parent,
|
|
|
|
node->descRight,
|
|
|
|
ncols,
|
|
|
|
node->keyColIdx,
|
|
|
|
node->tab_eq_funcoids,
|
|
|
|
node->tab_hash_funcs,
|
2019-03-22 12:09:32 +01:00
|
|
|
node->tab_collations,
|
2019-02-09 09:35:57 +01:00
|
|
|
nbuckets,
|
|
|
|
0,
|
|
|
|
node->planstate->state->es_query_cxt,
|
|
|
|
node->hashtablecxt,
|
|
|
|
node->hashtempcxt,
|
|
|
|
false);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
if (!subplan->unknownEqFalse)
|
|
|
|
{
|
|
|
|
if (ncols == 1)
|
|
|
|
nbuckets = 1; /* there can only be one entry */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nbuckets /= 16;
|
|
|
|
if (nbuckets < 1)
|
|
|
|
nbuckets = 1;
|
|
|
|
}
|
2019-02-09 09:35:57 +01:00
|
|
|
|
|
|
|
if (node->hashnulls)
|
2020-02-29 19:48:09 +01:00
|
|
|
ResetTupleHashTable(node->hashnulls);
|
2019-02-09 09:35:57 +01:00
|
|
|
else
|
|
|
|
node->hashnulls = BuildTupleHashTableExt(node->parent,
|
|
|
|
node->descRight,
|
|
|
|
ncols,
|
|
|
|
node->keyColIdx,
|
|
|
|
node->tab_eq_funcoids,
|
|
|
|
node->tab_hash_funcs,
|
2019-03-22 12:09:32 +01:00
|
|
|
node->tab_collations,
|
2019-02-09 09:35:57 +01:00
|
|
|
nbuckets,
|
|
|
|
0,
|
|
|
|
node->planstate->state->es_query_cxt,
|
|
|
|
node->hashtablecxt,
|
|
|
|
node->hashtempcxt,
|
|
|
|
false);
|
2003-01-12 05:03:34 +01:00
|
|
|
}
|
2020-02-29 19:48:09 +01:00
|
|
|
else
|
|
|
|
node->hashnulls = NULL;
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We are probably in a short-lived expression-evaluation context. Switch
|
2007-02-27 02:11:26 +01:00
|
|
|
* to the per-query context for manipulating the child plan.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
2007-02-27 02:11:26 +01:00
|
|
|
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset subplan to start.
|
|
|
|
*/
|
2010-07-12 19:01:06 +02:00
|
|
|
ExecReScan(planstate);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Scan the subplan and load the hash table(s). Note that when there are
|
|
|
|
* duplicate rows coming out of the sub-select, only one copy is stored.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
for (slot = ExecProcNode(planstate);
|
|
|
|
!TupIsNull(slot);
|
|
|
|
slot = ExecProcNode(planstate))
|
|
|
|
{
|
2003-06-25 23:30:34 +02:00
|
|
|
int col = 1;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *plst;
|
2003-01-12 05:03:34 +01:00
|
|
|
bool isnew;
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Load up the Params representing the raw sub-select outputs, then
|
|
|
|
* form the projection tuple to store in the hashtable.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
2003-06-25 23:30:34 +02:00
|
|
|
foreach(plst, subplan->paramIds)
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
int paramid = lfirst_int(plst);
|
2003-06-25 23:30:34 +02:00
|
|
|
ParamExecData *prmdata;
|
|
|
|
|
|
|
|
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
|
|
|
|
Assert(prmdata->execPlan == NULL);
|
2005-03-16 22:38:10 +01:00
|
|
|
prmdata->value = slot_getattr(slot, col,
|
2003-06-25 23:30:34 +02:00
|
|
|
&(prmdata->isnull));
|
|
|
|
col++;
|
2003-01-12 05:03:34 +01:00
|
|
|
}
|
2017-01-19 23:12:38 +01:00
|
|
|
slot = ExecProject(node->projRight);
|
2003-06-25 01:14:49 +02:00
|
|
|
|
2003-06-25 23:30:34 +02:00
|
|
|
/*
|
|
|
|
* If result contains any nulls, store separately or not at all.
|
|
|
|
*/
|
2005-03-16 22:38:10 +01:00
|
|
|
if (slotNoNulls(slot))
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
2003-06-25 23:30:34 +02:00
|
|
|
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
|
|
|
node->havehashrows = true;
|
|
|
|
}
|
|
|
|
else if (node->hashnulls)
|
|
|
|
{
|
|
|
|
(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
|
|
|
|
node->havenullrows = true;
|
2003-01-12 05:03:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Reset innerecontext after each inner tuple to free any memory used
|
2010-07-28 06:50:50 +02:00
|
|
|
* during ExecProject.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
ResetExprContext(innerecontext);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Since the projected tuples are in the sub-query's context and not the
|
|
|
|
* main context, we'd better clear the tuple slot before there's any
|
|
|
|
* chance of a reset of the sub-query's context. Else we will have the
|
|
|
|
* potential for a double free attempt. (XXX possibly no longer needed,
|
|
|
|
* but can't hurt.)
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
ExecClearTuple(node->projRight->pi_state.resultslot);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
}
|
|
|
|
|
2018-02-16 06:55:31 +01:00
|
|
|
/*
|
|
|
|
* execTuplesUnequal
|
|
|
|
* Return true if two tuples are definitely unequal in the indicated
|
|
|
|
* fields.
|
|
|
|
*
|
|
|
|
* Nulls are neither equal nor unequal to anything else. A true result
|
|
|
|
* is obtained only if there are non-null fields that compare not-equal.
|
|
|
|
*
|
|
|
|
* slot1, slot2: the tuples to compare (must have same columns!)
|
|
|
|
* numCols: the number of attributes to be examined
|
|
|
|
* matchColIdx: array of attribute column numbers
|
|
|
|
* eqFunctions: array of fmgr lookup info for the equality functions to use
|
|
|
|
* evalContext: short-term memory context for executing the functions
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
execTuplesUnequal(TupleTableSlot *slot1,
|
|
|
|
TupleTableSlot *slot2,
|
|
|
|
int numCols,
|
|
|
|
AttrNumber *matchColIdx,
|
|
|
|
FmgrInfo *eqfunctions,
|
2019-03-22 12:09:32 +01:00
|
|
|
const Oid *collations,
|
2018-02-16 06:55:31 +01:00
|
|
|
MemoryContext evalContext)
|
|
|
|
{
|
|
|
|
MemoryContext oldContext;
|
|
|
|
bool result;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Reset and switch into the temp context. */
|
|
|
|
MemoryContextReset(evalContext);
|
|
|
|
oldContext = MemoryContextSwitchTo(evalContext);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We cannot report a match without checking all the fields, but we can
|
|
|
|
* report a non-match as soon as we find unequal fields. So, start
|
|
|
|
* comparing at the last field (least significant sort key). That's the
|
|
|
|
* most likely to be different if we are dealing with sorted input.
|
|
|
|
*/
|
|
|
|
result = false;
|
|
|
|
|
|
|
|
for (i = numCols; --i >= 0;)
|
|
|
|
{
|
|
|
|
AttrNumber att = matchColIdx[i];
|
|
|
|
Datum attr1,
|
|
|
|
attr2;
|
|
|
|
bool isNull1,
|
|
|
|
isNull2;
|
|
|
|
|
|
|
|
attr1 = slot_getattr(slot1, att, &isNull1);
|
|
|
|
|
|
|
|
if (isNull1)
|
|
|
|
continue; /* can't prove anything here */
|
|
|
|
|
|
|
|
attr2 = slot_getattr(slot2, att, &isNull2);
|
|
|
|
|
|
|
|
if (isNull2)
|
|
|
|
continue; /* can't prove anything here */
|
|
|
|
|
|
|
|
/* Apply the type-specific equality function */
|
2019-03-22 12:09:32 +01:00
|
|
|
if (!DatumGetBool(FunctionCall2Coll(&eqfunctions[i],
|
|
|
|
collations[i],
|
2019-05-22 18:55:34 +02:00
|
|
|
attr1, attr2)))
|
2018-02-16 06:55:31 +01:00
|
|
|
{
|
|
|
|
result = true; /* they are unequal */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2003-01-12 05:03:34 +01:00
|
|
|
/*
|
|
|
|
* findPartialMatch: does the hashtable contain an entry that is not
|
|
|
|
* provably distinct from the tuple?
|
|
|
|
*
|
|
|
|
* We have to scan the whole hashtable; we can't usefully use hashkeys
|
|
|
|
* to guide probing, since we might get partial matches on tuples with
|
|
|
|
* hashkeys quite unrelated to what we'd get from the given tuple.
|
2012-10-11 18:20:56 +02:00
|
|
|
*
|
|
|
|
* Caller must provide the equality functions to use, since in cross-type
|
|
|
|
* cases these are different from the hashtable's internal functions.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
static bool
|
2012-10-11 18:20:56 +02:00
|
|
|
findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
|
|
|
|
FmgrInfo *eqfunctions)
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
|
|
|
int numCols = hashtable->numCols;
|
|
|
|
AttrNumber *keyColIdx = hashtable->keyColIdx;
|
|
|
|
TupleHashIterator hashiter;
|
2003-08-04 02:43:34 +02:00
|
|
|
TupleHashEntry entry;
|
2003-01-12 05:03:34 +01:00
|
|
|
|
2007-04-27 01:24:46 +02:00
|
|
|
InitTupleHashIterator(hashtable, &hashiter);
|
2016-10-15 02:22:51 +02:00
|
|
|
while ((entry = ScanTupleHashTable(hashtable, &hashiter)) != NULL)
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
2017-07-26 02:37:17 +02:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
|
2006-06-28 19:05:49 +02:00
|
|
|
ExecStoreMinimalTuple(entry->firstTuple, hashtable->tableslot, false);
|
2007-02-06 03:59:15 +01:00
|
|
|
if (!execTuplesUnequal(slot, hashtable->tableslot,
|
2003-01-12 05:03:34 +01:00
|
|
|
numCols, keyColIdx,
|
2012-10-11 18:20:56 +02:00
|
|
|
eqfunctions,
|
2019-03-22 12:09:32 +01:00
|
|
|
hashtable->tab_collations,
|
2003-01-12 05:03:34 +01:00
|
|
|
hashtable->tempcxt))
|
2007-04-27 01:24:46 +02:00
|
|
|
{
|
|
|
|
TermTupleHashIterator(&hashiter);
|
2003-01-12 05:03:34 +01:00
|
|
|
return true;
|
2007-04-27 01:24:46 +02:00
|
|
|
}
|
2003-01-12 05:03:34 +01:00
|
|
|
}
|
2007-04-27 01:24:46 +02:00
|
|
|
/* No TermTupleHashIterator call needed here */
|
2003-01-12 05:03:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-03-16 22:38:10 +01:00
|
|
|
* slotAllNulls: is the slot completely NULL?
|
|
|
|
*
|
|
|
|
* This does not test for dropped columns, which is OK because we only
|
|
|
|
* use it on projected tuples.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
slotAllNulls(TupleTableSlot *slot)
|
|
|
|
{
|
|
|
|
int ncols = slot->tts_tupleDescriptor->natts;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 1; i <= ncols; i++)
|
|
|
|
{
|
|
|
|
if (!slot_attisnull(slot, i))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* slotNoNulls: is the slot entirely not NULL?
|
|
|
|
*
|
|
|
|
* This does not test for dropped columns, which is OK because we only
|
|
|
|
* use it on projected tuples.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
static bool
|
2005-03-16 22:38:10 +01:00
|
|
|
slotNoNulls(TupleTableSlot *slot)
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
2005-03-16 22:38:10 +01:00
|
|
|
int ncols = slot->tts_tupleDescriptor->natts;
|
2003-08-04 02:43:34 +02:00
|
|
|
int i;
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
for (i = 1; i <= ncols; i++)
|
|
|
|
{
|
2005-03-16 22:38:10 +01:00
|
|
|
if (slot_attisnull(slot, i))
|
2003-01-12 05:03:34 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecInitSubPlan
|
2006-02-28 05:10:28 +01:00
|
|
|
*
|
2007-02-27 02:11:26 +01:00
|
|
|
* Create a SubPlanState for a SubPlan; this is the SubPlan-specific part
|
|
|
|
* of ExecInitExpr(). We split it out so that it can be used for InitPlans
|
|
|
|
* as well as regular SubPlans. Note that we don't link the SubPlan into
|
|
|
|
* the parent's subPlan list, because that shouldn't happen for InitPlans.
|
|
|
|
* Instead, ExecInitExpr() does that one part.
|
1998-02-13 04:26:53 +01:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
2007-02-27 02:11:26 +01:00
|
|
|
SubPlanState *
|
|
|
|
ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2007-02-27 02:11:26 +01:00
|
|
|
SubPlanState *sstate = makeNode(SubPlanState);
|
|
|
|
EState *estate = parent->state;
|
2002-12-05 16:50:39 +01:00
|
|
|
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
sstate->subplan = subplan;
|
2002-12-05 16:50:39 +01:00
|
|
|
|
2007-02-27 02:11:26 +01:00
|
|
|
/* Link the SubPlanState to already-initialized subplan */
|
|
|
|
sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates,
|
|
|
|
subplan->plan_id - 1);
|
|
|
|
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
/* ... and to its parent's state */
|
|
|
|
sstate->parent = parent;
|
|
|
|
|
2007-02-27 02:11:26 +01:00
|
|
|
/* Initialize subexpressions */
|
|
|
|
sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
sstate->args = ExecInitExprList(subplan->args, parent);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2002-12-05 16:50:39 +01:00
|
|
|
/*
|
2007-02-27 02:11:26 +01:00
|
|
|
* initialize my state
|
2002-12-05 16:50:39 +01:00
|
|
|
*/
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->curTuple = NULL;
|
2012-06-21 23:26:07 +02:00
|
|
|
sstate->curArray = PointerGetDatum(NULL);
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->projLeft = NULL;
|
|
|
|
sstate->projRight = NULL;
|
|
|
|
sstate->hashtable = NULL;
|
|
|
|
sstate->hashnulls = NULL;
|
2010-07-28 06:50:50 +02:00
|
|
|
sstate->hashtablecxt = NULL;
|
|
|
|
sstate->hashtempcxt = NULL;
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->innerecontext = NULL;
|
|
|
|
sstate->keyColIdx = NULL;
|
2018-02-16 06:55:31 +01:00
|
|
|
sstate->tab_eq_funcoids = NULL;
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->tab_hash_funcs = NULL;
|
|
|
|
sstate->tab_eq_funcs = NULL;
|
2019-03-22 12:09:32 +01:00
|
|
|
sstate->tab_collations = NULL;
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->lhs_hash_funcs = NULL;
|
|
|
|
sstate->cur_eq_funcs = NULL;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
/*
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
* If this is an initplan or MULTIEXPR subplan, it has output parameters
|
|
|
|
* that the parent plan will use, so mark those parameters as needing
|
|
|
|
* evaluation. We don't actually run the subplan until we first need one
|
|
|
|
* of its outputs.
|
2003-02-09 01:30:41 +01:00
|
|
|
*
|
2008-10-04 23:56:55 +02:00
|
|
|
* A CTE subplan's output parameter is never to be evaluated in the normal
|
|
|
|
* way, so skip this in that case.
|
|
|
|
*
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
* Note that we don't set parent->chgParam here: the parent plan hasn't
|
|
|
|
* been run yet, so no need to force it to re-run.
|
1998-02-13 04:26:53 +01:00
|
|
|
*/
|
2008-10-04 23:56:55 +02:00
|
|
|
if (subplan->setParam != NIL && subplan->subLinkType != CTE_SUBLINK)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *lst;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2002-12-14 01:17:59 +01:00
|
|
|
foreach(lst, subplan->setParam)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
int paramid = lfirst_int(lst);
|
2003-02-09 01:30:41 +01:00
|
|
|
ParamExecData *prm = &(estate->es_param_exec_vals[paramid]);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2007-02-27 02:11:26 +01:00
|
|
|
prm->execPlan = sstate;
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
|
|
|
}
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If we are going to hash the subquery output, initialize relevant stuff.
|
|
|
|
* (We don't create the hashtable until needed, though.)
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
|
|
|
if (subplan->useHashTable)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
int ncols,
|
|
|
|
i;
|
2018-02-16 06:55:31 +01:00
|
|
|
TupleDesc tupDescLeft;
|
|
|
|
TupleDesc tupDescRight;
|
2019-08-05 17:20:21 +02:00
|
|
|
Oid *cross_eq_funcoids;
|
2003-01-12 05:03:34 +01:00
|
|
|
TupleTableSlot *slot;
|
2005-12-28 02:30:02 +01:00
|
|
|
List *oplist,
|
|
|
|
*lefttlist,
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
*righttlist;
|
2005-12-28 02:30:02 +01:00
|
|
|
ListCell *l;
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/* We need a memory context to hold the hash table(s) */
|
2010-07-28 06:50:50 +02:00
|
|
|
sstate->hashtablecxt =
|
2003-01-12 05:03:34 +01:00
|
|
|
AllocSetContextCreate(CurrentMemoryContext,
|
|
|
|
"Subplan HashTable Context",
|
Add macros to make AllocSetContextCreate() calls simpler and safer.
I found that half a dozen (nearly 5%) of our AllocSetContextCreate calls
had typos in the context-sizing parameters. While none of these led to
especially significant problems, they did create minor inefficiencies,
and it's now clear that expecting people to copy-and-paste those calls
accurately is not a great idea. Let's reduce the risk of future errors
by introducing single macros that encapsulate the common use-cases.
Three such macros are enough to cover all but two special-purpose contexts;
those two calls can be left as-is, I think.
While this patch doesn't in itself improve matters for third-party
extensions, it doesn't break anything for them either, and they can
gradually adopt the simplified notation over time.
In passing, change TopMemoryContext to use the default allocation
parameters. Formerly it could only be extended 8K at a time. That was
probably reasonable when this code was written; but nowadays we create
many more contexts than we did then, so that it's not unusual to have a
couple hundred K in TopMemoryContext, even without considering various
dubious code that sticks other things there. There seems no good reason
not to let it use growing blocks like most other contexts.
Back-patch to 9.6, mostly because that's still close enough to HEAD that
it's easy to do so, and keeping the branches in sync can be expected to
avoid some future back-patching pain. The bugs fixed by these changes
don't seem to be significant enough to justify fixing them further back.
Discussion: <21072.1472321324@sss.pgh.pa.us>
2016-08-27 23:50:38 +02:00
|
|
|
ALLOCSET_DEFAULT_SIZES);
|
2010-07-28 06:50:50 +02:00
|
|
|
/* and a small one for the hash tables to use as temp storage */
|
|
|
|
sstate->hashtempcxt =
|
|
|
|
AllocSetContextCreate(CurrentMemoryContext,
|
|
|
|
"Subplan HashTable Temp Context",
|
Add macros to make AllocSetContextCreate() calls simpler and safer.
I found that half a dozen (nearly 5%) of our AllocSetContextCreate calls
had typos in the context-sizing parameters. While none of these led to
especially significant problems, they did create minor inefficiencies,
and it's now clear that expecting people to copy-and-paste those calls
accurately is not a great idea. Let's reduce the risk of future errors
by introducing single macros that encapsulate the common use-cases.
Three such macros are enough to cover all but two special-purpose contexts;
those two calls can be left as-is, I think.
While this patch doesn't in itself improve matters for third-party
extensions, it doesn't break anything for them either, and they can
gradually adopt the simplified notation over time.
In passing, change TopMemoryContext to use the default allocation
parameters. Formerly it could only be extended 8K at a time. That was
probably reasonable when this code was written; but nowadays we create
many more contexts than we did then, so that it's not unusual to have a
couple hundred K in TopMemoryContext, even without considering various
dubious code that sticks other things there. There seems no good reason
not to let it use growing blocks like most other contexts.
Back-patch to 9.6, mostly because that's still close enough to HEAD that
it's easy to do so, and keeping the branches in sync can be expected to
avoid some future back-patching pain. The bugs fixed by these changes
don't seem to be significant enough to justify fixing them further back.
Discussion: <21072.1472321324@sss.pgh.pa.us>
2016-08-27 23:50:38 +02:00
|
|
|
ALLOCSET_SMALL_SIZES);
|
2003-01-12 05:03:34 +01:00
|
|
|
/* and a short-lived exprcontext for function evaluation */
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->innerecontext = CreateExprContext(estate);
|
2003-01-12 05:03:34 +01:00
|
|
|
/* Silly little array of column numbers 1..n */
|
2005-12-28 02:30:02 +01:00
|
|
|
ncols = list_length(subplan->paramIds);
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->keyColIdx = (AttrNumber *) palloc(ncols * sizeof(AttrNumber));
|
2003-01-12 05:03:34 +01:00
|
|
|
for (i = 0; i < ncols; i++)
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->keyColIdx[i] = i + 1;
|
2003-08-04 02:43:34 +02:00
|
|
|
|
2003-01-12 05:03:34 +01:00
|
|
|
/*
|
|
|
|
* We use ExecProject to evaluate the lefthand and righthand
|
2005-10-15 04:49:52 +02:00
|
|
|
* expression lists and form tuples. (You might think that we could
|
|
|
|
* use the sub-select's output tuples directly, but that is not the
|
|
|
|
* case if we had to insert any run-time coercions of the sub-select's
|
|
|
|
* output datatypes; anyway this avoids storing any resjunk columns
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
* that might be in the sub-select's output.) Run through the
|
2005-10-15 04:49:52 +02:00
|
|
|
* combining expressions to build tlists for the lefthand and
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
* righthand sides.
|
2003-01-12 05:03:34 +01:00
|
|
|
*
|
2005-11-22 19:17:34 +01:00
|
|
|
* We also extract the combining operators themselves to initialize
|
|
|
|
* the equality and hashing functions for the hash tables.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
if (IsA(subplan->testexpr, OpExpr))
|
2005-12-28 02:30:02 +01:00
|
|
|
{
|
|
|
|
/* single combining operator */
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
oplist = list_make1(subplan->testexpr);
|
2005-12-28 02:30:02 +01:00
|
|
|
}
|
2019-01-29 21:26:44 +01:00
|
|
|
else if (is_andclause(subplan->testexpr))
|
2005-12-28 02:30:02 +01:00
|
|
|
{
|
|
|
|
/* multiple combining operators */
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
oplist = castNode(BoolExpr, subplan->testexpr)->args;
|
2005-12-28 02:30:02 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* shouldn't see anything else in a hashable subplan */
|
|
|
|
elog(ERROR, "unrecognized testexpr type: %d",
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
(int) nodeTag(subplan->testexpr));
|
2005-12-28 02:30:02 +01:00
|
|
|
oplist = NIL; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
Assert(list_length(oplist) == ncols);
|
|
|
|
|
2003-01-12 05:03:34 +01:00
|
|
|
lefttlist = righttlist = NIL;
|
2018-02-16 06:55:31 +01:00
|
|
|
sstate->tab_eq_funcoids = (Oid *) palloc(ncols * sizeof(Oid));
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
|
|
|
|
sstate->tab_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
|
2019-03-22 12:09:32 +01:00
|
|
|
sstate->tab_collations = (Oid *) palloc(ncols * sizeof(Oid));
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
|
|
|
|
sstate->cur_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
|
2019-08-05 17:20:21 +02:00
|
|
|
/* we'll need the cross-type equality fns below, but not in sstate */
|
|
|
|
cross_eq_funcoids = (Oid *) palloc(ncols * sizeof(Oid));
|
|
|
|
|
2003-01-12 05:03:34 +01:00
|
|
|
i = 1;
|
2005-12-28 02:30:02 +01:00
|
|
|
foreach(l, oplist)
|
2003-01-12 05:03:34 +01:00
|
|
|
{
|
Improve castNode notation by introducing list-extraction-specific variants.
This extends the castNode() notation introduced by commit 5bcab1114 to
provide, in one step, extraction of a list cell's pointer and coercion to
a concrete node type. For example, "lfirst_node(Foo, lc)" is the same
as "castNode(Foo, lfirst(lc))". Almost half of the uses of castNode
that have appeared so far include a list extraction call, so this is
pretty widely useful, and it saves a few more keystrokes compared to the
old way.
As with the previous patch, back-patch the addition of these macros to
pg_list.h, so that the notation will be available when back-patching.
Patch by me, after an idea of Andrew Gierth's.
Discussion: https://postgr.es/m/14197.1491841216@sss.pgh.pa.us
2017-04-10 19:51:29 +02:00
|
|
|
OpExpr *opexpr = lfirst_node(OpExpr, l);
|
2003-01-12 05:03:34 +01:00
|
|
|
Expr *expr;
|
|
|
|
TargetEntry *tle;
|
2007-02-06 03:59:15 +01:00
|
|
|
Oid rhs_eq_oper;
|
2007-01-30 02:33:36 +01:00
|
|
|
Oid left_hashfn;
|
|
|
|
Oid right_hashfn;
|
2003-01-12 05:03:34 +01:00
|
|
|
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
Assert(list_length(opexpr->args) == 2);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/* Process lefthand argument */
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
expr = (Expr *) linitial(opexpr->args);
|
2005-04-06 18:34:07 +02:00
|
|
|
tle = makeTargetEntry(expr,
|
|
|
|
i,
|
|
|
|
NULL,
|
|
|
|
false);
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
lefttlist = lappend(lefttlist, tle);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
|
|
|
/* Process righthand argument */
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
expr = (Expr *) lsecond(opexpr->args);
|
2005-04-06 18:34:07 +02:00
|
|
|
tle = makeTargetEntry(expr,
|
|
|
|
i,
|
|
|
|
NULL,
|
|
|
|
false);
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
righttlist = lappend(righttlist, tle);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
2007-02-06 03:59:15 +01:00
|
|
|
/* Lookup the equality function (potentially cross-type) */
|
2019-08-05 17:20:21 +02:00
|
|
|
cross_eq_funcoids[i - 1] = opexpr->opfuncid;
|
2007-02-27 02:11:26 +01:00
|
|
|
fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
|
2011-03-20 01:29:08 +01:00
|
|
|
fmgr_info_set_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]);
|
2007-02-06 03:59:15 +01:00
|
|
|
|
|
|
|
/* Look up the equality function for the RHS type */
|
|
|
|
if (!get_compatible_hash_operators(opexpr->opno,
|
|
|
|
NULL, &rhs_eq_oper))
|
|
|
|
elog(ERROR, "could not find compatible hash operator for operator %u",
|
|
|
|
opexpr->opno);
|
2019-08-05 17:20:21 +02:00
|
|
|
sstate->tab_eq_funcoids[i - 1] = get_opcode(rhs_eq_oper);
|
|
|
|
fmgr_info(sstate->tab_eq_funcoids[i - 1],
|
|
|
|
&sstate->tab_eq_funcs[i - 1]);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
2007-01-30 02:33:36 +01:00
|
|
|
/* Lookup the associated hash functions */
|
|
|
|
if (!get_op_hash_functions(opexpr->opno,
|
|
|
|
&left_hashfn, &right_hashfn))
|
2003-07-21 19:05:12 +02:00
|
|
|
elog(ERROR, "could not find hash function for hash operator %u",
|
2003-06-23 00:04:55 +02:00
|
|
|
opexpr->opno);
|
2007-02-27 02:11:26 +01:00
|
|
|
fmgr_info(left_hashfn, &sstate->lhs_hash_funcs[i - 1]);
|
|
|
|
fmgr_info(right_hashfn, &sstate->tab_hash_funcs[i - 1]);
|
2003-06-23 00:04:55 +02:00
|
|
|
|
2019-03-22 12:09:32 +01:00
|
|
|
/* Set collation */
|
|
|
|
sstate->tab_collations[i - 1] = opexpr->inputcollid;
|
|
|
|
|
2003-01-12 05:03:34 +01:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Construct tupdescs, slots and projection nodes for left and right
|
|
|
|
* sides. The lefthand expressions will be evaluated in the parent
|
|
|
|
* plan node's exprcontext, which we don't have access to here.
|
|
|
|
* Fortunately we can just pass NULL for now and fill it in later
|
|
|
|
* (hack alert!). The righthand expressions will be evaluated in our
|
|
|
|
* own innerecontext.
|
2003-01-12 05:03:34 +01:00
|
|
|
*/
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
tupDescLeft = ExecTypeFromTL(lefttlist);
|
Introduce notion of different types of slots (without implementing them).
Upcoming work intends to allow pluggable ways to introduce new ways of
storing table data. Accessing those table access methods from the
executor requires TupleTableSlots to be carry tuples in the native
format of such storage methods; otherwise there'll be a significant
conversion overhead.
Different access methods will require different data to store tuples
efficiently (just like virtual, minimal, heap already require fields
in TupleTableSlot). To allow that without requiring additional pointer
indirections, we want to have different structs (embedding
TupleTableSlot) for different types of slots. Thus different types of
slots are needed, which requires adapting creators of slots.
The slot that most efficiently can represent a type of tuple in an
executor node will often depend on the type of slot a child node
uses. Therefore we need to track the type of slot is returned by
nodes, so parent slots can create slots based on that.
Relatedly, JIT compilation of tuple deforming needs to know which type
of slot a certain expression refers to, so it can create an
appropriate deforming function for the type of tuple in the slot.
But not all nodes will only return one type of slot, e.g. an append
node will potentially return different types of slots for each of its
subplans.
Therefore add function that allows to query the type of a node's
result slot, and whether it'll always be the same type (whether it's
fixed). This can be queried using ExecGetResultSlotOps().
The scan, result, inner, outer type of slots are automatically
inferred from ExecInitScanTupleSlot(), ExecInitResultSlot(),
left/right subtrees respectively. If that's not correct for a node,
that can be overwritten using new fields in PlanState.
This commit does not introduce the actually abstracted implementation
of different kind of TupleTableSlots, that will be left for a followup
commit. The different types of slots introduced will, for now, still
use the same backing implementation.
While this already partially invalidates the big comment in
tuptable.h, it seems to make more sense to update it later, when the
different TupleTableSlot implementations actually exist.
Author: Ashutosh Bapat and Andres Freund, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
2018-11-16 07:00:30 +01:00
|
|
|
slot = ExecInitExtraTupleSlot(estate, tupDescLeft, &TTSOpsVirtual);
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
|
2007-11-15 22:14:46 +01:00
|
|
|
NULL,
|
|
|
|
slot,
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
parent,
|
2007-11-15 22:14:46 +01:00
|
|
|
NULL);
|
2003-01-12 05:03:34 +01:00
|
|
|
|
Remove WITH OIDS support, change oid catalog column visibility.
Previously tables declared WITH OIDS, including a significant fraction
of the catalog tables, stored the oid column not as a normal column,
but as part of the tuple header.
This special column was not shown by default, which was somewhat odd,
as it's often (consider e.g. pg_class.oid) one of the more important
parts of a row. Neither pg_dump nor COPY included the contents of the
oid column by default.
The fact that the oid column was not an ordinary column necessitated a
significant amount of special case code to support oid columns. That
already was painful for the existing, but upcoming work aiming to make
table storage pluggable, would have required expanding and duplicating
that "specialness" significantly.
WITH OIDS has been deprecated since 2005 (commit ff02d0a05280e0).
Remove it.
Removing includes:
- CREATE TABLE and ALTER TABLE syntax for declaring the table to be
WITH OIDS has been removed (WITH (oids[ = true]) will error out)
- pg_dump does not support dumping tables declared WITH OIDS and will
issue a warning when dumping one (and ignore the oid column).
- restoring an pg_dump archive with pg_restore will warn when
restoring a table with oid contents (and ignore the oid column)
- COPY will refuse to load binary dump that includes oids.
- pg_upgrade will error out when encountering tables declared WITH
OIDS, they have to be altered to remove the oid column first.
- Functionality to access the oid of the last inserted row (like
plpgsql's RESULT_OID, spi's SPI_lastoid, ...) has been removed.
The syntax for declaring a table WITHOUT OIDS (or WITH (oids = false)
for CREATE TABLE) is still supported. While that requires a bit of
support code, it seems unnecessary to break applications / dumps that
do not use oids, and are explicit about not using them.
The biggest user of WITH OID columns was postgres' catalog. This
commit changes all 'magic' oid columns to be columns that are normally
declared and stored. To reduce unnecessary query breakage all the
newly added columns are still named 'oid', even if a table's column
naming scheme would indicate 'reloid' or such. This obviously
requires adapting a lot code, mostly replacing oid access via
HeapTupleGetOid() with access to the underlying Form_pg_*->oid column.
The bootstrap process now assigns oids for all oid columns in
genbki.pl that do not have an explicit value (starting at the largest
oid previously used), only oids assigned later by oids will be above
FirstBootstrapObjectId. As the oid column now is a normal column the
special bootstrap syntax for oids has been removed.
Oids are not automatically assigned during insertion anymore, all
backend code explicitly assigns oids with GetNewOidWithIndex(). For
the rare case that insertions into the catalog via SQL are called for
the new pg_nextoid() function can be used (which only works on catalog
tables).
The fact that oid columns on system tables are now normal columns
means that they will be included in the set of columns expanded
by * (i.e. SELECT * FROM pg_class will now include the table's oid,
previously it did not). It'd not technically be hard to hide oid
column by default, but that'd mean confusing behavior would either
have to be carried forward forever, or it'd cause breakage down the
line.
While it's not unlikely that further adjustments are needed, the
scope/invasiveness of the patch makes it worthwhile to get merge this
now. It's painful to maintain externally, too complicated to commit
after the code code freeze, and a dependency of a number of other
patches.
Catversion bump, for obvious reasons.
Author: Andres Freund, with contributions by John Naylor
Discussion: https://postgr.es/m/20180930034810.ywp2c7awz7opzcfr@alap3.anarazel.de
2018-11-21 00:36:57 +01:00
|
|
|
sstate->descRight = tupDescRight = ExecTypeFromTL(righttlist);
|
Introduce notion of different types of slots (without implementing them).
Upcoming work intends to allow pluggable ways to introduce new ways of
storing table data. Accessing those table access methods from the
executor requires TupleTableSlots to be carry tuples in the native
format of such storage methods; otherwise there'll be a significant
conversion overhead.
Different access methods will require different data to store tuples
efficiently (just like virtual, minimal, heap already require fields
in TupleTableSlot). To allow that without requiring additional pointer
indirections, we want to have different structs (embedding
TupleTableSlot) for different types of slots. Thus different types of
slots are needed, which requires adapting creators of slots.
The slot that most efficiently can represent a type of tuple in an
executor node will often depend on the type of slot a child node
uses. Therefore we need to track the type of slot is returned by
nodes, so parent slots can create slots based on that.
Relatedly, JIT compilation of tuple deforming needs to know which type
of slot a certain expression refers to, so it can create an
appropriate deforming function for the type of tuple in the slot.
But not all nodes will only return one type of slot, e.g. an append
node will potentially return different types of slots for each of its
subplans.
Therefore add function that allows to query the type of a node's
result slot, and whether it'll always be the same type (whether it's
fixed). This can be queried using ExecGetResultSlotOps().
The scan, result, inner, outer type of slots are automatically
inferred from ExecInitScanTupleSlot(), ExecInitResultSlot(),
left/right subtrees respectively. If that's not correct for a node,
that can be overwritten using new fields in PlanState.
This commit does not introduce the actually abstracted implementation
of different kind of TupleTableSlots, that will be left for a followup
commit. The different types of slots introduced will, for now, still
use the same backing implementation.
While this already partially invalidates the big comment in
tuptable.h, it seems to make more sense to update it later, when the
different TupleTableSlot implementations actually exist.
Author: Ashutosh Bapat and Andres Freund, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
2018-11-16 07:00:30 +01:00
|
|
|
slot = ExecInitExtraTupleSlot(estate, tupDescRight, &TTSOpsVirtual);
|
2007-02-27 02:11:26 +01:00
|
|
|
sstate->projRight = ExecBuildProjectionInfo(righttlist,
|
2007-11-15 22:14:46 +01:00
|
|
|
sstate->innerecontext,
|
|
|
|
slot,
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
sstate->planstate,
|
2007-11-15 22:14:46 +01:00
|
|
|
NULL);
|
2018-02-16 06:55:31 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create comparator for lookups of rows in the table (potentially
|
2019-08-05 17:20:21 +02:00
|
|
|
* cross-type comparisons).
|
2018-02-16 06:55:31 +01:00
|
|
|
*/
|
|
|
|
sstate->cur_eq_comp = ExecBuildGroupingEqual(tupDescLeft, tupDescRight,
|
Introduce notion of different types of slots (without implementing them).
Upcoming work intends to allow pluggable ways to introduce new ways of
storing table data. Accessing those table access methods from the
executor requires TupleTableSlots to be carry tuples in the native
format of such storage methods; otherwise there'll be a significant
conversion overhead.
Different access methods will require different data to store tuples
efficiently (just like virtual, minimal, heap already require fields
in TupleTableSlot). To allow that without requiring additional pointer
indirections, we want to have different structs (embedding
TupleTableSlot) for different types of slots. Thus different types of
slots are needed, which requires adapting creators of slots.
The slot that most efficiently can represent a type of tuple in an
executor node will often depend on the type of slot a child node
uses. Therefore we need to track the type of slot is returned by
nodes, so parent slots can create slots based on that.
Relatedly, JIT compilation of tuple deforming needs to know which type
of slot a certain expression refers to, so it can create an
appropriate deforming function for the type of tuple in the slot.
But not all nodes will only return one type of slot, e.g. an append
node will potentially return different types of slots for each of its
subplans.
Therefore add function that allows to query the type of a node's
result slot, and whether it'll always be the same type (whether it's
fixed). This can be queried using ExecGetResultSlotOps().
The scan, result, inner, outer type of slots are automatically
inferred from ExecInitScanTupleSlot(), ExecInitResultSlot(),
left/right subtrees respectively. If that's not correct for a node,
that can be overwritten using new fields in PlanState.
This commit does not introduce the actually abstracted implementation
of different kind of TupleTableSlots, that will be left for a followup
commit. The different types of slots introduced will, for now, still
use the same backing implementation.
While this already partially invalidates the big comment in
tuptable.h, it seems to make more sense to update it later, when the
different TupleTableSlot implementations actually exist.
Author: Ashutosh Bapat and Andres Freund, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
2018-11-16 07:00:30 +01:00
|
|
|
&TTSOpsVirtual, &TTSOpsMinimalTuple,
|
2018-02-16 06:55:31 +01:00
|
|
|
ncols,
|
|
|
|
sstate->keyColIdx,
|
2019-08-05 17:20:21 +02:00
|
|
|
cross_eq_funcoids,
|
2019-03-22 12:09:32 +01:00
|
|
|
sstate->tab_collations,
|
2018-02-16 06:55:31 +01:00
|
|
|
parent);
|
2003-01-12 05:03:34 +01:00
|
|
|
}
|
2007-02-27 02:11:26 +01:00
|
|
|
|
|
|
|
return sstate;
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
|
|
* ExecSetParamPlan
|
|
|
|
*
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
* Executes a subplan and sets its output parameters.
|
2000-10-05 21:11:39 +02:00
|
|
|
*
|
2010-07-12 19:01:06 +02:00
|
|
|
* This is called from ExecEvalParamExec() when the value of a PARAM_EXEC
|
2000-10-05 21:11:39 +02:00
|
|
|
* parameter is requested and the param's execPlan field is set (indicating
|
2014-05-06 18:12:18 +02:00
|
|
|
* that the param has not yet been evaluated). This allows lazy evaluation
|
2000-10-05 21:11:39 +02:00
|
|
|
* of initplans: we don't run the subplan until/unless we need its output.
|
|
|
|
* Note that this routine MUST clear the execPlan fields of the plan's
|
|
|
|
* output parameters after evaluating them!
|
Fix failure with initplans used conditionally during EvalPlanQual rechecks.
The EvalPlanQual machinery assumes that any initplans (that is,
uncorrelated sub-selects) used during an EPQ recheck would have already
been evaluated during the main query; this is implicit in the fact that
execPlan pointers are not copied into the EPQ estate's es_param_exec_vals.
But it's possible for that assumption to fail, if the initplan is only
reached conditionally. For example, a sub-select inside a CASE expression
could be reached during a recheck when it had not been previously, if the
CASE test depends on a column that was just updated.
This bug is old, appearing to date back to my rewrite of EvalPlanQual in
commit 9f2ee8f28, but was not detected until Kyle Samson reported a case.
To fix, force all not-yet-evaluated initplans used within the EPQ plan
subtree to be evaluated at the start of the recheck, before entering the
EPQ environment. This could be inefficient, if such an initplan is
expensive and goes unused again during the recheck --- but that's piling
one layer of improbability atop another. It doesn't seem worth adding
more complexity to prevent that, at least not in the back branches.
It was convenient to use the new-in-v11 ExecEvalParamExecParams function
to implement this, but I didn't like either its name or the specifics of
its API, so revise that.
Back-patch all the way. Rather than rewrite the patch to avoid depending
on bms_next_member() in the oldest branches, I chose to back-patch that
function into 9.4 and 9.3. (This isn't the first time back-patches have
needed that, and it exhausted my patience.) I also chose to back-patch
some test cases added by commits 71404af2a and 342a1ffa2 into 9.4 and 9.3,
so that the 9.x versions of eval-plan-qual.spec are all the same.
Andrew Gierth diagnosed the problem and contributed the added test cases,
though the actual code changes are by me.
Discussion: https://postgr.es/m/A033A40A-B234-4324-BE37-272279F7B627@tripadvisor.com
2018-09-15 19:42:33 +02:00
|
|
|
*
|
|
|
|
* The results of this function are stored in the EState associated with the
|
|
|
|
* ExprContext (particularly, its ecxt_param_exec_vals); any pass-by-ref
|
|
|
|
* result Datums are allocated in the EState's per-query memory. The passed
|
|
|
|
* econtext can be any ExprContext belonging to that EState; which one is
|
|
|
|
* important only to the extent that the ExprContext's per-tuple memory
|
|
|
|
* context is used to evaluate any parameters passed down to the subplan.
|
|
|
|
* (Thus in principle, the shorter-lived the ExprContext the better, since
|
|
|
|
* that data isn't needed after we return. In practice, because initplan
|
|
|
|
* parameters are never more complex than Vars, Aggrefs, etc, evaluating them
|
|
|
|
* currently never leaks any memory anyway.)
|
1998-02-13 04:26:53 +01:00
|
|
|
* ----------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
SubPlan *subplan = node->subplan;
|
2002-12-05 16:50:39 +01:00
|
|
|
PlanState *planstate = node->planstate;
|
2003-08-04 02:43:34 +02:00
|
|
|
SubLinkType subLinkType = subplan->subLinkType;
|
2018-08-17 16:04:26 +02:00
|
|
|
EState *estate = planstate->state;
|
|
|
|
ScanDirection dir = estate->es_direction;
|
2000-07-12 04:37:39 +02:00
|
|
|
MemoryContext oldcontext;
|
1998-02-13 04:26:53 +01:00
|
|
|
TupleTableSlot *slot;
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
ListCell *pvar;
|
2004-08-29 07:07:03 +02:00
|
|
|
ListCell *l;
|
1998-02-26 05:46:47 +01:00
|
|
|
bool found = false;
|
2014-11-25 18:21:22 +01:00
|
|
|
ArrayBuildStateAny *astate = NULL;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2002-12-13 20:46:01 +01:00
|
|
|
if (subLinkType == ANY_SUBLINK ||
|
|
|
|
subLinkType == ALL_SUBLINK)
|
2003-07-21 19:05:12 +02:00
|
|
|
elog(ERROR, "ANY/ALL subselect unsupported as initplan");
|
2008-10-04 23:56:55 +02:00
|
|
|
if (subLinkType == CTE_SUBLINK)
|
|
|
|
elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2018-08-17 16:04:26 +02:00
|
|
|
/*
|
|
|
|
* Enforce forward scan direction regardless of caller. It's hard but not
|
|
|
|
* impossible to get here in backward scan, so make it work anyway.
|
|
|
|
*/
|
|
|
|
estate->es_direction = ForwardScanDirection;
|
|
|
|
|
2014-11-25 18:21:22 +01:00
|
|
|
/* Initialize ArrayBuildStateAny in caller's context, if needed */
|
|
|
|
if (subLinkType == ARRAY_SUBLINK)
|
|
|
|
astate = initArrayResultAny(subplan->firstColType,
|
2015-02-22 02:24:48 +01:00
|
|
|
CurrentMemoryContext, true);
|
2014-11-25 18:21:22 +01:00
|
|
|
|
2007-05-17 21:35:08 +02:00
|
|
|
/*
|
2008-10-04 23:56:55 +02:00
|
|
|
* Must switch to per-query memory context.
|
2007-05-17 21:35:08 +02:00
|
|
|
*/
|
2008-10-04 23:56:55 +02:00
|
|
|
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
/*
|
|
|
|
* Set Params of this plan from parent plan correlation values. (Any
|
|
|
|
* calculation we have to do is done in the parent econtext, since the
|
|
|
|
* Param values don't need to have per-query lifetime.) Currently, we
|
|
|
|
* expect only MULTIEXPR_SUBLINK plans to have any correlation values.
|
|
|
|
*/
|
|
|
|
Assert(subplan->parParam == NIL || subLinkType == MULTIEXPR_SUBLINK);
|
|
|
|
Assert(list_length(subplan->parParam) == list_length(node->args));
|
|
|
|
|
|
|
|
forboth(l, subplan->parParam, pvar, node->args)
|
|
|
|
{
|
|
|
|
int paramid = lfirst_int(l);
|
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
|
|
|
|
|
|
|
prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
|
|
|
|
econtext,
|
2017-01-19 23:12:38 +01:00
|
|
|
&(prm->isnull));
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
|
|
|
|
}
|
|
|
|
|
2008-10-04 23:56:55 +02:00
|
|
|
/*
|
|
|
|
* Run the plan. (If it needs to be rescanned, the first ExecProcNode
|
|
|
|
* call will take care of that.)
|
|
|
|
*/
|
2002-12-05 16:50:39 +01:00
|
|
|
for (slot = ExecProcNode(planstate);
|
1998-02-26 05:46:47 +01:00
|
|
|
!TupIsNull(slot);
|
2002-12-05 16:50:39 +01:00
|
|
|
slot = ExecProcNode(planstate))
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2005-03-16 22:38:10 +01:00
|
|
|
TupleDesc tdesc = slot->tts_tupleDescriptor;
|
1998-02-13 04:26:53 +01:00
|
|
|
int i = 1;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2002-12-13 20:46:01 +01:00
|
|
|
if (subLinkType == EXISTS_SUBLINK)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2008-10-04 23:56:55 +02:00
|
|
|
/* There can be only one setParam... */
|
2004-05-26 06:41:50 +02:00
|
|
|
int paramid = linitial_int(subplan->setParam);
|
2003-02-09 01:30:41 +01:00
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
prm->execPlan = NULL;
|
2000-07-12 04:37:39 +02:00
|
|
|
prm->value = BoolGetDatum(true);
|
1998-02-13 04:26:53 +01:00
|
|
|
prm->isnull = false;
|
1999-11-15 03:00:15 +01:00
|
|
|
found = true;
|
1998-02-13 04:26:53 +01:00
|
|
|
break;
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2003-04-09 01:20:04 +02:00
|
|
|
if (subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
Datum dvalue;
|
|
|
|
bool disnull;
|
2003-04-09 01:20:04 +02:00
|
|
|
|
|
|
|
found = true;
|
|
|
|
/* stash away current value */
|
2017-08-20 20:19:07 +02:00
|
|
|
Assert(subplan->firstColType == TupleDescAttr(tdesc, 0)->atttypid);
|
2005-03-16 22:38:10 +01:00
|
|
|
dvalue = slot_getattr(slot, 1, &disnull);
|
2014-11-25 18:21:22 +01:00
|
|
|
astate = accumArrayResultAny(astate, dvalue, disnull,
|
|
|
|
subplan->firstColType, oldcontext);
|
2003-04-09 01:20:04 +02:00
|
|
|
/* keep scanning subplan to collect all values */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1999-11-15 03:00:15 +01:00
|
|
|
if (found &&
|
2002-12-13 20:46:01 +01:00
|
|
|
(subLinkType == EXPR_SUBLINK ||
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
subLinkType == MULTIEXPR_SUBLINK ||
|
2005-12-28 02:30:02 +01:00
|
|
|
subLinkType == ROWCOMPARE_SUBLINK))
|
2003-07-21 19:05:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_CARDINALITY_VIOLATION),
|
2003-09-25 08:58:07 +02:00
|
|
|
errmsg("more than one row returned by a subquery used as an expression")));
|
1999-11-15 03:00:15 +01:00
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We need to copy the subplan's tuple into our own context, in case
|
|
|
|
* any of the params are pass-by-ref type --- the pointers stored in
|
|
|
|
* the param structs will point at this copied tuple! node->curTuple
|
|
|
|
* keeps track of the copied tuple for eventual freeing.
|
1998-02-19 05:47:07 +01:00
|
|
|
*/
|
1999-11-15 04:28:07 +01:00
|
|
|
if (node->curTuple)
|
1999-12-16 23:20:03 +01:00
|
|
|
heap_freetuple(node->curTuple);
|
Make TupleTableSlots extensible, finish split of existing slot type.
This commit completes the work prepared in 1a0586de36, splitting the
old TupleTableSlot implementation (which could store buffer, heap,
minimal and virtual slots) into four different slot types. As
described in the aforementioned commit, this is done with the goal of
making tuple table slots extensible, to allow for pluggable table
access methods.
To achieve runtime extensibility for TupleTableSlots, operations on
slots that can differ between types of slots are performed using the
TupleTableSlotOps struct provided at slot creation time. That
includes information from the size of TupleTableSlot struct to be
allocated, initialization, deforming etc. See the struct's definition
for more detailed information about callbacks TupleTableSlotOps.
I decided to rename TTSOpsBufferTuple to TTSOpsBufferHeapTuple and
ExecCopySlotTuple to ExecCopySlotHeapTuple, as that seems more
consistent with other naming introduced in recent patches.
There's plenty optimization potential in the slot implementation, but
according to benchmarking the state after this commit has similar
performance characteristics to before this set of changes, which seems
sufficient.
There's a few changes in execReplication.c that currently need to poke
through the slot abstraction, that'll be repaired once the pluggable
storage patchset provides the necessary infrastructure.
Author: Andres Freund and Ashutosh Bapat, with changes by Amit Khandekar
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
2018-11-17 01:35:11 +01:00
|
|
|
node->curTuple = ExecCopySlotHeapTuple(slot);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2003-02-09 01:30:41 +01:00
|
|
|
/*
|
|
|
|
* Now set all the setParam params from the columns of the tuple
|
|
|
|
*/
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, subplan->setParam)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
int paramid = lfirst_int(l);
|
2003-02-09 01:30:41 +01:00
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
prm->execPlan = NULL;
|
2005-03-16 22:38:10 +01:00
|
|
|
prm->value = heap_getattr(node->curTuple, i, tdesc,
|
|
|
|
&(prm->isnull));
|
1998-02-13 04:26:53 +01:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2007-08-26 23:44:25 +02:00
|
|
|
if (subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
2008-10-04 23:56:55 +02:00
|
|
|
/* There can be only one setParam... */
|
2007-08-26 23:44:25 +02:00
|
|
|
int paramid = linitial_int(subplan->setParam);
|
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
|
|
|
|
2012-06-21 23:26:07 +02:00
|
|
|
/*
|
|
|
|
* We build the result array in query context so it won't disappear;
|
|
|
|
* to avoid leaking memory across repeated calls, we have to remember
|
|
|
|
* the latest value, much as for curTuple above.
|
|
|
|
*/
|
|
|
|
if (node->curArray != PointerGetDatum(NULL))
|
|
|
|
pfree(DatumGetPointer(node->curArray));
|
2014-11-25 18:21:22 +01:00
|
|
|
node->curArray = makeArrayResultAny(astate,
|
|
|
|
econtext->ecxt_per_query_memory,
|
|
|
|
true);
|
2012-06-21 23:26:07 +02:00
|
|
|
prm->execPlan = NULL;
|
|
|
|
prm->value = node->curArray;
|
2007-08-26 23:44:25 +02:00
|
|
|
prm->isnull = false;
|
|
|
|
}
|
|
|
|
else if (!found)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2002-12-13 20:46:01 +01:00
|
|
|
if (subLinkType == EXISTS_SUBLINK)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2008-10-04 23:56:55 +02:00
|
|
|
/* There can be only one setParam... */
|
2004-05-26 06:41:50 +02:00
|
|
|
int paramid = linitial_int(subplan->setParam);
|
2003-02-09 01:30:41 +01:00
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
prm->execPlan = NULL;
|
2000-07-12 04:37:39 +02:00
|
|
|
prm->value = BoolGetDatum(false);
|
1998-02-13 04:26:53 +01:00
|
|
|
prm->isnull = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
2014-06-18 19:22:25 +02:00
|
|
|
/* For other sublink types, set all the output params to NULL */
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, subplan->setParam)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
int paramid = lfirst_int(l);
|
2003-02-09 01:30:41 +01:00
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-02-13 04:26:53 +01:00
|
|
|
prm->execPlan = NULL;
|
2000-07-12 04:37:39 +02:00
|
|
|
prm->value = (Datum) 0;
|
1998-02-13 04:26:53 +01:00
|
|
|
prm->isnull = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-10-05 21:11:39 +02:00
|
|
|
MemoryContextSwitchTo(oldcontext);
|
2018-08-17 16:04:26 +02:00
|
|
|
|
|
|
|
/* restore scan direction */
|
|
|
|
estate->es_direction = dir;
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
|
|
|
|
Fix failure with initplans used conditionally during EvalPlanQual rechecks.
The EvalPlanQual machinery assumes that any initplans (that is,
uncorrelated sub-selects) used during an EPQ recheck would have already
been evaluated during the main query; this is implicit in the fact that
execPlan pointers are not copied into the EPQ estate's es_param_exec_vals.
But it's possible for that assumption to fail, if the initplan is only
reached conditionally. For example, a sub-select inside a CASE expression
could be reached during a recheck when it had not been previously, if the
CASE test depends on a column that was just updated.
This bug is old, appearing to date back to my rewrite of EvalPlanQual in
commit 9f2ee8f28, but was not detected until Kyle Samson reported a case.
To fix, force all not-yet-evaluated initplans used within the EPQ plan
subtree to be evaluated at the start of the recheck, before entering the
EPQ environment. This could be inefficient, if such an initplan is
expensive and goes unused again during the recheck --- but that's piling
one layer of improbability atop another. It doesn't seem worth adding
more complexity to prevent that, at least not in the back branches.
It was convenient to use the new-in-v11 ExecEvalParamExecParams function
to implement this, but I didn't like either its name or the specifics of
its API, so revise that.
Back-patch all the way. Rather than rewrite the patch to avoid depending
on bms_next_member() in the oldest branches, I chose to back-patch that
function into 9.4 and 9.3. (This isn't the first time back-patches have
needed that, and it exhausted my patience.) I also chose to back-patch
some test cases added by commits 71404af2a and 342a1ffa2 into 9.4 and 9.3,
so that the 9.x versions of eval-plan-qual.spec are all the same.
Andrew Gierth diagnosed the problem and contributed the added test cases,
though the actual code changes are by me.
Discussion: https://postgr.es/m/A033A40A-B234-4324-BE37-272279F7B627@tripadvisor.com
2018-09-15 19:42:33 +02:00
|
|
|
/*
|
|
|
|
* ExecSetParamPlanMulti
|
|
|
|
*
|
|
|
|
* Apply ExecSetParamPlan to evaluate any not-yet-evaluated initplan output
|
|
|
|
* parameters whose ParamIDs are listed in "params". Any listed params that
|
|
|
|
* are not initplan outputs are ignored.
|
|
|
|
*
|
|
|
|
* As with ExecSetParamPlan, any ExprContext belonging to the current EState
|
|
|
|
* can be used, but in principle a shorter-lived ExprContext is better than a
|
|
|
|
* longer-lived one.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ExecSetParamPlanMulti(const Bitmapset *params, ExprContext *econtext)
|
|
|
|
{
|
|
|
|
int paramid;
|
|
|
|
|
|
|
|
paramid = -1;
|
|
|
|
while ((paramid = bms_next_member(params, paramid)) >= 0)
|
|
|
|
{
|
|
|
|
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
|
|
|
|
|
|
|
|
if (prm->execPlan != NULL)
|
|
|
|
{
|
|
|
|
/* Parameter not evaluated yet, so go do it */
|
|
|
|
ExecSetParamPlan(prm->execPlan, econtext);
|
|
|
|
/* ExecSetParamPlan should have processed this param... */
|
|
|
|
Assert(prm->execPlan == NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-07-21 19:05:12 +02:00
|
|
|
/*
|
|
|
|
* Mark an initplan as needing recalculation
|
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
void
|
2003-08-08 23:42:59 +02:00
|
|
|
ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2002-12-05 16:50:39 +01:00
|
|
|
PlanState *planstate = node->planstate;
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
SubPlan *subplan = node->subplan;
|
2002-12-13 20:46:01 +01:00
|
|
|
EState *estate = parent->state;
|
2004-05-26 06:41:50 +02:00
|
|
|
ListCell *l;
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2003-07-21 19:05:12 +02:00
|
|
|
/* sanity checks */
|
2003-02-09 01:30:41 +01:00
|
|
|
if (subplan->parParam != NIL)
|
2003-07-21 19:05:12 +02:00
|
|
|
elog(ERROR, "direct correlated subquery unsupported as initplan");
|
2003-02-09 01:30:41 +01:00
|
|
|
if (subplan->setParam == NIL)
|
2003-07-21 19:05:12 +02:00
|
|
|
elog(ERROR, "setParam list of initplan is empty");
|
2003-02-09 01:30:41 +01:00
|
|
|
if (bms_is_empty(planstate->plan->extParam))
|
2003-07-21 19:05:12 +02:00
|
|
|
elog(ERROR, "extParam set of initplan is empty");
|
1998-02-26 05:46:47 +01:00
|
|
|
|
|
|
|
/*
|
2008-10-04 23:56:55 +02:00
|
|
|
* Don't actually re-scan: it'll happen inside ExecSetParamPlan if needed.
|
1998-02-13 04:26:53 +01:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2000-10-05 21:11:39 +02:00
|
|
|
/*
|
2008-10-04 23:56:55 +02:00
|
|
|
* Mark this subplan's output parameters as needing recalculation.
|
|
|
|
*
|
|
|
|
* CTE subplans are never executed via parameter recalculation; instead
|
|
|
|
* they get run when called by nodeCtescan.c. So don't mark the output
|
2009-06-11 16:49:15 +02:00
|
|
|
* parameter of a CTE subplan as dirty, but do set the chgParam bit for it
|
|
|
|
* so that dependent plan nodes will get told to rescan.
|
2000-10-05 21:11:39 +02:00
|
|
|
*/
|
2004-05-26 06:41:50 +02:00
|
|
|
foreach(l, subplan->setParam)
|
1998-02-13 04:26:53 +01:00
|
|
|
{
|
2004-05-31 01:40:41 +02:00
|
|
|
int paramid = lfirst_int(l);
|
2003-02-09 01:30:41 +01:00
|
|
|
ParamExecData *prm = &(estate->es_param_exec_vals[paramid]);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2008-10-04 23:56:55 +02:00
|
|
|
if (subplan->subLinkType != CTE_SUBLINK)
|
|
|
|
prm->execPlan = node;
|
|
|
|
|
2003-02-09 01:30:41 +01:00
|
|
|
parent->chgParam = bms_add_member(parent->chgParam, paramid);
|
1998-02-13 04:26:53 +01:00
|
|
|
}
|
|
|
|
}
|
2008-08-22 02:16:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ExecInitAlternativeSubPlan
|
|
|
|
*
|
|
|
|
* Initialize for execution of one of a set of alternative subplans.
|
|
|
|
*/
|
|
|
|
AlternativeSubPlanState *
|
|
|
|
ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
|
|
|
|
{
|
|
|
|
AlternativeSubPlanState *asstate = makeNode(AlternativeSubPlanState);
|
|
|
|
double num_calls;
|
2009-06-11 16:49:15 +02:00
|
|
|
SubPlan *subplan1;
|
|
|
|
SubPlan *subplan2;
|
2008-08-22 02:16:04 +02:00
|
|
|
Cost cost1;
|
|
|
|
Cost cost2;
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
ListCell *lc;
|
2008-08-22 02:16:04 +02:00
|
|
|
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
asstate->subplan = asplan;
|
2008-08-22 02:16:04 +02:00
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Initialize subplans. (Can we get away with only initializing the one
|
|
|
|
* we're going to use?)
|
2008-08-22 02:16:04 +02:00
|
|
|
*/
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
foreach(lc, asplan->subplans)
|
|
|
|
{
|
Improve castNode notation by introducing list-extraction-specific variants.
This extends the castNode() notation introduced by commit 5bcab1114 to
provide, in one step, extraction of a list cell's pointer and coercion to
a concrete node type. For example, "lfirst_node(Foo, lc)" is the same
as "castNode(Foo, lfirst(lc))". Almost half of the uses of castNode
that have appeared so far include a list extraction call, so this is
pretty widely useful, and it saves a few more keystrokes compared to the
old way.
As with the previous patch, back-patch the addition of these macros to
pg_list.h, so that the notation will be available when back-patching.
Patch by me, after an idea of Andrew Gierth's.
Discussion: https://postgr.es/m/14197.1491841216@sss.pgh.pa.us
2017-04-10 19:51:29 +02:00
|
|
|
SubPlan *sp = lfirst_node(SubPlan, lc);
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
SubPlanState *sps = ExecInitSubPlan(sp, parent);
|
|
|
|
|
|
|
|
asstate->subplans = lappend(asstate->subplans, sps);
|
|
|
|
parent->subPlan = lappend(parent->subPlan, sps);
|
|
|
|
}
|
2008-08-22 02:16:04 +02:00
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Select the one to be used. For this, we need an estimate of the number
|
|
|
|
* of executions of the subplan. We use the number of output rows
|
2014-05-06 18:12:18 +02:00
|
|
|
* expected from the parent plan node. This is a good estimate if we are
|
2009-06-11 16:49:15 +02:00
|
|
|
* in the parent's targetlist, and an underestimate (but probably not by
|
|
|
|
* more than a factor of 2) if we are in the qual.
|
2008-08-22 02:16:04 +02:00
|
|
|
*/
|
|
|
|
num_calls = parent->plan->plan_rows;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The planner saved enough info so that we don't have to work very hard
|
|
|
|
* to estimate the total cost, given the number-of-calls estimate.
|
|
|
|
*/
|
|
|
|
Assert(list_length(asplan->subplans) == 2);
|
|
|
|
subplan1 = (SubPlan *) linitial(asplan->subplans);
|
|
|
|
subplan2 = (SubPlan *) lsecond(asplan->subplans);
|
|
|
|
|
|
|
|
cost1 = subplan1->startup_cost + num_calls * subplan1->per_call_cost;
|
|
|
|
cost2 = subplan2->startup_cost + num_calls * subplan2->per_call_cost;
|
|
|
|
|
|
|
|
if (cost1 < cost2)
|
|
|
|
asstate->active = 0;
|
|
|
|
else
|
|
|
|
asstate->active = 1;
|
|
|
|
|
|
|
|
return asstate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ExecAlternativeSubPlan
|
|
|
|
*
|
|
|
|
* Execute one of a set of alternative subplans.
|
|
|
|
*
|
|
|
|
* Note: in future we might consider changing to different subplans on the
|
|
|
|
* fly, in case the original rowcount estimate turns out to be way off.
|
|
|
|
*/
|
Faster expression evaluation and targetlist projection.
This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.
This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.
The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
out operation metadata sequentially; including the avoidance of
nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
constant re-checks at evaluation time
Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.
The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
overhead of expression evaluation, by caching state in prepared
statements. That'd be helpful in OLTPish scenarios where
initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
been made here too.
The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
initialization, whereas previously they were done during
execution. In edge cases this can lead to errors being raised that
previously wouldn't have been, e.g. a NULL array being coerced to a
different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
during expression initialization, previously it was re-built
every time a domain check was evaluated. For normal queries this
doesn't change much, but e.g. for plpgsql functions, which caches
ExprStates, the old set could stick around longer. The behavior
around might still change.
Author: Andres Freund, with significant changes by Tom Lane,
changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
2017-03-14 23:45:36 +01:00
|
|
|
Datum
|
2008-08-22 02:16:04 +02:00
|
|
|
ExecAlternativeSubPlan(AlternativeSubPlanState *node,
|
|
|
|
ExprContext *econtext,
|
2017-01-19 23:12:38 +01:00
|
|
|
bool *isNull)
|
2008-08-22 02:16:04 +02:00
|
|
|
{
|
|
|
|
/* Just pass control to the active subplan */
|
Improve castNode notation by introducing list-extraction-specific variants.
This extends the castNode() notation introduced by commit 5bcab1114 to
provide, in one step, extraction of a list cell's pointer and coercion to
a concrete node type. For example, "lfirst_node(Foo, lc)" is the same
as "castNode(Foo, lfirst(lc))". Almost half of the uses of castNode
that have appeared so far include a list extraction call, so this is
pretty widely useful, and it saves a few more keystrokes compared to the
old way.
As with the previous patch, back-patch the addition of these macros to
pg_list.h, so that the notation will be available when back-patching.
Patch by me, after an idea of Andrew Gierth's.
Discussion: https://postgr.es/m/14197.1491841216@sss.pgh.pa.us
2017-04-10 19:51:29 +02:00
|
|
|
SubPlanState *activesp = list_nth_node(SubPlanState,
|
|
|
|
node->subplans, node->active);
|
2008-08-22 02:16:04 +02:00
|
|
|
|
2017-01-19 23:12:38 +01:00
|
|
|
return ExecSubPlan(activesp, econtext, isNull);
|
2008-08-22 02:16:04 +02:00
|
|
|
}
|