| /* |
| * 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 "access/attnum.h" |
| #include "nodes/makefuncs.h" |
| #include "nodes/nodes.h" |
| #include "nodes/parsenodes.h" |
| #include "nodes/pg_list.h" |
| #include "nodes/primnodes.h" |
| #include "parser/parse_node.h" |
| #include "parser/parse_relation.h" |
| #include "parser/parse_target.h" |
| |
| #include "parser/cypher_expr.h" |
| #include "parser/cypher_item.h" |
| #include "parser/cypher_parse_node.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 tranformed 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); |
| RangeTblEntry *rte = nsitem->p_rte; |
| |
| /* 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, rte, |
| RTERangeTablePosn(pstate, |
| rte, |
| NULL), |
| 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, rte); |
| } |
| |
| Assert(name == NULL && var == NULL); /* lists not the same length? */ |
| |
| return te_list; |
| } |