/* * For PostgreSQL Database Management System: * (formerly known as Postgres, then as Postgres95) * * Portions Copyright (c) 1996-2010, The PostgreSQL Global Development Group * * Portions Copyright (c) 1994, The Regents of the University of California * * Permission to use, copy, modify, and distribute this software and its documentation for any purpose, * without fee, and without a written agreement is hereby granted, provided that the above copyright notice * and this paragraph and the following two paragraphs appear in all copies. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA * HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #include "postgres.h" #include "nodes/makefuncs.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "parser/cypher_expr.h" #include "parser/cypher_item.h" static List *ExpandAllTables(ParseState *pstate, int location); static List *expand_rel_attrs(ParseState *pstate, RangeTblEntry *rte, int rtindex, int sublevels_up, int location); // see transformTargetEntry() TargetEntry *transform_cypher_item(cypher_parsestate *cpstate, Node *node, Node *expr, ParseExprKind expr_kind, char *colname, bool resjunk) { ParseState *pstate = (ParseState *)cpstate; if (!expr) expr = transform_cypher_expr(cpstate, node, expr_kind); if (!colname && !resjunk) colname = FigureColname(node); return makeTargetEntry((Expr *)expr, (AttrNumber)pstate->p_next_resno++, colname, resjunk); } // see transformTargetList() List *transform_cypher_item_list(cypher_parsestate *cpstate, List *item_list, List **groupClause, ParseExprKind expr_kind) { List *target_list = NIL; ListCell *li; List *group_clause = NIL; bool hasAgg = false; bool expand_star; expand_star = (expr_kind != EXPR_KIND_UPDATE_SOURCE); foreach (li, item_list) { ResTarget *item = lfirst(li); TargetEntry *te; if (expand_star) { if (IsA(item->val, ColumnRef)) { ColumnRef *cref = (ColumnRef *) item->val; if (IsA(llast(cref->fields), A_Star)) { ParseState *pstate = &cpstate->pstate; /* we only allow a bare '*' */ if (list_length(cref->fields) != 1) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid number of fields for *"), parser_errposition(pstate, cref->location))); } target_list = list_concat(target_list, ExpandAllTables(pstate, cref->location)); continue; } } } /* clear the exprHasAgg flag to check transform for an aggregate */ cpstate->exprHasAgg = false; /* transform the item */ te = transform_cypher_item(cpstate, item->val, NULL, expr_kind, item->name, false); target_list = lappend(target_list, te); /* * Did the transformed item contain an aggregate function? If it didn't, * add it to the potential group_clause. If it did, flag that we found * an aggregate in an expression */ if (!cpstate->exprHasAgg) { group_clause = lappend(group_clause, item->val); } else { hasAgg = true; } } /* * If we found an aggregate function, we need to return the group_clause, * even if NIL. parseCheckAggregates at the end of transform_cypher_return * will verify if it is valid. */ if (hasAgg) { *groupClause = group_clause; } return target_list; } /* * From PG's ExpandAllTables() * Transforms '*' (in the target list) into a list of targetlist entries. */ static List *ExpandAllTables(ParseState *pstate, int location) { List *target = NIL; bool found_table = false; ListCell *l; foreach(l, pstate->p_namespace) { ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); /* Ignore table-only items */ if (!nsitem->p_cols_visible) continue; /* Should not have any lateral-only items when parsing targetlist */ Assert(!nsitem->p_lateral_only); /* Remember we found a p_cols_visible item */ found_table = true; target = list_concat(target, expand_rel_attrs(pstate, nsitem->p_rte, nsitem->p_rtindex, 0, location)); } /* Check for "RETURN *;" */ if (!found_table) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("RETURN * without a pattern is not valid"), parser_errposition(pstate, location))); return target; } /* * From PG's expandRelAttrs * Modified to exclude hidden variables and aliases in RETURN * */ static List *expand_rel_attrs(ParseState *pstate, RangeTblEntry *rte, int rtindex, int sublevels_up, int location) { List *names, *vars; ListCell *name, *var; List *te_list = NIL; int var_prefix_len = strlen(AGE_DEFAULT_VARNAME_PREFIX); int alias_prefix_len = strlen(AGE_DEFAULT_ALIAS_PREFIX); expandRTE(rte, rtindex, sublevels_up, location, false, &names, &vars); /* * Require read access to the table. This is normally redundant with the * markVarForSelectPriv calls below, but not if the table has zero * columns. */ rte->requiredPerms |= ACL_SELECT; /* iterate through the variables */ forboth(name, names, var, vars) { char *label = strVal(lfirst(name)); Var *varnode = (Var *)lfirst(var); TargetEntry *te; /* we want to skip our "hidden" variables */ if (strncmp(AGE_DEFAULT_VARNAME_PREFIX, label, var_prefix_len) == 0) continue; /* we want to skip out "hidden" aliases */ if (strncmp(AGE_DEFAULT_ALIAS_PREFIX, label, alias_prefix_len) == 0) continue; /* add this variable to the list */ te = makeTargetEntry((Expr *)varnode, (AttrNumber)pstate->p_next_resno++, label, false); te_list = lappend(te_list, te); /* Require read access to each column */ markVarForSelectPriv(pstate, varnode); } Assert(name == NULL && var == NULL); /* lists not the same length? */ return te_list; }