diff options
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r-- | src/backend/parser/parse_clause.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c new file mode 100644 index 00000000000..8e08e00a2ea --- /dev/null +++ b/src/backend/parser/parse_clause.c @@ -0,0 +1,407 @@ +/*------------------------------------------------------------------------- + * + * parse_clause.c-- + * handle clauses in parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.1 1997/11/25 22:05:35 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "postgres.h" +#include "access/heapam.h" +#include "parser/parse_clause.h" +#include "parser/parse_expr.h" +#include "parser/parse_node.h" +#include "parser/parse_oper.h" +#include "parser/parse_relation.h" +#include "parser/parse_target.h" +#include "catalog/pg_type.h" + +#ifdef 0 +#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_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 "nodes/makefuncs.h" /* for makeResdom(), etc. */ +#include "nodes/nodeFuncs.h" +#include "commands/sequence.h" + +#include "optimizer/clauses.h" + +#include "miscadmin.h" + +#include "port-protos.h" /* strdup() */ +#endif + +/* + * 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) + * + */ +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. + */ +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 */ +} + +/***************************************************************************** + * + * Where Clause + * + *****************************************************************************/ + +/* + * transformWhereClause - + * transforms the qualification and make sure it is of type Boolean + * + */ +Node * +transformWhereClause(ParseState *pstate, Node *a_expr) +{ + Node *qual; + + if (a_expr == NULL) + return (Node *) NULL; /* no qualifiers */ + + pstate->p_in_where_clause = true; + qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST); + pstate->p_in_where_clause = false; + if (exprType(qual) != BOOLOID) + { + elog(WARN, + "where clause must return type bool, not %s", + typeidTypeName(exprType(qual))); + } + return qual; +} + +/***************************************************************************** + * + * Sort Clause + * + *****************************************************************************/ + +/* + * find_targetlist_entry - + * returns the Resdom in the target list matching the specified varname + * and range + * + */ +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; +} + +/* + * transformGroupClause - + * transform a Group By clause + * + */ +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 + * + */ +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; +} |