diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 7f4da92..ac4f620 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -563,25 +563,6 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) assign_expr_collations(pstate, funcexpr); /* - * The function parameters cannot make use of any variables from other - * FROM items. (Compare to transformRangeSubselect(); the coding is - * different though because we didn't parse as a sub-select with its own - * level of namespace.) - * - * XXX this will need further work to support SQL99's LATERAL() feature, - * wherein such references would indeed be legal. - */ - if (pstate->p_relnamespace || pstate->p_varnamespace) - { - if (contain_vars_of_level(funcexpr, 0)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("function expression in FROM cannot refer to other relations of same query level"), - parser_errposition(pstate, - locate_var_of_level(funcexpr, 0)))); - } - - /* * Disallow aggregate functions in the expression. (No reason to postpone * this check until parseCheckAggregates.) */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 04f9622..b7c93c6 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -18,6 +18,8 @@ #include "commands/trigger.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" +#include "optimizer/var.h" #include "parser/analyze.h" #include "parser/parse_coerce.h" #include "parser/parsetree.h" @@ -60,6 +62,7 @@ static List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree); static Query *fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown); +static void substituteRTEFunction(Query *parsetree, RangeTblEntry *rte, int rt_index); /* @@ -1557,6 +1560,14 @@ fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown) continue; } + if (rte->rtekind == RTE_FUNCTION) + { + + substituteRTEFunction(parsetree, rte, rt_index); + continue; + + } + /* * Joins and other non-relation RTEs can be ignored completely. */ @@ -2239,3 +2250,192 @@ QueryRewrite(Query *parsetree) return results; } + +static void +substituteRTEFunction(Query *parsetree, RangeTblEntry *rte, int rt_index) { + bool is_lateral = false; + FuncExpr *funcExpr; + ListCell *lcell; + + if (!IsA(rte->funcexpr, FuncExpr)) { + elog(ERROR, "unrecognized range function type: %d", (int) nodeTag(rte->funcexpr)); + } + + funcExpr = (FuncExpr *) rte->funcexpr; + + if (!funcExpr->args) { + return; + } + + foreach(lcell, funcExpr->args) { + Node *arg = (Node *) lfirst(lcell); + + is_lateral = contain_vars_of_level(arg, 0); + if (is_lateral) { + break; + } + } + + + if (is_lateral) { + List* from; + List *rt_sub = NIL; + Relids rt_sub_ids = NULL; + Query *subquery = makeNode(Query); + List *subquery_from = NIL; + TargetEntry *subquery_te = makeTargetEntry((Expr *) funcExpr, 1, NULL, false); + Node *subquery_quals = NULL; + int i; + + + Assert(parsetree->jointree != NULL && IsA(parsetree->jointree, FromExpr)); + + + from = parsetree->jointree->fromlist; + + /* + * Construct FROM list of the substitution subquery. + */ + foreach (lcell, from) { + Node *fnode = lfirst(lcell); + int rt_ind_f = 0; + + if (IsA(fnode, RangeTblRef)) { + rt_ind_f = ((RangeTblRef *) fnode)->rtindex; + } else if (IsA(fnode, JoinExpr)) { + rt_ind_f = ((JoinExpr *) fnode)->rtindex; + } else { + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(fnode)); + } + + if (rt_ind_f < rt_index) { + subquery_from = lappend(subquery_from, fnode); + } else { + /* Really found RangeTblRef for the function that we're just replacing? */ + Assert(IsA(fnode, RangeTblRef) && rt_ind_f == rt_index); + + break; + } + } + Assert(subquery_from != NIL); + + /* + * Copy the corresponding RTEs into the subquery's range table. + */ + i = 1; + foreach (lcell, parsetree->rtable) { + Node *rtnode = lfirst(lcell); + + if (i < rt_index) { + rt_sub = lappend(rt_sub, rtnode); + rt_sub_ids = bms_add_member(rt_sub_ids, i); + i++; + } else { + break; + } + } + + /* + * Construct qualification (WHERE) of the subquery. + * The result may only reference RTEs located before the function we're rewriting. + */ + if (parsetree->jointree->quals) { + Node *quals = parsetree->jointree->quals; + Relids varnos = NULL; + + if (IsA(quals, BoolExpr) && ((BoolExpr *) quals)->boolop == AND_EXPR) { + BoolExpr *andExpr = (BoolExpr *) quals; + ListCell *arg; + List *argsUsable = NIL; + + /* + * Only subclauses referencing members of 'rt_sub' can be added. + * The other can't be applied until result of the subquery is known. + */ + + /* + * TODO + * If some subclause is AND operator, flatten it recursively. + * + * (i.e. do something like + * optimizer/utils/clauses.c:simplify_and_arguments() + * ) + * + */ + foreach (arg, andExpr->args) { + Node *andOperand = lfirst(arg); + + varnos = pull_varnos(andOperand); + if (bms_is_subset(varnos, rt_sub_ids)) { + argsUsable = lappend(argsUsable, andOperand); + } + bms_free(varnos); + } + + if (argsUsable != NIL) { + if (list_length(argsUsable) == 1) { + subquery_quals = (Node *) list_nth(argsUsable, 0); + list_free(argsUsable); + } else { + subquery_quals = (Node *) make_andclause(argsUsable); + } + } + } else { + /* + * non-AND operators as well as other expression types are used whole or not at all. + */ + varnos = pull_varnos(quals); + + if (bms_is_subset(varnos, rt_sub_ids)) { + subquery_quals = (Node *) copyObject(quals); + } + bms_free(varnos); + } + } + + Assert(rt_sub_ids != NULL); + bms_free(rt_sub_ids); + + /* + * TODO + * re-check all attributes + */ + subquery->commandType = CMD_SELECT; + subquery->querySource = QSRC_INSTEAD_RULE; + subquery->utilityStmt = NULL; + subquery->resultRelation = 0; + subquery->intoClause = NULL; + subquery->hasAggs = false; + subquery->hasWindowFuncs = false; + subquery->hasSubLinks = false; + subquery->hasDistinctOn = false; + subquery->hasRecursive = false; + subquery->hasModifyingCTE = false; + subquery->hasForUpdate = false; + subquery->returningList = NIL; + subquery->groupClause = NIL; + subquery->havingQual = NULL; + subquery->windowClause = NIL; + subquery->distinctClause = NIL; + subquery->sortClause = NIL; + subquery->limitOffset = NULL; + subquery->limitCount = NULL; + subquery->rowMarks = NULL; + subquery->setOperations = NULL; + subquery->constraintDeps = NULL; + + subquery->rtable = rt_sub; + subquery->jointree = makeNode(FromExpr); + subquery->jointree->fromlist = subquery_from; + subquery->jointree->quals = subquery_quals; + + subquery->targetList = lappend(NIL, subquery_te); + + + rte->rtekind = RTE_SUBQUERY; + rte->subquery = subquery; + rte->relid = InvalidOid; + rte->alias = NULL; + rte->inFromCl = false; + } +}