diff options
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 2692 |
1 files changed, 15 insertions, 2677 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index bee5132a8b9..4a3800a8a4e 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -7,46 +7,30 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.49 1997/11/20 23:22:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.50 1997/11/25 22:00:27 momjian Exp $ * *------------------------------------------------------------------------- */ + #include <stdio.h> #include <stdlib.h> #include <string.h> #include "postgres.h" -#include "nodes/nodes.h" -#include "nodes/params.h" -#include "nodes/primnodes.h" -#include "nodes/parsenodes.h" -#include "nodes/relation.h" -#include "parse.h" /* for AND, OR, etc. */ -#include "catalog/pg_aggregate.h" -#include "catalog/pg_type.h" /* for INT4OID, etc. */ -#include "catalog/pg_proc.h" -#include "utils/elog.h" -#include "utils/builtins.h" /* namecmp(), textout() */ -#include "utils/lsyscache.h" -#include "utils/palloc.h" -#include "utils/mcxt.h" -#include "utils/syscache.h" -#include "utils/acl.h" -#include "parser/parse_query.h" -#include "parser/parse_state.h" -#include "nodes/makefuncs.h" /* for makeResdom(), etc. */ -#include "nodes/nodeFuncs.h" -#include "commands/sequence.h" - -#include "optimizer/clauses.h" -#include "access/heapam.h" -#include "miscadmin.h" - -#include "port-protos.h" /* strdup() */ +#include "access/heapam.h" +#include "nodes/makefuncs.h" +#include "nodes/memnodes.h" +#include "nodes/pg_list.h" +#include "parser/analyze.h" +#include "parser/parse_agg.h" +#include "parser/parse_node.h" +#include "parser/parse_relation.h" +#include "parser/parse_target.h" +#include "parser/parse_clause.h" +#include "utils/builtins.h" +#include "utils/mcxt.h" -/* convert the parse tree into a query tree */ static Query *transformStmt(ParseState *pstate, Node *stmt); - static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, AppendStmt *stmt); static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt); @@ -55,74 +39,7 @@ static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt); static Query *transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt); static Query *transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt); static Query *transformCursorStmt(ParseState *pstate, CursorStmt *stmt); -static Node *handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno); - -#define EXPR_COLUMN_FIRST 1 -#define EXPR_RELATION_FIRST 2 -static Node *transformExpr(ParseState *pstate, Node *expr, int precedence); -static Node *transformIdent(ParseState *pstate, Node *expr, int precedence); - -static void makeRangeTable(ParseState *pstate, char *relname, List *frmList); -static List *expandAllTables(ParseState *pstate); -static char *figureColname(Node *expr, Node *resval); -static List *makeTargetNames(ParseState *pstate, List *cols); -static List *transformTargetList(ParseState *pstate, List *targetlist); -static TargetEntry *make_targetlist_expr(ParseState *pstate, - char *colname, Node *expr, - List *arrayRef); -static bool inWhereClause = false; -static Node *transformWhereClause(ParseState *pstate, Node *a_expr); -static List *transformGroupClause(ParseState *pstate, List *grouplist, - List *targetlist); -static List *transformSortClause(ParseState *pstate, - List *orderlist, List *targetlist, - char *uniqueFlag); - -static void parseFromClause(ParseState *pstate, List *frmList); -static Node *ParseFunc(ParseState *pstate, char *funcname, - List *fargs, int *curr_resno); -static List *setup_tlist(char *attname, Oid relid); -static List *setup_base_tlist(Oid typeid); -static void make_arguments(int nargs, List *fargs, Oid *input_typeids, - Oid *function_typeids); -static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg); -static void finalizeAggregates(ParseState *pstate, Query *qry); -static void parseCheckAggregates(ParseState *pstate, Query *qry); -static ParseState *makeParseState(void); -static Node *parser_typecast(Value *expr, TypeName *typename, int typlen); -static Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen); -static Aggreg *ParseAgg(char *aggname, Oid basetype, Node *target); - -/***************************************************************************** - * - *****************************************************************************/ - -/* - * makeParseState() -- - * allocate and initialize a new ParseState. - * the CALLERS is responsible for freeing the ParseState* returned - * - */ -static ParseState * -makeParseState(void) -{ - ParseState *pstate; - - pstate = malloc(sizeof(ParseState)); - pstate->p_last_resno = 1; - pstate->p_rtable = NIL; - pstate->p_numAgg = 0; - pstate->p_aggs = NIL; - pstate->p_is_insert = false; - pstate->p_insert_columns = NIL; - pstate->p_is_update = false; - pstate->p_is_rule = false; - pstate->p_target_relation = NULL; - pstate->p_target_rangetblentry = NULL; - - return (pstate); -} /* * parse_analyze - @@ -144,11 +61,9 @@ parse_analyze(List *pl) result->len = length(pl); result->qtrees = (Query **) malloc(result->len * sizeof(Query *)); - inWhereClause = false; /* to avoid nextval(sequence) in WHERE */ - while (pl != NIL) { - pstate = makeParseState(); + pstate = make_parsestate(); result->qtrees[i++] = transformStmt(pstate, lfirst(pl)); pl = lnext(pl); if (pstate->p_target_relation != NULL) @@ -620,2580 +535,3 @@ transformCursorStmt(ParseState *pstate, CursorStmt *stmt) return (Query *) qry; } - -/***************************************************************************** - * - * Transform Exprs, Aggs, etc. - * - *****************************************************************************/ - -/* - * transformExpr - - * analyze and transform expressions. Type checking and type casting is - * done here. The optimizer and the executor cannot handle the original - * (raw) expressions collected by the parse tree. Hence the transformation - * here. - */ -static Node * -transformExpr(ParseState *pstate, Node *expr, int precedence) -{ - Node *result = NULL; - - if (expr == NULL) - return NULL; - - switch (nodeTag(expr)) - { - case T_Attr: - { - Attr *att = (Attr *) expr; - Node *temp; - - /* what if att.attrs == "*"?? */ - temp = handleNestedDots(pstate, att, &pstate->p_last_resno); - if (att->indirection != NIL) - { - List *idx = att->indirection; - - while (idx != NIL) - { - A_Indices *ai = (A_Indices *) lfirst(idx); - Node *lexpr = NULL, - *uexpr; - - uexpr = transformExpr(pstate, ai->uidx, precedence); /* must exists */ - if (exprType(uexpr) != INT4OID) - elog(WARN, "array index expressions must be int4's"); - if (ai->lidx != NULL) - { - lexpr = transformExpr(pstate, ai->lidx, precedence); - if (exprType(lexpr) != INT4OID) - elog(WARN, "array index expressions must be int4's"); - } -#if 0 - pfree(ai->uidx); - if (ai->lidx != NULL) - pfree(ai->lidx); -#endif - ai->lidx = lexpr; - ai->uidx = uexpr; - - /* - * note we reuse the list of indices, make sure we - * don't free them! Otherwise, make a new list - * here - */ - idx = lnext(idx); - } - result = (Node *) make_array_ref(temp, att->indirection); - } - else - { - result = temp; - } - break; - } - case T_A_Const: - { - A_Const *con = (A_Const *) expr; - Value *val = &con->val; - - if (con->typename != NULL) - { - result = parser_typecast(val, con->typename, -1); - } - else - { - result = (Node *) make_const(val); - } - break; - } - case T_ParamNo: - { - ParamNo *pno = (ParamNo *) expr; - Oid toid; - int paramno; - Param *param; - - paramno = pno->number; - toid = param_type(paramno); - if (!OidIsValid(toid)) - { - elog(WARN, "Parameter '$%d' is out of range", - paramno); - } - param = makeNode(Param); - param->paramkind = PARAM_NUM; - param->paramid = (AttrNumber) paramno; - param->paramname = "<unnamed>"; - param->paramtype = (Oid) toid; - param->param_tlist = (List *) NULL; - - result = (Node *) param; - break; - } - case T_A_Expr: - { - A_Expr *a = (A_Expr *) expr; - - switch (a->oper) - { - case OP: - { - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - result = (Node *) make_op(a->opname, lexpr, rexpr); - } - break; - case ISNULL: - { - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - - result = ParseFunc(pstate, - "nullvalue", lcons(lexpr, NIL), - &pstate->p_last_resno); - } - break; - case NOTNULL: - { - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - - result = ParseFunc(pstate, - "nonnullvalue", lcons(lexpr, NIL), - &pstate->p_last_resno); - } - break; - case AND: - { - Expr *expr = makeNode(Expr); - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - if (exprType(lexpr) != BOOLOID) - elog(WARN, - "left-hand side of AND is type '%s', not bool", - tname(get_id_type(exprType(lexpr)))); - if (exprType(rexpr) != BOOLOID) - elog(WARN, - "right-hand side of AND is type '%s', not bool", - tname(get_id_type(exprType(rexpr)))); - expr->typeOid = BOOLOID; - expr->opType = AND_EXPR; - expr->args = makeList(lexpr, rexpr, -1); - result = (Node *) expr; - } - break; - case OR: - { - Expr *expr = makeNode(Expr); - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - if (exprType(lexpr) != BOOLOID) - elog(WARN, - "left-hand side of OR is type '%s', not bool", - tname(get_id_type(exprType(lexpr)))); - if (exprType(rexpr) != BOOLOID) - elog(WARN, - "right-hand side of OR is type '%s', not bool", - tname(get_id_type(exprType(rexpr)))); - expr->typeOid = BOOLOID; - expr->opType = OR_EXPR; - expr->args = makeList(lexpr, rexpr, -1); - result = (Node *) expr; - } - break; - case NOT: - { - Expr *expr = makeNode(Expr); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - if (exprType(rexpr) != BOOLOID) - elog(WARN, - "argument to NOT is type '%s', not bool", - tname(get_id_type(exprType(rexpr)))); - expr->typeOid = BOOLOID; - expr->opType = NOT_EXPR; - expr->args = makeList(rexpr, -1); - result = (Node *) expr; - } - break; - } - break; - } - case T_Ident: - { - - /* - * look for a column name or a relation name (the default - * behavior) - */ - result = transformIdent(pstate, expr, precedence); - break; - } - case T_FuncCall: - { - FuncCall *fn = (FuncCall *) expr; - List *args; - - /* transform the list of arguments */ - foreach(args, fn->args) - lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence); - result = ParseFunc(pstate, - fn->funcname, fn->args, &pstate->p_last_resno); - break; - } - default: - /* should not reach here */ - elog(WARN, "transformExpr: does not know how to transform %d\n", - nodeTag(expr)); - break; - } - - return result; -} - -static Node * -transformIdent(ParseState *pstate, Node *expr, int precedence) -{ - Ident *ident = (Ident *) expr; - RangeTblEntry *rte; - Node *column_result, - *relation_result, - *result; - - column_result = relation_result = result = 0; - /* try to find the ident as a column */ - if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL) - { - Attr *att = makeNode(Attr); - - att->relname = rte->refname; - att->attrs = lcons(makeString(ident->name), NIL); - column_result = - (Node *) handleNestedDots(pstate, att, &pstate->p_last_resno); - } - - /* try to find the ident as a relation */ - if (refnameRangeTableEntry(pstate->p_rtable, ident->name) != NULL) - { - ident->isRel = TRUE; - relation_result = (Node *) ident; - } - - /* choose the right result based on the precedence */ - if (precedence == EXPR_COLUMN_FIRST) - { - if (column_result) - result = column_result; - else - result = relation_result; - } - else - { - if (relation_result) - result = relation_result; - else - result = column_result; - } - - if (result == NULL) - elog(WARN, "attribute '%s' not found", ident->name); - - return result; -} - -/***************************************************************************** - * - * From Clause - * - *****************************************************************************/ - -/* - * parseFromClause - - * turns the table references specified in the from-clause into a - * range table. The range table may grow as we transform the expressions - * in the target list. (Note that this happens because in POSTQUEL, we - * allow references to relations not specified in the from-clause. We - * also allow that in our POST-SQL) - * - */ -static void -parseFromClause(ParseState *pstate, List *frmList) -{ - List *fl; - - foreach(fl, frmList) - { - RangeVar *r = lfirst(fl); - RelExpr *baserel = r->relExpr; - char *relname = baserel->relname; - char *refname = r->name; - RangeTblEntry *rte; - - if (refname == NULL) - refname = relname; - - /* - * marks this entry to indicate it comes from the FROM clause. In - * SQL, the target list can only refer to range variables - * specified in the from clause but we follow the more powerful - * POSTQUEL semantics and automatically generate the range - * variable if not specified. However there are times we need to - * know whether the entries are legitimate. - * - * eg. select * from foo f where f.x = 1; will generate wrong answer - * if we expand * to foo.x. - */ - rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE); - } -} - -/* - * makeRangeTable - - * make a range table with the specified relation (optional) and the - * from-clause. - */ -static void -makeRangeTable(ParseState *pstate, char *relname, List *frmList) -{ - RangeTblEntry *rte; - - parseFromClause(pstate, frmList); - - if (relname == NULL) - return; - - if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1) - rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE); - else - rte = refnameRangeTableEntry(pstate->p_rtable, relname); - - pstate->p_target_rangetblentry = rte; - Assert(pstate->p_target_relation == NULL); - pstate->p_target_relation = heap_open(rte->relid); - Assert(pstate->p_target_relation != NULL); - /* will close relation later */ -} - -/* - * exprType - - * returns the Oid of the type of the expression. (Used for typechecking.) - */ -Oid -exprType(Node *expr) -{ - Oid type = (Oid) 0; - - switch (nodeTag(expr)) - { - case T_Func: - type = ((Func *) expr)->functype; - break; - case T_Iter: - type = ((Iter *) expr)->itertype; - break; - case T_Var: - type = ((Var *) expr)->vartype; - break; - case T_Expr: - type = ((Expr *) expr)->typeOid; - break; - case T_Const: - type = ((Const *) expr)->consttype; - break; - case T_ArrayRef: - type = ((ArrayRef *) expr)->refelemtype; - break; - case T_Aggreg: - type = ((Aggreg *) expr)->aggtype; - break; - case T_Param: - type = ((Param *) expr)->paramtype; - break; - case T_Ident: - /* is this right? */ - type = UNKNOWNOID; - break; - default: - elog(WARN, "exprType: don't know how to get type for %d node", - nodeTag(expr)); - break; - } - return type; -} - -/* - * expandAllTables - - * turns '*' (in the target list) into a list of attributes - * (of all relations in the range table) - */ -static List * -expandAllTables(ParseState *pstate) -{ - List *target = NIL; - List *legit_rtable = NIL; - List *rt, - *rtable; - - rtable = pstate->p_rtable; - if (pstate->p_is_rule) - { - - /* - * skip first two entries, "*new*" and "*current*" - */ - rtable = lnext(lnext(pstate->p_rtable)); - } - - /* this should not happen */ - if (rtable == NULL) - elog(WARN, "cannot expand: null p_rtable"); - - /* - * go through the range table and make a list of range table entries - * which we will expand. - */ - foreach(rt, rtable) - { - RangeTblEntry *rte = lfirst(rt); - - /* - * we only expand those specify in the from clause. (This will - * also prevent us from using the wrong table in inserts: eg. - * tenk2 in "insert into tenk2 select * from tenk1;") - */ - if (!rte->inFromCl) - continue; - legit_rtable = lappend(legit_rtable, rte); - } - - foreach(rt, legit_rtable) - { - RangeTblEntry *rte = lfirst(rt); - List *temp = target; - - if (temp == NIL) - target = expandAll(pstate, rte->relname, rte->refname, - &pstate->p_last_resno); - else - { - while (temp != NIL && lnext(temp) != NIL) - temp = lnext(temp); - lnext(temp) = expandAll(pstate, rte->relname, rte->refname, - &pstate->p_last_resno); - } - } - return target; -} - - -/* - * figureColname - - * if the name of the resulting column is not specified in the target - * list, we have to guess. - * - */ -static char * -figureColname(Node *expr, Node *resval) -{ - switch (nodeTag(expr)) - { - case T_Aggreg: - return (char *) /* XXX */ - ((Aggreg *) expr)->aggname; - case T_Expr: - if (((Expr *) expr)->opType == FUNC_EXPR) - { - if (nodeTag(resval) == T_FuncCall) - return ((FuncCall *) resval)->funcname; - } - break; - default: - break; - } - - return "?column?"; -} - -/***************************************************************************** - * - * Target list - * - *****************************************************************************/ - -/* - * makeTargetNames - - * generate a list of column names if not supplied or - * test supplied column names to make sure they are in target table - * (used exclusively for inserts) - */ -static List * -makeTargetNames(ParseState *pstate, List *cols) -{ - List *tl = NULL; - - /* Generate ResTarget if not supplied */ - - if (cols == NIL) - { - int numcol; - int i; - AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs; - - numcol = pstate->p_target_relation->rd_rel->relnatts; - for (i = 0; i < numcol; i++) - { - Ident *id = makeNode(Ident); - - id->name = palloc(NAMEDATALEN); - StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN); - id->indirection = NIL; - id->isRel = false; - if (tl == NIL) - cols = tl = lcons(id, NIL); - else - { - lnext(tl) = lcons(id, NIL); - tl = lnext(tl); - } - } - } - else - { - foreach(tl, cols) - { - List *nxt; - char *name = ((Ident *) lfirst(tl))->name; - - /* elog on failure */ - varattno(pstate->p_target_relation, name); - foreach(nxt, lnext(tl)) - if (!strcmp(name, ((Ident *) lfirst(nxt))->name)) - elog (WARN, "Attribute '%s' should be specified only once", name); - } - } - - return cols; -} - -/* - * transformTargetList - - * turns a list of ResTarget's into a list of TargetEntry's - */ -static List * -transformTargetList(ParseState *pstate, List *targetlist) -{ - List *p_target = NIL; - List *tail_p_target = NIL; - - while (targetlist != NIL) - { - ResTarget *res = (ResTarget *) lfirst(targetlist); - TargetEntry *tent = makeNode(TargetEntry); - - switch (nodeTag(res->val)) - { - case T_Ident: - { - Node *expr; - Oid type_id; - int type_len; - char *identname; - char *resname; - - identname = ((Ident *) res->val)->name; - handleTargetColname(pstate, &res->name, NULL, identname); - - /* - * here we want to look for column names only, not relation - * names (even though they can be stored in Ident nodes, too) - */ - expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); - type_id = exprType(expr); - type_len = tlen(get_id_type(type_id)); - resname = (res->name) ? res->name : identname; - tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++, - (Oid) type_id, - (Size) type_len, - resname, - (Index) 0, - (Oid) 0, - 0); - - tent->expr = expr; - break; - } - case T_ParamNo: - case T_FuncCall: - case T_A_Const: - case T_A_Expr: - { - Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); - - handleTargetColname(pstate, &res->name, NULL, NULL); - /* note indirection has not been transformed */ - if (pstate->p_is_insert && res->indirection != NIL) - { - /* this is an array assignment */ - char *val; - char *str, - *save_str; - List *elt; - int i = 0, - ndims; - int lindx[MAXDIM], - uindx[MAXDIM]; - int resdomno; - Relation rd; - Value *constval; - - if (exprType(expr) != UNKNOWNOID || - !IsA(expr, Const)) - elog(WARN, "yyparse: string constant expected"); - - val = (char *) textout((struct varlena *) - ((Const *) expr)->constvalue); - str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2); - foreach(elt, res->indirection) - { - A_Indices *aind = (A_Indices *) lfirst(elt); - - aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST); - if (!IsA(aind->uidx, Const)) - elog(WARN, - "Array Index for Append should be a constant"); - uindx[i] = ((Const *) aind->uidx)->constvalue; - if (aind->lidx != NULL) - { - aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST); - if (!IsA(aind->lidx, Const)) - elog(WARN, - "Array Index for Append should be a constant"); - lindx[i] = ((Const *) aind->lidx)->constvalue; - } - else - { - lindx[i] = 1; - } - if (lindx[i] > uindx[i]) - elog(WARN, "yyparse: lower index cannot be greater than upper index"); - sprintf(str, "[%d:%d]", lindx[i], uindx[i]); - str += strlen(str); - i++; - } - sprintf(str, "=%s", val); - rd = pstate->p_target_relation; - Assert(rd != NULL); - resdomno = varattno(rd, res->name); - ndims = att_attnelems(rd, resdomno); - if (i != ndims) - elog(WARN, "yyparse: array dimensions do not match"); - constval = makeNode(Value); - constval->type = T_String; - constval->val.str = save_str; - tent = make_targetlist_expr(pstate, res->name, - (Node *) make_const(constval), - NULL); - pfree(save_str); - } - else - { - char *colname = res->name; - - /* this is not an array assignment */ - if (colname == NULL) - { - - /* - * if you're wondering why this is here, look - * at the yacc grammar for why a name can be - * missing. -ay - */ - colname = figureColname(expr, res->val); - } - if (res->indirection) - { - List *ilist = res->indirection; - - while (ilist != NIL) - { - A_Indices *ind = lfirst(ilist); - - ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); - ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); - ilist = lnext(ilist); - } - } - res->name = colname; - tent = make_targetlist_expr(pstate, res->name, expr, - res->indirection); - } - break; - } - case T_Attr: - { - Oid type_id; - int type_len; - Attr *att = (Attr *) res->val; - Node *result; - char *attrname; - char *resname; - Resdom *resnode; - List *attrs = att->attrs; - - /* - * Target item is a single '*', expand all tables (eg. - * SELECT * FROM emp) - */ - if (att->relname != NULL && !strcmp(att->relname, "*")) - { - if (tail_p_target == NIL) - p_target = tail_p_target = expandAllTables(pstate); - else - lnext(tail_p_target) = expandAllTables(pstate); - - while (lnext(tail_p_target) != NIL) - /* make sure we point to the last target entry */ - tail_p_target = lnext(tail_p_target); - - /* - * skip rest of while loop - */ - targetlist = lnext(targetlist); - continue; - } - - /* - * Target item is relation.*, expand the table (eg. - * SELECT emp.*, dname FROM emp, dept) - */ - attrname = strVal(lfirst(att->attrs)); - if (att->attrs != NIL && !strcmp(attrname, "*")) - { - - /* - * tail_p_target is the target list we're building - * in the while loop. Make sure we fix it after - * appending more nodes. - */ - if (tail_p_target == NIL) - p_target = tail_p_target = expandAll(pstate, att->relname, - att->relname, &pstate->p_last_resno); - else - lnext(tail_p_target) = - expandAll(pstate, att->relname, att->relname, - &pstate->p_last_resno); - while (lnext(tail_p_target) != NIL) - /* make sure we point to the last target entry */ - tail_p_target = lnext(tail_p_target); - - /* - * skip the rest of the while loop - */ - targetlist = lnext(targetlist); - continue; - } - - - /* - * Target item is fully specified: ie. - * relation.attribute - */ - result = handleNestedDots(pstate, att, &pstate->p_last_resno); - handleTargetColname(pstate, &res->name, att->relname, attrname); - if (att->indirection != NIL) - { - List *ilist = att->indirection; - - while (ilist != NIL) - { - A_Indices *ind = lfirst(ilist); - - ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); - ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); - ilist = lnext(ilist); - } - result = (Node *) make_array_ref(result, att->indirection); - } - type_id = exprType(result); - type_len = tlen(get_id_type(type_id)); - /* move to last entry */ - while (lnext(attrs) != NIL) - attrs = lnext(attrs); - resname = (res->name) ? res->name : strVal(lfirst(attrs)); - resnode = makeResdom((AttrNumber) pstate->p_last_resno++, - (Oid) type_id, - (Size) type_len, - resname, - (Index) 0, - (Oid) 0, - 0); - tent->resdom = resnode; - tent->expr = result; - break; - } - default: - /* internal error */ - elog(WARN, - "internal error: do not know how to transform targetlist"); - break; - } - - if (p_target == NIL) - { - p_target = tail_p_target = lcons(tent, NIL); - } - else - { - lnext(tail_p_target) = lcons(tent, NIL); - tail_p_target = lnext(tail_p_target); - } - targetlist = lnext(targetlist); - } - - return p_target; -} - - -/* - * make_targetlist_expr - - * make a TargetEntry from an expression - * - * arrayRef is a list of transformed A_Indices - */ -static TargetEntry * -make_targetlist_expr(ParseState *pstate, - char *colname, - Node *expr, - List *arrayRef) -{ - Oid type_id, - attrtype; - int type_len, - attrlen; - int resdomno; - Relation rd; - bool attrisset; - TargetEntry *tent; - Resdom *resnode; - - if (expr == NULL) - elog(WARN, "make_targetlist_expr: invalid use of NULL expression"); - - type_id = exprType(expr); - if (type_id == InvalidOid) - { - type_len = 0; - } - else - type_len = tlen(get_id_type(type_id)); - - /* I have no idea what the following does! */ - /* It appears to process target columns that will be receiving results */ - if (pstate->p_is_insert || pstate->p_is_update) - { - - /* - * append or replace query -- append, replace work only on one - * relation, so multiple occurence of same resdomno is bogus - */ - rd = pstate->p_target_relation; - Assert(rd != NULL); - resdomno = varattno(rd, colname); - attrisset = varisset(rd, colname); - attrtype = att_typeid(rd, resdomno); - if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL)) - attrtype = GetArrayElementType(attrtype); - if (attrtype == BPCHAROID || attrtype == VARCHAROID) - { - attrlen = rd->rd_att->attrs[resdomno - 1]->attlen; - } - else - { - attrlen = tlen(get_id_type(attrtype)); - } -#if 0 - if (Input_is_string && Typecast_ok) - { - Datum val; - - if (type_id == typeid(type("unknown"))) - { - val = (Datum) textout((struct varlena *) - ((Const) lnext(expr))->constvalue); - } - else - { - val = ((Const) lnext(expr))->constvalue; - } - if (attrisset) - { - lnext(expr) = makeConst(attrtype, - attrlen, - val, - false, - true, - true, /* is set */ - false); - } - else - { - lnext(expr) = - makeConst(attrtype, - attrlen, - (Datum) fmgr(typeid_get_retinfunc(attrtype), - val, get_typelem(attrtype), -1), - false, - true /* Maybe correct-- 80% chance */ , - false, /* is not a set */ - false); - } - } - else if ((Typecast_ok) && (attrtype != type_id)) - { - lnext(expr) = - parser_typecast2(expr, get_id_type(attrtype)); - } - else if (attrtype != type_id) - { - if ((attrtype == INT2OID) && (type_id == INT4OID)) - lfirst(expr) = lispInteger(INT2OID); /* handle CASHOID too */ - else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID)) - lfirst(expr) = lispInteger(FLOAT4OID); - else - elog(WARN, "unequal type in tlist : %s \n", colname); - } - - Input_is_string = false; - Input_is_integer = false; - Typecast_ok = true; -#endif - - if (attrtype != type_id) - { - if (IsA(expr, Const)) - { - /* try to cast the constant */ - if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx)) - { - /* updating a single item */ - Oid typelem = get_typelem(attrtype); - - expr = (Node *) parser_typecast2(expr, - type_id, - get_id_type(typelem), - attrlen); - } - else - expr = (Node *) parser_typecast2(expr, - type_id, - get_id_type(attrtype), - attrlen); - } - else - { - /* currently, we can't handle casting of expressions */ - elog(WARN, "parser: attribute '%s' is of type '%s' but expression is of type '%s'", - colname, - get_id_typname(attrtype), - get_id_typname(type_id)); - } - } - - if (arrayRef != NIL) - { - Expr *target_expr; - Attr *att = makeNode(Attr); - List *ar = arrayRef; - List *upperIndexpr = NIL; - List *lowerIndexpr = NIL; - - att->relname = pstrdup(RelationGetRelationName(rd)->data); - att->attrs = lcons(makeString(colname), NIL); - target_expr = (Expr *) handleNestedDots(pstate, att, - &pstate->p_last_resno); - while (ar != NIL) - { - A_Indices *ind = lfirst(ar); - - if (lowerIndexpr || (!upperIndexpr && ind->lidx)) - { - - /* - * XXX assume all lowerIndexpr is non-null in this - * case - */ - lowerIndexpr = lappend(lowerIndexpr, ind->lidx); - } - upperIndexpr = lappend(upperIndexpr, ind->uidx); - ar = lnext(ar); - } - - expr = (Node *) make_array_set(target_expr, - upperIndexpr, - lowerIndexpr, - (Expr *) expr); - attrtype = att_typeid(rd, resdomno); - attrlen = tlen(get_id_type(attrtype)); - } - } - else - { - resdomno = pstate->p_last_resno++; - attrtype = type_id; - attrlen = type_len; - } - tent = makeNode(TargetEntry); - - resnode = makeResdom((AttrNumber) resdomno, - (Oid) attrtype, - (Size) attrlen, - colname, - (Index) 0, - (Oid) 0, - 0); - - tent->resdom = resnode; - tent->expr = expr; - - return tent; -} - - -/***************************************************************************** - * - * Where Clause - * - *****************************************************************************/ - -/* - * transformWhereClause - - * transforms the qualification and make sure it is of type Boolean - * - */ -static Node * -transformWhereClause(ParseState *pstate, Node *a_expr) -{ - Node *qual; - - if (a_expr == NULL) - return (Node *) NULL; /* no qualifiers */ - - inWhereClause = true; - qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST); - inWhereClause = false; - if (exprType(qual) != BOOLOID) - { - elog(WARN, - "where clause must return type bool, not %s", - tname(get_id_type(exprType(qual)))); - } - return qual; -} - -/***************************************************************************** - * - * Sort Clause - * - *****************************************************************************/ - -/* - * find_targetlist_entry - - * returns the Resdom in the target list matching the specified varname - * and range - * - */ -static TargetEntry * -find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist) -{ - List *i; - int real_rtable_pos = 0, - target_pos = 0; - TargetEntry *target_result = NULL; - - if (sortgroupby->range) - real_rtable_pos = refnameRangeTablePosn(pstate->p_rtable, - sortgroupby->range); - - foreach(i, tlist) - { - TargetEntry *target = (TargetEntry *) lfirst(i); - Resdom *resnode = target->resdom; - Var *var = (Var *) target->expr; - char *resname = resnode->resname; - int test_rtable_pos = var->varno; - -#ifdef PARSEDEBUG - printf("find_targetlist_entry- target name is %s, position %d, resno %d\n", - (sortgroupby->name ? sortgroupby->name : "(null)"), target_pos + 1, sortgroupby->resno); -#endif - - if (!sortgroupby->name) - { - if (sortgroupby->resno == ++target_pos) - { - target_result = target; - break; - } - } - else - { - if (!strcmp(resname, sortgroupby->name)) - { - if (sortgroupby->range) - { - if (real_rtable_pos == test_rtable_pos) - { - if (target_result != NULL) - elog(WARN, "Order/Group By '%s' is ambiguous", sortgroupby->name); - else - target_result = target; - } - } - else - { - if (target_result != NULL) - elog(WARN, "Order/Group By '%s' is ambiguous", sortgroupby->name); - else - target_result = target; - } - } - } - } - return target_result; -} - -static Oid -any_ordering_op(int restype) -{ - Operator order_op; - Oid order_opid; - - order_op = oper("<", restype, restype, false); - order_opid = oprid(order_op); - - return order_opid; -} - -/* - * transformGroupClause - - * transform a Group By clause - * - */ -static List * -transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist) -{ - List *glist = NIL, - *gl = NIL; - - while (grouplist != NIL) - { - GroupClause *grpcl = makeNode(GroupClause); - TargetEntry *restarget; - Resdom *resdom; - - restarget = find_targetlist_entry(pstate, lfirst(grouplist), targetlist); - - if (restarget == NULL) - elog(WARN, "The field being grouped by must appear in the target list"); - - grpcl->entry = restarget; - resdom = restarget->resdom; - grpcl->grpOpoid = oprid(oper("<", - resdom->restype, - resdom->restype, false)); - if (glist == NIL) - gl = glist = lcons(grpcl, NIL); - else - { - List *i; - - foreach (i, glist) - { - GroupClause *gcl = (GroupClause *) lfirst (i); - - if ( gcl->entry == grpcl->entry ) - break; - } - if ( i == NIL ) /* not in grouplist already */ - { - lnext(gl) = lcons(grpcl, NIL); - gl = lnext(gl); - } - else - pfree (grpcl); /* get rid of this */ - } - grouplist = lnext(grouplist); - } - - return glist; -} - -/* - * transformSortClause - - * transform an Order By clause - * - */ -static List * -transformSortClause(ParseState *pstate, - List *orderlist, List *targetlist, - char *uniqueFlag) -{ - List *sortlist = NIL; - List *s = NIL; - - while (orderlist != NIL) - { - SortGroupBy *sortby = lfirst(orderlist); - SortClause *sortcl = makeNode(SortClause); - TargetEntry *restarget; - Resdom *resdom; - - restarget = find_targetlist_entry(pstate, sortby, targetlist); - if (restarget == NULL) - elog(WARN, "The field being ordered by must appear in the target list"); - - sortcl->resdom = resdom = restarget->resdom; - sortcl->opoid = oprid(oper(sortby->useOp, - resdom->restype, - resdom->restype, false)); - if (sortlist == NIL) - { - s = sortlist = lcons(sortcl, NIL); - } - else - { - List *i; - - foreach (i, sortlist) - { - SortClause *scl = (SortClause *) lfirst (i); - - if ( scl->resdom == sortcl->resdom ) - break; - } - if ( i == NIL ) /* not in sortlist already */ - { - lnext(s) = lcons(sortcl, NIL); - s = lnext(s); - } - else - pfree (sortcl); /* get rid of this */ - } - orderlist = lnext(orderlist); - } - - if (uniqueFlag) - { - List *i; - - if (uniqueFlag[0] == '*') - { - - /* - * concatenate all elements from target list that are not - * already in the sortby list - */ - foreach(i, targetlist) - { - TargetEntry *tlelt = (TargetEntry *) lfirst(i); - - s = sortlist; - while (s != NIL) - { - SortClause *sortcl = lfirst(s); - - if (sortcl->resdom == tlelt->resdom) - break; - s = lnext(s); - } - if (s == NIL) - { - /* not a member of the sortclauses yet */ - SortClause *sortcl = makeNode(SortClause); - - sortcl->resdom = tlelt->resdom; - sortcl->opoid = any_ordering_op(tlelt->resdom->restype); - - sortlist = lappend(sortlist, sortcl); - } - } - } - else - { - TargetEntry *tlelt = NULL; - char *uniqueAttrName = uniqueFlag; - - /* only create sort clause with the specified unique attribute */ - foreach(i, targetlist) - { - tlelt = (TargetEntry *) lfirst(i); - if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0) - break; - } - if (i == NIL) - { - elog(WARN, "The field specified in the UNIQUE ON clause is not in the targetlist"); - } - s = sortlist; - foreach(s, sortlist) - { - SortClause *sortcl = lfirst(s); - - if (sortcl->resdom == tlelt->resdom) - break; - } - if (s == NIL) - { - /* not a member of the sortclauses yet */ - SortClause *sortcl = makeNode(SortClause); - - sortcl->resdom = tlelt->resdom; - sortcl->opoid = any_ordering_op(tlelt->resdom->restype); - - sortlist = lappend(sortlist, sortcl); - } - } - - } - - return sortlist; -} - -/* - ** HandleNestedDots -- - ** Given a nested dot expression (i.e. (relation func ... attr), build up - ** a tree with of Iter and Func nodes. - */ -static Node * -handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno) -{ - List *mutator_iter; - Node *retval = NULL; - - if (attr->paramNo != NULL) - { - Param *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST); - - retval = - ParseFunc(pstate, strVal(lfirst(attr->attrs)), - lcons(param, NIL), - curr_resno); - } - else - { - Ident *ident = makeNode(Ident); - - ident->name = attr->relname; - ident->isRel = TRUE; - retval = - ParseFunc(pstate, strVal(lfirst(attr->attrs)), - lcons(ident, NIL), - curr_resno); - } - - foreach(mutator_iter, lnext(attr->attrs)) - { - retval = ParseFunc(pstate, strVal(lfirst(mutator_iter)), - lcons(retval, NIL), - curr_resno); - } - - return (retval); -} - -/* - ** make_arguments -- - ** Given the number and types of arguments to a function, and the - ** actual arguments and argument types, do the necessary typecasting. - */ -static void -make_arguments(int nargs, - List *fargs, - Oid *input_typeids, - Oid *function_typeids) -{ - - /* - * there are two ways an input typeid can differ from a function - * typeid : either the input type inherits the function type, so no - * typecasting is necessary, or the input type can be typecast into - * the function type. right now, we only typecast unknowns, and that - * is all we check for. - */ - - List *current_fargs; - int i; - - for (i = 0, current_fargs = fargs; - i < nargs; - i++, current_fargs = lnext(current_fargs)) - { - - if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != InvalidOid) - { - lfirst(current_fargs) = - parser_typecast2(lfirst(current_fargs), - input_typeids[i], - get_id_type(function_typeids[i]), - -1); - } - } -} - -/* - ** setup_tlist -- - ** Build a tlist that says which attribute to project to. - ** This routine is called by ParseFunc() to set up a target list - ** on a tuple parameter or return value. Due to a bug in 4.0, - ** it's not possible to refer to system attributes in this case. - */ -static List * -setup_tlist(char *attname, Oid relid) -{ - TargetEntry *tle; - Resdom *resnode; - Var *varnode; - Oid typeid; - int attno; - - attno = get_attnum(relid, attname); - if (attno < 0) - elog(WARN, "cannot reference attribute '%s' of tuple params/return values for functions", attname); - - typeid = find_atttype(relid, attname); - resnode = makeResdom(1, - typeid, - tlen(get_id_type(typeid)), - get_attname(relid, attno), - 0, - (Oid) 0, - 0); - varnode = makeVar(-1, attno, typeid, -1, attno); - - tle = makeNode(TargetEntry); - tle->resdom = resnode; - tle->expr = (Node *) varnode; - return (lcons(tle, NIL)); -} - -/* - ** setup_base_tlist -- - ** Build a tlist that extracts a base type from the tuple - ** returned by the executor. - */ -static List * -setup_base_tlist(Oid typeid) -{ - TargetEntry *tle; - Resdom *resnode; - Var *varnode; - - resnode = makeResdom(1, - typeid, - tlen(get_id_type(typeid)), - "<noname>", - 0, - (Oid) 0, - 0); - varnode = makeVar(-1, 1, typeid, -1, 1); - tle = makeNode(TargetEntry); - tle->resdom = resnode; - tle->expr = (Node *) varnode; - - return (lcons(tle, NIL)); -} - -/* - * ParseComplexProjection - - * handles function calls with a single argument that is of complex type. - * This routine returns NULL if it can't handle the projection (eg. sets). - */ -static Node * -ParseComplexProjection(ParseState *pstate, - char *funcname, - Node *first_arg, - bool *attisset) -{ - Oid argtype; - Oid argrelid; - Name relname; - Relation rd; - Oid relid; - int attnum; - - switch (nodeTag(first_arg)) - { - case T_Iter: - { - Func *func; - Iter *iter; - - iter = (Iter *) first_arg; - func = (Func *) ((Expr *) iter->iterexpr)->oper; - argtype = funcid_get_rettype(func->funcid); - argrelid = typeid_get_relid(argtype); - if (argrelid && - ((attnum = get_attnum(argrelid, funcname)) - != InvalidAttrNumber)) - { - - /* - * the argument is a function returning a tuple, so - * funcname may be a projection - */ - - /* add a tlist to the func node and return the Iter */ - rd = heap_openr(tname(get_id_type(argtype))); - if (RelationIsValid(rd)) - { - relid = RelationGetRelationId(rd); - relname = RelationGetRelationName(rd); - heap_close(rd); - } - if (RelationIsValid(rd)) - { - func->func_tlist = - setup_tlist(funcname, argrelid); - iter->itertype = att_typeid(rd, attnum); - return ((Node *) iter); - } - else - { - elog(WARN, - "Function '%s' has bad returntype %d", - funcname, argtype); - } - } - else - { - /* drop through */ - ; - } - break; - } - case T_Var: - { - - /* - * The argument is a set, so this is either a projection - * or a function call on this set. - */ - *attisset = true; - break; - } - case T_Expr: - { - Expr *expr = (Expr *) first_arg; - Func *funcnode; - - if (expr->opType != FUNC_EXPR) - break; - - funcnode = (Func *) expr->oper; - argtype = funcid_get_rettype(funcnode->funcid); - argrelid = typeid_get_relid(argtype); - - /* - * the argument is a function returning a tuple, so - * funcname may be a projection - */ - if (argrelid && - (attnum = get_attnum(argrelid, funcname)) - != InvalidAttrNumber) - { - - /* add a tlist to the func node */ - rd = heap_openr(tname(get_id_type(argtype))); - if (RelationIsValid(rd)) - { - relid = RelationGetRelationId(rd); - relname = RelationGetRelationName(rd); - heap_close(rd); - } - if (RelationIsValid(rd)) - { - Expr *newexpr; - - funcnode->func_tlist = - setup_tlist(funcname, argrelid); - funcnode->functype = att_typeid(rd, attnum); - - newexpr = makeNode(Expr); - newexpr->typeOid = funcnode->functype; - newexpr->opType = FUNC_EXPR; - newexpr->oper = (Node *) funcnode; - newexpr->args = lcons(first_arg, NIL); - - return ((Node *) newexpr); - } - - } - - elog(WARN, "Function '%s' has bad returntype %d", - funcname, argtype); - break; - } - case T_Param: - { - Param *param = (Param *) first_arg; - - /* - * If the Param is a complex type, this could be a - * projection - */ - rd = heap_openr(tname(get_id_type(param->paramtype))); - if (RelationIsValid(rd)) - { - relid = RelationGetRelationId(rd); - relname = RelationGetRelationName(rd); - heap_close(rd); - } - if (RelationIsValid(rd) && - (attnum = get_attnum(relid, funcname)) - != InvalidAttrNumber) - { - - param->paramtype = att_typeid(rd, attnum); - param->param_tlist = setup_tlist(funcname, relid); - return ((Node *) param); - } - break; - } - default: - break; - } - - return NULL; -} - -static Node * -ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno) -{ - Oid rettype = (Oid) 0; - Oid argrelid = (Oid) 0; - Oid funcid = (Oid) 0; - List *i = NIL; - Node *first_arg = NULL; - char *relname = NULL; - char *refname = NULL; - Relation rd; - Oid relid; - int nargs; - Func *funcnode; - Oid oid_array[8]; - Oid *true_oid_array; - Node *retval; - bool retset; - bool exists; - bool attisset = false; - Oid toid = (Oid) 0; - Expr *expr; - - if (fargs) - { - first_arg = lfirst(fargs); - if (first_arg == NULL) - elog(WARN, "function '%s' does not allow NULL input", funcname); - } - - /* - * * check for projection methods: if function takes one argument, and * - * that argument is a relation, param, or PQ function returning a - * complex * type, then the function could be a projection. - */ - if (length(fargs) == 1) - { - - if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel) - { - RangeTblEntry *rte; - Ident *ident = (Ident *) first_arg; - - /* - * first arg is a relation. This could be a projection. - */ - refname = ident->name; - - rte = refnameRangeTableEntry(pstate->p_rtable, refname); - if (rte == NULL) - rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE); - - relname = rte->relname; - relid = rte->relid; - - /* - * If the attr isn't a set, just make a var for it. If it is - * a set, treat it like a function and drop through. - */ - if (get_attnum(relid, funcname) != InvalidAttrNumber) - { - Oid dummyTypeId; - - return - ((Node *) make_var(pstate, - refname, - funcname, - &dummyTypeId)); - } - else - { - /* drop through - attr is a set */ - ; - } - } - else if (ISCOMPLEX(exprType(first_arg))) - { - - /* - * Attempt to handle projection of a complex argument. If - * ParseComplexProjection can't handle the projection, we have - * to keep going. - */ - retval = ParseComplexProjection(pstate, - funcname, - first_arg, - &attisset); - if (attisset) - { - toid = exprType(first_arg); - rd = heap_openr(tname(get_id_type(toid))); - if (RelationIsValid(rd)) - { - relname = RelationGetRelationName(rd)->data; - heap_close(rd); - } - else - elog(WARN, - "Type '%s' is not a relation type", - tname(get_id_type(toid))); - argrelid = typeid_get_relid(toid); - - /* - * A projection contains either an attribute name or the - * "*". - */ - if ((get_attnum(argrelid, funcname) == InvalidAttrNumber) - && strcmp(funcname, "*")) - { - elog(WARN, "Functions on sets are not yet supported"); - } - } - - if (retval) - return retval; - } - else - { - - /* - * Parsing aggregates. - */ - Oid basetype; - - /* - * the aggregate count is a special case, ignore its base - * type. Treat it as zero - */ - if (strcmp(funcname, "count") == 0) - basetype = 0; - else - basetype = exprType(lfirst(fargs)); - if (SearchSysCacheTuple(AGGNAME, - PointerGetDatum(funcname), - ObjectIdGetDatum(basetype), - 0, 0)) - { - Aggreg *aggreg = ParseAgg(funcname, basetype, lfirst(fargs)); - - AddAggToParseState(pstate, aggreg); - return (Node *) aggreg; - } - } - } - - - /* - * * If we dropped through to here it's really a function (or a set, - * which * is implemented as a function.) * extract arg type info and - * transform relation name arguments into * varnodes of the - * appropriate form. - */ - MemSet(&oid_array[0], 0, 8 * sizeof(Oid)); - - nargs = 0; - foreach(i, fargs) - { - int vnum; - RangeTblEntry *rte; - Node *pair = lfirst(i); - - if (nodeTag(pair) == T_Ident && ((Ident *) pair)->isRel) - { - - /* - * a relation - */ - refname = ((Ident *) pair)->name; - - rte = refnameRangeTableEntry(pstate->p_rtable, refname); - if (rte == NULL) - rte = addRangeTableEntry(pstate, refname, refname, - FALSE, FALSE); - relname = rte->relname; - - vnum = refnameRangeTablePosn(pstate->p_rtable, rte->refname); - - /* - * for func(relname), the param to the function is the tuple - * under consideration. we build a special VarNode to reflect - * this -- it has varno set to the correct range table entry, - * but has varattno == 0 to signal that the whole tuple is the - * argument. - */ - toid = typeid(type(relname)); - /* replace it in the arg list */ - lfirst(fargs) = - makeVar(vnum, 0, toid, vnum, 0); - } - else if (!attisset) - { /* set functions don't have parameters */ - - /* - * any functiona args which are typed "unknown", but aren't - * constants, we don't know what to do with, because we can't - * cast them - jolly - */ - if (exprType(pair) == UNKNOWNOID && - !IsA(pair, Const)) - { - elog(WARN, "ParseFunc: no function named '%s' that takes in an unknown type as argument #%d", funcname, nargs); - } - else - toid = exprType(pair); - } - - oid_array[nargs++] = toid; - } - - /* - * func_get_detail looks up the function in the catalogs, does - * disambiguation for polymorphic functions, handles inheritance, and - * returns the funcid and type and set or singleton status of the - * function's return value. it also returns the true argument types - * to the function. if func_get_detail returns true, the function - * exists. otherwise, there was an error. - */ - if (attisset) - { /* we know all of these fields already */ - - /* - * We create a funcnode with a placeholder function SetEval. - * SetEval() never actually gets executed. When the function - * evaluation routines see it, they use the funcid projected out - * from the relation as the actual function to call. Example: - * retrieve (emp.mgr.name) The plan for this will scan the emp - * relation, projecting out the mgr attribute, which is a funcid. - * This function is then called (instead of SetEval) and "name" is - * projected from its result. - */ - funcid = SetEvalRegProcedure; - rettype = toid; - retset = true; - true_oid_array = oid_array; - exists = true; - } - else - { - exists = func_get_detail(funcname, nargs, oid_array, &funcid, - &rettype, &retset, &true_oid_array); - } - - if (!exists) - elog(WARN, "no such attribute or function '%s'", funcname); - - /* got it */ - funcnode = makeNode(Func); - funcnode->funcid = funcid; - funcnode->functype = rettype; - funcnode->funcisindex = false; - funcnode->funcsize = 0; - funcnode->func_fcache = NULL; - funcnode->func_tlist = NIL; - funcnode->func_planlist = NIL; - - /* perform the necessary typecasting */ - make_arguments(nargs, fargs, oid_array, true_oid_array); - - /* - * for functions returning base types, we want to project out the - * return value. set up a target list to do that. the executor will - * ignore these for c functions, and do the right thing for postquel - * functions. - */ - - if (typeid_get_relid(rettype) == InvalidOid) - funcnode->func_tlist = setup_base_tlist(rettype); - - /* - * For sets, we want to make a targetlist to project out this - * attribute of the set tuples. - */ - if (attisset) - { - if (!strcmp(funcname, "*")) - { - funcnode->func_tlist = - expandAll(pstate, relname, refname, curr_resno); - } - else - { - funcnode->func_tlist = setup_tlist(funcname, argrelid); - rettype = find_atttype(argrelid, funcname); - } - } - - /* - * Sequence handling. - */ - if (funcid == SeqNextValueRegProcedure || - funcid == SeqCurrValueRegProcedure) - { - Const *seq; - char *seqrel; - text *seqname; - int32 aclcheck_result = -1; - extern text *lower (text *string); - - Assert(length(fargs) == 1); - seq = (Const *) lfirst(fargs); - if (!IsA((Node *) seq, Const)) - elog(WARN, "%s: only constant sequence names are acceptable", funcname); - seqname = lower ((text*)DatumGetPointer(seq->constvalue)); - pfree (DatumGetPointer(seq->constvalue)); - seq->constvalue = PointerGetDatum (seqname); - seqrel = textout(seqname); - - if ((aclcheck_result = pg_aclcheck(seqrel, GetPgUserName(), - ((funcid == SeqNextValueRegProcedure) ? ACL_WR : ACL_RD))) - != ACLCHECK_OK) - elog(WARN, "%s.%s: %s", - seqrel, funcname, aclcheck_error_strings[aclcheck_result]); - - pfree(seqrel); - - if (funcid == SeqNextValueRegProcedure && inWhereClause) - elog(WARN, "nextval of a sequence in WHERE disallowed"); - } - - expr = makeNode(Expr); - expr->typeOid = rettype; - expr->opType = FUNC_EXPR; - expr->oper = (Node *) funcnode; - expr->args = fargs; - retval = (Node *) expr; - - /* - * if the function returns a set of values, then we need to iterate - * over all the returned values in the executor, so we stick an iter - * node here. if it returns a singleton, then we don't need the iter - * node. - */ - - if (retset) - { - Iter *iter = makeNode(Iter); - - iter->itertype = rettype; - iter->iterexpr = retval; - retval = (Node *) iter; - } - - return (retval); -} - -/***************************************************************************** - * - *****************************************************************************/ - -/* - * AddAggToParseState - - * add the aggregate to the list of unique aggregates in pstate. - * - * SIDE EFFECT: aggno in target list entry will be modified - */ -static void -AddAggToParseState(ParseState *pstate, Aggreg *aggreg) -{ - List *ag; - int i; - - /* - * see if we have the aggregate already (we only need to record the - * aggregate once) - */ - i = 0; - foreach(ag, pstate->p_aggs) - { - Aggreg *a = lfirst(ag); - - if (!strcmp(a->aggname, aggreg->aggname) && - equal(a->target, aggreg->target)) - { - - /* fill in the aggno and we're done */ - aggreg->aggno = i; - return; - } - i++; - } - - /* not found, new aggregate */ - aggreg->aggno = i; - pstate->p_numAgg++; - pstate->p_aggs = lappend(pstate->p_aggs, aggreg); - return; -} - -/* - * finalizeAggregates - - * fill in qry_aggs from pstate. Also checks to make sure that aggregates - * are used in the proper place. - */ -static void -finalizeAggregates(ParseState *pstate, Query *qry) -{ - List *l; - int i; - - parseCheckAggregates(pstate, qry); - - qry->qry_numAgg = pstate->p_numAgg; - qry->qry_aggs = - (Aggreg **) palloc(sizeof(Aggreg *) * qry->qry_numAgg); - i = 0; - foreach(l, pstate->p_aggs) - qry->qry_aggs[i++] = (Aggreg *) lfirst(l); -} - -/* - * contain_agg_clause-- - * Recursively find aggreg nodes from a clause. - * - * Returns true if any aggregate found. - */ -static bool -contain_agg_clause(Node *clause) -{ - if (clause == NULL) - return FALSE; - else if (IsA(clause, Aggreg)) - return TRUE; - else if (IsA(clause, Iter)) - return contain_agg_clause(((Iter *) clause)->iterexpr); - else if (single_node(clause)) - return FALSE; - else if (or_clause(clause)) - { - List *temp; - - foreach(temp, ((Expr *) clause)->args) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - return FALSE; - } - else if (is_funcclause(clause)) - { - List *temp; - - foreach(temp, ((Expr *) clause)->args) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - return FALSE; - } - else if (IsA(clause, ArrayRef)) - { - List *temp; - - foreach(temp, ((ArrayRef *) clause)->refupperindexpr) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - foreach(temp, ((ArrayRef *) clause)->reflowerindexpr) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - if (contain_agg_clause(((ArrayRef *) clause)->refexpr)) - return TRUE; - if (contain_agg_clause(((ArrayRef *) clause)->refassgnexpr)) - return TRUE; - return FALSE; - } - else if (not_clause(clause)) - return contain_agg_clause((Node *) get_notclausearg((Expr *) clause)); - else if (is_opclause(clause)) - return (contain_agg_clause((Node *) get_leftop((Expr *) clause)) || - contain_agg_clause((Node *) get_rightop((Expr *) clause))); - - return FALSE; -} - -/* - * exprIsAggOrGroupCol - - * returns true if the expression does not contain non-group columns. - */ -static bool -exprIsAggOrGroupCol(Node *expr, List *groupClause) -{ - List *gl; - - if (expr == NULL || IsA(expr, Const) || - IsA(expr, Param) ||IsA(expr, Aggreg)) - return TRUE; - - foreach(gl, groupClause) - { - GroupClause *grpcl = lfirst(gl); - - if (equal(expr, grpcl->entry->expr)) - return TRUE; - } - - if (IsA(expr, Expr)) - { - List *temp; - - foreach(temp, ((Expr *) expr)->args) - if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) - return FALSE; - return TRUE; - } - - return FALSE; -} - -/* - * tleIsAggOrGroupCol - - * returns true if the TargetEntry is Agg or GroupCol. - */ -static bool -tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause) -{ - Node *expr = tle->expr; - List *gl; - - if (expr == NULL || IsA(expr, Const) ||IsA(expr, Param)) - return TRUE; - - foreach(gl, groupClause) - { - GroupClause *grpcl = lfirst(gl); - - if (tle->resdom->resno == grpcl->entry->resdom->resno) - { - if (contain_agg_clause((Node *) expr)) - elog(WARN, "parser: aggregates not allowed in GROUP BY clause"); - return TRUE; - } - } - - if (IsA(expr, Aggreg)) - return TRUE; - - if (IsA(expr, Expr)) - { - List *temp; - - foreach(temp, ((Expr *) expr)->args) - if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) - return FALSE; - return TRUE; - } - - return FALSE; -} - -/* - * parseCheckAggregates - - * this should really be done earlier but the current grammar - * cannot differentiate functions from aggregates. So we have do check - * here when the target list and the qualifications are finalized. - */ -static void -parseCheckAggregates(ParseState *pstate, Query *qry) -{ - List *tl; - - Assert(pstate->p_numAgg > 0); - - /* - * aggregates never appear in WHERE clauses. (we have to check where - * clause first because if there is an aggregate, the check for - * non-group column in target list may fail.) - */ - if (contain_agg_clause(qry->qual)) - elog(WARN, "parser: aggregates not allowed in WHERE clause"); - - /* - * the target list can only contain aggregates, group columns and - * functions thereof. - */ - foreach(tl, qry->targetList) - { - TargetEntry *tle = lfirst(tl); - - if (!tleIsAggOrGroupCol(tle, qry->groupClause)) - elog(WARN, - "parser: illegal use of aggregates or non-group column in target list"); - } - - /* - * the expression specified in the HAVING clause has the same - * restriction as those in the target list. - */ -/* - * Need to change here when we get HAVING works. Currently - * qry->havingQual is NULL. - vadim 04/05/97 - if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause)) - elog(WARN, - "parser: illegal use of aggregates or non-group column in HAVING clause"); - */ - return; -} - -/* not used -#define PSIZE(PTR) (*((int32 *)(PTR) - 1)) -*/ - -static Node * -parser_typecast(Value *expr, TypeName *typename, int typlen) -{ - /* check for passing non-ints */ - Const *adt; - Datum lcp; - Type tp; - char type_string[NAMEDATALEN]; - int32 len; - char *cp = NULL; - char *const_string = NULL; - bool string_palloced = false; - - switch (nodeTag(expr)) - { - case T_String: - const_string = DatumGetPointer(expr->val.str); - break; - case T_Integer: - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%ld", expr->val.ival); - break; - default: - elog(WARN, - "parser_typecast: cannot cast this expression to type \"%s\"", - typename->name); - } - - if (typename->arrayBounds != NIL) - { - sprintf(type_string, "_%s", typename->name); - tp = (Type) type(type_string); - } - else - { - tp = (Type) type(typename->name); - } - - len = tlen(tp); - -#if 0 /* fix me */ - switch (CInteger(lfirst(expr))) - { - case INT4OID: /* int4 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%d", ((Const *) lnext(expr))->constvalue); - break; - - case NAMEOID: /* char16 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%s", ((Const *) lnext(expr))->constvalue); - break; - - case CHAROID: /* char */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%c", ((Const) lnext(expr))->constvalue); - break; - - case FLOAT8OID: /* float8 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%f", ((Const) lnext(expr))->constvalue); - break; - - case CASHOID: /* money */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%d", - (int) ((Const *) expr)->constvalue); - break; - - case TEXTOID: /* text */ - const_string = DatumGetPointer(((Const) lnext(expr))->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - - case UNKNOWNOID: /* unknown */ - const_string = DatumGetPointer(((Const) lnext(expr))->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - - default: - elog(WARN, "unknown type %d", CInteger(lfirst(expr))); - } -#endif - - cp = instr2(tp, const_string, typlen); - - if (!tbyvalue(tp)) - { -/* - if (len >= 0 && len != PSIZE(cp)) { - char *pp; - pp = (char *) palloc(len); - memmove(pp, cp, len); - cp = pp; - } -*/ - lcp = PointerGetDatum(cp); - } - else - { - switch (len) - { - case 1: - lcp = Int8GetDatum(cp); - break; - case 2: - lcp = Int16GetDatum(cp); - break; - case 4: - lcp = Int32GetDatum(cp); - break; - default: - lcp = PointerGetDatum(cp); - break; - } - } - - adt = makeConst(typeid(tp), - len, - (Datum) lcp, - false, - tbyvalue(tp), - false, /* not a set */ - true /* is cast */ ); - - if (string_palloced) - pfree(const_string); - - return (Node *) adt; -} - -static Node * -parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen) -{ - /* check for passing non-ints */ - Const *adt; - Datum lcp; - int32 len = tlen(tp); - char *cp = NULL; - - char *const_string = NULL; - bool string_palloced = false; - - Assert(IsA(expr, Const)); - - switch (exprType) - { - case 0: /* NULL */ - break; - case INT4OID: /* int4 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%d", - (int) ((Const *) expr)->constvalue); - break; - case NAMEOID: /* char16 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%s", - (char *) ((Const *) expr)->constvalue); - break; - case CHAROID: /* char */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%c", - (char) ((Const *) expr)->constvalue); - break; - case FLOAT4OID: /* float4 */ - { - float32 floatVal = - DatumGetFloat32(((Const *) expr)->constvalue); - - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%f", *floatVal); - break; - } - case FLOAT8OID: /* float8 */ - { - float64 floatVal = - DatumGetFloat64(((Const *) expr)->constvalue); - - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%f", *floatVal); - break; - } - case CASHOID: /* money */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%ld", - (long) ((Const *) expr)->constvalue); - break; - case TEXTOID: /* text */ - const_string = - DatumGetPointer(((Const *) expr)->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - case UNKNOWNOID: /* unknown */ - const_string = - DatumGetPointer(((Const *) expr)->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - default: - elog(WARN, "unknown type %u ", exprType); - } - - if (!exprType) - { - adt = makeConst(typeid(tp), - (Size) 0, - (Datum) NULL, - true, /* isnull */ - false, /* was omitted */ - false, /* not a set */ - true /* is cast */ ); - return ((Node *) adt); - } - - cp = instr2(tp, const_string, typlen); - - - if (!tbyvalue(tp)) - { -/* - if (len >= 0 && len != PSIZE(cp)) { - char *pp; - pp = (char *) palloc(len); - memmove(pp, cp, len); - cp = pp; - } -*/ - lcp = PointerGetDatum(cp); - } - else - { - switch (len) - { - case 1: - lcp = Int8GetDatum(cp); - break; - case 2: - lcp = Int16GetDatum(cp); - break; - case 4: - lcp = Int32GetDatum(cp); - break; - default: - lcp = PointerGetDatum(cp); - break; - } - } - - adt = makeConst(typeid(tp), - (Size) len, - (Datum) lcp, - false, - false, /* was omitted */ - false, /* not a set */ - true /* is cast */ ); - - /* - * printf("adt %s : %u %d %d\n",CString(expr),typeid(tp) , len,cp); - */ - if (string_palloced) - pfree(const_string); - - return ((Node *) adt); -} - -static Aggreg * -ParseAgg(char *aggname, Oid basetype, Node *target) -{ - Oid fintype; - Oid vartype; - Oid xfn1; - Form_pg_aggregate aggform; - Aggreg *aggreg; - HeapTuple theAggTuple; - - theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname), - ObjectIdGetDatum(basetype), - 0, 0); - if (!HeapTupleIsValid(theAggTuple)) - { - elog(WARN, "aggregate %s does not exist", aggname); - } - - aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple); - fintype = aggform->aggfinaltype; - xfn1 = aggform->aggtransfn1; - - if (nodeTag(target) != T_Var && nodeTag(target) != T_Expr) - elog(WARN, "parser: aggregate can only be applied on an attribute or expression"); - - /* only aggregates with transfn1 need a base type */ - if (OidIsValid(xfn1)) - { - basetype = aggform->aggbasetype; - if (nodeTag(target) == T_Var) - vartype = ((Var *) target)->vartype; - else - vartype = ((Expr *) target)->typeOid; - - if (basetype != vartype) - { - Type tp1, - tp2; - - tp1 = get_id_type(basetype); - tp2 = get_id_type(vartype); - elog(NOTICE, "Aggregate type mismatch:"); - elog(WARN, "%s works on %s, not %s", aggname, - tname(tp1), tname(tp2)); - } - } - - aggreg = makeNode(Aggreg); - aggreg->aggname = pstrdup(aggname); - aggreg->basetype = aggform->aggbasetype; - aggreg->aggtype = fintype; - - aggreg->target = target; - - return aggreg; -} |