blob: c90073b7c06bbbe1e357519eb350a01f1cb87e49 [file] [log] [blame]
/*
* 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;
}