blob: 5438524513c521ba2b973ce2d8245aa840b733c5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*-------------------------------------------------------------------------
*
* transform.c
* This file contains methods to transform the query tree
*
* Author: Siva Narayanan
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/parsenodes.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/transform.h"
#include "optimizer/var.h"
#include "utils/lsyscache.h"
#include "catalog/pg_proc.h"
#include "catalog/namespace.h"
#include "parser/parse_oper.h"
#include "parser/parse_coerce.h"
#include "lib/stringinfo.h"
#include "catalog/pg_operator.h"
/**
* Static declarations
*/
static Node* normalize_query_jointree(Node *node);
static Node *normalize_query_jointree_mutator(Node *node, void *context);
static Node *replace_sirv_functions_mutator(Node *node, void *context);
static void replace_sirvf_tle(Query *query, int tleOffset);
static void replace_sirvf_rte(Query *query, int rteIndex);
static Node *replace_sirvf_tle_expr_mutator(Node *node, void *context);
static SubLink *make_sirvf_subselect(FuncExpr *fe);
static Query *make_sirvf_subquery(FuncExpr *fe);
static bool safe_to_replace_sirvf_tle(Query *query);
static bool safe_to_replace_sirvf_rte(Query *query);
static void wrap_vars_with_fieldselect(List *targetlist, int varno, Oid newvartype);
/**
* Preprocess query structure for consumption by the optimizer
*/
Query *preprocess_query_optimizer(Query *query, ParamListInfo boundParams)
{
#ifdef USE_ASSERT_CHECKING
Query *qcopy = (Query *) copyObject(query);
#endif
/* fold all constant expressions */
Query *res = fold_constants(query, boundParams, GPOPT_MAX_FOLDED_CONSTANT_SIZE);
#ifdef USE_ASSERT_CHECKING
Assert(equal(qcopy, query) && "Preprocessing should not modify original query object");
#endif
return res;
}
/**
* Normalize query before planning.
*/
Query *normalize_query(Query *query)
{
#ifdef USE_ASSERT_CHECKING
Query *qcopy = (Query *) copyObject(query);
#endif
/**
* Normalize the jointree
*/
Query *res = (Query *) normalize_query_jointree_mutator((Node *) query, NULL);
/**
* MPP-12635 Replace all instances of single row returning volatile (sirv) functions
*/
res = (Query *) replace_sirv_functions_mutator((Node *) res, NULL);
#ifdef USE_ASSERT_CHECKING
Assert(equal(qcopy, query) && "Normalization should not modify original query object");
#endif
return res;
}
/**
* This method takes a join tree that contains from expr and translates them to
* explicit join expressions.
* For example:
* select * from t1, t2, t3 where pred(t1.a,t2.b,t3.c);
* is translated to
* select * from t1 cross join t2 cross join t3 where pred(t1.a,t2.b,t3.c)
*
* Note that this does not use the generic expression mutator framework because
* the recursion is highly specialized.
*/
static Node* normalize_query_jointree(Node *node)
{
Node *result = NULL;
if (!node)
return NULL;
#ifdef USE_ASSERT_CHECKING
Node *exprCopy = copyObject(node);
#endif
switch(nodeTag(node))
{
case T_FromExpr:
{
FromExpr *oldFrom = (FromExpr *) node;
FromExpr *from = (FromExpr *) copyObject(oldFrom);
if (oldFrom->fromlist)
{
Node *newFromExprEntry = (Node *) normalize_query_jointree((Node *) oldFrom->fromlist);
from->fromlist = list_make1(newFromExprEntry);
}
Assert(equal(from->quals, oldFrom->quals));
result = (Node *) from;
break;
}
case T_List:
{
List *crossJoinList = (List *) copyObject(node);
if (list_length(crossJoinList) == 1)
{
Node *entry = lfirst(list_head(crossJoinList));
result = normalize_query_jointree(entry);
}
else
{
Node *larg = lfirst(list_head(crossJoinList));
Assert(larg);
larg = normalize_query_jointree(larg);
List *rest = list_delete_first(crossJoinList);
Node *rarg = normalize_query_jointree((Node *) rest);
JoinExpr *join = makeNode(JoinExpr);
join->jointype = JOIN_INNER;
join->isNatural = false;
join->larg = larg;
join->rarg = rarg;
join->quals = NULL; /* Cross product */
join->rtindex = 0;
join->subqfromlist = NIL;
join->usingClause = NIL;
result = (Node *) join;
}
break;
}
case T_JoinExpr:
{
JoinExpr *join = (JoinExpr *) copyObject(node);
join->larg = normalize_query_jointree(join->larg);
join->rarg = normalize_query_jointree(join->rarg);
result = (Node *) join;
break;
}
case T_RangeTblRef:
{
result = (Node *) node;
break;
}
default:
Assert(false && "Unrecognized entry in jointree");
break;
}
Assert(result);
/* Assert that the input is unmodified */
#ifdef USE_ASSERT_CHECKING
Assert(equal(node, exprCopy));
#endif
return result;
}
/**
* This method walks through a query tree, finds the join tree and normalizes them.
* It also walks sublinks and subqueries and normalizes their jointrees as well.
* E.g. a query of the form SELECT x, y FROM t1, t2, t3 where x = y and y = z is transformed to:
* SELECT x,y FROM (t1 INNER JOIN (t2 INNER JOIN t3)) where x = y
*
*/
static Node *normalize_query_jointree_mutator(Node *node, void *context)
{
Assert(context == NULL);
if (!node)
{
return NULL;
}
switch(nodeTag(node))
{
case T_Query:
{
Query *newQuery = (Query *) copyObject(node);
newQuery->jointree = (FromExpr*) normalize_query_jointree((Node *) newQuery->jointree);
newQuery = (Query *) query_tree_mutator(newQuery, normalize_query_jointree_mutator, context, 0);
return (Node *) newQuery;
}
default:
break;
}
return expression_tree_mutator(node, normalize_query_jointree_mutator, context);
}
/**
* Mutator that walks the query tree and replaces single row returning volatile (sirv) functions with a more complicated
* construct so that it may be evaluated in a initplan subsequently. Reason for this is that this sirv function may perform
* DDL/DML/dispatching and it can do all this only if it is evaluated on the master as an initplan. See MPP-12635 for details.
*/
static Node *replace_sirv_functions_mutator(Node *node, void *context)
{
Assert(context == NULL);
if (!node)
{
return NULL;
}
/**
* Do not recurse into sublinks
*/
if (IsA(node, SubLink))
{
return node;
}
if (IsA(node, Query))
{
/**
* Find sirv functions in the targetlist and replace them
*/
Query *query = (Query *) copyObject(node);
if (safe_to_replace_sirvf_tle(query))
{
for (int tleOffset = 0; tleOffset < list_length(query->targetList); tleOffset++)
{
replace_sirvf_tle(query, tleOffset + 1);
}
}
/**
* Find sirv functions in the range table entries and replace them
*/
if (safe_to_replace_sirvf_rte(query))
{
for (int rteOffset = 0; rteOffset < list_length(query->rtable); rteOffset++)
{
replace_sirvf_rte(query, rteOffset + 1);
}
}
return (Node *) query_tree_mutator(query, replace_sirv_functions_mutator, context, 0);
}
return expression_tree_mutator(node, replace_sirv_functions_mutator, context);
}
/**
* Replace all sirv function references in a TLE (see replace_sirv_functions_mutator for an explanation)
*/
static void replace_sirvf_tle(Query *query, int tleOffset)
{
Assert(query);
Assert(safe_to_replace_sirvf_tle(query));
TargetEntry *tle = list_nth(query->targetList, tleOffset - 1);
tle->expr = (Expr *) replace_sirvf_tle_expr_mutator((Node *) tle->expr, NULL);
/**
* If the resulting targetlist entry has a sublink, the query's flag must be set
*/
if (contain_subplans((Node *) tle->expr))
{
query->hasSubLinks = true;
}
}
/**
* Is a function expression a sirvf?
*/
static bool is_sirv_funcexpr(FuncExpr *fe)
{
bool res = (!fe->funcretset /* Set returning functions cannot become initplans */
&& !fe->is_tablefunc /* Ignore table functions */
&& !contain_vars_of_level_or_above((Node *) fe->args, 0) /* Must be variable free */
&& !contain_subplans((Node *) fe->args) /* Must not contain sublinks */
&& func_volatile(fe->funcid) == PROVOLATILE_VOLATILE /* Must be a volatile function */
&& fe->funcresulttype != RECORDOID /* Record types cannot be handled currently */
);
/**
* Function cannot be sequence related
*/
Oid funcid = fe->funcid;
res = res && !(funcid == NEXTVAL_FUNC_OID || funcid == CURRVAL_FUNC_OID || funcid == SETVAL_FUNC_OID);
return res;
}
/**
* This mutator replaces all instances of a function that is a sirvf with a subselect.
* Note that this must only work on expressions that occur in the targetlist.
*/
static Node *replace_sirvf_tle_expr_mutator(Node *node, void *context)
{
Assert(context == NULL);
if (!node)
return NULL;
if (IsA(node, FuncExpr)
&& is_sirv_funcexpr((FuncExpr *) node))
{
node = (Node *) make_sirvf_subselect((FuncExpr *) node);
}
return expression_tree_mutator(node, replace_sirvf_tle_expr_mutator, context);
}
/**
* Given a sirv function, wrap it up in a subselect and return the sublink node
*/
static SubLink *make_sirvf_subselect(FuncExpr *fe)
{
Assert(is_sirv_funcexpr(fe));
Query *query = makeNode(Query);
query->commandType = CMD_SELECT;
query->querySource = QSRC_PLANNER;
query->jointree = makeNode(FromExpr);
TargetEntry *tle = (TargetEntry *) makeTargetEntry((Expr *) fe, 1, "sirvf", false);
query->targetList = list_make1(tle);
SubLink *sublink = makeNode(SubLink);
sublink->location = -1;
sublink->subLinkType = EXPR_SUBLINK;
sublink->subselect = (Node *) query;
return sublink;
}
/**
* Given a sirv function expression, create a subquery (derived table) from it.
*/
static Query *make_sirvf_subquery(FuncExpr *fe)
{
SubLink *sl = make_sirvf_subselect(fe);
Query *sq = makeNode(Query);
sq->commandType = CMD_SELECT;
sq->querySource = QSRC_PLANNER;
sq->jointree = makeNode(FromExpr);
TargetEntry *tle = (TargetEntry *) makeTargetEntry((Expr *) sl, 1, "sirvf_sq", false);
sq->targetList = list_make1(tle);
sq->hasSubLinks = true;
return sq;
}
/**
* Is it safe to replace tle's in this query? Very conservatively, the jointree must be empty
*/
static bool safe_to_replace_sirvf_tle(Query *query)
{
return (query->jointree->fromlist == NIL);
}
/**
* Does a query return utmost single row?
*/
static bool single_row_query(Query *query)
{
/**
* If targetlist has a SRF, then no guarantee
*/
if (expression_returns_set( (Node *) query->targetList))
{
return false;
}
/**
* Every range table entry must return utmost one row.
*/
ListCell *lcrte = NULL;
foreach (lcrte, query->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lcrte);
switch(rte->rtekind)
{
case RTE_FUNCTION:
{
FuncExpr *fe = (FuncExpr *) rte->funcexpr;
if (fe->funcretset)
{
/* SRF in FROM clause */
return false;
}
break;
}
case RTE_SUBQUERY:
{
Query *sq = (Query *) rte->subquery;
/**
* Recurse into subquery to see if it returns
* utmost one row.
*/
if (!single_row_query(sq))
{
return false;
}
break;
}
default:
{
return false;
}
}
}
return true;
}
/**
* Is it safe to replace rte's in this query? This is the
* case if the query returns utmost one row.
*/
static bool safe_to_replace_sirvf_rte(Query *query)
{
return single_row_query(query);
}
/**
* If a range table entry contains a sirv function, this must be replaced with a derived table (subquery)
* with a sublink - this will eventually be turned into an initplan.
* Conceptually, SELECT * FROM FOO(1) t1 is turned into SELECT * FROM (SELECT (SELECT FOO(1))) t1.
*/
static void replace_sirvf_rte(Query *query, int rteIndex)
{
Assert(query);
Assert(safe_to_replace_sirvf_rte(query));
RangeTblEntry *rte = (RangeTblEntry *) list_nth(query->rtable, rteIndex - 1);
if (rte->rtekind == RTE_FUNCTION)
{
FuncExpr *fe = (FuncExpr *) rte->funcexpr;
Assert(fe);
/**
* Transform function expression's inputs
*/
fe->args = (List *) replace_sirvf_tle_expr_mutator((Node *) fe->args, NULL);
/**
* If the resulting targetlist entry has a sublink, the query's flag must be set
*/
if (contain_subplans((Node *) fe->args))
{
query->hasSubLinks = true;
}
/**
* If function expression is a SIRV, then further transformations
* need to happen
*/
if (is_sirv_funcexpr(fe))
{
bool returns_record = (get_typtype(fe->funcresulttype) == 'c');
if (returns_record)
{
/**
* Need to extract out relevant vars using fieldselect
*/
wrap_vars_with_fieldselect(query->targetList,
rteIndex,
fe->funcresulttype
);
}
Query *subquery = make_sirvf_subquery(fe);
rte->funcexpr = NULL;
rte->funccoltypes = NIL;
rte->funcuserdata = NULL;
rte->funccoltypmods = NIL;
/**
* Turn the range table entry to the kind RTE_SUBQUERY
*/
rte->rtekind = RTE_SUBQUERY;
rte->subquery = subquery;
}
}
}
/**
* Context for mutator
*/
typedef struct fieldselect_mutator_context
{
plan_tree_base_prefix base;
int varno; /* What is the relid of vars that are to be changed? */
Oid recordtype;
} fieldselect_mutator_context;
static Node *wrap_vars_with_fieldselect_mutator(Node *node, fieldselect_mutator_context *ctx);
/**
* Iterate over expression and wrap vars with specific varno
* with a fieldselect
*/
static Node *wrap_vars_with_fieldselect_mutator(Node *node, fieldselect_mutator_context *ctx)
{
Assert(ctx);
if (node == NULL)
{
return NULL;
}
if (IsA(node, Var))
{
Var *v = (Var *) node;
if (v->varno == ctx->varno)
{
Var *v1 = (Var *) copyObject(v);
v1->varattno = 1; /* Attribute is a record */
v1->vartype = ctx->recordtype; /* What is the composite type */
v1->vartypmod = -1;
FieldSelect *fs = (FieldSelect *) makeNode(FieldSelect);
fs->arg = (Expr *) v1;
fs->fieldnum = v->varattno;
fs->resulttype = v->vartype;
fs->resulttypmod = v->vartypmod;
return (Node *) fs;
}
return (Node *) v;
}
return expression_tree_mutator(node, wrap_vars_with_fieldselect_mutator, ctx);
}
/**
* Wrap vars with specified varno with a fieldselect.
*/
static void wrap_vars_with_fieldselect(List *targetlist, int varno, Oid recordtype)
{
fieldselect_mutator_context ctx;
ctx.base.node = NULL;
ctx.varno = varno;
ctx.recordtype = recordtype;
ListCell *lc = NULL;
int tleOffset = 0;
foreach_with_count(lc, targetlist, tleOffset)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
lfirst(lc) = (TargetEntry *) wrap_vars_with_fieldselect_mutator((Node *) tle, &ctx);
}
}