1996-07-09 06:22:35 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-13 23:22:53 +00:00
|
|
|
* nodeFuncs.c
|
2008-08-25 22:42:34 +00:00
|
|
|
* Various general-purpose manipulations of Node trees
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
2018-01-02 23:30:12 -05:00
|
|
|
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
2000-01-26 05:58:53 +00:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/nodes/nodeFuncs.c
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-10-31 10:42:56 +00:00
|
|
|
#include "postgres.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
|
2011-02-08 23:04:18 +02:00
|
|
|
#include "catalog/pg_collation.h"
|
2008-08-25 22:42:34 +00:00
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "miscadmin.h"
|
2012-03-23 17:29:57 -04:00
|
|
|
#include "nodes/makefuncs.h"
|
2015-09-17 11:24:49 -04:00
|
|
|
#include "nodes/execnodes.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
#include "nodes/nodeFuncs.h"
|
2008-08-25 22:42:34 +00:00
|
|
|
#include "nodes/relation.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/lsyscache.h"
|
2003-06-29 00:33:44 +00:00
|
|
|
|
1996-07-09 06:22:35 +00:00
|
|
|
|
2008-08-25 22:42:34 +00:00
|
|
|
static bool expression_returns_set_walker(Node *node, void *context);
|
2008-08-28 23:09:48 +00:00
|
|
|
static int leftmostLoc(int loc1, int loc2);
|
2016-06-10 16:03:37 -04:00
|
|
|
static bool fix_opfuncids_walker(Node *node, void *context);
|
2015-09-17 11:24:49 -04:00
|
|
|
static bool planstate_walk_subplans(List *plans, bool (*walker) (),
|
2017-06-21 14:39:04 -04:00
|
|
|
void *context);
|
2015-09-17 11:24:49 -04:00
|
|
|
static bool planstate_walk_members(List *plans, PlanState **planstates,
|
|
|
|
bool (*walker) (), void *context);
|
1997-08-19 21:40:56 +00:00
|
|
|
|
2002-12-12 15:49:42 +00:00
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
2008-08-25 22:42:34 +00:00
|
|
|
* exprType -
|
2009-03-10 22:09:26 +00:00
|
|
|
* returns the Oid of the type of the expression's result.
|
2008-08-25 22:42:34 +00:00
|
|
|
*/
|
|
|
|
Oid
|
2011-12-07 21:46:56 +02:00
|
|
|
exprType(const Node *expr)
|
2008-08-25 22:42:34 +00:00
|
|
|
{
|
|
|
|
Oid type;
|
|
|
|
|
|
|
|
if (!expr)
|
|
|
|
return InvalidOid;
|
|
|
|
|
|
|
|
switch (nodeTag(expr))
|
|
|
|
{
|
|
|
|
case T_Var:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const Var *) expr)->vartype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_Const:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const Const *) expr)->consttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_Param:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const Param *) expr)->paramtype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_Aggref:
|
2016-06-26 14:33:38 -04:00
|
|
|
type = ((const Aggref *) expr)->aggtype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingFunc:
|
|
|
|
type = INT4OID;
|
|
|
|
break;
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowFunc:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const WindowFunc *) expr)->wintype;
|
2008-12-28 18:54:01 +00:00
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ArrayRef:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const ArrayRef *arrayref = (const ArrayRef *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
/* slice and/or store operations yield the array type */
|
|
|
|
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
|
|
|
|
type = arrayref->refarraytype;
|
|
|
|
else
|
|
|
|
type = arrayref->refelemtype;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const FuncExpr *) expr)->funcresulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
2009-10-08 02:39:25 +00:00
|
|
|
case T_NamedArgExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = exprType((Node *) ((const NamedArgExpr *) expr)->arg);
|
2009-10-08 02:39:25 +00:00
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_OpExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const OpExpr *) expr)->opresulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_DistinctExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const DistinctExpr *) expr)->opresulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_NullIfExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const NullIfExpr *) expr)->opresulttype;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ScalarArrayOpExpr:
|
|
|
|
type = BOOLOID;
|
|
|
|
break;
|
|
|
|
case T_BoolExpr:
|
|
|
|
type = BOOLOID;
|
|
|
|
break;
|
|
|
|
case T_SubLink:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const SubLink *sublink = (const SubLink *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
if (sublink->subLinkType == EXPR_SUBLINK ||
|
|
|
|
sublink->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
|
|
|
/* get the type of the subselect's first target column */
|
|
|
|
Query *qtree = (Query *) sublink->subselect;
|
|
|
|
TargetEntry *tent;
|
|
|
|
|
|
|
|
if (!qtree || !IsA(qtree, Query))
|
|
|
|
elog(ERROR, "cannot get type for untransformed sublink");
|
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 13:51:29 -04:00
|
|
|
tent = linitial_node(TargetEntry, qtree->targetList);
|
2008-08-25 22:42:34 +00:00
|
|
|
Assert(!tent->resjunk);
|
|
|
|
type = exprType((Node *) tent->expr);
|
|
|
|
if (sublink->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
2014-11-25 12:21:22 -05:00
|
|
|
type = get_promoted_array_type(type);
|
2008-08-25 22:42:34 +00:00
|
|
|
if (!OidIsValid(type))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("could not find array type for data type %s",
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00
|
|
|
format_type_be(exprType((Node *) tent->expr)))));
|
2008-08-25 22:42:34 +00: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 13:22:25 -04:00
|
|
|
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
|
|
|
|
{
|
|
|
|
/* MULTIEXPR is always considered to return RECORD */
|
|
|
|
type = RECORDOID;
|
|
|
|
}
|
2008-08-25 22:42:34 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* for all other sublink types, result is boolean */
|
|
|
|
type = BOOLOID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubPlan:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const SubPlan *subplan = (const SubPlan *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
if (subplan->subLinkType == EXPR_SUBLINK ||
|
|
|
|
subplan->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
|
|
|
/* get the type of the subselect's first target column */
|
|
|
|
type = subplan->firstColType;
|
|
|
|
if (subplan->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
2014-11-25 12:21:22 -05:00
|
|
|
type = get_promoted_array_type(type);
|
2008-08-25 22:42:34 +00:00
|
|
|
if (!OidIsValid(type))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("could not find array type for data type %s",
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00
|
|
|
format_type_be(subplan->firstColType))));
|
2008-08-25 22:42:34 +00: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 13:22:25 -04:00
|
|
|
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
|
|
|
|
{
|
|
|
|
/* MULTIEXPR is always considered to return RECORD */
|
|
|
|
type = RECORDOID;
|
|
|
|
}
|
2008-08-25 22:42:34 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* for all other subplan types, result is boolean */
|
|
|
|
type = BOOLOID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_AlternativeSubPlan:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
/* subplans should all return the same thing */
|
|
|
|
type = exprType((Node *) linitial(asplan->subplans));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FieldSelect:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const FieldSelect *) expr)->resulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_FieldStore:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const FieldStore *) expr)->resulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const RelabelType *) expr)->resulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_CoerceViaIO:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const CoerceViaIO *) expr)->resulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_ArrayCoerceExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const ArrayCoerceExpr *) expr)->resulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_ConvertRowtypeExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const ConvertRowtypeExpr *) expr)->resulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
2011-03-11 16:27:51 -05:00
|
|
|
case T_CollateExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = exprType((Node *) ((const CollateExpr *) expr)->arg);
|
2011-03-11 16:27:51 -05:00
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_CaseExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const CaseExpr *) expr)->casetype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_CaseTestExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const CaseTestExpr *) expr)->typeId;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_ArrayExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const ArrayExpr *) expr)->array_typeid;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_RowExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const RowExpr *) expr)->row_typeid;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_RowCompareExpr:
|
|
|
|
type = BOOLOID;
|
|
|
|
break;
|
|
|
|
case T_CoalesceExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const CoalesceExpr *) expr)->coalescetype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const MinMaxExpr *) expr)->minmaxtype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
2016-08-16 20:33:01 -04:00
|
|
|
case T_SQLValueFunction:
|
|
|
|
type = ((const SQLValueFunction *) expr)->type;
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_XmlExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
if (((const XmlExpr *) expr)->op == IS_DOCUMENT)
|
2008-08-25 22:42:34 +00:00
|
|
|
type = BOOLOID;
|
2011-12-07 21:46:56 +02:00
|
|
|
else if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE)
|
2008-08-25 22:42:34 +00:00
|
|
|
type = TEXTOID;
|
|
|
|
else
|
|
|
|
type = XMLOID;
|
|
|
|
break;
|
|
|
|
case T_NullTest:
|
|
|
|
type = BOOLOID;
|
|
|
|
break;
|
|
|
|
case T_BooleanTest:
|
|
|
|
type = BOOLOID;
|
|
|
|
break;
|
|
|
|
case T_CoerceToDomain:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const CoerceToDomain *) expr)->resulttype;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_CoerceToDomainValue:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const CoerceToDomainValue *) expr)->typeId;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_SetToDefault:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = ((const SetToDefault *) expr)->typeId;
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case T_CurrentOfExpr:
|
|
|
|
type = BOOLOID;
|
|
|
|
break;
|
2017-04-06 08:33:16 -04:00
|
|
|
case T_NextValueExpr:
|
|
|
|
type = ((const NextValueExpr *) expr)->typeId;
|
|
|
|
break;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_InferenceElem:
|
|
|
|
{
|
|
|
|
const InferenceElem *n = (const InferenceElem *) expr;
|
|
|
|
|
|
|
|
type = exprType((Node *) n->expr);
|
|
|
|
}
|
|
|
|
break;
|
2008-10-21 20:42:53 +00:00
|
|
|
case T_PlaceHolderVar:
|
2011-12-07 21:46:56 +02:00
|
|
|
type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
2008-10-21 20:42:53 +00:00
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
|
|
|
type = InvalidOid; /* keep compiler quiet */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* exprTypmod -
|
2009-03-10 22:09:26 +00:00
|
|
|
* returns the type-specific modifier of the expression's result type,
|
2014-05-06 12:12:18 -04:00
|
|
|
* if it can be determined. In many cases, it can't and we return -1.
|
2008-08-25 22:42:34 +00:00
|
|
|
*/
|
|
|
|
int32
|
2011-12-07 21:46:56 +02:00
|
|
|
exprTypmod(const Node *expr)
|
2008-08-25 22:42:34 +00:00
|
|
|
{
|
|
|
|
if (!expr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
switch (nodeTag(expr))
|
|
|
|
{
|
|
|
|
case T_Var:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const Var *) expr)->vartypmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_Const:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const Const *) expr)->consttypmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_Param:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const Param *) expr)->paramtypmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ArrayRef:
|
|
|
|
/* typmod is the same for array or element */
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const ArrayRef *) expr)->reftypmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_FuncExpr:
|
|
|
|
{
|
|
|
|
int32 coercedTypmod;
|
|
|
|
|
|
|
|
/* Be smart about length-coercion functions... */
|
|
|
|
if (exprIsLengthCoercion(expr, &coercedTypmod))
|
|
|
|
return coercedTypmod;
|
|
|
|
}
|
|
|
|
break;
|
2009-10-08 02:39:25 +00:00
|
|
|
case T_NamedArgExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
return exprTypmod((Node *) ((const NamedArgExpr *) expr)->arg);
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_NullIfExpr:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Result is either first argument or NULL, so we can report
|
|
|
|
* first argument's typmod if known.
|
|
|
|
*/
|
2011-12-07 21:46:56 +02:00
|
|
|
const NullIfExpr *nexpr = (const NullIfExpr *) expr;
|
2011-03-19 20:29:08 -04:00
|
|
|
|
|
|
|
return exprTypmod((Node *) linitial(nexpr->args));
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_SubLink:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const SubLink *sublink = (const SubLink *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
if (sublink->subLinkType == EXPR_SUBLINK ||
|
|
|
|
sublink->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
|
|
|
/* get the typmod of the subselect's first target column */
|
|
|
|
Query *qtree = (Query *) sublink->subselect;
|
|
|
|
TargetEntry *tent;
|
|
|
|
|
|
|
|
if (!qtree || !IsA(qtree, Query))
|
|
|
|
elog(ERROR, "cannot get type for untransformed sublink");
|
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 13:51:29 -04:00
|
|
|
tent = linitial_node(TargetEntry, qtree->targetList);
|
2008-08-25 22:42:34 +00:00
|
|
|
Assert(!tent->resjunk);
|
|
|
|
return exprTypmod((Node *) tent->expr);
|
|
|
|
/* note we don't need to care if it's an array */
|
|
|
|
}
|
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 13:22:25 -04:00
|
|
|
/* otherwise, result is RECORD or BOOLEAN, typmod is -1 */
|
2008-08-25 22:42:34 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-03-10 22:09:26 +00:00
|
|
|
case T_SubPlan:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const SubPlan *subplan = (const SubPlan *) expr;
|
2009-03-10 22:09:26 +00:00
|
|
|
|
|
|
|
if (subplan->subLinkType == EXPR_SUBLINK ||
|
|
|
|
subplan->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
|
|
|
/* get the typmod of the subselect's first target column */
|
|
|
|
/* note we don't need to care if it's an array */
|
|
|
|
return subplan->firstColTypmod;
|
|
|
|
}
|
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 13:22:25 -04:00
|
|
|
/* otherwise, result is RECORD or BOOLEAN, typmod is -1 */
|
2009-03-10 22:09:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_AlternativeSubPlan:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr;
|
2009-03-10 22:09:26 +00:00
|
|
|
|
|
|
|
/* subplans should all return the same thing */
|
|
|
|
return exprTypmod((Node *) linitial(asplan->subplans));
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_FieldSelect:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const FieldSelect *) expr)->resulttypmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_RelabelType:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const RelabelType *) expr)->resulttypmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ArrayCoerceExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const ArrayCoerceExpr *) expr)->resulttypmod;
|
2011-03-11 16:27:51 -05:00
|
|
|
case T_CollateExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
return exprTypmod((Node *) ((const CollateExpr *) expr)->arg);
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_CaseExpr:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If all the alternatives agree on type/typmod, return that
|
|
|
|
* typmod, else use -1
|
|
|
|
*/
|
2012-06-10 15:20:04 -04:00
|
|
|
const CaseExpr *cexpr = (const CaseExpr *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
Oid casetype = cexpr->casetype;
|
|
|
|
int32 typmod;
|
|
|
|
ListCell *arg;
|
|
|
|
|
|
|
|
if (!cexpr->defresult)
|
|
|
|
return -1;
|
|
|
|
if (exprType((Node *) cexpr->defresult) != casetype)
|
|
|
|
return -1;
|
|
|
|
typmod = exprTypmod((Node *) cexpr->defresult);
|
|
|
|
if (typmod < 0)
|
|
|
|
return -1; /* no point in trying harder */
|
|
|
|
foreach(arg, cexpr->args)
|
|
|
|
{
|
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 13:51:29 -04:00
|
|
|
CaseWhen *w = lfirst_node(CaseWhen, arg);
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
if (exprType((Node *) w->result) != casetype)
|
|
|
|
return -1;
|
|
|
|
if (exprTypmod((Node *) w->result) != typmod)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return typmod;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CaseTestExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const CaseTestExpr *) expr)->typeMod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ArrayExpr:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If all the elements agree on type/typmod, return that
|
|
|
|
* typmod, else use -1
|
|
|
|
*/
|
2012-06-10 15:20:04 -04:00
|
|
|
const ArrayExpr *arrayexpr = (const ArrayExpr *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
Oid commontype;
|
|
|
|
int32 typmod;
|
|
|
|
ListCell *elem;
|
|
|
|
|
|
|
|
if (arrayexpr->elements == NIL)
|
|
|
|
return -1;
|
|
|
|
typmod = exprTypmod((Node *) linitial(arrayexpr->elements));
|
|
|
|
if (typmod < 0)
|
|
|
|
return -1; /* no point in trying harder */
|
|
|
|
if (arrayexpr->multidims)
|
|
|
|
commontype = arrayexpr->array_typeid;
|
|
|
|
else
|
|
|
|
commontype = arrayexpr->element_typeid;
|
|
|
|
foreach(elem, arrayexpr->elements)
|
|
|
|
{
|
|
|
|
Node *e = (Node *) lfirst(elem);
|
|
|
|
|
|
|
|
if (exprType(e) != commontype)
|
|
|
|
return -1;
|
|
|
|
if (exprTypmod(e) != typmod)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return typmod;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoalesceExpr:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If all the alternatives agree on type/typmod, return that
|
|
|
|
* typmod, else use -1
|
|
|
|
*/
|
2011-12-07 21:46:56 +02:00
|
|
|
const CoalesceExpr *cexpr = (const CoalesceExpr *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
Oid coalescetype = cexpr->coalescetype;
|
|
|
|
int32 typmod;
|
|
|
|
ListCell *arg;
|
|
|
|
|
|
|
|
if (exprType((Node *) linitial(cexpr->args)) != coalescetype)
|
|
|
|
return -1;
|
|
|
|
typmod = exprTypmod((Node *) linitial(cexpr->args));
|
|
|
|
if (typmod < 0)
|
|
|
|
return -1; /* no point in trying harder */
|
|
|
|
for_each_cell(arg, lnext(list_head(cexpr->args)))
|
|
|
|
{
|
|
|
|
Node *e = (Node *) lfirst(arg);
|
|
|
|
|
|
|
|
if (exprType(e) != coalescetype)
|
|
|
|
return -1;
|
|
|
|
if (exprTypmod(e) != typmod)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return typmod;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If all the alternatives agree on type/typmod, return that
|
|
|
|
* typmod, else use -1
|
|
|
|
*/
|
2011-12-07 21:46:56 +02:00
|
|
|
const MinMaxExpr *mexpr = (const MinMaxExpr *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
Oid minmaxtype = mexpr->minmaxtype;
|
|
|
|
int32 typmod;
|
|
|
|
ListCell *arg;
|
|
|
|
|
|
|
|
if (exprType((Node *) linitial(mexpr->args)) != minmaxtype)
|
|
|
|
return -1;
|
|
|
|
typmod = exprTypmod((Node *) linitial(mexpr->args));
|
|
|
|
if (typmod < 0)
|
|
|
|
return -1; /* no point in trying harder */
|
|
|
|
for_each_cell(arg, lnext(list_head(mexpr->args)))
|
|
|
|
{
|
|
|
|
Node *e = (Node *) lfirst(arg);
|
|
|
|
|
|
|
|
if (exprType(e) != minmaxtype)
|
|
|
|
return -1;
|
|
|
|
if (exprTypmod(e) != typmod)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return typmod;
|
|
|
|
}
|
|
|
|
break;
|
2016-08-16 20:33:01 -04:00
|
|
|
case T_SQLValueFunction:
|
|
|
|
return ((const SQLValueFunction *) expr)->typmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_CoerceToDomain:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const CoerceToDomain *) expr)->resulttypmod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_CoerceToDomainValue:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const CoerceToDomainValue *) expr)->typeMod;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_SetToDefault:
|
2011-12-07 21:46:56 +02:00
|
|
|
return ((const SetToDefault *) expr)->typeMod;
|
2008-10-21 20:42:53 +00:00
|
|
|
case T_PlaceHolderVar:
|
2011-12-07 21:46:56 +02:00
|
|
|
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
2008-08-25 22:42:34 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-03-19 20:29:08 -04:00
|
|
|
/*
|
|
|
|
* exprIsLengthCoercion
|
|
|
|
* Detect whether an expression tree is an application of a datatype's
|
|
|
|
* typmod-coercion function. Optionally extract the result's typmod.
|
|
|
|
*
|
|
|
|
* If coercedTypmod is not NULL, the typmod is stored there if the expression
|
|
|
|
* is a length-coercion function, else -1 is stored there.
|
|
|
|
*
|
|
|
|
* Note that a combined type-and-length coercion will be treated as a
|
|
|
|
* length coercion by this routine.
|
|
|
|
*/
|
|
|
|
bool
|
2011-12-07 21:46:56 +02:00
|
|
|
exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod)
|
2011-03-19 20:29:08 -04:00
|
|
|
{
|
|
|
|
if (coercedTypmod != NULL)
|
|
|
|
*coercedTypmod = -1; /* default result on failure */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scalar-type length coercions are FuncExprs, array-type length coercions
|
|
|
|
* are ArrayCoerceExprs
|
|
|
|
*/
|
|
|
|
if (expr && IsA(expr, FuncExpr))
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const FuncExpr *func = (const FuncExpr *) expr;
|
2011-03-19 20:29:08 -04:00
|
|
|
int nargs;
|
|
|
|
Const *second_arg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it didn't come from a coercion context, reject.
|
|
|
|
*/
|
|
|
|
if (func->funcformat != COERCE_EXPLICIT_CAST &&
|
|
|
|
func->funcformat != COERCE_IMPLICIT_CAST)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's not a two-argument or three-argument function with the
|
|
|
|
* second argument being an int4 constant, it can't have been created
|
|
|
|
* from a length coercion (it must be a type coercion, instead).
|
|
|
|
*/
|
|
|
|
nargs = list_length(func->args);
|
|
|
|
if (nargs < 2 || nargs > 3)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
second_arg = (Const *) lsecond(func->args);
|
|
|
|
if (!IsA(second_arg, Const) ||
|
|
|
|
second_arg->consttype != INT4OID ||
|
|
|
|
second_arg->constisnull)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, it is indeed a length-coercion function.
|
|
|
|
*/
|
|
|
|
if (coercedTypmod != NULL)
|
|
|
|
*coercedTypmod = DatumGetInt32(second_arg->constvalue);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (expr && IsA(expr, ArrayCoerceExpr))
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const ArrayCoerceExpr *acoerce = (const ArrayCoerceExpr *) expr;
|
2011-03-19 20:29:08 -04:00
|
|
|
|
|
|
|
/* It's not a length coercion unless there's a nondefault typmod */
|
|
|
|
if (acoerce->resulttypmod < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, it is indeed a length-coercion expression.
|
|
|
|
*/
|
|
|
|
if (coercedTypmod != NULL)
|
|
|
|
*coercedTypmod = acoerce->resulttypmod;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-23 17:29:57 -04:00
|
|
|
/*
|
|
|
|
* relabel_to_typmod
|
|
|
|
* Add a RelabelType node that changes just the typmod of the expression.
|
|
|
|
*
|
|
|
|
* This is primarily intended to be used during planning. Therefore, it
|
|
|
|
* strips any existing RelabelType nodes to maintain the planner's invariant
|
2012-10-12 13:35:00 -04:00
|
|
|
* that there are not adjacent RelabelTypes.
|
2012-03-23 17:29:57 -04:00
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
relabel_to_typmod(Node *expr, int32 typmod)
|
|
|
|
{
|
|
|
|
Oid type = exprType(expr);
|
|
|
|
Oid coll = exprCollation(expr);
|
|
|
|
|
|
|
|
/* Strip any existing RelabelType node(s) */
|
|
|
|
while (expr && IsA(expr, RelabelType))
|
|
|
|
expr = (Node *) ((RelabelType *) expr)->arg;
|
|
|
|
|
|
|
|
/* Apply new typmod, preserving the previous exposed type and collation */
|
|
|
|
return (Node *) makeRelabelType((Expr *) expr, type, typmod, coll,
|
2012-10-12 13:35:00 -04:00
|
|
|
COERCE_EXPLICIT_CAST);
|
2012-03-23 17:29:57 -04:00
|
|
|
}
|
|
|
|
|
2013-07-23 18:21:19 -04:00
|
|
|
/*
|
|
|
|
* strip_implicit_coercions: remove implicit coercions at top level of tree
|
|
|
|
*
|
|
|
|
* This doesn't modify or copy the input expression tree, just return a
|
|
|
|
* pointer to a suitable place within it.
|
|
|
|
*
|
|
|
|
* Note: there isn't any useful thing we can do with a RowExpr here, so
|
|
|
|
* just return it unchanged, even if it's marked as an implicit coercion.
|
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
strip_implicit_coercions(Node *node)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(node, FuncExpr))
|
|
|
|
{
|
|
|
|
FuncExpr *f = (FuncExpr *) node;
|
|
|
|
|
|
|
|
if (f->funcformat == COERCE_IMPLICIT_CAST)
|
|
|
|
return strip_implicit_coercions(linitial(f->args));
|
|
|
|
}
|
|
|
|
else if (IsA(node, RelabelType))
|
|
|
|
{
|
|
|
|
RelabelType *r = (RelabelType *) node;
|
|
|
|
|
|
|
|
if (r->relabelformat == COERCE_IMPLICIT_CAST)
|
|
|
|
return strip_implicit_coercions((Node *) r->arg);
|
|
|
|
}
|
|
|
|
else if (IsA(node, CoerceViaIO))
|
|
|
|
{
|
|
|
|
CoerceViaIO *c = (CoerceViaIO *) node;
|
|
|
|
|
|
|
|
if (c->coerceformat == COERCE_IMPLICIT_CAST)
|
|
|
|
return strip_implicit_coercions((Node *) c->arg);
|
|
|
|
}
|
|
|
|
else if (IsA(node, ArrayCoerceExpr))
|
|
|
|
{
|
|
|
|
ArrayCoerceExpr *c = (ArrayCoerceExpr *) node;
|
|
|
|
|
|
|
|
if (c->coerceformat == COERCE_IMPLICIT_CAST)
|
|
|
|
return strip_implicit_coercions((Node *) c->arg);
|
|
|
|
}
|
|
|
|
else if (IsA(node, ConvertRowtypeExpr))
|
|
|
|
{
|
|
|
|
ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node;
|
|
|
|
|
|
|
|
if (c->convertformat == COERCE_IMPLICIT_CAST)
|
|
|
|
return strip_implicit_coercions((Node *) c->arg);
|
|
|
|
}
|
|
|
|
else if (IsA(node, CoerceToDomain))
|
|
|
|
{
|
|
|
|
CoerceToDomain *c = (CoerceToDomain *) node;
|
|
|
|
|
|
|
|
if (c->coercionformat == COERCE_IMPLICIT_CAST)
|
|
|
|
return strip_implicit_coercions((Node *) c->arg);
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2011-03-19 20:29:08 -04:00
|
|
|
/*
|
|
|
|
* expression_returns_set
|
|
|
|
* Test whether an expression returns a set result.
|
|
|
|
*
|
|
|
|
* Because we use expression_tree_walker(), this can also be applied to
|
2017-08-16 00:22:32 -04:00
|
|
|
* whole targetlists; it'll produce true if any one of the tlist items
|
2011-03-19 20:29:08 -04:00
|
|
|
* returns a set.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
expression_returns_set(Node *clause)
|
|
|
|
{
|
|
|
|
return expression_returns_set_walker(clause, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
expression_returns_set_walker(Node *node, void *context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
if (IsA(node, FuncExpr))
|
|
|
|
{
|
|
|
|
FuncExpr *expr = (FuncExpr *) node;
|
|
|
|
|
|
|
|
if (expr->funcretset)
|
|
|
|
return true;
|
|
|
|
/* else fall through to check args */
|
|
|
|
}
|
|
|
|
if (IsA(node, OpExpr))
|
|
|
|
{
|
|
|
|
OpExpr *expr = (OpExpr *) node;
|
|
|
|
|
|
|
|
if (expr->opretset)
|
|
|
|
return true;
|
|
|
|
/* else fall through to check args */
|
|
|
|
}
|
|
|
|
|
2017-06-14 11:10:05 -04:00
|
|
|
/* Avoid recursion for some cases that parser checks not to return a set */
|
2011-03-19 20:29:08 -04:00
|
|
|
if (IsA(node, Aggref))
|
|
|
|
return false;
|
|
|
|
if (IsA(node, WindowFunc))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return expression_tree_walker(node, expression_returns_set_walker,
|
|
|
|
context);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-08 23:04:18 +02:00
|
|
|
/*
|
|
|
|
* exprCollation -
|
|
|
|
* returns the Oid of the collation of the expression's result.
|
2011-03-19 20:29:08 -04:00
|
|
|
*
|
|
|
|
* Note: expression nodes that can invoke functions generally have an
|
|
|
|
* "inputcollid" field, which is what the function should use as collation.
|
|
|
|
* That is the resolved common collation of the node's inputs. It is often
|
|
|
|
* but not always the same as the result collation; in particular, if the
|
|
|
|
* function produces a non-collatable result type from collatable inputs
|
|
|
|
* or vice versa, the two are different.
|
2011-02-08 23:04:18 +02:00
|
|
|
*/
|
|
|
|
Oid
|
2011-12-07 21:46:56 +02:00
|
|
|
exprCollation(const Node *expr)
|
2011-02-08 23:04:18 +02:00
|
|
|
{
|
|
|
|
Oid coll;
|
|
|
|
|
|
|
|
if (!expr)
|
|
|
|
return InvalidOid;
|
|
|
|
|
|
|
|
switch (nodeTag(expr))
|
|
|
|
{
|
|
|
|
case T_Var:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const Var *) expr)->varcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_Const:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const Const *) expr)->constcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_Param:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const Param *) expr)->paramcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_Aggref:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const Aggref *) expr)->aggcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingFunc:
|
|
|
|
coll = InvalidOid;
|
|
|
|
break;
|
2011-02-08 23:04:18 +02:00
|
|
|
case T_WindowFunc:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const WindowFunc *) expr)->wincollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_ArrayRef:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const ArrayRef *) expr)->refcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const FuncExpr *) expr)->funccollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_NamedArgExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = exprCollation((Node *) ((const NamedArgExpr *) expr)->arg);
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_OpExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const OpExpr *) expr)->opcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_DistinctExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const DistinctExpr *) expr)->opcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_NullIfExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const NullIfExpr *) expr)->opcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always boolean */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_BoolExpr:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always boolean */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_SubLink:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const SubLink *sublink = (const SubLink *) expr;
|
2011-02-08 23:04:18 +02:00
|
|
|
|
|
|
|
if (sublink->subLinkType == EXPR_SUBLINK ||
|
|
|
|
sublink->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
/* get the collation of subselect's first target column */
|
2011-02-08 23:04:18 +02:00
|
|
|
Query *qtree = (Query *) sublink->subselect;
|
|
|
|
TargetEntry *tent;
|
|
|
|
|
|
|
|
if (!qtree || !IsA(qtree, Query))
|
|
|
|
elog(ERROR, "cannot get collation for untransformed sublink");
|
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 13:51:29 -04:00
|
|
|
tent = linitial_node(TargetEntry, qtree->targetList);
|
2011-02-08 23:04:18 +02:00
|
|
|
Assert(!tent->resjunk);
|
|
|
|
coll = exprCollation((Node *) tent->expr);
|
2011-03-19 20:29:08 -04:00
|
|
|
/* collation doesn't change if it's converted to array */
|
2011-02-08 23:04:18 +02:00
|
|
|
}
|
|
|
|
else
|
2011-03-19 20:29:08 -04: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 13:22:25 -04:00
|
|
|
/* otherwise, result is RECORD or BOOLEAN */
|
2011-02-08 23:04:18 +02:00
|
|
|
coll = InvalidOid;
|
2011-03-19 20:29:08 -04:00
|
|
|
}
|
2011-02-08 23:04:18 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubPlan:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const SubPlan *subplan = (const SubPlan *) expr;
|
2011-02-08 23:04:18 +02:00
|
|
|
|
|
|
|
if (subplan->subLinkType == EXPR_SUBLINK ||
|
|
|
|
subplan->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
/* get the collation of subselect's first target column */
|
2011-02-08 23:04:18 +02:00
|
|
|
coll = subplan->firstColCollation;
|
2011-03-19 20:29:08 -04:00
|
|
|
/* collation doesn't change if it's converted to array */
|
2011-02-08 23:04:18 +02:00
|
|
|
}
|
|
|
|
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 13:22:25 -04:00
|
|
|
/* otherwise, result is RECORD or BOOLEAN */
|
2011-02-08 23:04:18 +02:00
|
|
|
coll = InvalidOid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_AlternativeSubPlan:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr;
|
2011-02-08 23:04:18 +02:00
|
|
|
|
|
|
|
/* subplans should all return the same thing */
|
|
|
|
coll = exprCollation((Node *) linitial(asplan->subplans));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FieldSelect:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const FieldSelect *) expr)->resultcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_FieldStore:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always composite */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const RelabelType *) expr)->resultcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_CoerceViaIO:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const CoerceViaIO *) expr)->resultcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_ArrayCoerceExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const ArrayCoerceExpr *) expr)->resultcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_ConvertRowtypeExpr:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always composite */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
2011-03-11 16:27:51 -05:00
|
|
|
case T_CollateExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const CollateExpr *) expr)->collOid;
|
2011-03-11 16:27:51 -05:00
|
|
|
break;
|
2011-02-08 23:04:18 +02:00
|
|
|
case T_CaseExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const CaseExpr *) expr)->casecollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_CaseTestExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const CaseTestExpr *) expr)->collation;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_ArrayExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const ArrayExpr *) expr)->array_collid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_RowExpr:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always composite */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_RowCompareExpr:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always boolean */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_CoalesceExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const CoalesceExpr *) expr)->coalescecollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const MinMaxExpr *) expr)->minmaxcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
2016-08-16 20:33:01 -04:00
|
|
|
case T_SQLValueFunction:
|
|
|
|
coll = InvalidOid; /* all cases return non-collatable types */
|
|
|
|
break;
|
2011-02-08 23:04:18 +02:00
|
|
|
case T_XmlExpr:
|
2011-04-10 11:42:00 -04:00
|
|
|
|
2011-03-19 20:29:08 -04:00
|
|
|
/*
|
|
|
|
* XMLSERIALIZE returns text from non-collatable inputs, so its
|
2011-04-10 11:42:00 -04:00
|
|
|
* collation is always default. The other cases return boolean or
|
|
|
|
* XML, which are non-collatable.
|
2011-03-19 20:29:08 -04:00
|
|
|
*/
|
2011-12-07 21:46:56 +02:00
|
|
|
if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE)
|
2011-02-08 23:04:18 +02:00
|
|
|
coll = DEFAULT_COLLATION_OID;
|
|
|
|
else
|
|
|
|
coll = InvalidOid;
|
|
|
|
break;
|
|
|
|
case T_NullTest:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always boolean */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_BooleanTest:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always boolean */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_CoerceToDomain:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const CoerceToDomain *) expr)->resultcollid;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_CoerceToDomainValue:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const CoerceToDomainValue *) expr)->collation;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_SetToDefault:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const SetToDefault *) expr)->collation;
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
case T_CurrentOfExpr:
|
2011-04-10 11:42:00 -04:00
|
|
|
coll = InvalidOid; /* result is always boolean */
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
2017-04-06 08:33:16 -04:00
|
|
|
case T_NextValueExpr:
|
|
|
|
coll = InvalidOid; /* result is always an integer type */
|
|
|
|
break;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_InferenceElem:
|
|
|
|
coll = exprCollation((Node *) ((const InferenceElem *) expr)->expr);
|
|
|
|
break;
|
2011-02-08 23:04:18 +02:00
|
|
|
case T_PlaceHolderVar:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
|
|
|
coll = InvalidOid; /* keep compiler quiet */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return coll;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-03-19 20:29:08 -04:00
|
|
|
* exprInputCollation -
|
|
|
|
* returns the Oid of the collation a function should use, if available.
|
|
|
|
*
|
|
|
|
* Result is InvalidOid if the node type doesn't store this information.
|
2011-02-08 23:04:18 +02:00
|
|
|
*/
|
|
|
|
Oid
|
2011-12-07 21:46:56 +02:00
|
|
|
exprInputCollation(const Node *expr)
|
2011-02-08 23:04:18 +02:00
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
Oid coll;
|
|
|
|
|
|
|
|
if (!expr)
|
|
|
|
return InvalidOid;
|
|
|
|
|
|
|
|
switch (nodeTag(expr))
|
2011-02-08 23:04:18 +02:00
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_Aggref:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const Aggref *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_WindowFunc:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const WindowFunc *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const FuncExpr *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_OpExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const OpExpr *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_DistinctExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const DistinctExpr *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_NullIfExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const NullIfExpr *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const ScalarArrayOpExpr *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
coll = ((const MinMaxExpr *) expr)->inputcollid;
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
coll = InvalidOid;
|
|
|
|
break;
|
2011-02-08 23:04:18 +02:00
|
|
|
}
|
2011-03-19 20:29:08 -04:00
|
|
|
return coll;
|
2011-02-08 23:04:18 +02:00
|
|
|
}
|
|
|
|
|
2008-08-25 22:42:34 +00:00
|
|
|
/*
|
2011-03-19 20:29:08 -04:00
|
|
|
* exprSetCollation -
|
|
|
|
* Assign collation information to an expression tree node.
|
2008-08-25 22:42:34 +00:00
|
|
|
*
|
2011-03-19 20:29:08 -04:00
|
|
|
* Note: since this is only used during parse analysis, we don't need to
|
|
|
|
* worry about subplans or PlaceHolderVars.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
2011-03-19 20:29:08 -04:00
|
|
|
void
|
|
|
|
exprSetCollation(Node *expr, Oid collation)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
switch (nodeTag(expr))
|
2008-08-25 22:42:34 +00:00
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_Var:
|
|
|
|
((Var *) expr)->varcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_Const:
|
|
|
|
((Const *) expr)->constcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_Param:
|
|
|
|
((Param *) expr)->paramcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_Aggref:
|
|
|
|
((Aggref *) expr)->aggcollid = collation;
|
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingFunc:
|
|
|
|
Assert(!OidIsValid(collation));
|
|
|
|
break;
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_WindowFunc:
|
|
|
|
((WindowFunc *) expr)->wincollid = collation;
|
|
|
|
break;
|
|
|
|
case T_ArrayRef:
|
|
|
|
((ArrayRef *) expr)->refcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
|
|
|
((FuncExpr *) expr)->funccollid = collation;
|
|
|
|
break;
|
|
|
|
case T_NamedArgExpr:
|
|
|
|
Assert(collation == exprCollation((Node *) ((NamedArgExpr *) expr)->arg));
|
|
|
|
break;
|
|
|
|
case T_OpExpr:
|
|
|
|
((OpExpr *) expr)->opcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_DistinctExpr:
|
|
|
|
((DistinctExpr *) expr)->opcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_NullIfExpr:
|
|
|
|
((NullIfExpr *) expr)->opcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always boolean */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_BoolExpr:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always boolean */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_SubLink:
|
|
|
|
#ifdef USE_ASSERT_CHECKING
|
|
|
|
{
|
|
|
|
SubLink *sublink = (SubLink *) expr;
|
2008-08-25 22:42:34 +00:00
|
|
|
|
2011-03-19 20:29:08 -04:00
|
|
|
if (sublink->subLinkType == EXPR_SUBLINK ||
|
|
|
|
sublink->subLinkType == ARRAY_SUBLINK)
|
|
|
|
{
|
|
|
|
/* get the collation of subselect's first target column */
|
|
|
|
Query *qtree = (Query *) sublink->subselect;
|
|
|
|
TargetEntry *tent;
|
2008-08-25 22:42:34 +00:00
|
|
|
|
2011-03-19 20:29:08 -04:00
|
|
|
if (!qtree || !IsA(qtree, Query))
|
|
|
|
elog(ERROR, "cannot set collation for untransformed sublink");
|
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 13:51:29 -04:00
|
|
|
tent = linitial_node(TargetEntry, qtree->targetList);
|
2011-03-19 20:29:08 -04:00
|
|
|
Assert(!tent->resjunk);
|
|
|
|
Assert(collation == exprCollation((Node *) tent->expr));
|
|
|
|
}
|
|
|
|
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 13:22:25 -04:00
|
|
|
/* otherwise, result is RECORD or BOOLEAN */
|
2011-03-19 20:29:08 -04:00
|
|
|
Assert(!OidIsValid(collation));
|
|
|
|
}
|
|
|
|
}
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
#endif /* USE_ASSERT_CHECKING */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_FieldSelect:
|
|
|
|
((FieldSelect *) expr)->resultcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_FieldStore:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always composite */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
|
|
|
((RelabelType *) expr)->resultcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_CoerceViaIO:
|
|
|
|
((CoerceViaIO *) expr)->resultcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_ArrayCoerceExpr:
|
|
|
|
((ArrayCoerceExpr *) expr)->resultcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_ConvertRowtypeExpr:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always composite */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_CaseExpr:
|
|
|
|
((CaseExpr *) expr)->casecollid = collation;
|
|
|
|
break;
|
|
|
|
case T_ArrayExpr:
|
|
|
|
((ArrayExpr *) expr)->array_collid = collation;
|
|
|
|
break;
|
|
|
|
case T_RowExpr:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always composite */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_RowCompareExpr:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always boolean */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_CoalesceExpr:
|
|
|
|
((CoalesceExpr *) expr)->coalescecollid = collation;
|
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
|
|
|
((MinMaxExpr *) expr)->minmaxcollid = collation;
|
|
|
|
break;
|
2016-08-16 20:33:01 -04:00
|
|
|
case T_SQLValueFunction:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* no collatable results */
|
2016-08-16 20:33:01 -04:00
|
|
|
break;
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_XmlExpr:
|
|
|
|
Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ?
|
|
|
|
(collation == DEFAULT_COLLATION_OID) :
|
|
|
|
(collation == InvalidOid));
|
|
|
|
break;
|
|
|
|
case T_NullTest:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always boolean */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_BooleanTest:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always boolean */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
|
|
|
case T_CoerceToDomain:
|
|
|
|
((CoerceToDomain *) expr)->resultcollid = collation;
|
|
|
|
break;
|
|
|
|
case T_CoerceToDomainValue:
|
|
|
|
((CoerceToDomainValue *) expr)->collation = collation;
|
|
|
|
break;
|
|
|
|
case T_SetToDefault:
|
|
|
|
((SetToDefault *) expr)->collation = collation;
|
|
|
|
break;
|
|
|
|
case T_CurrentOfExpr:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always boolean */
|
2011-03-19 20:29:08 -04:00
|
|
|
break;
|
2017-04-06 08:33:16 -04:00
|
|
|
case T_NextValueExpr:
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:18:54 -04:00
|
|
|
Assert(!OidIsValid(collation)); /* result is always an integer
|
|
|
|
* type */
|
2017-04-06 08:33:16 -04:00
|
|
|
break;
|
2011-03-19 20:29:08 -04:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-03-19 20:29:08 -04:00
|
|
|
* exprSetInputCollation -
|
|
|
|
* Assign input-collation information to an expression tree node.
|
2008-08-25 22:42:34 +00:00
|
|
|
*
|
2011-03-19 20:29:08 -04:00
|
|
|
* This is a no-op for node types that don't store their input collation.
|
|
|
|
* Note we omit RowCompareExpr, which needs special treatment since it
|
|
|
|
* contains multiple input collation OIDs.
|
2008-08-25 22:42:34 +00:00
|
|
|
*/
|
2011-03-19 20:29:08 -04:00
|
|
|
void
|
|
|
|
exprSetInputCollation(Node *expr, Oid inputcollation)
|
2008-08-25 22:42:34 +00:00
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
switch (nodeTag(expr))
|
2008-08-25 22:42:34 +00:00
|
|
|
{
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_Aggref:
|
|
|
|
((Aggref *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
case T_WindowFunc:
|
|
|
|
((WindowFunc *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
|
|
|
((FuncExpr *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
case T_OpExpr:
|
|
|
|
((OpExpr *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
case T_DistinctExpr:
|
|
|
|
((DistinctExpr *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
case T_NullIfExpr:
|
|
|
|
((NullIfExpr *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
|
|
|
((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
|
|
|
((MinMaxExpr *) expr)->inputcollid = inputcollation;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
}
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-28 23:09:48 +00:00
|
|
|
/*
|
|
|
|
* exprLocation -
|
|
|
|
* returns the parse location of an expression tree, for error reports
|
|
|
|
*
|
|
|
|
* -1 is returned if the location can't be determined.
|
|
|
|
*
|
|
|
|
* For expressions larger than a single token, the intent here is to
|
|
|
|
* return the location of the expression's leftmost token, not necessarily
|
|
|
|
* the topmost Node's location field. For example, an OpExpr's location
|
|
|
|
* field will point at the operator name, but if it is not a prefix operator
|
|
|
|
* then we should return the location of the left-hand operand instead.
|
|
|
|
* The reason is that we want to reference the entire expression not just
|
|
|
|
* that operator, and pointing to its start seems to be the most natural way.
|
|
|
|
*
|
|
|
|
* The location is not perfect --- for example, since the grammar doesn't
|
|
|
|
* explicitly represent parentheses in the parsetree, given something that
|
|
|
|
* had been written "(a + b) * c" we are going to point at "a" not "(".
|
|
|
|
* But it should be plenty good enough for error reporting purposes.
|
|
|
|
*
|
|
|
|
* You might think that this code is overly general, for instance why check
|
|
|
|
* the operands of a FuncExpr node, when the function name can be expected
|
|
|
|
* to be to the left of them? There are a couple of reasons. The grammar
|
|
|
|
* sometimes builds expressions that aren't quite what the user wrote;
|
|
|
|
* for instance x IS NOT BETWEEN ... becomes a NOT-expression whose keyword
|
|
|
|
* pointer is to the right of its leftmost argument. Also, nodes that were
|
|
|
|
* inserted implicitly by parse analysis (such as FuncExprs for implicit
|
|
|
|
* coercions) will have location -1, and so we can have odd combinations of
|
|
|
|
* known and unknown locations in a tree.
|
|
|
|
*/
|
|
|
|
int
|
2011-12-07 21:46:56 +02:00
|
|
|
exprLocation(const Node *expr)
|
2008-08-28 23:09:48 +00:00
|
|
|
{
|
|
|
|
int loc;
|
|
|
|
|
|
|
|
if (expr == NULL)
|
|
|
|
return -1;
|
|
|
|
switch (nodeTag(expr))
|
|
|
|
{
|
2008-09-01 20:42:46 +00:00
|
|
|
case T_RangeVar:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const RangeVar *) expr)->location;
|
2008-09-01 20:42:46 +00:00
|
|
|
break;
|
2017-03-08 12:39:37 -03:00
|
|
|
case T_TableFunc:
|
|
|
|
loc = ((const TableFunc *) expr)->location;
|
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_Var:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const Var *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_Const:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const Const *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_Param:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const Param *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_Aggref:
|
|
|
|
/* function name should always be the first thing */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const Aggref *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingFunc:
|
|
|
|
loc = ((const GroupingFunc *) expr)->location;
|
|
|
|
break;
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowFunc:
|
|
|
|
/* function name should always be the first thing */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const WindowFunc *) expr)->location;
|
2008-12-28 18:54:01 +00:00
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_ArrayRef:
|
|
|
|
/* just use array argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const FuncExpr *fexpr = (const FuncExpr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* consider both function name and leftmost arg */
|
|
|
|
loc = leftmostLoc(fexpr->location,
|
|
|
|
exprLocation((Node *) fexpr->args));
|
|
|
|
}
|
|
|
|
break;
|
2009-10-08 02:39:25 +00:00
|
|
|
case T_NamedArgExpr:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const NamedArgExpr *na = (const NamedArgExpr *) expr;
|
2009-10-08 02:39:25 +00:00
|
|
|
|
|
|
|
/* consider both argument name and value */
|
|
|
|
loc = leftmostLoc(na->location,
|
|
|
|
exprLocation((Node *) na->arg));
|
|
|
|
}
|
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_OpExpr:
|
|
|
|
case T_DistinctExpr: /* struct-equivalent to OpExpr */
|
|
|
|
case T_NullIfExpr: /* struct-equivalent to OpExpr */
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const OpExpr *opexpr = (const OpExpr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* consider both operator name and leftmost arg */
|
|
|
|
loc = leftmostLoc(opexpr->location,
|
|
|
|
exprLocation((Node *) opexpr->args));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* consider both operator name and leftmost arg */
|
|
|
|
loc = leftmostLoc(saopexpr->location,
|
|
|
|
exprLocation((Node *) saopexpr->args));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_BoolExpr:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const BoolExpr *bexpr = (const BoolExpr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Same as above, to handle either NOT or AND/OR. We can't
|
|
|
|
* special-case NOT because of the way that it's used for
|
|
|
|
* things like IS NOT BETWEEN.
|
|
|
|
*/
|
|
|
|
loc = leftmostLoc(bexpr->location,
|
|
|
|
exprLocation((Node *) bexpr->args));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubLink:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const SubLink *sublink = (const SubLink *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* check the testexpr, if any, and the operator/keyword */
|
|
|
|
loc = leftmostLoc(exprLocation(sublink->testexpr),
|
|
|
|
sublink->location);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FieldSelect:
|
|
|
|
/* just use argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const FieldSelect *) expr)->arg);
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_FieldStore:
|
|
|
|
/* just use argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const FieldStore *) expr)->arg);
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const RelabelType *rexpr = (const RelabelType *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* Much as above */
|
|
|
|
loc = leftmostLoc(rexpr->location,
|
|
|
|
exprLocation((Node *) rexpr->arg));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoerceViaIO:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const CoerceViaIO *cexpr = (const CoerceViaIO *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* Much as above */
|
|
|
|
loc = leftmostLoc(cexpr->location,
|
|
|
|
exprLocation((Node *) cexpr->arg));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ArrayCoerceExpr:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const ArrayCoerceExpr *cexpr = (const ArrayCoerceExpr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* Much as above */
|
|
|
|
loc = leftmostLoc(cexpr->location,
|
|
|
|
exprLocation((Node *) cexpr->arg));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ConvertRowtypeExpr:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const ConvertRowtypeExpr *cexpr = (const ConvertRowtypeExpr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* Much as above */
|
|
|
|
loc = leftmostLoc(cexpr->location,
|
|
|
|
exprLocation((Node *) cexpr->arg));
|
|
|
|
}
|
|
|
|
break;
|
2011-03-11 16:27:51 -05:00
|
|
|
case T_CollateExpr:
|
|
|
|
/* just use argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const CollateExpr *) expr)->arg);
|
2011-03-11 16:27:51 -05:00
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_CaseExpr:
|
|
|
|
/* CASE keyword should always be the first thing */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const CaseExpr *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_CaseWhen:
|
|
|
|
/* WHEN keyword should always be the first thing */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const CaseWhen *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_ArrayExpr:
|
|
|
|
/* the location points at ARRAY or [, which must be leftmost */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const ArrayExpr *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_RowExpr:
|
|
|
|
/* the location points at ROW or (, which must be leftmost */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const RowExpr *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_RowCompareExpr:
|
|
|
|
/* just use leftmost argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const RowCompareExpr *) expr)->largs);
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_CoalesceExpr:
|
|
|
|
/* COALESCE keyword should always be the first thing */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const CoalesceExpr *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
|
|
|
/* GREATEST/LEAST keyword should always be the first thing */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const MinMaxExpr *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
2016-08-16 20:33:01 -04:00
|
|
|
case T_SQLValueFunction:
|
|
|
|
/* function keyword should always be the first thing */
|
|
|
|
loc = ((const SQLValueFunction *) expr)->location;
|
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_XmlExpr:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const XmlExpr *xexpr = (const XmlExpr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* consider both function name and leftmost arg */
|
|
|
|
loc = leftmostLoc(xexpr->location,
|
|
|
|
exprLocation((Node *) xexpr->args));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_NullTest:
|
2015-02-22 14:40:27 -05:00
|
|
|
{
|
|
|
|
const NullTest *nexpr = (const NullTest *) expr;
|
|
|
|
|
|
|
|
/* Much as above */
|
|
|
|
loc = leftmostLoc(nexpr->location,
|
|
|
|
exprLocation((Node *) nexpr->arg));
|
|
|
|
}
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_BooleanTest:
|
2015-02-22 14:40:27 -05:00
|
|
|
{
|
|
|
|
const BooleanTest *bexpr = (const BooleanTest *) expr;
|
|
|
|
|
|
|
|
/* Much as above */
|
|
|
|
loc = leftmostLoc(bexpr->location,
|
|
|
|
exprLocation((Node *) bexpr->arg));
|
|
|
|
}
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_CoerceToDomain:
|
|
|
|
{
|
2011-12-07 21:46:56 +02:00
|
|
|
const CoerceToDomain *cexpr = (const CoerceToDomain *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* Much as above */
|
|
|
|
loc = leftmostLoc(cexpr->location,
|
|
|
|
exprLocation((Node *) cexpr->arg));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoerceToDomainValue:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const CoerceToDomainValue *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_SetToDefault:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const SetToDefault *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_TargetEntry:
|
|
|
|
/* just use argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const TargetEntry *) expr)->expr);
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
2008-09-01 20:42:46 +00:00
|
|
|
case T_IntoClause:
|
|
|
|
/* use the contained RangeVar's location --- close enough */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const IntoClause *) expr)->rel);
|
2008-09-01 20:42:46 +00:00
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_List:
|
|
|
|
{
|
|
|
|
/* report location of first list member that has a location */
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
loc = -1; /* just to suppress compiler warning */
|
2011-12-07 21:46:56 +02:00
|
|
|
foreach(lc, (const List *) expr)
|
2008-08-28 23:09:48 +00:00
|
|
|
{
|
|
|
|
loc = exprLocation((Node *) lfirst(lc));
|
|
|
|
if (loc >= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_A_Expr:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const A_Expr *aexpr = (const A_Expr *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* use leftmost of operator or left operand (if any) */
|
|
|
|
/* we assume right operand can't be to left of operator */
|
|
|
|
loc = leftmostLoc(aexpr->location,
|
|
|
|
exprLocation(aexpr->lexpr));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ColumnRef:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const ColumnRef *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_ParamRef:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const ParamRef *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_A_Const:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const A_Const *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_FuncCall:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const FuncCall *fc = (const FuncCall *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/* consider both function name and leftmost arg */
|
2009-12-15 17:57:48 +00:00
|
|
|
/* (we assume any ORDER BY nodes must be to right of name) */
|
2008-08-28 23:09:48 +00:00
|
|
|
loc = leftmostLoc(fc->location,
|
|
|
|
exprLocation((Node *) fc->args));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_A_ArrayExpr:
|
|
|
|
/* the location points at ARRAY or [, which must be leftmost */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const A_ArrayExpr *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
|
|
|
case T_ResTarget:
|
|
|
|
/* we need not examine the contained expression (if any) */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const ResTarget *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
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 13:22:25 -04:00
|
|
|
case T_MultiAssignRef:
|
|
|
|
loc = exprLocation(((const MultiAssignRef *) expr)->source);
|
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_TypeCast:
|
|
|
|
{
|
2012-06-10 15:20:04 -04:00
|
|
|
const TypeCast *tc = (const TypeCast *) expr;
|
2008-08-28 23:09:48 +00:00
|
|
|
|
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* This could represent CAST(), ::, or TypeName 'literal', so
|
|
|
|
* any of the components might be leftmost.
|
2008-08-28 23:09:48 +00:00
|
|
|
*/
|
|
|
|
loc = exprLocation(tc->arg);
|
2009-07-16 06:33:46 +00:00
|
|
|
loc = leftmostLoc(loc, tc->typeName->location);
|
2008-08-28 23:09:48 +00:00
|
|
|
loc = leftmostLoc(loc, tc->location);
|
|
|
|
}
|
|
|
|
break;
|
2011-02-08 23:04:18 +02:00
|
|
|
case T_CollateClause:
|
2011-03-11 16:27:51 -05:00
|
|
|
/* just use argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation(((const CollateClause *) expr)->arg);
|
2011-02-08 23:04:18 +02:00
|
|
|
break;
|
2008-09-01 20:42:46 +00:00
|
|
|
case T_SortBy:
|
|
|
|
/* just use argument's location (ignore operator, if any) */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation(((const SortBy *) expr)->node);
|
2008-09-01 20:42:46 +00:00
|
|
|
break;
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowDef:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const WindowDef *) expr)->location;
|
2008-12-28 18:54:01 +00:00
|
|
|
break;
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
2015-07-25 14:39:00 -04:00
|
|
|
case T_RangeTableSample:
|
|
|
|
loc = ((const RangeTableSample *) expr)->location;
|
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_TypeName:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const TypeName *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
2013-11-21 19:37:02 -05:00
|
|
|
case T_ColumnDef:
|
|
|
|
loc = ((const ColumnDef *) expr)->location;
|
|
|
|
break;
|
2009-07-30 02:45:38 +00:00
|
|
|
case T_Constraint:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const Constraint *) expr)->location;
|
2009-07-30 02:45:38 +00:00
|
|
|
break;
|
Support ordered-set (WITHIN GROUP) aggregates.
This patch introduces generic support for ordered-set and hypothetical-set
aggregate functions, as well as implementations of the instances defined in
SQL:2008 (percentile_cont(), percentile_disc(), rank(), dense_rank(),
percent_rank(), cume_dist()). We also added mode() though it is not in the
spec, as well as versions of percentile_cont() and percentile_disc() that
can compute multiple percentile values in one pass over the data.
Unlike the original submission, this patch puts full control of the sorting
process in the hands of the aggregate's support functions. To allow the
support functions to find out how they're supposed to sort, a new API
function AggGetAggref() is added to nodeAgg.c. This allows retrieval of
the aggregate call's Aggref node, which may have other uses beyond the
immediate need. There is also support for ordered-set aggregates to
install cleanup callback functions, so that they can be sure that
infrastructure such as tuplesort objects gets cleaned up.
In passing, make some fixes in the recently-added support for variadic
aggregates, and make some editorial adjustments in the recent FILTER
additions for aggregates. Also, simplify use of IsBinaryCoercible() by
allowing it to succeed whenever the target type is ANY or ANYELEMENT.
It was inconsistent that it dealt with other polymorphic target types
but not these.
Atri Sharma and Andrew Gierth; reviewed by Pavel Stehule and Vik Fearing,
and rather heavily editorialized upon by Tom Lane
2013-12-23 16:11:35 -05:00
|
|
|
case T_FunctionParameter:
|
|
|
|
/* just use typename's location */
|
|
|
|
loc = exprLocation((Node *) ((const FunctionParameter *) expr)->argType);
|
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
case T_XmlSerialize:
|
|
|
|
/* XMLSERIALIZE keyword should always be the first thing */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const XmlSerialize *) expr)->location;
|
2008-08-28 23:09:48 +00:00
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingSet:
|
|
|
|
loc = ((const GroupingSet *) expr)->location;
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_WithClause:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const WithClause *) expr)->location;
|
2008-10-04 21:56:55 +00:00
|
|
|
break;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_InferClause:
|
|
|
|
loc = ((const InferClause *) expr)->location;
|
|
|
|
break;
|
|
|
|
case T_OnConflictClause:
|
|
|
|
loc = ((const OnConflictClause *) expr)->location;
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_CommonTableExpr:
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = ((const CommonTableExpr *) expr)->location;
|
2008-10-04 21:56:55 +00:00
|
|
|
break;
|
2008-10-21 20:42:53 +00:00
|
|
|
case T_PlaceHolderVar:
|
|
|
|
/* just use argument's location */
|
2011-12-07 21:46:56 +02:00
|
|
|
loc = exprLocation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
2008-10-21 20:42:53 +00:00
|
|
|
break;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_InferenceElem:
|
|
|
|
/* just use nested expr's location */
|
|
|
|
loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr);
|
|
|
|
break;
|
Code review focused on new node types added by partitioning support.
Fix failure to check that we got a plain Const from const-simplification of
a coercion request. This is the cause of bug #14666 from Tian Bing: there
is an int4 to money cast, but it's only stable not immutable (because of
dependence on lc_monetary), resulting in a FuncExpr that the code was
miserably unequipped to deal with, or indeed even to notice that it was
failing to deal with. Add test cases around this coercion behavior.
In view of the above, sprinkle the code liberally with castNode() macros,
in hope of catching the next such bug a bit sooner. Also, change some
functions that were randomly declared to take Node* to take more specific
pointer types. And change some struct fields that were declared Node*
but could be given more specific types, allowing removal of assorted
explicit casts.
Place PARTITION_MAX_KEYS check a bit closer to the code it's protecting.
Likewise check only-one-key-for-list-partitioning restriction in a less
random place.
Avoid not-per-project-style usages like !strcmp(...).
Fix assorted failures to avoid scribbling on the input of parse
transformation. I'm not sure how necessary this is, but it's entirely
silly for these functions to be expending cycles to avoid that and not
getting it right.
Add guards against partitioning on system columns.
Put backend/nodes/ support code into an order that matches handling
of these node types elsewhere.
Annotate the fact that somebody added location fields to PartitionBoundSpec
and PartitionRangeDatum but forgot to handle them in
outfuncs.c/readfuncs.c. This is fairly harmless for production purposes
(since readfuncs.c would just substitute -1 anyway) but it's still bogus.
It's not worth forcing a post-beta1 initdb just to fix this, but if we
have another reason to force initdb before 10.0, we should go back and
clean this up.
Contrariwise, somebody added location fields to PartitionElem and
PartitionSpec but forgot to teach exprLocation() about them.
Consolidate duplicative code in transformPartitionBound().
Improve a couple of error messages.
Improve assorted commentary.
Re-pgindent the files touched by this patch; this affects a few comment
blocks that must have been added quite recently.
Report: https://postgr.es/m/20170524024550.29935.14396@wrigleys.postgresql.org
2017-05-28 23:20:28 -04:00
|
|
|
case T_PartitionElem:
|
|
|
|
loc = ((const PartitionElem *) expr)->location;
|
|
|
|
break;
|
|
|
|
case T_PartitionSpec:
|
|
|
|
loc = ((const PartitionSpec *) expr)->location;
|
|
|
|
break;
|
Implement table partitioning.
Table partitioning is like table inheritance and reuses much of the
existing infrastructure, but there are some important differences.
The parent is called a partitioned table and is always empty; it may
not have indexes or non-inherited constraints, since those make no
sense for a relation with no data of its own. The children are called
partitions and contain all of the actual data. Each partition has an
implicit partitioning constraint. Multiple inheritance is not
allowed, and partitioning and inheritance can't be mixed. Partitions
can't have extra columns and may not allow nulls unless the parent
does. Tuples inserted into the parent are automatically routed to the
correct partition, so tuple-routing ON INSERT triggers are not needed.
Tuple routing isn't yet supported for partitions which are foreign
tables, and it doesn't handle updates that cross partition boundaries.
Currently, tables can be range-partitioned or list-partitioned. List
partitioning is limited to a single column, but range partitioning can
involve multiple columns. A partitioning "column" can be an
expression.
Because table partitioning is less general than table inheritance, it
is hoped that it will be easier to reason about properties of
partitions, and therefore that this will serve as a better foundation
for a variety of possible optimizations, including query planner
optimizations. The tuple routing based which this patch does based on
the implicit partitioning constraints is an example of this, but it
seems likely that many other useful optimizations are also possible.
Amit Langote, reviewed and tested by Robert Haas, Ashutosh Bapat,
Amit Kapila, Rajkumar Raghuwanshi, Corey Huinker, Jaime Casanova,
Rushabh Lathia, Erik Rijkers, among others. Minor revisions by me.
2016-12-07 13:17:43 -05:00
|
|
|
case T_PartitionBoundSpec:
|
|
|
|
loc = ((const PartitionBoundSpec *) expr)->location;
|
|
|
|
break;
|
|
|
|
case T_PartitionRangeDatum:
|
|
|
|
loc = ((const PartitionRangeDatum *) expr)->location;
|
|
|
|
break;
|
2008-08-28 23:09:48 +00:00
|
|
|
default:
|
|
|
|
/* for any other node type it's just unknown... */
|
|
|
|
loc = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return loc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* leftmostLoc - support for exprLocation
|
|
|
|
*
|
|
|
|
* Take the minimum of two parse location values, but ignore unknowns
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
leftmostLoc(int loc1, int loc2)
|
|
|
|
{
|
|
|
|
if (loc1 < 0)
|
|
|
|
return loc2;
|
|
|
|
else if (loc2 < 0)
|
|
|
|
return loc1;
|
|
|
|
else
|
|
|
|
return Min(loc1, loc2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-10 16:03:37 -04:00
|
|
|
/*
|
|
|
|
* fix_opfuncids
|
|
|
|
* Calculate opfuncid field from opno for each OpExpr node in given tree.
|
|
|
|
* The given tree can be anything expression_tree_walker handles.
|
|
|
|
*
|
|
|
|
* The argument is modified in-place. (This is OK since we'd want the
|
|
|
|
* same change for any node, even if it gets visited more than once due to
|
|
|
|
* shared structure.)
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
fix_opfuncids(Node *node)
|
|
|
|
{
|
|
|
|
/* This tree walk requires no special setup, so away we go... */
|
|
|
|
fix_opfuncids_walker(node, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
fix_opfuncids_walker(Node *node, void *context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
if (IsA(node, OpExpr))
|
|
|
|
set_opfuncid((OpExpr *) node);
|
|
|
|
else if (IsA(node, DistinctExpr))
|
|
|
|
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
|
|
|
|
else if (IsA(node, NullIfExpr))
|
|
|
|
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
|
|
|
|
else if (IsA(node, ScalarArrayOpExpr))
|
|
|
|
set_sa_opfuncid((ScalarArrayOpExpr *) node);
|
|
|
|
return expression_tree_walker(node, fix_opfuncids_walker, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_opfuncid
|
|
|
|
* Set the opfuncid (procedure OID) in an OpExpr node,
|
|
|
|
* if it hasn't been set already.
|
|
|
|
*
|
|
|
|
* Because of struct equivalence, this can also be used for
|
|
|
|
* DistinctExpr and NullIfExpr nodes.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_opfuncid(OpExpr *opexpr)
|
|
|
|
{
|
|
|
|
if (opexpr->opfuncid == InvalidOid)
|
|
|
|
opexpr->opfuncid = get_opcode(opexpr->opno);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_sa_opfuncid
|
|
|
|
* As above, for ScalarArrayOpExpr nodes.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
|
|
|
|
{
|
|
|
|
if (opexpr->opfuncid == InvalidOid)
|
|
|
|
opexpr->opfuncid = get_opcode(opexpr->opno);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check_functions_in_node -
|
|
|
|
* apply checker() to each function OID contained in given expression node
|
|
|
|
*
|
2017-08-16 00:22:32 -04:00
|
|
|
* Returns true if the checker() function does; for nodes representing more
|
|
|
|
* than one function call, returns true if the checker() function does so
|
|
|
|
* for any of those functions. Returns false if node does not invoke any
|
2016-06-10 16:03:37 -04:00
|
|
|
* SQL-visible function. Caller must not pass node == NULL.
|
|
|
|
*
|
|
|
|
* This function examines only the given node; it does not recurse into any
|
|
|
|
* sub-expressions. Callers typically prefer to keep control of the recursion
|
|
|
|
* for themselves, in case additional checks should be made, or because they
|
|
|
|
* have special rules about which parts of the tree need to be visited.
|
|
|
|
*
|
Code review for NextValueExpr expression node type.
Add missing infrastructure for this node type, notably in ruleutils.c where
its lack could demonstrably cause EXPLAIN to fail. Add outfuncs/readfuncs
support. (outfuncs support is useful today for debugging purposes. The
readfuncs support may never be needed, since at present it would only
matter for parallel query and NextValueExpr should never appear in a
parallelizable query; but it seems like a bad idea to have a primnode type
that isn't fully supported here.) Teach planner infrastructure that
NextValueExpr is a volatile, parallel-unsafe, non-leaky expression node
with cost cpu_operator_cost. Given its limited scope of usage, there
*might* be no live bug today from the lack of that knowledge, but it's
certainly going to bite us on the rear someday. Teach pg_stat_statements
about the new node type, too.
While at it, also teach cost_qual_eval() that MinMaxExpr, SQLValueFunction,
XmlExpr, and CoerceToDomain should be charged as cpu_operator_cost.
Failing to do this for SQLValueFunction was an oversight in my commit
0bb51aa96. The others are longer-standing oversights, but no time like the
present to fix them. (In principle, CoerceToDomain could have cost much
higher than this, but it doesn't presently seem worth trying to examine the
domain's constraints here.)
Modify execExprInterp.c to execute NextValueExpr as an out-of-line
function; it seems quite unlikely to me that it's worth insisting that
it be inlined in all expression eval methods. Besides, providing the
out-of-line function doesn't stop anyone from inlining if they want to.
Adjust some places where NextValueExpr support had been inserted with the
aid of a dartboard rather than keeping it in the same order as elsewhere.
Discussion: https://postgr.es/m/23862.1499981661@sss.pgh.pa.us
2017-07-14 15:25:43 -04:00
|
|
|
* Note: we ignore MinMaxExpr, SQLValueFunction, XmlExpr, CoerceToDomain,
|
|
|
|
* and NextValueExpr nodes, because they do not contain SQL function OIDs.
|
|
|
|
* However, they can invoke SQL-visible functions, so callers should take
|
|
|
|
* thought about how to treat them.
|
2016-06-10 16:03:37 -04:00
|
|
|
*/
|
|
|
|
bool
|
|
|
|
check_functions_in_node(Node *node, check_function_callback checker,
|
|
|
|
void *context)
|
|
|
|
{
|
|
|
|
switch (nodeTag(node))
|
|
|
|
{
|
|
|
|
case T_Aggref:
|
|
|
|
{
|
|
|
|
Aggref *expr = (Aggref *) node;
|
|
|
|
|
|
|
|
if (checker(expr->aggfnoid, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_WindowFunc:
|
|
|
|
{
|
|
|
|
WindowFunc *expr = (WindowFunc *) node;
|
|
|
|
|
|
|
|
if (checker(expr->winfnoid, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
|
|
|
{
|
|
|
|
FuncExpr *expr = (FuncExpr *) node;
|
|
|
|
|
|
|
|
if (checker(expr->funcid, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_OpExpr:
|
|
|
|
case T_DistinctExpr: /* struct-equivalent to OpExpr */
|
|
|
|
case T_NullIfExpr: /* struct-equivalent to OpExpr */
|
|
|
|
{
|
|
|
|
OpExpr *expr = (OpExpr *) node;
|
|
|
|
|
|
|
|
/* Set opfuncid if it wasn't set already */
|
|
|
|
set_opfuncid(expr);
|
|
|
|
if (checker(expr->opfuncid, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
|
|
|
{
|
|
|
|
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
|
|
|
|
|
|
|
set_sa_opfuncid(expr);
|
|
|
|
if (checker(expr->opfuncid, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoerceViaIO:
|
|
|
|
{
|
|
|
|
CoerceViaIO *expr = (CoerceViaIO *) node;
|
|
|
|
Oid iofunc;
|
|
|
|
Oid typioparam;
|
|
|
|
bool typisvarlena;
|
|
|
|
|
|
|
|
/* check the result type's input function */
|
|
|
|
getTypeInputInfo(expr->resulttype,
|
|
|
|
&iofunc, &typioparam);
|
|
|
|
if (checker(iofunc, context))
|
|
|
|
return true;
|
|
|
|
/* check the input type's output function */
|
|
|
|
getTypeOutputInfo(exprType((Node *) expr->arg),
|
|
|
|
&iofunc, &typisvarlena);
|
|
|
|
if (checker(iofunc, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RowCompareExpr:
|
|
|
|
{
|
|
|
|
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
|
|
|
ListCell *opid;
|
|
|
|
|
|
|
|
foreach(opid, rcexpr->opnos)
|
|
|
|
{
|
|
|
|
Oid opfuncid = get_opcode(lfirst_oid(opid));
|
|
|
|
|
|
|
|
if (checker(opfuncid, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
2008-08-25 22:42:34 +00:00
|
|
|
* Standard expression-tree walking support
|
|
|
|
*
|
|
|
|
* We used to have near-duplicate code in many different routines that
|
|
|
|
* understood how to recurse through an expression node tree. That was
|
|
|
|
* a pain to maintain, and we frequently had bugs due to some particular
|
|
|
|
* routine neglecting to support a particular node type. In most cases,
|
|
|
|
* these routines only actually care about certain node types, and don't
|
|
|
|
* care about other types except insofar as they have to recurse through
|
|
|
|
* non-primitive node types. Therefore, we now provide generic tree-walking
|
|
|
|
* logic to consolidate the redundant "boilerplate" code. There are
|
|
|
|
* two versions: expression_tree_walker() and expression_tree_mutator().
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* expression_tree_walker() is designed to support routines that traverse
|
|
|
|
* a tree in a read-only fashion (although it will also work for routines
|
|
|
|
* that modify nodes in-place but never add/delete/replace nodes).
|
|
|
|
* A walker routine should look like this:
|
|
|
|
*
|
|
|
|
* bool my_walker (Node *node, my_struct *context)
|
|
|
|
* {
|
|
|
|
* if (node == NULL)
|
|
|
|
* return false;
|
|
|
|
* // check for nodes that special work is required for, eg:
|
|
|
|
* if (IsA(node, Var))
|
|
|
|
* {
|
|
|
|
* ... do special actions for Var nodes
|
|
|
|
* }
|
|
|
|
* else if (IsA(node, ...))
|
|
|
|
* {
|
|
|
|
* ... do special actions for other node types
|
|
|
|
* }
|
|
|
|
* // for any node type not specially processed, do:
|
|
|
|
* return expression_tree_walker(node, my_walker, (void *) context);
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* The "context" argument points to a struct that holds whatever context
|
|
|
|
* information the walker routine needs --- it can be used to return data
|
|
|
|
* gathered by the walker, too. This argument is not touched by
|
|
|
|
* expression_tree_walker, but it is passed down to recursive sub-invocations
|
|
|
|
* of my_walker. The tree walk is started from a setup routine that
|
|
|
|
* fills in the appropriate context struct, calls my_walker with the top-level
|
|
|
|
* node of the tree, and then examines the results.
|
|
|
|
*
|
|
|
|
* The walker routine should return "false" to continue the tree walk, or
|
|
|
|
* "true" to abort the walk and immediately return "true" to the top-level
|
2014-05-06 12:12:18 -04:00
|
|
|
* caller. This can be used to short-circuit the traversal if the walker
|
|
|
|
* has found what it came for. "false" is returned to the top-level caller
|
2008-08-25 22:42:34 +00:00
|
|
|
* iff no invocation of the walker returned "true".
|
|
|
|
*
|
|
|
|
* The node types handled by expression_tree_walker include all those
|
|
|
|
* normally found in target lists and qualifier clauses during the planning
|
|
|
|
* stage. In particular, it handles List nodes since a cnf-ified qual clause
|
|
|
|
* will have List structure at the top level, and it handles TargetEntry nodes
|
|
|
|
* so that a scan of a target list can be handled without additional code.
|
|
|
|
* Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are
|
|
|
|
* handled, so that query jointrees and setOperation trees can be processed
|
|
|
|
* without additional code.
|
|
|
|
*
|
|
|
|
* expression_tree_walker will handle SubLink nodes by recursing normally
|
|
|
|
* into the "testexpr" subtree (which is an expression belonging to the outer
|
|
|
|
* plan). It will also call the walker on the sub-Query node; however, when
|
|
|
|
* expression_tree_walker itself is called on a Query node, it does nothing
|
|
|
|
* and returns "false". The net effect is that unless the walker does
|
|
|
|
* something special at a Query node, sub-selects will not be visited during
|
|
|
|
* an expression tree walk. This is exactly the behavior wanted in many cases
|
|
|
|
* --- and for those walkers that do want to recurse into sub-selects, special
|
|
|
|
* behavior is typically needed anyway at the entry to a sub-select (such as
|
|
|
|
* incrementing a depth counter). A walker that wants to examine sub-selects
|
|
|
|
* should include code along the lines of:
|
|
|
|
*
|
|
|
|
* if (IsA(node, Query))
|
|
|
|
* {
|
|
|
|
* adjust context for subquery;
|
|
|
|
* result = query_tree_walker((Query *) node, my_walker, context,
|
|
|
|
* 0); // adjust flags as needed
|
|
|
|
* restore context if needed;
|
|
|
|
* return result;
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* query_tree_walker is a convenience routine (see below) that calls the
|
|
|
|
* walker on all the expression subtrees of the given Query node.
|
|
|
|
*
|
|
|
|
* expression_tree_walker will handle SubPlan nodes by recursing normally
|
|
|
|
* into the "testexpr" and the "args" list (which are expressions belonging to
|
2014-05-06 12:12:18 -04:00
|
|
|
* the outer plan). It will not touch the completed subplan, however. Since
|
2008-08-25 22:42:34 +00:00
|
|
|
* there is no link to the original Query, it is not possible to recurse into
|
|
|
|
* subselects of an already-planned expression tree. This is OK for current
|
|
|
|
* uses, but may need to be revisited in future.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool
|
|
|
|
expression_tree_walker(Node *node,
|
|
|
|
bool (*walker) (),
|
|
|
|
void *context)
|
|
|
|
{
|
|
|
|
ListCell *temp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The walker has already visited the current node, and so we need only
|
|
|
|
* recurse into any sub-nodes it has.
|
|
|
|
*
|
|
|
|
* We assume that the walker is not interested in List nodes per se, so
|
|
|
|
* when we expect a List we just recurse directly to self without
|
|
|
|
* bothering to call the walker.
|
|
|
|
*/
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
|
|
check_stack_depth();
|
|
|
|
|
|
|
|
switch (nodeTag(node))
|
|
|
|
{
|
|
|
|
case T_Var:
|
|
|
|
case T_Const:
|
|
|
|
case T_Param:
|
|
|
|
case T_CaseTestExpr:
|
Code review for NextValueExpr expression node type.
Add missing infrastructure for this node type, notably in ruleutils.c where
its lack could demonstrably cause EXPLAIN to fail. Add outfuncs/readfuncs
support. (outfuncs support is useful today for debugging purposes. The
readfuncs support may never be needed, since at present it would only
matter for parallel query and NextValueExpr should never appear in a
parallelizable query; but it seems like a bad idea to have a primnode type
that isn't fully supported here.) Teach planner infrastructure that
NextValueExpr is a volatile, parallel-unsafe, non-leaky expression node
with cost cpu_operator_cost. Given its limited scope of usage, there
*might* be no live bug today from the lack of that knowledge, but it's
certainly going to bite us on the rear someday. Teach pg_stat_statements
about the new node type, too.
While at it, also teach cost_qual_eval() that MinMaxExpr, SQLValueFunction,
XmlExpr, and CoerceToDomain should be charged as cpu_operator_cost.
Failing to do this for SQLValueFunction was an oversight in my commit
0bb51aa96. The others are longer-standing oversights, but no time like the
present to fix them. (In principle, CoerceToDomain could have cost much
higher than this, but it doesn't presently seem worth trying to examine the
domain's constraints here.)
Modify execExprInterp.c to execute NextValueExpr as an out-of-line
function; it seems quite unlikely to me that it's worth insisting that
it be inlined in all expression eval methods. Besides, providing the
out-of-line function doesn't stop anyone from inlining if they want to.
Adjust some places where NextValueExpr support had been inserted with the
aid of a dartboard rather than keeping it in the same order as elsewhere.
Discussion: https://postgr.es/m/23862.1499981661@sss.pgh.pa.us
2017-07-14 15:25:43 -04:00
|
|
|
case T_SQLValueFunction:
|
|
|
|
case T_CoerceToDomainValue:
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_SetToDefault:
|
|
|
|
case T_CurrentOfExpr:
|
2017-04-06 08:33:16 -04:00
|
|
|
case T_NextValueExpr:
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_RangeTblRef:
|
2009-12-15 17:57:48 +00:00
|
|
|
case T_SortGroupClause:
|
2008-08-25 22:42:34 +00:00
|
|
|
/* primitive node types with no expression subnodes */
|
|
|
|
break;
|
2013-07-18 17:10:16 -04:00
|
|
|
case T_WithCheckOption:
|
|
|
|
return walker(((WithCheckOption *) node)->qual, context);
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_Aggref:
|
|
|
|
{
|
|
|
|
Aggref *expr = (Aggref *) node;
|
|
|
|
|
|
|
|
/* recurse directly on List */
|
Support ordered-set (WITHIN GROUP) aggregates.
This patch introduces generic support for ordered-set and hypothetical-set
aggregate functions, as well as implementations of the instances defined in
SQL:2008 (percentile_cont(), percentile_disc(), rank(), dense_rank(),
percent_rank(), cume_dist()). We also added mode() though it is not in the
spec, as well as versions of percentile_cont() and percentile_disc() that
can compute multiple percentile values in one pass over the data.
Unlike the original submission, this patch puts full control of the sorting
process in the hands of the aggregate's support functions. To allow the
support functions to find out how they're supposed to sort, a new API
function AggGetAggref() is added to nodeAgg.c. This allows retrieval of
the aggregate call's Aggref node, which may have other uses beyond the
immediate need. There is also support for ordered-set aggregates to
install cleanup callback functions, so that they can be sure that
infrastructure such as tuplesort objects gets cleaned up.
In passing, make some fixes in the recently-added support for variadic
aggregates, and make some editorial adjustments in the recent FILTER
additions for aggregates. Also, simplify use of IsBinaryCoercible() by
allowing it to succeed whenever the target type is ANY or ANYELEMENT.
It was inconsistent that it dealt with other polymorphic target types
but not these.
Atri Sharma and Andrew Gierth; reviewed by Pavel Stehule and Vik Fearing,
and rather heavily editorialized upon by Tom Lane
2013-12-23 16:11:35 -05:00
|
|
|
if (expression_tree_walker((Node *) expr->aggdirectargs,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
2008-08-25 22:42:34 +00:00
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
2009-12-15 17:57:48 +00:00
|
|
|
if (expression_tree_walker((Node *) expr->aggorder,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
if (expression_tree_walker((Node *) expr->aggdistinct,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
2013-07-16 20:15:36 -04:00
|
|
|
if (walker((Node *) expr->aggfilter, context))
|
|
|
|
return true;
|
2008-08-25 22:42:34 +00:00
|
|
|
}
|
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingFunc:
|
|
|
|
{
|
|
|
|
GroupingFunc *grouping = (GroupingFunc *) node;
|
|
|
|
|
|
|
|
if (expression_tree_walker((Node *) grouping->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowFunc:
|
|
|
|
{
|
|
|
|
WindowFunc *expr = (WindowFunc *) node;
|
|
|
|
|
|
|
|
/* recurse directly on List */
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
2013-07-16 20:15:36 -04:00
|
|
|
if (walker((Node *) expr->aggfilter, context))
|
|
|
|
return true;
|
2008-12-28 18:54:01 +00:00
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ArrayRef:
|
|
|
|
{
|
|
|
|
ArrayRef *aref = (ArrayRef *) node;
|
|
|
|
|
|
|
|
/* recurse directly for upper/lower array index lists */
|
|
|
|
if (expression_tree_walker((Node *) aref->refupperindexpr,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
if (expression_tree_walker((Node *) aref->reflowerindexpr,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
/* walker must see the refexpr and refassgnexpr, however */
|
|
|
|
if (walker(aref->refexpr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(aref->refassgnexpr, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
|
|
|
{
|
|
|
|
FuncExpr *expr = (FuncExpr *) node;
|
|
|
|
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2009-10-08 02:39:25 +00:00
|
|
|
case T_NamedArgExpr:
|
|
|
|
return walker(((NamedArgExpr *) node)->arg, context);
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_OpExpr:
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_DistinctExpr: /* struct-equivalent to OpExpr */
|
|
|
|
case T_NullIfExpr: /* struct-equivalent to OpExpr */
|
2008-08-25 22:42:34 +00:00
|
|
|
{
|
|
|
|
OpExpr *expr = (OpExpr *) node;
|
|
|
|
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ScalarArrayOpExpr:
|
|
|
|
{
|
|
|
|
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
|
|
|
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_BoolExpr:
|
|
|
|
{
|
|
|
|
BoolExpr *expr = (BoolExpr *) node;
|
|
|
|
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubLink:
|
|
|
|
{
|
|
|
|
SubLink *sublink = (SubLink *) node;
|
|
|
|
|
|
|
|
if (walker(sublink->testexpr, context))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also invoke the walker on the sublink's Query node, so it
|
|
|
|
* can recurse into the sub-query if it wants to.
|
|
|
|
*/
|
|
|
|
return walker(sublink->subselect, context);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubPlan:
|
|
|
|
{
|
|
|
|
SubPlan *subplan = (SubPlan *) node;
|
|
|
|
|
|
|
|
/* recurse into the testexpr, but not into the Plan */
|
|
|
|
if (walker(subplan->testexpr, context))
|
|
|
|
return true;
|
|
|
|
/* also examine args list */
|
|
|
|
if (expression_tree_walker((Node *) subplan->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_AlternativeSubPlan:
|
|
|
|
return walker(((AlternativeSubPlan *) node)->subplans, context);
|
|
|
|
case T_FieldSelect:
|
|
|
|
return walker(((FieldSelect *) node)->arg, context);
|
|
|
|
case T_FieldStore:
|
|
|
|
{
|
|
|
|
FieldStore *fstore = (FieldStore *) node;
|
|
|
|
|
|
|
|
if (walker(fstore->arg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(fstore->newvals, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
|
|
|
return walker(((RelabelType *) node)->arg, context);
|
|
|
|
case T_CoerceViaIO:
|
|
|
|
return walker(((CoerceViaIO *) node)->arg, context);
|
|
|
|
case T_ArrayCoerceExpr:
|
Support arrays over domains.
Allowing arrays with a domain type as their element type was left un-done
in the original domain patch, but not for any very good reason. This
omission leads to such surprising results as array_agg() not working on
a domain column, because the parser can't identify a suitable output type
for the polymorphic aggregate.
In order to fix this, first clean up the APIs of coerce_to_domain() and
some internal functions in parse_coerce.c so that we consistently pass
around a CoercionContext along with CoercionForm. Previously, we sometimes
passed an "isExplicit" boolean flag instead, which is strictly less
information; and coerce_to_domain() didn't even get that, but instead had
to reverse-engineer isExplicit from CoercionForm. That's contrary to the
documentation in primnodes.h that says that CoercionForm only affects
display and not semantics. I don't think this change fixes any live bugs,
but it makes things more consistent. The main reason for doing it though
is that now build_coercion_expression() receives ccontext, which it needs
in order to be able to recursively invoke coerce_to_target_type().
Next, reimplement ArrayCoerceExpr so that the node does not directly know
any details of what has to be done to the individual array elements while
performing the array coercion. Instead, the per-element processing is
represented by a sub-expression whose input is a source array element and
whose output is a target array element. This simplifies life in
parse_coerce.c, because it can build that sub-expression by a recursive
invocation of coerce_to_target_type(). The executor now handles the
per-element processing as a compiled expression instead of hard-wired code.
The main advantage of this is that we can use a single ArrayCoerceExpr to
handle as many as three successive steps per element: base type conversion,
typmod coercion, and domain constraint checking. The old code used two
stacked ArrayCoerceExprs to handle type + typmod coercion, which was pretty
inefficient, and adding yet another array deconstruction to do domain
constraint checking seemed very unappetizing.
In the case where we just need a single, very simple coercion function,
doing this straightforwardly leads to a noticeable increase in the
per-array-element runtime cost. Hence, add an additional shortcut evalfunc
in execExprInterp.c that skips unnecessary overhead for that specific form
of expression. The runtime speed of simple cases is within 1% or so of
where it was before, while cases that previously required two levels of
array processing are significantly faster.
Finally, create an implicit array type for every domain type, as we do for
base types, enums, etc. Everything except the array-coercion case seems
to just work without further effort.
Tom Lane, reviewed by Andrew Dunstan
Discussion: https://postgr.es/m/9852.1499791473@sss.pgh.pa.us
2017-09-30 13:40:56 -04:00
|
|
|
{
|
|
|
|
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
|
|
|
|
|
|
|
|
if (walker(acoerce->arg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(acoerce->elemexpr, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ConvertRowtypeExpr:
|
|
|
|
return walker(((ConvertRowtypeExpr *) node)->arg, context);
|
2011-03-11 16:27:51 -05:00
|
|
|
case T_CollateExpr:
|
|
|
|
return walker(((CollateExpr *) node)->arg, context);
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_CaseExpr:
|
|
|
|
{
|
|
|
|
CaseExpr *caseexpr = (CaseExpr *) node;
|
|
|
|
|
|
|
|
if (walker(caseexpr->arg, context))
|
|
|
|
return true;
|
|
|
|
/* we assume walker doesn't care about CaseWhens, either */
|
|
|
|
foreach(temp, caseexpr->args)
|
|
|
|
{
|
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 13:51:29 -04:00
|
|
|
CaseWhen *when = lfirst_node(CaseWhen, temp);
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
if (walker(when->expr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(when->result, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (walker(caseexpr->defresult, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ArrayExpr:
|
|
|
|
return walker(((ArrayExpr *) node)->elements, context);
|
|
|
|
case T_RowExpr:
|
2008-10-06 17:39:26 +00:00
|
|
|
/* Assume colnames isn't interesting */
|
2008-08-25 22:42:34 +00:00
|
|
|
return walker(((RowExpr *) node)->args, context);
|
|
|
|
case T_RowCompareExpr:
|
|
|
|
{
|
|
|
|
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
|
|
|
|
|
|
|
if (walker(rcexpr->largs, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rcexpr->rargs, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoalesceExpr:
|
|
|
|
return walker(((CoalesceExpr *) node)->args, context);
|
|
|
|
case T_MinMaxExpr:
|
|
|
|
return walker(((MinMaxExpr *) node)->args, context);
|
|
|
|
case T_XmlExpr:
|
|
|
|
{
|
|
|
|
XmlExpr *xexpr = (XmlExpr *) node;
|
|
|
|
|
|
|
|
if (walker(xexpr->named_args, context))
|
|
|
|
return true;
|
|
|
|
/* we assume walker doesn't care about arg_names */
|
|
|
|
if (walker(xexpr->args, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_NullTest:
|
|
|
|
return walker(((NullTest *) node)->arg, context);
|
|
|
|
case T_BooleanTest:
|
|
|
|
return walker(((BooleanTest *) node)->arg, context);
|
|
|
|
case T_CoerceToDomain:
|
|
|
|
return walker(((CoerceToDomain *) node)->arg, context);
|
|
|
|
case T_TargetEntry:
|
|
|
|
return walker(((TargetEntry *) node)->expr, context);
|
|
|
|
case T_Query:
|
|
|
|
/* Do nothing with a sub-Query, per discussion above */
|
|
|
|
break;
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowClause:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
WindowClause *wc = (WindowClause *) node;
|
2008-12-28 18:54:01 +00:00
|
|
|
|
|
|
|
if (walker(wc->partitionClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(wc->orderClause, context))
|
|
|
|
return true;
|
2010-02-12 17:33:21 +00:00
|
|
|
if (walker(wc->startOffset, context))
|
|
|
|
return true;
|
|
|
|
if (walker(wc->endOffset, context))
|
|
|
|
return true;
|
2008-12-28 18:54:01 +00:00
|
|
|
}
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_CommonTableExpr:
|
|
|
|
{
|
|
|
|
CommonTableExpr *cte = (CommonTableExpr *) node;
|
|
|
|
|
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* Invoke the walker on the CTE's Query node, so it can
|
|
|
|
* recurse into the sub-query if it wants to.
|
2008-10-04 21:56:55 +00:00
|
|
|
*/
|
|
|
|
return walker(cte->ctequery, context);
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_List:
|
|
|
|
foreach(temp, (List *) node)
|
|
|
|
{
|
|
|
|
if (walker((Node *) lfirst(temp), context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FromExpr:
|
|
|
|
{
|
|
|
|
FromExpr *from = (FromExpr *) node;
|
|
|
|
|
|
|
|
if (walker(from->fromlist, context))
|
|
|
|
return true;
|
|
|
|
if (walker(from->quals, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_OnConflictExpr:
|
|
|
|
{
|
2015-05-23 21:35:49 -04:00
|
|
|
OnConflictExpr *onconflict = (OnConflictExpr *) node;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
|
|
|
|
if (walker((Node *) onconflict->arbiterElems, context))
|
|
|
|
return true;
|
|
|
|
if (walker(onconflict->arbiterWhere, context))
|
|
|
|
return true;
|
|
|
|
if (walker(onconflict->onConflictSet, context))
|
|
|
|
return true;
|
|
|
|
if (walker(onconflict->onConflictWhere, context))
|
|
|
|
return true;
|
2015-05-13 00:13:22 +02:00
|
|
|
if (walker(onconflict->exclRelTlist, context))
|
|
|
|
return true;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
}
|
|
|
|
break;
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
case T_MergeAction:
|
|
|
|
{
|
|
|
|
MergeAction *action = (MergeAction *) node;
|
|
|
|
|
|
|
|
if (walker(action->targetList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(action->qual, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_JoinExpr:
|
|
|
|
{
|
|
|
|
JoinExpr *join = (JoinExpr *) node;
|
|
|
|
|
|
|
|
if (walker(join->larg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(join->rarg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(join->quals, context))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* alias clause, using list are deemed uninteresting.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SetOperationStmt:
|
|
|
|
{
|
|
|
|
SetOperationStmt *setop = (SetOperationStmt *) node;
|
|
|
|
|
|
|
|
if (walker(setop->larg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(setop->rarg, context))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* groupClauses are deemed uninteresting */
|
|
|
|
}
|
|
|
|
break;
|
2008-10-21 20:42:53 +00:00
|
|
|
case T_PlaceHolderVar:
|
|
|
|
return walker(((PlaceHolderVar *) node)->phexpr, context);
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_InferenceElem:
|
|
|
|
return walker(((InferenceElem *) node)->expr, context);
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_AppendRelInfo:
|
|
|
|
{
|
|
|
|
AppendRelInfo *appinfo = (AppendRelInfo *) node;
|
|
|
|
|
|
|
|
if (expression_tree_walker((Node *) appinfo->translated_vars,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-21 20:42:53 +00:00
|
|
|
case T_PlaceHolderInfo:
|
|
|
|
return walker(((PlaceHolderInfo *) node)->ph_var, context);
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
2013-11-21 19:37:02 -05:00
|
|
|
case T_RangeTblFunction:
|
|
|
|
return walker(((RangeTblFunction *) node)->funcexpr, context);
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
2015-07-25 14:39:00 -04:00
|
|
|
case T_TableSampleClause:
|
|
|
|
{
|
|
|
|
TableSampleClause *tsc = (TableSampleClause *) node;
|
|
|
|
|
|
|
|
if (expression_tree_walker((Node *) tsc->args,
|
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
if (walker((Node *) tsc->repeatable, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2017-03-08 12:39:37 -03:00
|
|
|
case T_TableFunc:
|
|
|
|
{
|
|
|
|
TableFunc *tf = (TableFunc *) node;
|
|
|
|
|
|
|
|
if (walker(tf->ns_uris, context))
|
|
|
|
return true;
|
|
|
|
if (walker(tf->docexpr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(tf->rowexpr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(tf->colexprs, context))
|
|
|
|
return true;
|
|
|
|
if (walker(tf->coldefexprs, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
|
|
(int) nodeTag(node));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* query_tree_walker --- initiate a walk of a Query's expressions
|
|
|
|
*
|
|
|
|
* This routine exists just to reduce the number of places that need to know
|
|
|
|
* where all the expression subtrees of a Query are. Note it can be used
|
|
|
|
* for starting a walk at top level of a Query regardless of whether the
|
|
|
|
* walker intends to descend into subqueries. It is also useful for
|
|
|
|
* descending into subqueries within a walker.
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
2008-08-25 22:42:34 +00:00
|
|
|
* Some callers want to suppress visitation of certain items in the sub-Query,
|
|
|
|
* typically because they need to process them specially, or don't actually
|
|
|
|
* want to recurse into subqueries. This is supported by the flags argument,
|
|
|
|
* which is the bitwise OR of flag values to suppress visitation of
|
|
|
|
* indicated items. (More flag bits may be added as needed.)
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
|
|
|
bool
|
2008-08-25 22:42:34 +00:00
|
|
|
query_tree_walker(Query *query,
|
|
|
|
bool (*walker) (),
|
|
|
|
void *context,
|
|
|
|
int flags)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2008-08-25 22:42:34 +00:00
|
|
|
Assert(query != NULL && IsA(query, Query));
|
|
|
|
|
|
|
|
if (walker((Node *) query->targetList, context))
|
|
|
|
return true;
|
2013-07-18 17:10:16 -04:00
|
|
|
if (walker((Node *) query->withCheckOptions, context))
|
|
|
|
return true;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
if (walker((Node *) query->onConflict, context))
|
|
|
|
return true;
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
if (walker((Node *) query->mergeSourceTargetList, context))
|
|
|
|
return true;
|
|
|
|
if (walker((Node *) query->mergeActionList, context))
|
|
|
|
return true;
|
2008-08-25 22:42:34 +00:00
|
|
|
if (walker((Node *) query->returningList, context))
|
|
|
|
return true;
|
|
|
|
if (walker((Node *) query->jointree, context))
|
|
|
|
return true;
|
|
|
|
if (walker(query->setOperations, context))
|
|
|
|
return true;
|
|
|
|
if (walker(query->havingQual, context))
|
|
|
|
return true;
|
|
|
|
if (walker(query->limitOffset, context))
|
|
|
|
return true;
|
|
|
|
if (walker(query->limitCount, context))
|
|
|
|
return true;
|
2008-10-04 21:56:55 +00:00
|
|
|
if (!(flags & QTW_IGNORE_CTE_SUBQUERIES))
|
|
|
|
{
|
|
|
|
if (walker((Node *) query->cteList, context))
|
|
|
|
return true;
|
|
|
|
}
|
2011-03-19 20:29:08 -04:00
|
|
|
if (!(flags & QTW_IGNORE_RANGE_TABLE))
|
|
|
|
{
|
|
|
|
if (range_table_walker(query->rtable, walker, context, flags))
|
|
|
|
return true;
|
|
|
|
}
|
2008-08-25 22:42:34 +00:00
|
|
|
return false;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
2008-08-25 22:42:34 +00:00
|
|
|
/*
|
|
|
|
* range_table_walker is just the part of query_tree_walker that scans
|
|
|
|
* a query's rangetable. This is split out since it can be useful on
|
|
|
|
* its own.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
range_table_walker(List *rtable,
|
|
|
|
bool (*walker) (),
|
|
|
|
void *context,
|
|
|
|
int flags)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2008-08-25 22:42:34 +00:00
|
|
|
ListCell *rt;
|
|
|
|
|
|
|
|
foreach(rt, rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
|
|
|
|
2008-10-04 21:56:55 +00:00
|
|
|
/* For historical reasons, visiting RTEs is not the default */
|
|
|
|
if (flags & QTW_EXAMINE_RTES)
|
|
|
|
if (walker(rte, context))
|
|
|
|
return true;
|
|
|
|
|
2008-08-25 22:42:34 +00:00
|
|
|
switch (rte->rtekind)
|
|
|
|
{
|
|
|
|
case RTE_RELATION:
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
2015-07-25 14:39:00 -04:00
|
|
|
if (walker(rte->tablesample, context))
|
|
|
|
return true;
|
2015-05-15 14:37:10 -04:00
|
|
|
break;
|
2009-06-11 14:49:15 +00:00
|
|
|
case RTE_CTE:
|
2017-03-31 23:17:18 -05:00
|
|
|
case RTE_NAMEDTUPLESTORE:
|
2008-08-25 22:42:34 +00:00
|
|
|
/* nothing to do */
|
|
|
|
break;
|
|
|
|
case RTE_SUBQUERY:
|
|
|
|
if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
|
|
|
|
if (walker(rte->subquery, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case RTE_JOIN:
|
|
|
|
if (!(flags & QTW_IGNORE_JOINALIASES))
|
|
|
|
if (walker(rte->joinaliasvars, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case RTE_FUNCTION:
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
2013-11-21 19:37:02 -05:00
|
|
|
if (walker(rte->functions, context))
|
2008-08-25 22:42:34 +00:00
|
|
|
return true;
|
|
|
|
break;
|
2017-03-08 12:39:37 -03:00
|
|
|
case RTE_TABLEFUNC:
|
|
|
|
if (walker(rte->tablefunc, context))
|
|
|
|
return true;
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case RTE_VALUES:
|
|
|
|
if (walker(rte->values_lists, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
}
|
2014-04-12 21:04:58 -04:00
|
|
|
|
|
|
|
if (walker(rte->securityQuals, context))
|
|
|
|
return true;
|
2008-08-25 22:42:34 +00:00
|
|
|
}
|
|
|
|
return false;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
2008-08-25 22:42:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* expression_tree_mutator() is designed to support routines that make a
|
|
|
|
* modified copy of an expression tree, with some nodes being added,
|
|
|
|
* removed, or replaced by new subtrees. The original tree is (normally)
|
|
|
|
* not changed. Each recursion level is responsible for returning a copy of
|
|
|
|
* (or appropriately modified substitute for) the subtree it is handed.
|
|
|
|
* A mutator routine should look like this:
|
|
|
|
*
|
|
|
|
* Node * my_mutator (Node *node, my_struct *context)
|
|
|
|
* {
|
|
|
|
* if (node == NULL)
|
|
|
|
* return NULL;
|
|
|
|
* // check for nodes that special work is required for, eg:
|
|
|
|
* if (IsA(node, Var))
|
|
|
|
* {
|
|
|
|
* ... create and return modified copy of Var node
|
|
|
|
* }
|
|
|
|
* else if (IsA(node, ...))
|
|
|
|
* {
|
|
|
|
* ... do special transformations of other node types
|
|
|
|
* }
|
|
|
|
* // for any node type not specially processed, do:
|
|
|
|
* return expression_tree_mutator(node, my_mutator, (void *) context);
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* The "context" argument points to a struct that holds whatever context
|
|
|
|
* information the mutator routine needs --- it can be used to return extra
|
|
|
|
* data gathered by the mutator, too. This argument is not touched by
|
|
|
|
* expression_tree_mutator, but it is passed down to recursive sub-invocations
|
|
|
|
* of my_mutator. The tree walk is started from a setup routine that
|
|
|
|
* fills in the appropriate context struct, calls my_mutator with the
|
|
|
|
* top-level node of the tree, and does any required post-processing.
|
|
|
|
*
|
|
|
|
* Each level of recursion must return an appropriately modified Node.
|
|
|
|
* If expression_tree_mutator() is called, it will make an exact copy
|
|
|
|
* of the given Node, but invoke my_mutator() to copy the sub-node(s)
|
|
|
|
* of that Node. In this way, my_mutator() has full control over the
|
|
|
|
* copying process but need not directly deal with expression trees
|
|
|
|
* that it has no interest in.
|
|
|
|
*
|
|
|
|
* Just as for expression_tree_walker, the node types handled by
|
|
|
|
* expression_tree_mutator include all those normally found in target lists
|
|
|
|
* and qualifier clauses during the planning stage.
|
|
|
|
*
|
|
|
|
* expression_tree_mutator will handle SubLink nodes by recursing normally
|
|
|
|
* into the "testexpr" subtree (which is an expression belonging to the outer
|
|
|
|
* plan). It will also call the mutator on the sub-Query node; however, when
|
|
|
|
* expression_tree_mutator itself is called on a Query node, it does nothing
|
|
|
|
* and returns the unmodified Query node. The net effect is that unless the
|
|
|
|
* mutator does something special at a Query node, sub-selects will not be
|
|
|
|
* visited or modified; the original sub-select will be linked to by the new
|
|
|
|
* SubLink node. Mutators that want to descend into sub-selects will usually
|
|
|
|
* do so by recognizing Query nodes and calling query_tree_mutator (below).
|
|
|
|
*
|
|
|
|
* expression_tree_mutator will handle a SubPlan node by recursing into the
|
|
|
|
* "testexpr" and the "args" list (which belong to the outer plan), but it
|
|
|
|
* will simply copy the link to the inner plan, since that's typically what
|
|
|
|
* expression tree mutators want. A mutator that wants to modify the subplan
|
|
|
|
* can force appropriate behavior by recognizing SubPlan expression nodes
|
|
|
|
* and doing the right thing.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Node *
|
|
|
|
expression_tree_mutator(Node *node,
|
|
|
|
Node *(*mutator) (),
|
|
|
|
void *context)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The mutator has already decided not to modify the current node, but we
|
|
|
|
* must call the mutator for any sub-nodes.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define FLATCOPY(newnode, node, nodetype) \
|
|
|
|
( (newnode) = (nodetype *) palloc(sizeof(nodetype)), \
|
|
|
|
memcpy((newnode), (node), sizeof(nodetype)) )
|
|
|
|
|
|
|
|
#define CHECKFLATCOPY(newnode, node, nodetype) \
|
|
|
|
( AssertMacro(IsA((node), nodetype)), \
|
|
|
|
(newnode) = (nodetype *) palloc(sizeof(nodetype)), \
|
|
|
|
memcpy((newnode), (node), sizeof(nodetype)) )
|
|
|
|
|
|
|
|
#define MUTATE(newfield, oldfield, fieldtype) \
|
|
|
|
( (newfield) = (fieldtype) mutator((Node *) (oldfield), context) )
|
|
|
|
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
|
|
check_stack_depth();
|
|
|
|
|
|
|
|
switch (nodeTag(node))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Primitive node types with no expression subnodes. Var and
|
|
|
|
* Const are frequent enough to deserve special cases, the others
|
|
|
|
* we just use copyObject for.
|
|
|
|
*/
|
|
|
|
case T_Var:
|
|
|
|
{
|
|
|
|
Var *var = (Var *) node;
|
|
|
|
Var *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, var, Var);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Const:
|
|
|
|
{
|
|
|
|
Const *oldnode = (Const *) node;
|
|
|
|
Const *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, oldnode, Const);
|
|
|
|
/* XXX we don't bother with datumCopy; should we? */
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Param:
|
|
|
|
case T_CaseTestExpr:
|
Code review for NextValueExpr expression node type.
Add missing infrastructure for this node type, notably in ruleutils.c where
its lack could demonstrably cause EXPLAIN to fail. Add outfuncs/readfuncs
support. (outfuncs support is useful today for debugging purposes. The
readfuncs support may never be needed, since at present it would only
matter for parallel query and NextValueExpr should never appear in a
parallelizable query; but it seems like a bad idea to have a primnode type
that isn't fully supported here.) Teach planner infrastructure that
NextValueExpr is a volatile, parallel-unsafe, non-leaky expression node
with cost cpu_operator_cost. Given its limited scope of usage, there
*might* be no live bug today from the lack of that knowledge, but it's
certainly going to bite us on the rear someday. Teach pg_stat_statements
about the new node type, too.
While at it, also teach cost_qual_eval() that MinMaxExpr, SQLValueFunction,
XmlExpr, and CoerceToDomain should be charged as cpu_operator_cost.
Failing to do this for SQLValueFunction was an oversight in my commit
0bb51aa96. The others are longer-standing oversights, but no time like the
present to fix them. (In principle, CoerceToDomain could have cost much
higher than this, but it doesn't presently seem worth trying to examine the
domain's constraints here.)
Modify execExprInterp.c to execute NextValueExpr as an out-of-line
function; it seems quite unlikely to me that it's worth insisting that
it be inlined in all expression eval methods. Besides, providing the
out-of-line function doesn't stop anyone from inlining if they want to.
Adjust some places where NextValueExpr support had been inserted with the
aid of a dartboard rather than keeping it in the same order as elsewhere.
Discussion: https://postgr.es/m/23862.1499981661@sss.pgh.pa.us
2017-07-14 15:25:43 -04:00
|
|
|
case T_SQLValueFunction:
|
|
|
|
case T_CoerceToDomainValue:
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_SetToDefault:
|
|
|
|
case T_CurrentOfExpr:
|
2017-04-06 08:33:16 -04:00
|
|
|
case T_NextValueExpr:
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_RangeTblRef:
|
2009-12-15 17:57:48 +00:00
|
|
|
case T_SortGroupClause:
|
2008-08-25 22:42:34 +00:00
|
|
|
return (Node *) copyObject(node);
|
2013-07-18 17:10:16 -04:00
|
|
|
case T_WithCheckOption:
|
|
|
|
{
|
2014-05-06 12:12:18 -04:00
|
|
|
WithCheckOption *wco = (WithCheckOption *) node;
|
|
|
|
WithCheckOption *newnode;
|
2013-07-18 17:10:16 -04:00
|
|
|
|
|
|
|
FLATCOPY(newnode, wco, WithCheckOption);
|
|
|
|
MUTATE(newnode->qual, wco->qual, Node *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_Aggref:
|
|
|
|
{
|
|
|
|
Aggref *aggref = (Aggref *) node;
|
|
|
|
Aggref *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, aggref, Aggref);
|
Fix handling of argument and result datatypes for partial aggregation.
When doing partial aggregation, the args list of the upper (combining)
Aggref node is replaced by a Var representing the output of the partial
aggregation steps, which has either the aggregate's transition data type
or a serialized representation of that. However, nodeAgg.c blindly
continued to use the args list as an indication of the user-level argument
types. This broke resolution of polymorphic transition datatypes at
executor startup (though it accidentally failed to fail for the ANYARRAY
case, which is likely the only one anyone had tested). Moreover, the
constructed FuncExpr passed to the finalfunc contained completely wrong
information, which would have led to bogus answers or crashes for any case
where the finalfunc examined that information (which is only likely to be
with polymorphic aggregates using a non-polymorphic transition type).
As an independent bug, apply_partialaggref_adjustment neglected to resolve
a polymorphic transition datatype before assigning it as the output type
of the lower-level Aggref node. This again accidentally failed to fail
for ANYARRAY but would be unlikely to work in other cases.
To fix the first problem, record the user-level argument types in a
separate OID-list field of Aggref, and look to that rather than the args
list when asking what the argument types were. (It turns out to be
convenient to include any "direct" arguments in this list too, although
those are not currently subject to being overwritten.)
Rather than adding yet another resolve_aggregate_transtype() call to fix
the second problem, add an aggtranstype field to Aggref, and store the
resolved transition type OID there when the planner first computes it.
(By doing this in the planner and not the parser, we can allow the
aggregate's transition type to change from time to time, although no DDL
support yet exists for that.) This saves nothing of consequence for
simple non-polymorphic aggregates, but for polymorphic transition types
we save a catalog lookup during executor startup as well as several
planner lookups that are new in 9.6 due to parallel query planning.
In passing, fix an error that was introduced into count_agg_clauses_walker
some time ago: it was applying exprTypmod() to something that wasn't an
expression node at all, but a TargetEntry. exprTypmod silently returned
-1 so that there was not an obvious failure, but this broke the intended
sensitivity of aggregate space consumption estimates to the typmod of
varchar and similar data types. This part needs to be back-patched.
Catversion bump due to change of stored Aggref nodes.
Discussion: <8229.1466109074@sss.pgh.pa.us>
2016-06-17 21:44:37 -04:00
|
|
|
/* assume mutation doesn't change types of arguments */
|
|
|
|
newnode->aggargtypes = list_copy(aggref->aggargtypes);
|
Support ordered-set (WITHIN GROUP) aggregates.
This patch introduces generic support for ordered-set and hypothetical-set
aggregate functions, as well as implementations of the instances defined in
SQL:2008 (percentile_cont(), percentile_disc(), rank(), dense_rank(),
percent_rank(), cume_dist()). We also added mode() though it is not in the
spec, as well as versions of percentile_cont() and percentile_disc() that
can compute multiple percentile values in one pass over the data.
Unlike the original submission, this patch puts full control of the sorting
process in the hands of the aggregate's support functions. To allow the
support functions to find out how they're supposed to sort, a new API
function AggGetAggref() is added to nodeAgg.c. This allows retrieval of
the aggregate call's Aggref node, which may have other uses beyond the
immediate need. There is also support for ordered-set aggregates to
install cleanup callback functions, so that they can be sure that
infrastructure such as tuplesort objects gets cleaned up.
In passing, make some fixes in the recently-added support for variadic
aggregates, and make some editorial adjustments in the recent FILTER
additions for aggregates. Also, simplify use of IsBinaryCoercible() by
allowing it to succeed whenever the target type is ANY or ANYELEMENT.
It was inconsistent that it dealt with other polymorphic target types
but not these.
Atri Sharma and Andrew Gierth; reviewed by Pavel Stehule and Vik Fearing,
and rather heavily editorialized upon by Tom Lane
2013-12-23 16:11:35 -05:00
|
|
|
MUTATE(newnode->aggdirectargs, aggref->aggdirectargs, List *);
|
2008-08-25 22:42:34 +00:00
|
|
|
MUTATE(newnode->args, aggref->args, List *);
|
2009-12-15 17:57:48 +00:00
|
|
|
MUTATE(newnode->aggorder, aggref->aggorder, List *);
|
|
|
|
MUTATE(newnode->aggdistinct, aggref->aggdistinct, List *);
|
2013-07-16 20:15:36 -04:00
|
|
|
MUTATE(newnode->aggfilter, aggref->aggfilter, Expr *);
|
2008-08-25 22:42:34 +00:00
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingFunc:
|
|
|
|
{
|
2015-05-23 21:35:49 -04:00
|
|
|
GroupingFunc *grouping = (GroupingFunc *) node;
|
|
|
|
GroupingFunc *newnode;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
|
|
|
|
FLATCOPY(newnode, grouping, GroupingFunc);
|
|
|
|
MUTATE(newnode->args, grouping->args, List *);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We assume here that mutating the arguments does not change
|
|
|
|
* the semantics, i.e. that the arguments are not mutated in a
|
|
|
|
* way that makes them semantically different from their
|
|
|
|
* previously matching expressions in the GROUP BY clause.
|
|
|
|
*
|
|
|
|
* If a mutator somehow wanted to do this, it would have to
|
|
|
|
* handle the refs and cols lists itself as appropriate.
|
|
|
|
*/
|
|
|
|
newnode->refs = list_copy(grouping->refs);
|
|
|
|
newnode->cols = list_copy(grouping->cols);
|
|
|
|
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowFunc:
|
|
|
|
{
|
|
|
|
WindowFunc *wfunc = (WindowFunc *) node;
|
|
|
|
WindowFunc *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, wfunc, WindowFunc);
|
|
|
|
MUTATE(newnode->args, wfunc->args, List *);
|
2013-07-16 20:15:36 -04:00
|
|
|
MUTATE(newnode->aggfilter, wfunc->aggfilter, Expr *);
|
2008-12-28 18:54:01 +00:00
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ArrayRef:
|
|
|
|
{
|
|
|
|
ArrayRef *arrayref = (ArrayRef *) node;
|
|
|
|
ArrayRef *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, arrayref, ArrayRef);
|
|
|
|
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
|
|
|
|
List *);
|
|
|
|
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
|
|
|
|
List *);
|
|
|
|
MUTATE(newnode->refexpr, arrayref->refexpr,
|
|
|
|
Expr *);
|
|
|
|
MUTATE(newnode->refassgnexpr, arrayref->refassgnexpr,
|
|
|
|
Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FuncExpr:
|
|
|
|
{
|
|
|
|
FuncExpr *expr = (FuncExpr *) node;
|
|
|
|
FuncExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, expr, FuncExpr);
|
|
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2009-10-08 02:39:25 +00:00
|
|
|
case T_NamedArgExpr:
|
|
|
|
{
|
|
|
|
NamedArgExpr *nexpr = (NamedArgExpr *) node;
|
|
|
|
NamedArgExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, nexpr, NamedArgExpr);
|
|
|
|
MUTATE(newnode->arg, nexpr->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_OpExpr:
|
|
|
|
{
|
|
|
|
OpExpr *expr = (OpExpr *) node;
|
|
|
|
OpExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, expr, OpExpr);
|
|
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_DistinctExpr:
|
|
|
|
{
|
|
|
|
DistinctExpr *expr = (DistinctExpr *) node;
|
|
|
|
DistinctExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, expr, DistinctExpr);
|
|
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2011-03-19 20:29:08 -04:00
|
|
|
case T_NullIfExpr:
|
|
|
|
{
|
|
|
|
NullIfExpr *expr = (NullIfExpr *) node;
|
|
|
|
NullIfExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, expr, NullIfExpr);
|
|
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_ScalarArrayOpExpr:
|
|
|
|
{
|
|
|
|
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
|
|
|
ScalarArrayOpExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, expr, ScalarArrayOpExpr);
|
|
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_BoolExpr:
|
|
|
|
{
|
|
|
|
BoolExpr *expr = (BoolExpr *) node;
|
|
|
|
BoolExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, expr, BoolExpr);
|
|
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubLink:
|
|
|
|
{
|
|
|
|
SubLink *sublink = (SubLink *) node;
|
|
|
|
SubLink *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, sublink, SubLink);
|
|
|
|
MUTATE(newnode->testexpr, sublink->testexpr, Node *);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also invoke the mutator on the sublink's Query node, so it
|
|
|
|
* can recurse into the sub-query if it wants to.
|
|
|
|
*/
|
|
|
|
MUTATE(newnode->subselect, sublink->subselect, Node *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubPlan:
|
|
|
|
{
|
|
|
|
SubPlan *subplan = (SubPlan *) node;
|
|
|
|
SubPlan *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, subplan, SubPlan);
|
|
|
|
/* transform testexpr */
|
|
|
|
MUTATE(newnode->testexpr, subplan->testexpr, Node *);
|
|
|
|
/* transform args list (params to be passed to subplan) */
|
|
|
|
MUTATE(newnode->args, subplan->args, List *);
|
|
|
|
/* but not the sub-Plan itself, which is referenced as-is */
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_AlternativeSubPlan:
|
|
|
|
{
|
|
|
|
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
|
|
|
|
AlternativeSubPlan *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, asplan, AlternativeSubPlan);
|
|
|
|
MUTATE(newnode->subplans, asplan->subplans, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FieldSelect:
|
|
|
|
{
|
|
|
|
FieldSelect *fselect = (FieldSelect *) node;
|
|
|
|
FieldSelect *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, fselect, FieldSelect);
|
|
|
|
MUTATE(newnode->arg, fselect->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FieldStore:
|
|
|
|
{
|
|
|
|
FieldStore *fstore = (FieldStore *) node;
|
|
|
|
FieldStore *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, fstore, FieldStore);
|
|
|
|
MUTATE(newnode->arg, fstore->arg, Expr *);
|
|
|
|
MUTATE(newnode->newvals, fstore->newvals, List *);
|
|
|
|
newnode->fieldnums = list_copy(fstore->fieldnums);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RelabelType:
|
|
|
|
{
|
|
|
|
RelabelType *relabel = (RelabelType *) node;
|
|
|
|
RelabelType *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, relabel, RelabelType);
|
|
|
|
MUTATE(newnode->arg, relabel->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoerceViaIO:
|
|
|
|
{
|
|
|
|
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
|
|
|
|
CoerceViaIO *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, iocoerce, CoerceViaIO);
|
|
|
|
MUTATE(newnode->arg, iocoerce->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ArrayCoerceExpr:
|
|
|
|
{
|
|
|
|
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
|
|
|
|
ArrayCoerceExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, acoerce, ArrayCoerceExpr);
|
|
|
|
MUTATE(newnode->arg, acoerce->arg, Expr *);
|
Support arrays over domains.
Allowing arrays with a domain type as their element type was left un-done
in the original domain patch, but not for any very good reason. This
omission leads to such surprising results as array_agg() not working on
a domain column, because the parser can't identify a suitable output type
for the polymorphic aggregate.
In order to fix this, first clean up the APIs of coerce_to_domain() and
some internal functions in parse_coerce.c so that we consistently pass
around a CoercionContext along with CoercionForm. Previously, we sometimes
passed an "isExplicit" boolean flag instead, which is strictly less
information; and coerce_to_domain() didn't even get that, but instead had
to reverse-engineer isExplicit from CoercionForm. That's contrary to the
documentation in primnodes.h that says that CoercionForm only affects
display and not semantics. I don't think this change fixes any live bugs,
but it makes things more consistent. The main reason for doing it though
is that now build_coercion_expression() receives ccontext, which it needs
in order to be able to recursively invoke coerce_to_target_type().
Next, reimplement ArrayCoerceExpr so that the node does not directly know
any details of what has to be done to the individual array elements while
performing the array coercion. Instead, the per-element processing is
represented by a sub-expression whose input is a source array element and
whose output is a target array element. This simplifies life in
parse_coerce.c, because it can build that sub-expression by a recursive
invocation of coerce_to_target_type(). The executor now handles the
per-element processing as a compiled expression instead of hard-wired code.
The main advantage of this is that we can use a single ArrayCoerceExpr to
handle as many as three successive steps per element: base type conversion,
typmod coercion, and domain constraint checking. The old code used two
stacked ArrayCoerceExprs to handle type + typmod coercion, which was pretty
inefficient, and adding yet another array deconstruction to do domain
constraint checking seemed very unappetizing.
In the case where we just need a single, very simple coercion function,
doing this straightforwardly leads to a noticeable increase in the
per-array-element runtime cost. Hence, add an additional shortcut evalfunc
in execExprInterp.c that skips unnecessary overhead for that specific form
of expression. The runtime speed of simple cases is within 1% or so of
where it was before, while cases that previously required two levels of
array processing are significantly faster.
Finally, create an implicit array type for every domain type, as we do for
base types, enums, etc. Everything except the array-coercion case seems
to just work without further effort.
Tom Lane, reviewed by Andrew Dunstan
Discussion: https://postgr.es/m/9852.1499791473@sss.pgh.pa.us
2017-09-30 13:40:56 -04:00
|
|
|
MUTATE(newnode->elemexpr, acoerce->elemexpr, Expr *);
|
2008-08-25 22:42:34 +00:00
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ConvertRowtypeExpr:
|
|
|
|
{
|
|
|
|
ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
|
|
|
|
ConvertRowtypeExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, convexpr, ConvertRowtypeExpr);
|
|
|
|
MUTATE(newnode->arg, convexpr->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2011-03-11 16:27:51 -05:00
|
|
|
case T_CollateExpr:
|
|
|
|
{
|
|
|
|
CollateExpr *collate = (CollateExpr *) node;
|
|
|
|
CollateExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, collate, CollateExpr);
|
|
|
|
MUTATE(newnode->arg, collate->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_CaseExpr:
|
|
|
|
{
|
|
|
|
CaseExpr *caseexpr = (CaseExpr *) node;
|
|
|
|
CaseExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, caseexpr, CaseExpr);
|
|
|
|
MUTATE(newnode->arg, caseexpr->arg, Expr *);
|
|
|
|
MUTATE(newnode->args, caseexpr->args, List *);
|
|
|
|
MUTATE(newnode->defresult, caseexpr->defresult, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CaseWhen:
|
|
|
|
{
|
|
|
|
CaseWhen *casewhen = (CaseWhen *) node;
|
|
|
|
CaseWhen *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, casewhen, CaseWhen);
|
|
|
|
MUTATE(newnode->expr, casewhen->expr, Expr *);
|
|
|
|
MUTATE(newnode->result, casewhen->result, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ArrayExpr:
|
|
|
|
{
|
|
|
|
ArrayExpr *arrayexpr = (ArrayExpr *) node;
|
|
|
|
ArrayExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, arrayexpr, ArrayExpr);
|
|
|
|
MUTATE(newnode->elements, arrayexpr->elements, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RowExpr:
|
|
|
|
{
|
|
|
|
RowExpr *rowexpr = (RowExpr *) node;
|
|
|
|
RowExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, rowexpr, RowExpr);
|
|
|
|
MUTATE(newnode->args, rowexpr->args, List *);
|
2008-10-06 17:39:26 +00:00
|
|
|
/* Assume colnames needn't be duplicated */
|
2008-08-25 22:42:34 +00:00
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RowCompareExpr:
|
|
|
|
{
|
|
|
|
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
|
|
|
RowCompareExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, rcexpr, RowCompareExpr);
|
|
|
|
MUTATE(newnode->largs, rcexpr->largs, List *);
|
|
|
|
MUTATE(newnode->rargs, rcexpr->rargs, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoalesceExpr:
|
|
|
|
{
|
|
|
|
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
|
|
|
CoalesceExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, coalesceexpr, CoalesceExpr);
|
|
|
|
MUTATE(newnode->args, coalesceexpr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_MinMaxExpr:
|
|
|
|
{
|
|
|
|
MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
|
|
|
|
MinMaxExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, minmaxexpr, MinMaxExpr);
|
|
|
|
MUTATE(newnode->args, minmaxexpr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_XmlExpr:
|
|
|
|
{
|
|
|
|
XmlExpr *xexpr = (XmlExpr *) node;
|
|
|
|
XmlExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, xexpr, XmlExpr);
|
|
|
|
MUTATE(newnode->named_args, xexpr->named_args, List *);
|
|
|
|
/* assume mutator does not care about arg_names */
|
|
|
|
MUTATE(newnode->args, xexpr->args, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_NullTest:
|
|
|
|
{
|
|
|
|
NullTest *ntest = (NullTest *) node;
|
|
|
|
NullTest *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, ntest, NullTest);
|
|
|
|
MUTATE(newnode->arg, ntest->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_BooleanTest:
|
|
|
|
{
|
|
|
|
BooleanTest *btest = (BooleanTest *) node;
|
|
|
|
BooleanTest *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, btest, BooleanTest);
|
|
|
|
MUTATE(newnode->arg, btest->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CoerceToDomain:
|
|
|
|
{
|
|
|
|
CoerceToDomain *ctest = (CoerceToDomain *) node;
|
|
|
|
CoerceToDomain *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, ctest, CoerceToDomain);
|
|
|
|
MUTATE(newnode->arg, ctest->arg, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_TargetEntry:
|
|
|
|
{
|
|
|
|
TargetEntry *targetentry = (TargetEntry *) node;
|
|
|
|
TargetEntry *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, targetentry, TargetEntry);
|
|
|
|
MUTATE(newnode->expr, targetentry->expr, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Query:
|
|
|
|
/* Do nothing with a sub-Query, per discussion above */
|
|
|
|
return node;
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowClause:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
WindowClause *wc = (WindowClause *) node;
|
|
|
|
WindowClause *newnode;
|
2008-12-28 18:54:01 +00:00
|
|
|
|
|
|
|
FLATCOPY(newnode, wc, WindowClause);
|
|
|
|
MUTATE(newnode->partitionClause, wc->partitionClause, List *);
|
|
|
|
MUTATE(newnode->orderClause, wc->orderClause, List *);
|
2010-02-12 17:33:21 +00:00
|
|
|
MUTATE(newnode->startOffset, wc->startOffset, Node *);
|
|
|
|
MUTATE(newnode->endOffset, wc->endOffset, Node *);
|
2008-12-28 18:54:01 +00:00
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_CommonTableExpr:
|
|
|
|
{
|
|
|
|
CommonTableExpr *cte = (CommonTableExpr *) node;
|
|
|
|
CommonTableExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, cte, CommonTableExpr);
|
|
|
|
|
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* Also invoke the mutator on the CTE's Query node, so it can
|
|
|
|
* recurse into the sub-query if it wants to.
|
2008-10-04 21:56:55 +00:00
|
|
|
*/
|
|
|
|
MUTATE(newnode->ctequery, cte->ctequery, Node *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_List:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We assume the mutator isn't interested in the list nodes
|
|
|
|
* per se, so just invoke it on each list element. NOTE: this
|
|
|
|
* would fail badly on a list with integer elements!
|
|
|
|
*/
|
|
|
|
List *resultlist;
|
|
|
|
ListCell *temp;
|
|
|
|
|
|
|
|
resultlist = NIL;
|
|
|
|
foreach(temp, (List *) node)
|
|
|
|
{
|
|
|
|
resultlist = lappend(resultlist,
|
|
|
|
mutator((Node *) lfirst(temp),
|
|
|
|
context));
|
|
|
|
}
|
|
|
|
return (Node *) resultlist;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_FromExpr:
|
|
|
|
{
|
|
|
|
FromExpr *from = (FromExpr *) node;
|
|
|
|
FromExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, from, FromExpr);
|
|
|
|
MUTATE(newnode->fromlist, from->fromlist, List *);
|
|
|
|
MUTATE(newnode->quals, from->quals, Node *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_OnConflictExpr:
|
|
|
|
{
|
2015-05-23 21:35:49 -04:00
|
|
|
OnConflictExpr *oc = (OnConflictExpr *) node;
|
|
|
|
OnConflictExpr *newnode;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
|
|
|
|
FLATCOPY(newnode, oc, OnConflictExpr);
|
|
|
|
MUTATE(newnode->arbiterElems, oc->arbiterElems, List *);
|
|
|
|
MUTATE(newnode->arbiterWhere, oc->arbiterWhere, Node *);
|
|
|
|
MUTATE(newnode->onConflictSet, oc->onConflictSet, List *);
|
|
|
|
MUTATE(newnode->onConflictWhere, oc->onConflictWhere, Node *);
|
2015-05-13 00:13:22 +02:00
|
|
|
MUTATE(newnode->exclRelTlist, oc->exclRelTlist, List *);
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
case T_MergeAction:
|
|
|
|
{
|
|
|
|
MergeAction *action = (MergeAction *) node;
|
|
|
|
MergeAction *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, action, MergeAction);
|
|
|
|
MUTATE(newnode->qual, action->qual, Node *);
|
|
|
|
MUTATE(newnode->targetList, action->targetList, List *);
|
|
|
|
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_JoinExpr:
|
|
|
|
{
|
|
|
|
JoinExpr *join = (JoinExpr *) node;
|
|
|
|
JoinExpr *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, join, JoinExpr);
|
|
|
|
MUTATE(newnode->larg, join->larg, Node *);
|
|
|
|
MUTATE(newnode->rarg, join->rarg, Node *);
|
|
|
|
MUTATE(newnode->quals, join->quals, Node *);
|
|
|
|
/* We do not mutate alias or using by default */
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SetOperationStmt:
|
|
|
|
{
|
|
|
|
SetOperationStmt *setop = (SetOperationStmt *) node;
|
|
|
|
SetOperationStmt *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, setop, SetOperationStmt);
|
|
|
|
MUTATE(newnode->larg, setop->larg, Node *);
|
|
|
|
MUTATE(newnode->rarg, setop->rarg, Node *);
|
|
|
|
/* We do not mutate groupClauses by default */
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-21 20:42:53 +00:00
|
|
|
case T_PlaceHolderVar:
|
|
|
|
{
|
|
|
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
|
|
|
PlaceHolderVar *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, phv, PlaceHolderVar);
|
|
|
|
MUTATE(newnode->phexpr, phv->phexpr, Expr *);
|
|
|
|
/* Assume we need not copy the relids bitmapset */
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_InferenceElem:
|
|
|
|
{
|
|
|
|
InferenceElem *inferenceelemdexpr = (InferenceElem *) node;
|
|
|
|
InferenceElem *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, inferenceelemdexpr, InferenceElem);
|
|
|
|
MUTATE(newnode->expr, newnode->expr, Node *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case T_AppendRelInfo:
|
|
|
|
{
|
|
|
|
AppendRelInfo *appinfo = (AppendRelInfo *) node;
|
|
|
|
AppendRelInfo *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, appinfo, AppendRelInfo);
|
|
|
|
MUTATE(newnode->translated_vars, appinfo->translated_vars, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-21 20:42:53 +00:00
|
|
|
case T_PlaceHolderInfo:
|
|
|
|
{
|
|
|
|
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
|
|
|
|
PlaceHolderInfo *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, phinfo, PlaceHolderInfo);
|
|
|
|
MUTATE(newnode->ph_var, phinfo->ph_var, PlaceHolderVar *);
|
|
|
|
/* Assume we need not copy the relids bitmapsets */
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
2013-11-21 19:37:02 -05:00
|
|
|
case T_RangeTblFunction:
|
|
|
|
{
|
|
|
|
RangeTblFunction *rtfunc = (RangeTblFunction *) node;
|
|
|
|
RangeTblFunction *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, rtfunc, RangeTblFunction);
|
|
|
|
MUTATE(newnode->funcexpr, rtfunc->funcexpr, Node *);
|
|
|
|
/* Assume we need not copy the coldef info lists */
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
2015-07-25 14:39:00 -04:00
|
|
|
case T_TableSampleClause:
|
|
|
|
{
|
|
|
|
TableSampleClause *tsc = (TableSampleClause *) node;
|
|
|
|
TableSampleClause *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, tsc, TableSampleClause);
|
|
|
|
MUTATE(newnode->args, tsc->args, List *);
|
|
|
|
MUTATE(newnode->repeatable, tsc->repeatable, Expr *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2017-03-08 12:39:37 -03:00
|
|
|
case T_TableFunc:
|
|
|
|
{
|
|
|
|
TableFunc *tf = (TableFunc *) node;
|
|
|
|
TableFunc *newnode;
|
|
|
|
|
|
|
|
FLATCOPY(newnode, tf, TableFunc);
|
|
|
|
MUTATE(newnode->ns_uris, tf->ns_uris, List *);
|
|
|
|
MUTATE(newnode->docexpr, tf->docexpr, Node *);
|
|
|
|
MUTATE(newnode->rowexpr, tf->rowexpr, Node *);
|
|
|
|
MUTATE(newnode->colexprs, tf->colexprs, List *);
|
|
|
|
MUTATE(newnode->coldefexprs, tf->coldefexprs, List *);
|
|
|
|
return (Node *) newnode;
|
|
|
|
}
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
|
|
(int) nodeTag(node));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* can't get here, but keep compiler happy */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* query_tree_mutator --- initiate modification of a Query's expressions
|
|
|
|
*
|
|
|
|
* This routine exists just to reduce the number of places that need to know
|
|
|
|
* where all the expression subtrees of a Query are. Note it can be used
|
|
|
|
* for starting a walk at top level of a Query regardless of whether the
|
2014-05-06 12:12:18 -04:00
|
|
|
* mutator intends to descend into subqueries. It is also useful for
|
2008-08-25 22:42:34 +00:00
|
|
|
* descending into subqueries within a mutator.
|
|
|
|
*
|
|
|
|
* Some callers want to suppress mutating of certain items in the Query,
|
|
|
|
* typically because they need to process them specially, or don't actually
|
|
|
|
* want to recurse into subqueries. This is supported by the flags argument,
|
|
|
|
* which is the bitwise OR of flag values to suppress mutating of
|
|
|
|
* indicated items. (More flag bits may be added as needed.)
|
|
|
|
*
|
|
|
|
* Normally the Query node itself is copied, but some callers want it to be
|
2014-05-06 12:12:18 -04:00
|
|
|
* modified in-place; they must pass QTW_DONT_COPY_QUERY in flags. All
|
2008-08-25 22:42:34 +00:00
|
|
|
* modified substructure is safely copied in any case.
|
|
|
|
*/
|
|
|
|
Query *
|
|
|
|
query_tree_mutator(Query *query,
|
|
|
|
Node *(*mutator) (),
|
|
|
|
void *context,
|
|
|
|
int flags)
|
|
|
|
{
|
|
|
|
Assert(query != NULL && IsA(query, Query));
|
|
|
|
|
|
|
|
if (!(flags & QTW_DONT_COPY_QUERY))
|
|
|
|
{
|
|
|
|
Query *newquery;
|
|
|
|
|
|
|
|
FLATCOPY(newquery, query, Query);
|
|
|
|
query = newquery;
|
|
|
|
}
|
|
|
|
|
|
|
|
MUTATE(query->targetList, query->targetList, List *);
|
2013-07-18 17:10:16 -04:00
|
|
|
MUTATE(query->withCheckOptions, query->withCheckOptions, List *);
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
MUTATE(query->onConflict, query->onConflict, OnConflictExpr *);
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
MUTATE(query->mergeSourceTargetList, query->mergeSourceTargetList, List *);
|
|
|
|
MUTATE(query->mergeActionList, query->mergeActionList, List *);
|
2008-08-25 22:42:34 +00:00
|
|
|
MUTATE(query->returningList, query->returningList, List *);
|
|
|
|
MUTATE(query->jointree, query->jointree, FromExpr *);
|
|
|
|
MUTATE(query->setOperations, query->setOperations, Node *);
|
|
|
|
MUTATE(query->havingQual, query->havingQual, Node *);
|
|
|
|
MUTATE(query->limitOffset, query->limitOffset, Node *);
|
|
|
|
MUTATE(query->limitCount, query->limitCount, Node *);
|
2008-10-04 21:56:55 +00:00
|
|
|
if (!(flags & QTW_IGNORE_CTE_SUBQUERIES))
|
|
|
|
MUTATE(query->cteList, query->cteList, List *);
|
2017-06-21 14:39:04 -04:00
|
|
|
else /* else copy CTE list as-is */
|
2008-10-04 21:56:55 +00:00
|
|
|
query->cteList = copyObject(query->cteList);
|
2008-08-25 22:42:34 +00:00
|
|
|
query->rtable = range_table_mutator(query->rtable,
|
|
|
|
mutator, context, flags);
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* range_table_mutator is just the part of query_tree_mutator that processes
|
|
|
|
* a query's rangetable. This is split out since it can be useful on
|
|
|
|
* its own.
|
|
|
|
*/
|
|
|
|
List *
|
|
|
|
range_table_mutator(List *rtable,
|
|
|
|
Node *(*mutator) (),
|
|
|
|
void *context,
|
|
|
|
int flags)
|
|
|
|
{
|
|
|
|
List *newrt = NIL;
|
|
|
|
ListCell *rt;
|
|
|
|
|
|
|
|
foreach(rt, rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
|
|
|
RangeTblEntry *newrte;
|
|
|
|
|
|
|
|
FLATCOPY(newrte, rte, RangeTblEntry);
|
|
|
|
switch (rte->rtekind)
|
|
|
|
{
|
|
|
|
case RTE_RELATION:
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
2015-07-25 14:39:00 -04:00
|
|
|
MUTATE(newrte->tablesample, rte->tablesample,
|
|
|
|
TableSampleClause *);
|
|
|
|
/* we don't bother to copy eref, aliases, etc; OK? */
|
2015-05-15 14:37:10 -04:00
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case RTE_CTE:
|
2017-03-31 23:17:18 -05:00
|
|
|
case RTE_NAMEDTUPLESTORE:
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
2015-07-25 14:39:00 -04:00
|
|
|
/* nothing to do */
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
|
|
|
case RTE_SUBQUERY:
|
|
|
|
if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
|
|
|
|
{
|
|
|
|
CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
|
|
|
|
MUTATE(newrte->subquery, newrte->subquery, Query *);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* else, copy RT subqueries as-is */
|
|
|
|
newrte->subquery = copyObject(rte->subquery);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RTE_JOIN:
|
|
|
|
if (!(flags & QTW_IGNORE_JOINALIASES))
|
|
|
|
MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* else, copy join aliases as-is */
|
|
|
|
newrte->joinaliasvars = copyObject(rte->joinaliasvars);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RTE_FUNCTION:
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
2013-11-21 19:37:02 -05:00
|
|
|
MUTATE(newrte->functions, rte->functions, List *);
|
2008-08-25 22:42:34 +00:00
|
|
|
break;
|
2017-03-08 12:39:37 -03:00
|
|
|
case RTE_TABLEFUNC:
|
|
|
|
MUTATE(newrte->tablefunc, rte->tablefunc, TableFunc *);
|
|
|
|
break;
|
2008-08-25 22:42:34 +00:00
|
|
|
case RTE_VALUES:
|
|
|
|
MUTATE(newrte->values_lists, rte->values_lists, List *);
|
|
|
|
break;
|
|
|
|
}
|
2014-04-12 21:04:58 -04:00
|
|
|
MUTATE(newrte->securityQuals, rte->securityQuals, List *);
|
2008-08-25 22:42:34 +00:00
|
|
|
newrt = lappend(newrt, newrte);
|
|
|
|
}
|
|
|
|
return newrt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* query_or_expression_tree_walker --- hybrid form
|
|
|
|
*
|
|
|
|
* This routine will invoke query_tree_walker if called on a Query node,
|
|
|
|
* else will invoke the walker directly. This is a useful way of starting
|
|
|
|
* the recursion when the walker's normal change of state is not appropriate
|
|
|
|
* for the outermost Query node.
|
|
|
|
*/
|
1996-07-09 06:22:35 +00:00
|
|
|
bool
|
2008-08-25 22:42:34 +00:00
|
|
|
query_or_expression_tree_walker(Node *node,
|
|
|
|
bool (*walker) (),
|
|
|
|
void *context,
|
|
|
|
int flags)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
2008-08-25 22:42:34 +00:00
|
|
|
if (node && IsA(node, Query))
|
|
|
|
return query_tree_walker((Query *) node,
|
|
|
|
walker,
|
|
|
|
context,
|
|
|
|
flags);
|
|
|
|
else
|
|
|
|
return walker(node, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* query_or_expression_tree_mutator --- hybrid form
|
|
|
|
*
|
|
|
|
* This routine will invoke query_tree_mutator if called on a Query node,
|
|
|
|
* else will invoke the mutator directly. This is a useful way of starting
|
|
|
|
* the recursion when the mutator's normal change of state is not appropriate
|
|
|
|
* for the outermost Query node.
|
|
|
|
*/
|
|
|
|
Node *
|
|
|
|
query_or_expression_tree_mutator(Node *node,
|
|
|
|
Node *(*mutator) (),
|
|
|
|
void *context,
|
|
|
|
int flags)
|
|
|
|
{
|
|
|
|
if (node && IsA(node, Query))
|
|
|
|
return (Node *) query_tree_mutator((Query *) node,
|
|
|
|
mutator,
|
|
|
|
context,
|
|
|
|
flags);
|
|
|
|
else
|
|
|
|
return mutator(node, context);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* raw_expression_tree_walker --- walk raw parse trees
|
|
|
|
*
|
|
|
|
* This has exactly the same API as expression_tree_walker, but instead of
|
|
|
|
* walking post-analysis parse trees, it knows how to walk the node types
|
|
|
|
* found in raw grammar output. (There is not currently any need for a
|
|
|
|
* combined walker, so we keep them separate in the name of efficiency.)
|
|
|
|
* Unlike expression_tree_walker, there is no special rule about query
|
|
|
|
* boundaries: we descend to everything that's possibly interesting.
|
|
|
|
*
|
2016-05-23 19:08:26 -04:00
|
|
|
* Currently, the node type coverage here extends only to DML statements
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
* (SELECT/INSERT/UPDATE/DELETE/MERGE) and nodes that can appear in them,
|
|
|
|
* because this is used mainly during analysis of CTEs, and only DML
|
|
|
|
* statements can appear in CTEs.
|
2008-10-04 21:56:55 +00:00
|
|
|
*/
|
|
|
|
bool
|
2012-03-23 17:29:57 -04:00
|
|
|
raw_expression_tree_walker(Node *node,
|
|
|
|
bool (*walker) (),
|
|
|
|
void *context)
|
2008-10-04 21:56:55 +00:00
|
|
|
{
|
|
|
|
ListCell *temp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The walker has already visited the current node, and so we need only
|
|
|
|
* recurse into any sub-nodes it has.
|
|
|
|
*/
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
|
|
check_stack_depth();
|
|
|
|
|
|
|
|
switch (nodeTag(node))
|
|
|
|
{
|
|
|
|
case T_SetToDefault:
|
|
|
|
case T_CurrentOfExpr:
|
2016-08-16 20:33:01 -04:00
|
|
|
case T_SQLValueFunction:
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_Integer:
|
|
|
|
case T_Float:
|
|
|
|
case T_String:
|
|
|
|
case T_BitString:
|
|
|
|
case T_Null:
|
|
|
|
case T_ParamRef:
|
|
|
|
case T_A_Const:
|
|
|
|
case T_A_Star:
|
|
|
|
/* primitive node types with no subnodes */
|
|
|
|
break;
|
|
|
|
case T_Alias:
|
|
|
|
/* we assume the colnames list isn't interesting */
|
|
|
|
break;
|
|
|
|
case T_RangeVar:
|
|
|
|
return walker(((RangeVar *) node)->alias, context);
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingFunc:
|
|
|
|
return walker(((GroupingFunc *) node)->args, context);
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_SubLink:
|
|
|
|
{
|
|
|
|
SubLink *sublink = (SubLink *) node;
|
|
|
|
|
|
|
|
if (walker(sublink->testexpr, context))
|
|
|
|
return true;
|
|
|
|
/* we assume the operName is not interesting */
|
|
|
|
if (walker(sublink->subselect, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CaseExpr:
|
|
|
|
{
|
|
|
|
CaseExpr *caseexpr = (CaseExpr *) node;
|
|
|
|
|
|
|
|
if (walker(caseexpr->arg, context))
|
|
|
|
return true;
|
|
|
|
/* we assume walker doesn't care about CaseWhens, either */
|
|
|
|
foreach(temp, caseexpr->args)
|
|
|
|
{
|
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 13:51:29 -04:00
|
|
|
CaseWhen *when = lfirst_node(CaseWhen, temp);
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
if (walker(when->expr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(when->result, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (walker(caseexpr->defresult, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RowExpr:
|
2008-10-06 17:39:26 +00:00
|
|
|
/* Assume colnames isn't interesting */
|
2008-10-04 21:56:55 +00:00
|
|
|
return walker(((RowExpr *) node)->args, context);
|
|
|
|
case T_CoalesceExpr:
|
|
|
|
return walker(((CoalesceExpr *) node)->args, context);
|
|
|
|
case T_MinMaxExpr:
|
|
|
|
return walker(((MinMaxExpr *) node)->args, context);
|
|
|
|
case T_XmlExpr:
|
|
|
|
{
|
|
|
|
XmlExpr *xexpr = (XmlExpr *) node;
|
|
|
|
|
|
|
|
if (walker(xexpr->named_args, context))
|
|
|
|
return true;
|
|
|
|
/* we assume walker doesn't care about arg_names */
|
|
|
|
if (walker(xexpr->args, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_NullTest:
|
|
|
|
return walker(((NullTest *) node)->arg, context);
|
|
|
|
case T_BooleanTest:
|
|
|
|
return walker(((BooleanTest *) node)->arg, context);
|
|
|
|
case T_JoinExpr:
|
|
|
|
{
|
|
|
|
JoinExpr *join = (JoinExpr *) node;
|
|
|
|
|
|
|
|
if (walker(join->larg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(join->rarg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(join->quals, context))
|
|
|
|
return true;
|
|
|
|
if (walker(join->alias, context))
|
|
|
|
return true;
|
|
|
|
/* using list is deemed uninteresting */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_IntoClause:
|
|
|
|
{
|
|
|
|
IntoClause *into = (IntoClause *) node;
|
|
|
|
|
|
|
|
if (walker(into->rel, context))
|
|
|
|
return true;
|
|
|
|
/* colNames, options are deemed uninteresting */
|
Clean up the mess around EXPLAIN and materialized views.
Revert the matview-related changes in explain.c's API, as per recent
complaint from Robert Haas. The reason for these appears to have been
principally some ill-considered choices around having intorel_startup do
what ought to be parse-time checking, plus a poor arrangement for passing
it the view parsetree it needs to store into pg_rewrite when creating a
materialized view. Do the latter by having parse analysis stick a copy
into the IntoClause, instead of doing it at runtime. (On the whole,
I seriously question the choice to represent CREATE MATERIALIZED VIEW as a
variant of SELECT INTO/CREATE TABLE AS, because that means injecting even
more complexity into what was already a horrid legacy kluge. However,
I didn't go so far as to rethink that choice ... yet.)
I also moved several error checks into matview parse analysis, and
made the check for external Params in a matview more accurate.
In passing, clean things up a bit more around interpretOidsOption(),
and fix things so that we can use that to force no-oids for views,
sequences, etc, thereby eliminating the need to cons up "oids = false"
options when creating them.
catversion bump due to change in IntoClause. (I wonder though if we
really need readfuncs/outfuncs support for IntoClause anymore.)
2013-04-12 19:25:20 -04:00
|
|
|
/* viewQuery should be null in raw parsetree, but check it */
|
|
|
|
if (walker(into->viewQuery, context))
|
|
|
|
return true;
|
2008-10-04 21:56:55 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_List:
|
|
|
|
foreach(temp, (List *) node)
|
|
|
|
{
|
|
|
|
if (walker((Node *) lfirst(temp), context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2011-02-25 18:56:23 -05:00
|
|
|
case T_InsertStmt:
|
|
|
|
{
|
|
|
|
InsertStmt *stmt = (InsertStmt *) node;
|
|
|
|
|
|
|
|
if (walker(stmt->relation, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->cols, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->selectStmt, context))
|
|
|
|
return true;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
if (walker(stmt->onConflictClause, context))
|
|
|
|
return true;
|
2011-02-25 18:56:23 -05:00
|
|
|
if (walker(stmt->returningList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->withClause, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_DeleteStmt:
|
|
|
|
{
|
|
|
|
DeleteStmt *stmt = (DeleteStmt *) node;
|
|
|
|
|
|
|
|
if (walker(stmt->relation, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->usingClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->whereClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->returningList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->withClause, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_UpdateStmt:
|
|
|
|
{
|
|
|
|
UpdateStmt *stmt = (UpdateStmt *) node;
|
|
|
|
|
|
|
|
if (walker(stmt->relation, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->targetList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->whereClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->fromClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->returningList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->withClause, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
case T_MergeStmt:
|
|
|
|
{
|
|
|
|
MergeStmt *stmt = (MergeStmt *) node;
|
|
|
|
|
|
|
|
if (walker(stmt->relation, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->source_relation, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->join_condition, context))
|
|
|
|
return true;
|
2018-04-06 09:38:59 +01:00
|
|
|
if (walker(stmt->mergeWhenClauses, context))
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
return true;
|
2018-04-03 12:13:59 +01:00
|
|
|
if (walker(stmt->withClause, context))
|
|
|
|
return true;
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
}
|
|
|
|
break;
|
2018-04-06 09:38:59 +01:00
|
|
|
case T_MergeWhenClause:
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
{
|
2018-04-06 09:38:59 +01:00
|
|
|
MergeWhenClause *mergeWhenClause = (MergeWhenClause *) node;
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
|
2018-04-06 09:38:59 +01:00
|
|
|
if (walker(mergeWhenClause->condition, context))
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
return true;
|
2018-04-06 09:38:59 +01:00
|
|
|
if (walker(mergeWhenClause->targetList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(mergeWhenClause->cols, context))
|
|
|
|
return true;
|
|
|
|
if (walker(mergeWhenClause->values, context))
|
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table
using a source table or query. MERGE provides a single SQL
statement that can conditionally INSERT/UPDATE/DELETE rows
a task that would other require multiple PL statements.
e.g.
MERGE INTO target AS t
USING source AS s
ON t.tid = s.sid
WHEN MATCHED AND t.balance > s.delta THEN
UPDATE SET balance = t.balance - s.delta
WHEN MATCHED THEN
DELETE
WHEN NOT MATCHED AND s.delta > 0 THEN
INSERT VALUES (s.sid, s.delta)
WHEN NOT MATCHED THEN
DO NOTHING;
MERGE works with regular and partitioned tables, including
column and row security enforcement, as well as support for
row, statement and transition triggers.
MERGE is optimized for OLTP and is parameterizable, though
also useful for large scale ETL/ELT. MERGE is not intended
to be used in preference to existing single SQL commands
for INSERT, UPDATE or DELETE since there is some overhead.
MERGE can be used statically from PL/pgSQL.
MERGE does not yet support inheritance, write rules,
RETURNING clauses, updatable views or foreign tables.
MERGE follows SQL Standard per the most recent SQL:2016.
Includes full tests and documentation, including full
isolation tests to demonstrate the concurrent behavior.
This version written from scratch in 2017 by Simon Riggs,
using docs and tests originally written in 2009. Later work
from Pavan Deolasee has been both complex and deep, leaving
the lead author credit now in his hands.
Extensive discussion of concurrency from Peter Geoghegan,
with thanks for the time and effort contributed.
Various issues reported via sqlsmith by Andreas Seltenreich
Authors: Pavan Deolasee, Simon Riggs
Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs
Discussion:
https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com
https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
2018-04-03 09:28:16 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_SelectStmt:
|
|
|
|
{
|
|
|
|
SelectStmt *stmt = (SelectStmt *) node;
|
|
|
|
|
|
|
|
if (walker(stmt->distinctClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->intoClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->targetList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->fromClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->whereClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->groupClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->havingClause, context))
|
|
|
|
return true;
|
2008-12-28 18:54:01 +00:00
|
|
|
if (walker(stmt->windowClause, context))
|
|
|
|
return true;
|
2008-10-04 21:56:55 +00:00
|
|
|
if (walker(stmt->valuesLists, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->sortClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->limitOffset, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->limitCount, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->lockingClause, context))
|
|
|
|
return true;
|
2012-07-31 17:56:21 -04:00
|
|
|
if (walker(stmt->withClause, context))
|
|
|
|
return true;
|
2008-10-04 21:56:55 +00:00
|
|
|
if (walker(stmt->larg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->rarg, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_A_Expr:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
A_Expr *expr = (A_Expr *) node;
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
if (walker(expr->lexpr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(expr->rexpr, context))
|
|
|
|
return true;
|
|
|
|
/* operator name is deemed uninteresting */
|
|
|
|
}
|
|
|
|
break;
|
2014-06-16 15:55:05 -04:00
|
|
|
case T_BoolExpr:
|
|
|
|
{
|
|
|
|
BoolExpr *expr = (BoolExpr *) node;
|
|
|
|
|
|
|
|
if (walker(expr->args, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_ColumnRef:
|
|
|
|
/* we assume the fields contain nothing interesting */
|
|
|
|
break;
|
|
|
|
case T_FuncCall:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
FuncCall *fcall = (FuncCall *) node;
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
if (walker(fcall->args, context))
|
|
|
|
return true;
|
2009-12-15 17:57:48 +00:00
|
|
|
if (walker(fcall->agg_order, context))
|
|
|
|
return true;
|
2013-07-16 20:15:36 -04:00
|
|
|
if (walker(fcall->agg_filter, context))
|
|
|
|
return true;
|
2008-12-28 18:54:01 +00:00
|
|
|
if (walker(fcall->over, context))
|
|
|
|
return true;
|
2008-10-04 21:56:55 +00:00
|
|
|
/* function name is deemed uninteresting */
|
|
|
|
}
|
|
|
|
break;
|
2009-10-08 02:39:25 +00:00
|
|
|
case T_NamedArgExpr:
|
|
|
|
return walker(((NamedArgExpr *) node)->arg, context);
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_A_Indices:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
A_Indices *indices = (A_Indices *) node;
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
if (walker(indices->lidx, context))
|
|
|
|
return true;
|
|
|
|
if (walker(indices->uidx, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_A_Indirection:
|
|
|
|
{
|
|
|
|
A_Indirection *indir = (A_Indirection *) node;
|
|
|
|
|
|
|
|
if (walker(indir->arg, context))
|
|
|
|
return true;
|
|
|
|
if (walker(indir->indirection, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_A_ArrayExpr:
|
|
|
|
return walker(((A_ArrayExpr *) node)->elements, context);
|
|
|
|
case T_ResTarget:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
ResTarget *rt = (ResTarget *) node;
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
if (walker(rt->indirection, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rt->val, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
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 13:22:25 -04:00
|
|
|
case T_MultiAssignRef:
|
|
|
|
return walker(((MultiAssignRef *) node)->source, context);
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_TypeCast:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
TypeCast *tc = (TypeCast *) node;
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
if (walker(tc->arg, context))
|
|
|
|
return true;
|
2009-07-16 06:33:46 +00:00
|
|
|
if (walker(tc->typeName, context))
|
2008-10-04 21:56:55 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2011-02-08 23:04:18 +02:00
|
|
|
case T_CollateClause:
|
|
|
|
return walker(((CollateClause *) node)->arg, context);
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_SortBy:
|
|
|
|
return walker(((SortBy *) node)->node, context);
|
2008-12-28 18:54:01 +00:00
|
|
|
case T_WindowDef:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
WindowDef *wd = (WindowDef *) node;
|
2008-12-28 18:54:01 +00:00
|
|
|
|
|
|
|
if (walker(wd->partitionClause, context))
|
|
|
|
return true;
|
|
|
|
if (walker(wd->orderClause, context))
|
|
|
|
return true;
|
2010-02-12 17:33:21 +00:00
|
|
|
if (walker(wd->startOffset, context))
|
|
|
|
return true;
|
|
|
|
if (walker(wd->endOffset, context))
|
|
|
|
return true;
|
2008-12-28 18:54:01 +00:00
|
|
|
}
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_RangeSubselect:
|
|
|
|
{
|
|
|
|
RangeSubselect *rs = (RangeSubselect *) node;
|
|
|
|
|
|
|
|
if (walker(rs->subquery, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rs->alias, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RangeFunction:
|
|
|
|
{
|
|
|
|
RangeFunction *rf = (RangeFunction *) node;
|
|
|
|
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
2013-11-21 19:37:02 -05:00
|
|
|
if (walker(rf->functions, context))
|
2008-10-04 21:56:55 +00:00
|
|
|
return true;
|
|
|
|
if (walker(rf->alias, context))
|
|
|
|
return true;
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
2013-11-21 19:37:02 -05:00
|
|
|
if (walker(rf->coldeflist, context))
|
|
|
|
return true;
|
2008-10-04 21:56:55 +00:00
|
|
|
}
|
|
|
|
break;
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
2015-07-25 14:39:00 -04:00
|
|
|
case T_RangeTableSample:
|
|
|
|
{
|
|
|
|
RangeTableSample *rts = (RangeTableSample *) node;
|
|
|
|
|
|
|
|
if (walker(rts->relation, context))
|
|
|
|
return true;
|
|
|
|
/* method name is deemed uninteresting */
|
|
|
|
if (walker(rts->args, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rts->repeatable, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2017-03-08 12:39:37 -03:00
|
|
|
case T_RangeTableFunc:
|
|
|
|
{
|
|
|
|
RangeTableFunc *rtf = (RangeTableFunc *) node;
|
|
|
|
|
|
|
|
if (walker(rtf->docexpr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rtf->rowexpr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rtf->namespaces, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rtf->columns, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rtf->alias, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RangeTableFuncCol:
|
|
|
|
{
|
|
|
|
RangeTableFuncCol *rtfc = (RangeTableFuncCol *) node;
|
|
|
|
|
|
|
|
if (walker(rtfc->colexpr, context))
|
|
|
|
return true;
|
|
|
|
if (walker(rtfc->coldefexpr, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_TypeName:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
TypeName *tn = (TypeName *) node;
|
2008-10-04 21:56:55 +00:00
|
|
|
|
|
|
|
if (walker(tn->typmods, context))
|
|
|
|
return true;
|
|
|
|
if (walker(tn->arrayBounds, context))
|
|
|
|
return true;
|
|
|
|
/* type name itself is deemed uninteresting */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ColumnDef:
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
ColumnDef *coldef = (ColumnDef *) node;
|
2008-10-04 21:56:55 +00:00
|
|
|
|
2009-07-16 06:33:46 +00:00
|
|
|
if (walker(coldef->typeName, context))
|
2008-10-04 21:56:55 +00:00
|
|
|
return true;
|
|
|
|
if (walker(coldef->raw_default, context))
|
|
|
|
return true;
|
2011-03-26 16:35:25 -04:00
|
|
|
if (walker(coldef->collClause, context))
|
|
|
|
return true;
|
2008-10-04 21:56:55 +00:00
|
|
|
/* for now, constraints are ignored */
|
|
|
|
}
|
|
|
|
break;
|
2016-05-23 19:23:36 -04:00
|
|
|
case T_IndexElem:
|
|
|
|
{
|
|
|
|
IndexElem *indelem = (IndexElem *) node;
|
|
|
|
|
|
|
|
if (walker(indelem->expr, context))
|
|
|
|
return true;
|
|
|
|
/* collation and opclass names are deemed uninteresting */
|
|
|
|
}
|
|
|
|
break;
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
2015-05-16 03:40:59 +02:00
|
|
|
case T_GroupingSet:
|
|
|
|
return walker(((GroupingSet *) node)->content, context);
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_LockingClause:
|
|
|
|
return walker(((LockingClause *) node)->lockedRels, context);
|
|
|
|
case T_XmlSerialize:
|
|
|
|
{
|
|
|
|
XmlSerialize *xs = (XmlSerialize *) node;
|
|
|
|
|
|
|
|
if (walker(xs->expr, context))
|
|
|
|
return true;
|
2009-07-16 06:33:46 +00:00
|
|
|
if (walker(xs->typeName, context))
|
2008-10-04 21:56:55 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_WithClause:
|
|
|
|
return walker(((WithClause *) node)->ctes, context);
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
2015-05-08 05:31:36 +02:00
|
|
|
case T_InferClause:
|
|
|
|
{
|
|
|
|
InferClause *stmt = (InferClause *) node;
|
|
|
|
|
|
|
|
if (walker(stmt->indexElems, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->whereClause, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_OnConflictClause:
|
|
|
|
{
|
|
|
|
OnConflictClause *stmt = (OnConflictClause *) node;
|
|
|
|
|
|
|
|
if (walker(stmt->infer, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->targetList, context))
|
|
|
|
return true;
|
|
|
|
if (walker(stmt->whereClause, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2008-10-04 21:56:55 +00:00
|
|
|
case T_CommonTableExpr:
|
|
|
|
return walker(((CommonTableExpr *) node)->ctequery, context);
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
|
|
(int) nodeTag(node));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-17 11:24:49 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* planstate_tree_walker --- walk plan state trees
|
|
|
|
*
|
|
|
|
* The walker has already visited the current node, and so we need only
|
|
|
|
* recurse into any sub-nodes it has.
|
|
|
|
*/
|
|
|
|
bool
|
2016-04-27 11:29:45 -04:00
|
|
|
planstate_tree_walker(PlanState *planstate,
|
|
|
|
bool (*walker) (),
|
|
|
|
void *context)
|
2015-09-17 11:24:49 -04:00
|
|
|
{
|
|
|
|
Plan *plan = planstate->plan;
|
2015-09-22 21:42:00 -04:00
|
|
|
ListCell *lc;
|
2015-09-17 11:24:49 -04:00
|
|
|
|
|
|
|
/* initPlan-s */
|
|
|
|
if (planstate_walk_subplans(planstate->initPlan, walker, context))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* lefttree */
|
|
|
|
if (outerPlanState(planstate))
|
|
|
|
{
|
|
|
|
if (walker(outerPlanState(planstate), context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* righttree */
|
|
|
|
if (innerPlanState(planstate))
|
|
|
|
{
|
|
|
|
if (walker(innerPlanState(planstate), context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* special child plans */
|
|
|
|
switch (nodeTag(plan))
|
|
|
|
{
|
|
|
|
case T_ModifyTable:
|
|
|
|
if (planstate_walk_members(((ModifyTable *) plan)->plans,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00
|
|
|
((ModifyTableState *) planstate)->mt_plans,
|
2015-09-17 11:24:49 -04:00
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case T_Append:
|
|
|
|
if (planstate_walk_members(((Append *) plan)->appendplans,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00
|
|
|
((AppendState *) planstate)->appendplans,
|
2015-09-17 11:24:49 -04:00
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case T_MergeAppend:
|
|
|
|
if (planstate_walk_members(((MergeAppend *) plan)->mergeplans,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00
|
|
|
((MergeAppendState *) planstate)->mergeplans,
|
2015-09-17 11:24:49 -04:00
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case T_BitmapAnd:
|
|
|
|
if (planstate_walk_members(((BitmapAnd *) plan)->bitmapplans,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00
|
|
|
((BitmapAndState *) planstate)->bitmapplans,
|
2015-09-17 11:24:49 -04:00
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case T_BitmapOr:
|
|
|
|
if (planstate_walk_members(((BitmapOr *) plan)->bitmapplans,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 15:35:54 -04:00
|
|
|
((BitmapOrState *) planstate)->bitmapplans,
|
2015-09-17 11:24:49 -04:00
|
|
|
walker, context))
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case T_SubqueryScan:
|
|
|
|
if (walker(((SubqueryScanState *) planstate)->subplan, context))
|
|
|
|
return true;
|
|
|
|
break;
|
2015-09-22 21:42:00 -04:00
|
|
|
case T_CustomScan:
|
2016-06-09 18:02:36 -04:00
|
|
|
foreach(lc, ((CustomScanState *) planstate)->custom_ps)
|
2015-09-22 21:42:00 -04:00
|
|
|
{
|
|
|
|
if (walker((PlanState *) lfirst(lc), context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
2015-09-17 11:24:49 -04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* subPlan-s */
|
|
|
|
if (planstate_walk_subplans(planstate->subPlan, walker, context))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk a list of SubPlans (or initPlans, which also use SubPlan nodes).
|
|
|
|
*/
|
|
|
|
static bool
|
2016-04-27 11:29:45 -04:00
|
|
|
planstate_walk_subplans(List *plans,
|
|
|
|
bool (*walker) (),
|
|
|
|
void *context)
|
2015-09-17 11:24:49 -04:00
|
|
|
{
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
foreach(lc, plans)
|
|
|
|
{
|
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 13:51:29 -04:00
|
|
|
SubPlanState *sps = lfirst_node(SubPlanState, lc);
|
2015-09-17 11:24:49 -04:00
|
|
|
|
|
|
|
if (walker(sps->planstate, context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk the constituent plans of a ModifyTable, Append, MergeAppend,
|
|
|
|
* BitmapAnd, or BitmapOr node.
|
|
|
|
*
|
|
|
|
* Note: we don't actually need to examine the Plan list members, but
|
|
|
|
* we need the list in order to determine the length of the PlanState array.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
planstate_walk_members(List *plans, PlanState **planstates,
|
|
|
|
bool (*walker) (), void *context)
|
|
|
|
{
|
|
|
|
int nplans = list_length(plans);
|
|
|
|
int j;
|
|
|
|
|
|
|
|
for (j = 0; j < nplans; j++)
|
|
|
|
{
|
|
|
|
if (walker(planstates[j], context))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|