From a344a6e7b5d7b2b87f33a155c3ef88bdfdce3fd8 Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" Date: Tue, 15 Feb 2000 03:38:29 +0000 Subject: [PATCH] Carry column aliases from the parser frontend. Enables queries like SELECT a FROM t1 tx (a); Allow join syntax, including queries like SELECT * FROM t1 NATURAL JOIN t2; Update RTE structure to hold column aliases in an Attr structure. --- src/backend/catalog/heap.c | 13 +- src/backend/commands/explain.c | 10 +- src/backend/commands/view.c | 9 +- src/backend/executor/execMain.c | 5 +- src/backend/nodes/copyfuncs.c | 21 +- src/backend/nodes/equalfuncs.c | 22 +- src/backend/nodes/freefuncs.c | 27 +- src/backend/nodes/makefuncs.c | 25 +- src/backend/nodes/outfuncs.c | 20 +- src/backend/nodes/print.c | 9 +- src/backend/nodes/readfuncs.c | 65 +++- src/backend/optimizer/prep/prepunion.c | 8 +- src/backend/optimizer/util/clauses.c | 4 +- src/backend/parser/analyze.c | 49 +-- src/backend/parser/parse_agg.c | 4 +- src/backend/parser/parse_clause.c | 604 ++++++++++++++++++++++++++------- src/backend/parser/parse_expr.c | 61 ++-- src/backend/parser/parse_func.c | 58 +++- src/backend/parser/parse_relation.c | 247 +++++++++++--- src/backend/parser/parse_target.c | 55 ++- src/backend/utils/adt/ruleutils.c | 34 +- src/include/nodes/makefuncs.h | 5 +- src/include/nodes/parsenodes.h | 18 +- src/include/parser/parse_clause.h | 7 +- src/include/parser/parse_node.h | 11 +- src/include/parser/parse_relation.h | 21 +- src/include/parser/parsetree.h | 6 +- 27 files changed, 1098 insertions(+), 320 deletions(-) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 89c20ce5fa..2840e40633 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.120 2000/01/31 04:35:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.121 2000/02/15 03:36:34 thomas Exp $ * * * INTERFACE ROUTINES @@ -53,6 +53,7 @@ #include "optimizer/planmain.h" #include "optimizer/tlist.h" #include "optimizer/var.h" +#include "nodes/makefuncs.h" #include "parser/parse_clause.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" @@ -1719,7 +1720,8 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin, */ rte = makeNode(RangeTblEntry); rte->relname = RelationGetRelationName(rel); - rte->refname = RelationGetRelationName(rel); + rte->ref = makeNode(Attr); + rte->ref->relname = RelationGetRelationName(rel); rte->relid = RelationGetRelid(rel); rte->inh = false; rte->inFromCl = true; @@ -1798,7 +1800,8 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin) */ rte = makeNode(RangeTblEntry); rte->relname = RelationGetRelationName(rel); - rte->refname = RelationGetRelationName(rel); + rte->ref = makeNode(Attr); + rte->ref->relname = RelationGetRelationName(rel); rte->relid = RelationGetRelid(rel); rte->inh = false; rte->inFromCl = true; @@ -1919,8 +1922,8 @@ AddRelationRawConstraints(Relation rel, * its sole rangetable entry. We need a ParseState for transformExpr. */ pstate = make_parsestate(NULL); - makeRangeTable(pstate, NULL, NULL); - addRangeTableEntry(pstate, relname, relname, false, true, true); + makeRangeTable(pstate, NULL); + addRangeTableEntry(pstate, relname, makeAttr(relname, NULL), false, true, true); /* * Process column default expressions. diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 8406416b76..2b152b2fe5 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994-5, Regents of the University of California * - * $Id: explain.c,v 1.52 2000/01/26 05:56:13 momjian Exp $ + * $Id: explain.c,v 1.53 2000/02/15 03:36:39 thomas Exp $ * */ @@ -230,12 +230,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable); appendStringInfo(str, " on "); - if (strcmp(rte->refname, rte->relname) != 0) + if (strcmp(rte->ref->relname, rte->relname) != 0) { appendStringInfo(str, "%s ", stringStringInfo(rte->relname)); } - appendStringInfo(str, stringStringInfo(rte->refname)); + appendStringInfo(str, stringStringInfo(rte->ref->relname)); } break; case T_TidScan: @@ -244,12 +244,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) RangeTblEntry *rte = nth(((TidScan *) plan)->scan.scanrelid - 1, es->rtable); appendStringInfo(str, " on "); - if (strcmp(rte->refname, rte->relname) != 0) + if (strcmp(rte->ref->relname, rte->relname) != 0) { appendStringInfo(str, "%s ", stringStringInfo(rte->relname)); } - appendStringInfo(str, stringStringInfo(rte->refname)); + appendStringInfo(str, stringStringInfo(rte->ref->relname)); } break; default: diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 8742b895df..f4bb18762b 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: view.c,v 1.41 2000/01/26 05:56:14 momjian Exp $ + * $Id: view.c,v 1.42 2000/02/15 03:36:39 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "catalog/heap.h" #include "commands/creatinh.h" #include "commands/view.h" +#include "nodes/makefuncs.h" #include "parser/parse_relation.h" #include "parser/parse_type.h" #include "rewrite/rewriteDefine.h" @@ -225,9 +226,11 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse) * create the 2 new range table entries and form the new range * table... CURRENT first, then NEW.... */ - rt_entry1 = addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*", + rt_entry1 = addRangeTableEntry(NULL, (char *) viewName, + makeAttr("*CURRENT*", NULL), FALSE, FALSE, FALSE); - rt_entry2 = addRangeTableEntry(NULL, (char *) viewName, "*NEW*", + rt_entry2 = addRangeTableEntry(NULL, (char *) viewName, + makeAttr("*NEW*", NULL), FALSE, FALSE, FALSE); new_rt = lcons(rt_entry2, old_rt); new_rt = lcons(rt_entry1, new_rt); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 899885dc31..e3d1862f4e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.108 2000/02/03 00:02:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.109 2000/02/15 03:36:49 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -1508,7 +1508,8 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate) slot->ttc_buffer = InvalidBuffer; slot->ttc_whichplan = -1; rte->relname = RelationGetRelationName(rel); - rte->refname = rte->relname; + rte->ref = makeNode(Attr); + rte->ref->relname = rte->relname; rte->relid = RelationGetRelid(rel); /* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */ rtlist = lcons(rte, NIL); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index c5c9cb0782..adf0c7f198 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.104 2000/02/07 04:40:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.105 2000/02/15 03:37:08 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -688,6 +688,18 @@ _copyVar(Var *from) return newnode; } +static Attr * +_copyAttr(Attr *from) +{ + Attr *newnode = makeNode(Attr); + + if (from->relname) + newnode->relname = pstrdup(from->relname); + Node_Copy(from, newnode, attrs); + + return newnode; +} + /* ---------------- * _copyOper * ---------------- @@ -1327,8 +1339,8 @@ _copyRangeTblEntry(RangeTblEntry *from) if (from->relname) newnode->relname = pstrdup(from->relname); - if (from->refname) - newnode->refname = pstrdup(from->refname); + if (from->ref) + Node_Copy(from, newnode, ref); newnode->relid = from->relid; newnode->inh = from->inh; newnode->inFromCl = from->inFromCl; @@ -1571,6 +1583,9 @@ copyObject(void *from) case T_Var: retval = _copyVar(from); break; + case T_Attr: + retval = _copyAttr(from); + break; case T_Oper: retval = _copyOper(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 5888f49515..3ddc8d6c98 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.59 2000/02/07 04:40:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.60 2000/02/15 03:37:08 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -96,6 +96,17 @@ _equalExpr(Expr *a, Expr *b) } static bool +_equalAttr(Attr *a, Attr *b) +{ + if (!strcmp(a->relname, b->relname)) + return false; + if (length(a->attrs) != length(b->attrs)) + return false; + + return equal(a->attrs, b->attrs); +} + +static bool _equalVar(Var *a, Var *b) { if (a->varno != b->varno) @@ -633,14 +644,14 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) if (a->relname != b->relname) return false; } - if (a->refname && b->refname) + if (a->ref && b->ref) { - if (strcmp(a->refname, b->refname) != 0) + if (! equal(a->ref, b->ref)) return false; } else { - if (a->refname != b->refname) + if (a->ref != b->ref) return false; } if (a->relid != b->relid) @@ -845,6 +856,9 @@ equal(void *a, void *b) case T_EState: retval = _equalEState(a, b); break; + case T_Attr: + retval = _equalAttr(a, b); + break; case T_Integer: case T_String: case T_Float: diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c index ab308fe310..690da02de8 100644 --- a/src/backend/nodes/freefuncs.c +++ b/src/backend/nodes/freefuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.34 2000/02/07 04:40:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.35 2000/02/15 03:37:08 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -1013,8 +1013,19 @@ _freeRangeTblEntry(RangeTblEntry *node) { if (node->relname) pfree(node->relname); - if (node->refname) - pfree(node->refname); + if (node->ref) + freeObject(node->ref); + + pfree(node); +} + +static void +_freeAttr(Attr *node) +{ + if (node->relname) + pfree(node->relname); + if (node->attrs) + freeObject(node->attrs); pfree(node); } @@ -1308,6 +1319,9 @@ freeObject(void *node) case T_TypeCast: _freeTypeCast(node); break; + case T_Attr: + _freeAttr(node); + break; /* * VALUE NODES @@ -1332,3 +1346,10 @@ freeObject(void *node) break; } } + + + + + + + diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 221a83d713..e42930ef87 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.19 2000/01/26 05:56:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.20 2000/02/15 03:37:09 thomas Exp $ * * NOTES * Creator functions in POSTGRES 4.2 are generated automatically. Most of @@ -141,3 +141,26 @@ makeConst(Oid consttype, cnst->constiscast = constiscast; return cnst; } + +/* + * makeAttr - + * creates an Attr node + */ +Attr * +makeAttr(char *relname, char *attname) +{ + Attr *a = makeNode(Attr); + + a->relname = pstrdup(relname); + a->paramNo = NULL; + if (attname != NULL) + a->attrs = lcons(makeString(pstrdup(attname)), NIL); + a->indirection = NULL; + + return a; +} + + + + + diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 8923510e1b..e4c35cc277 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.106 2000/02/07 04:40:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.107 2000/02/15 03:37:09 thomas Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -954,8 +954,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) { appendStringInfo(str, " RTE :relname "); _outToken(str, node->relname); - appendStringInfo(str, " :refname "); - _outToken(str, node->refname); + appendStringInfo(str, " :ref "); + _outNode(str, node->ref); appendStringInfo(str, " :relid %u :inh %s :inFromCl %s :inJoinSet %s :skipAcl %s", node->relid, @@ -1273,18 +1273,10 @@ _outIdent(StringInfo str, Ident *node) static void _outAttr(StringInfo str, Attr *node) { - List *l; - - appendStringInfo(str, " ATTR "); + appendStringInfo(str, " ATTR :relname "); _outToken(str, node->relname); - appendStringInfo(str, " ("); - foreach(l, node->attrs) - { - _outNode(str, lfirst(l)); - if (lnext(l)) - appendStringInfo(str, " "); - } - appendStringInfo(str, ")"); + appendStringInfo(str, " :attrs "); + _outNode(str, node->attrs); } static void diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index ffba95949a..a84b829950 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.35 2000/01/26 05:56:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.36 2000/02/15 03:37:09 thomas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -133,7 +133,7 @@ print_rt(List *rtable) RangeTblEntry *rte = lfirst(l); printf("%d\t%s(%s)\t%u\t%d\t%s\n", - i, rte->relname, rte->refname, rte->relid, + i, rte->relname, rte->ref->relname, rte->relid, rte->inFromCl, (rte->inh ? "inh" : "")); i++; @@ -175,8 +175,9 @@ print_expr(Node *expr, List *rtable) { rt = rt_fetch(var->varno, rtable); relname = rt->relname; - if (rt->refname) - relname = rt->refname; /* table renamed */ + if (rt->ref) + if (rt->ref->relname) + relname = rt->relname; /* table renamed */ attname = get_attname(rt->relid, var->varattno); } break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 38db7def0b..7d56b603b8 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.82 2000/02/07 04:40:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.83 2000/02/15 03:37:09 thomas Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -1322,6 +1322,51 @@ _readTargetEntry() return local_node; } +static List * +_readList() +{ + List *local_node = NULL; + char *token; + int length; + + token = lsptok(NULL, &length); /* eat "(" */ + token = lsptok(NULL, &length); /* get "{" */ + while (strncmp(token, "{", length) == 0) + { + nconc(local_node, nodeRead(true)); + + token = lsptok(NULL, &length); /* eat ")" */ + if (strncmp(token, "}", length) != 0) + elog(ERROR, "badly formatted attribute list" + " in planstring \"%.10s\"...\n", token); + token = lsptok(NULL, &length); /* "{" or ")" */ + } + + return local_node; +} + +static Attr * +_readAttr() +{ + Attr *local_node; + char *token; + int length; + + local_node = makeNode(Attr); + + token = lsptok(NULL, &length); /* eat :relname */ + token = lsptok(NULL, &length); /* get relname */ + if (length == 0) + local_node->relname = pstrdup(""); + else + local_node->relname = debackslash(token, length); + + token = lsptok(NULL, &length); /* eat :attrs */ + local_node->attrs = _readList(); + + return local_node; +} + /* ---------------- * _readRangeTblEntry * ---------------- @@ -1342,12 +1387,8 @@ _readRangeTblEntry() else local_node->relname = debackslash(token, length); - token = lsptok(NULL, &length); /* eat :refname */ - token = lsptok(NULL, &length); /* get :refname */ - if (length == 0) - local_node->refname = NULL; - else - local_node->refname = debackslash(token, length); + token = lsptok(NULL, &length); /* eat :ref */ + local_node->ref = nodeRead(true); token = lsptok(NULL, &length); /* eat :relid */ token = lsptok(NULL, &length); /* get :relid */ @@ -1795,6 +1836,8 @@ parsePlanString(void) return_value = _readArray(); else if (length == 3 && strncmp(token, "VAR", length) == 0) return_value = _readVar(); + else if (length == 4 && strncmp(token, "ATTR", length) == 0) + return_value = _readAttr(); else if (length == 5 && strncmp(token, "CONST", length) == 0) return_value = _readConst(); else if (length == 4 && strncmp(token, "FUNC", length) == 0) @@ -1843,6 +1886,14 @@ parsePlanString(void) return_value = _readCaseWhen(); else if (length == 7 && strncmp(token, "ROWMARK", length) == 0) return_value = _readRowMark(); +#if 0 + else if (length == 1 && strncmp(token, "{", length) == 0) + { + /* raw list (of strings?) found in Attr structure - thomas 2000-02-09 */ + return_value = nodeRead(true); + token = lsptok(NULL, &length); /* eat trailing brace */ + } +#endif else elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token); diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 95005f166f..9a86cb2348 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.43 2000/02/03 06:12:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.44 2000/02/15 03:37:26 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -430,9 +430,9 @@ new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry) RangeTblEntry *new_entry = copyObject(old_entry); /* ??? someone tell me what the following is doing! - ay 11/94 */ - if (!strcmp(new_entry->refname, "*CURRENT*") || - !strcmp(new_entry->refname, "*NEW*")) - new_entry->refname = get_rel_name(new_relid); + if (!strcmp(new_entry->ref->relname, "*CURRENT*") || + !strcmp(new_entry->ref->relname, "*NEW*")) + new_entry->ref->relname = get_rel_name(new_relid); else new_entry->relname = get_rel_name(new_relid); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 53b4eec2cb..664a0dfaaa 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.58 2000/01/26 05:56:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.59 2000/02/15 03:37:36 thomas Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -556,7 +556,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node, elog(ERROR, "cache lookup of attribute %d in relation %u failed", var->varattno, rte->relid); elog(ERROR, "Sub-SELECT uses un-GROUPed attribute %s.%s from outer query", - rte->refname, attname); + rte->ref->relname, attname); } } } diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index a9dda032cd..5f7369af00 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.136 2000/02/05 00:20:38 wieck Exp $ + * $Id: analyze.c,v 1.137 2000/02/15 03:37:47 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -239,13 +239,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) qry->commandType = CMD_DELETE; /* set up a range table */ - makeRangeTable(pstate, NULL, NULL); + makeRangeTable(pstate, NULL); setTargetTable(pstate, stmt->relname); qry->distinctClause = NIL; /* fix where clause */ - qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL); + qry->qual = transformWhereClause(pstate, stmt->whereClause); qry->rtable = pstate->p_rtable; qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL); @@ -266,7 +266,6 @@ static Query * transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { Query *qry = makeNode(Query); - Node *fromQual; List *icolumns; List *attrnos; List *attnos; @@ -289,16 +288,16 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) */ /* set up a range table --- note INSERT target is not in it yet */ - makeRangeTable(pstate, stmt->fromClause, &fromQual); + makeRangeTable(pstate, stmt->fromClause); qry->targetList = transformTargetList(pstate, stmt->targetList); - qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual); + qry->qual = transformWhereClause(pstate, stmt->whereClause); /* Initial processing of HAVING clause is just like WHERE clause. * Additional work will be done in optimizer/plan/planner.c. */ - qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL); + qry->havingQual = transformWhereClause(pstate, stmt->havingClause); qry->groupClause = transformGroupClause(pstate, stmt->groupClause, @@ -974,6 +973,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) * */ if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL) + { if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0) transformFkeyGetPrimaryKey(fkconstraint); else if (pkey != NULL) @@ -997,6 +997,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found", fkconstraint->pktable_name); } + } /* * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK @@ -1207,7 +1208,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt) qry->commandType = CMD_UTILITY; /* take care of the where clause */ - stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL); + stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); + qry->hasSubLinks = pstate->p_hasSubLinks; stmt->rangetable = pstate->p_rtable; @@ -1231,7 +1233,8 @@ transformExtendStmt(ParseState *pstate, ExtendStmt *stmt) qry->commandType = CMD_UTILITY; /* take care of the where clause */ - stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL); + stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); + qry->hasSubLinks = pstate->p_hasSubLinks; stmt->rangetable = pstate->p_rtable; @@ -1268,9 +1271,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt) nothing_qry->commandType = CMD_NOTHING; - addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*", + addRangeTableEntry(pstate, stmt->object->relname, + makeAttr("*CURRENT*", NULL), FALSE, FALSE, FALSE); - addRangeTableEntry(pstate, stmt->object->relname, "*NEW*", + addRangeTableEntry(pstate, stmt->object->relname, + makeAttr("*NEW*", NULL), FALSE, FALSE, FALSE); nothing_qry->rtable = pstate->p_rtable; @@ -1290,9 +1295,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt) * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW' * equal to 2. */ - addRangeTableEntry(pstate, stmt->object->relname, "*CURRENT*", + addRangeTableEntry(pstate, stmt->object->relname, + makeAttr("*CURRENT*", NULL), FALSE, FALSE, FALSE); - addRangeTableEntry(pstate, stmt->object->relname, "*NEW*", + addRangeTableEntry(pstate, stmt->object->relname, + makeAttr("*NEW*", NULL), FALSE, FALSE, FALSE); pstate->p_last_resno = 1; @@ -1306,7 +1313,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt) } /* take care of the where clause */ - stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, NULL); + stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); + qry->hasSubLinks = pstate->p_hasSubLinks; qry->utilityStmt = (Node *) stmt; @@ -1323,12 +1331,11 @@ static Query * transformSelectStmt(ParseState *pstate, SelectStmt *stmt) { Query *qry = makeNode(Query); - Node *fromQual; qry->commandType = CMD_SELECT; /* set up a range table */ - makeRangeTable(pstate, stmt->fromClause, &fromQual); + makeRangeTable(pstate, stmt->fromClause); qry->into = stmt->into; qry->isTemp = stmt->istemp; @@ -1336,12 +1343,12 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) qry->targetList = transformTargetList(pstate, stmt->targetList); - qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual); + qry->qual = transformWhereClause(pstate, stmt->whereClause); /* Initial processing of HAVING clause is just like WHERE clause. * Additional work will be done in optimizer/plan/planner.c. */ - qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL); + qry->havingQual = transformWhereClause(pstate, stmt->havingClause); qry->groupClause = transformGroupClause(pstate, stmt->groupClause, @@ -1401,12 +1408,12 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) * the FROM clause is non-standard SQL syntax. We used to be able to * do this with REPLACE in POSTQUEL so we keep the feature. */ - makeRangeTable(pstate, stmt->fromClause, NULL); + makeRangeTable(pstate, stmt->fromClause); setTargetTable(pstate, stmt->relname); qry->targetList = transformTargetList(pstate, stmt->targetList); - qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL); + qry->qual = transformWhereClause(pstate, stmt->whereClause); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1866,7 +1873,7 @@ transformForUpdate(Query *qry, List *forUpdate) i = 1; foreach(l2, qry->rtable) { - if (strcmp(((RangeTblEntry *) lfirst(l2))->refname, relname) == 0) + if (strcmp(((RangeTblEntry *) lfirst(l2))->ref->relname, relname) == 0) { List *l3; diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 3c23a29246..afaa55e54f 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.33 2000/01/26 05:56:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.34 2000/02/15 03:37:47 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -111,7 +111,7 @@ check_ungrouped_columns_walker(Node *node, elog(ERROR, "cache lookup of attribute %d in relation %u failed", var->varattno, rte->relid); elog(ERROR, "Attribute %s.%s must be GROUPed or used in an aggregate function", - rte->refname, attname); + rte->ref->relname, attname); } /* Otherwise, recurse. */ return expression_tree_walker(node, check_ungrouped_columns_walker, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index b22691fa3c..d0a3eb1edb 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,16 +8,17 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.51 2000/01/27 18:11:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.52 2000/02/15 03:37:47 thomas Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" - #include "access/heapam.h" +#include "miscadmin.h" #include "optimizer/tlist.h" #include "parse.h" +#include "nodes/makefuncs.h" #include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -25,7 +26,6 @@ #include "parser/parse_relation.h" #include "parser/parse_target.h" - #define ORDER_CLAUSE 0 #define GROUP_CLAUSE 1 #define DISTINCT_ON_CLAUSE 2 @@ -34,28 +34,26 @@ static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"}; static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause); -static void parseFromClause(ParseState *pstate, List *frmList, Node **qual); -static char *transformTableEntry(ParseState *pstate, RangeVar *r); +static void parseFromClause(ParseState *pstate, List *frmList); +RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r); static List *addTargetToSortList(TargetEntry *tle, List *sortlist, List *targetlist, char *opname); static bool exprIsInSortList(Node *expr, List *sortList, List *targetList); -#ifdef ENABLE_OUTER_JOINS -static Node *transformUsingClause(ParseState *pstate, List *onList, - char *lname, char *rname); +#ifndef DISABLE_OUTER_JOINS +static Node *transformUsingClause(ParseState *pstate, List *using, List *left, List *right); #endif - /* * makeRangeTable - * Build the initial range table from the FROM clause. */ void -makeRangeTable(ParseState *pstate, List *frmList, Node **qual) +makeRangeTable(ParseState *pstate, List *frmList) { /* Currently, nothing to do except this: */ - parseFromClause(pstate, frmList, qual); + parseFromClause(pstate, frmList); } /* @@ -80,7 +78,8 @@ setTargetTable(ParseState *pstate, char *relname) if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0) || (sublevels_up != 0)) - rte = addRangeTableEntry(pstate, relname, relname, + rte = addRangeTableEntry(pstate, relname, + makeAttr(relname, NULL), FALSE, FALSE, FALSE); else rte = refnameRangeTableEntry(pstate, relname); @@ -94,40 +93,52 @@ setTargetTable(ParseState *pstate, char *relname) /* will close relation later, see analyze.c */ } -/* - * transformWhereClause - - * transforms the qualification and make sure it is of type Boolean - * - * Now accept an additional argument, which is a qualification derived - * from the JOIN/ON or JOIN/USING syntax. - * - thomas 1998-12-16 - */ + Node * -transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr) +mergeInnerJoinQuals(ParseState *pstate, Node *clause); + +Node * +mergeInnerJoinQuals(ParseState *pstate, Node *clause) { - A_Expr *expr; - Node *qual; + A_Expr *expr = (A_Expr *) pstate->p_join_quals; - if ((a_expr == NULL) && (o_expr == NULL)) - return NULL; /* no qualifiers */ + if (expr == NULL) + return clause; - if ((a_expr != NULL) && (o_expr != NULL)) + if (clause != NULL) { A_Expr *a = makeNode(A_Expr); a->oper = AND; a->opname = NULL; - a->lexpr = o_expr; - a->rexpr = a_expr; + a->lexpr = (Node *) expr; + a->rexpr = clause; expr = a; } - else if (o_expr != NULL) - expr = (A_Expr *) o_expr; - else - expr = (A_Expr *) a_expr; + + /* Make sure that we don't do this twice... */ + pstate->p_join_quals = NULL; + + return (Node *) expr; +} /* mergeInnerJoinQuals() */ + +/* + * transformWhereClause - + * transforms the qualification and make sure it is of type Boolean + */ +Node * +transformWhereClause(ParseState *pstate, Node *clause) +{ + Node *qual; + + if (pstate->p_join_quals != NULL) + clause = mergeInnerJoinQuals(pstate, clause); + + if (clause == NULL) + return NULL; pstate->p_in_where_clause = true; - qual = transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST); + qual = transformExpr(pstate, clause, EXPR_COLUMN_FIRST); pstate->p_in_where_clause = false; if (exprType(qual) != BOOLOID) @@ -138,98 +149,259 @@ transformWhereClause(ParseState *pstate, Node *a_expr, Node *o_expr) return qual; } -#ifdef ENABLE_OUTER_JOINS -static Attr * -makeAttr(char *relname, char *attname) +#ifndef DISABLE_JOIN_SYNTAX +char * +AttrString(Attr *attr); + +char * +AttrString(Attr *attr) { - Attr *a = makeNode(Attr); + Value *val; + + Assert(length(attr->attrs) == 1); + + val = lfirst(attr->attrs); - a->relname = relname; - a->paramNo = NULL; - a->attrs = lcons(makeString(attname), NIL); - a->indirection = NULL; + Assert(IsA(val, String)); - return a; + return strVal(val); +} + +List * +ListTableAsAttrs(ParseState *pstate, char *table); +List * +ListTableAsAttrs(ParseState *pstate, char *table) +{ + List *rlist = NULL; + List *col; + + Attr *attr = expandTable(pstate, table, TRUE); + foreach(col, attr->attrs) + { + Attr *a; + a = makeAttr(table, strVal((Value *) col)); + rlist = lappend(rlist, a); + } + + return rlist; +} + +List * +makeUniqueAttrList(List *candidates, List *idents); +List * +makeUniqueAttrList(List *attrs, List *filter) +{ + List *result = NULL; + List *candidate; + + foreach(candidate, attrs) + { + List *fmember; + bool match = FALSE; +// char *field; + Attr *cattr = lfirst(candidate); + + Assert(IsA(cattr, Attr)); + Assert(length(cattr->attrs) == 1); + +// field = strVal(lfirst(ccol)); +// bool match = FALSE; + + foreach(fmember, filter) + { + Attr *fattr = lfirst(fmember); + Assert(IsA(fattr, Attr)); + Assert(length(fattr->attrs) == 1); + + if (strcmp(strVal(lfirst(cattr->attrs)), strVal(lfirst(fattr->attrs))) == 0) + { + match = TRUE; + break; + } + } + + if (!match) + result = lappend(result, cattr); + } + + return result; +} + +List * +makeAttrList(Attr *attr); + +List * +makeAttrList(Attr *attr) +{ + List *result = NULL; + + char *name = attr->relname; + List *col; + + foreach (col, attr->attrs) + { + Attr *newattr = makeAttr(name, strVal((Value *) lfirst(col))); + + result = lappend(result, newattr); + } + + return result; +} + +/* ExpandAttrs() + * Take an existing attribute node and return a list of attribute nodes + * with one attribute name per node. + */ +List * +ExpandAttrs(Attr *attr); +List * +ExpandAttrs(Attr *attr) +{ + List *col; + char *relname = attr->relname; + List *rlist = NULL; + + Assert(attr != NULL); + + if ((attr->attrs == NULL) || (length(attr->attrs) <= 1)) + return lcons(attr, NIL); + + foreach(col, attr->attrs) + { + Attr *attr = lfirst(col); + + rlist = lappend(rlist, makeAttr(relname, AttrString(attr))); + } + + return rlist; } -#endif -#ifdef ENABLE_OUTER_JOINS /* transformUsingClause() * Take an ON or USING clause from a join expression and expand if necessary. */ static Node * -transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname) +transformUsingClause(ParseState *pstate, List *usingList, List *leftList, List *rightList) { A_Expr *expr = NULL; - List *on; - Node *qual; + List *using; - foreach(on, onList) + foreach(using, usingList) { - qual = lfirst(on); + List *col; + A_Expr *e; - /* - * Ident node means it is just a column name from a real USING - * clause... + Attr *uattr = lfirst(using); + Attr *lattr = NULL, *rattr = NULL; + + /* find the first instances of this column in the shape list + * and the last table in the shape list... */ - if (IsA(qual, Ident)) + foreach (col, leftList) { - Ident *i = (Ident *) qual; - Attr *lattr = makeAttr(lname, i->name); - Attr *rattr = makeAttr(rname, i->name); - A_Expr *e = makeNode(A_Expr); - - e->oper = OP; - e->opname = "="; - e->lexpr = (Node *) lattr; - e->rexpr = (Node *) rattr; + Attr *attr = lfirst(col); - if (expr != NULL) + if (strcmp(AttrString(attr), AttrString(uattr)) == 0) { - A_Expr *a = makeNode(A_Expr); + lattr = attr; + break; + } + } + foreach (col, rightList) + { + Attr *attr = lfirst(col); - a->oper = AND; - a->opname = NULL; - a->lexpr = (Node *) expr; - a->rexpr = (Node *) e; - expr = a; + if (strcmp(AttrString(attr), AttrString(uattr)) == 0) + { + rattr = attr; + break; } - else - expr = e; } - /* otherwise, we have an expression from an ON clause... */ - else + Assert((lattr != NULL) && (rattr != NULL)); + + e = makeNode(A_Expr); + e->oper = OP; + e->opname = "="; + e->lexpr = (Node *) lattr; + e->rexpr = (Node *) rattr; + + if (expr != NULL) { - if (expr != NULL) - { - A_Expr *a = makeNode(A_Expr); + A_Expr *a = makeNode(A_Expr); - a->oper = AND; - a->opname = NULL; - a->lexpr = (Node *) expr; - a->rexpr = (Node *) qual; - expr = a; - } - else - expr = (A_Expr *) qual; + a->oper = AND; + a->opname = NULL; + a->lexpr = (Node *) expr; + a->rexpr = (Node *) e; + expr = a; } + else + expr = e; } - return ((Node *) transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST)); -} + return ((Node *) transformExpr(pstate, (Node *) expr, EXPR_COLUMN_FIRST)); +} /* transformUsiongClause() */ #endif -static char * + +RangeTblEntry * transformTableEntry(ParseState *pstate, RangeVar *r) { RelExpr *baserel = r->relExpr; char *relname = baserel->relname; - char *refname = r->name; +#if 0 + char *refname; + List *columns; +#endif RangeTblEntry *rte; - if (refname == NULL) +#if 0 + if (r->name != NULL) + refname = r->name->relname; + else + refname = NULL; + + columns = ListTableAsAttrs(pstate, relname); + + /* alias might be specified... */ + if (r->name != NULL) + { +#ifndef DISABLE_JOIN_SYNTAX + if (length(columns) > 0) + { + if (length(r->name->attrs) > 0) + { + if (length(columns) != length(r->name->attrs)) + elog(ERROR, "'%s' has %d columns but %d %s specified", + relname, length(columns), length(r->name->attrs), + ((length(r->name->attrs) != 1)? "aliases": "alias")); + + aliasList = nconc(aliasList, r->name->attrs); + } + else + { + r->name->attrs = columns; + + aliasList = nconc(aliasList, r->name->attrs); + } + } + else + { + elog(NOTICE, "transformTableEntry: column aliases not handled (internal error)"); + } +#else + elog(ERROR, "Column aliases not yet supported"); +#endif + } + else + { refname = relname; + aliasList = nconc(aliasList, columns); + } +#endif + + if (r->name == NULL) + r->name = makeAttr(relname, NULL); /* * marks this entry to indicate it comes from the FROM clause. In SQL, @@ -242,11 +414,12 @@ transformTableEntry(ParseState *pstate, RangeVar *r) * we expand * to foo.x. */ - rte = addRangeTableEntry(pstate, relname, refname, + rte = addRangeTableEntry(pstate, relname, r->name, baserel->inh, TRUE, TRUE); - return refname; -} + return rte; +} /* transformTableEntry() */ + /* * parseFromClause - @@ -263,12 +436,13 @@ transformTableEntry(ParseState *pstate, RangeVar *r) * - thomas 1998-12-16 */ static void -parseFromClause(ParseState *pstate, List *frmList, Node **qual) +parseFromClause(ParseState *pstate, List *frmList) { - List *fl; +// List *shape, *alias; +// Node **qual; +// char *lname, *rname; - if (qual != NULL) - *qual = NULL; + List *fl; foreach(fl, frmList) { @@ -285,60 +459,258 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual) * eg. select * from foo f where f.x = 1; will generate wrong answer * if we expand * to foo.x. */ + + /* Plain vanilla inner join, just like we've always had? */ if (IsA(n, RangeVar)) + { transformTableEntry(pstate, (RangeVar *) n); + } + + /* A newfangled join expression? */ else if (IsA(n, JoinExpr)) { - JoinExpr *j = (JoinExpr *) n; +#ifndef DISABLE_JOIN_SYNTAX +// char *lname, *rname; + RangeTblEntry *l_rte, *r_rte; + Attr *l_name, *r_name; + JoinExpr *j = (JoinExpr *) n; -#ifdef ENABLE_OUTER_JOINS - char *lname = transformTableEntry(pstate, (RangeVar *) j->larg); + if (j->alias != NULL) + elog(ERROR, "JOIN table aliases are not supported"); -#endif - char *rname; + /* nested join? then handle the left one first... */ + if (IsA(j->larg, JoinExpr)) + { + parseFromClause(pstate, lcons(j->larg, NIL)); + l_name = ((JoinExpr *)j->larg)->alias; + } + else + { + Assert(IsA(j->larg, RangeVar)); + l_rte = transformTableEntry(pstate, (RangeVar *) j->larg); + l_name = expandTable(pstate, l_rte->ref->relname, TRUE); + } - if (IsA((Node *) j->rarg, RangeVar)) - rname = transformTableEntry(pstate, (RangeVar *) j->rarg); + if (IsA(j->rarg, JoinExpr)) + { +// elog(ERROR, "Nested JOINs are not yet supported"); + parseFromClause(pstate, lcons(j->rarg, NIL)); + l_name = ((JoinExpr *)j->larg)->alias; + } else - elog(ERROR, "Nested JOINs are not yet supported"); + { + Assert(IsA(j->rarg, RangeVar)); + r_rte = transformTableEntry(pstate, (RangeVar *) j->rarg); + r_name = expandTable(pstate, r_rte->ref->relname, TRUE); + } + + /* Natural join does not explicitly specify columns; must generate columns to join. + * Need to run through the list of columns from each table or join result + * and match up the column names. Use the first table, and check every + * column in the second table for a match. + */ + if (j->isNatural) + { + List *lx, *rx; + List *rlist = NULL; + + foreach(lx, l_name->attrs) + { + Ident *id = NULL; + Value *l_col = lfirst(lx); + Assert(IsA(l_col, String)); + + foreach(rx, r_name->attrs) + { + Value *r_col = lfirst(rx); + Assert(IsA(r_col, String)); + +// if (equal(l_col, r_col)) + if (strcmp(strVal(l_col), strVal(r_col)) == 0) + { + id = (Ident *) makeNode(Ident); + id->name = strVal(l_col); + break; + } + } + + /* right column matched? then keep as join column... */ + if (id != NULL) + rlist = lappend(rlist, id); + } + j->quals = rlist; + + printf("NATURAL JOIN columns are %s\n", nodeToString(rlist)); + } -#ifdef ENABLE_OUTER_JOINS if (j->jointype == INNER_P) { + /* CROSS JOIN */ + if (j->quals == NULL) + { + printf("CROSS JOIN...\n"); + } - /* + /* JOIN/USING * This is an inner join, so rip apart the join node and * transform into a traditional FROM list. NATURAL JOIN - * and USING clauses both change the shape of the result. + * and JOIN USING both change the shape of the result. * Need to generate a list of result columns to use for - * target list expansion and validation. Not doing this - * yet though! + * target list expansion and validation. */ - if (IsA(j->quals, List)) - j->quals = lcons(transformUsingClause(pstate, (List *) j->quals, lname, rname), NIL); + else if (IsA(j->quals, List)) + { + /* + * List of Ident nodes means column names from a real USING + * clause. Determine the shape of the joined table. + */ +// List *ltable, *rtable; + List *ucols, *ucol; + List *shape = NULL; + List *alias = NULL; + List *l_shape, *r_shape; + + List *l_cols = makeAttrList(l_name); + List *r_cols = makeAttrList(r_name); + + printf("USING input tables are:\n %s\n %s\n", + nodeToString(l_name), nodeToString(r_name)); + + printf("USING expanded tables are:\n %s\n %s\n", + nodeToString(l_cols), nodeToString(r_cols)); + + /* Columns from the USING clause... */ + ucols = (List *)j->quals; + foreach(ucol, ucols) + { + List *col; + Attr *l_attr = NULL, *r_attr = NULL; + Ident *id = lfirst(ucol); + + Attr *attr = makeAttr("", id->name); + + foreach(col, l_cols) + { + attr = lfirst(col); + if (strcmp(AttrString(attr), id->name) == 0) + { + l_attr = attr; + break; + } + } + + foreach(col, r_cols) + { + attr = lfirst(col); + if (strcmp(AttrString(attr), id->name) == 0) + { + r_attr = attr; + break; + } + } + + if (l_attr == NULL) + elog(ERROR, "USING column '%s' not found in table '%s'", + id->name, l_name->relname); + if (r_attr == NULL) + elog(ERROR, "USING column '%s' not found in table '%s'", + id->name, r_name->relname); + + shape = lappend(shape, l_attr); + alias = lappend(alias, makeAttr("", AttrString(l_attr))); + } + printf("JOIN/USING join columns are %s\n", nodeToString(shape)); + + /* Remaining columns from the left side... */ + l_shape = makeUniqueAttrList(makeAttrList(l_name), shape); + printf("JOIN/USING left columns are %s\n", nodeToString(l_shape)); + + r_shape = makeUniqueAttrList(makeAttrList(r_name), shape); + + printf("JOIN/USING right columns are %s\n", nodeToString(r_shape)); + + printf("JOIN/USING input quals are %s\n", nodeToString(j->quals)); + + j->quals = (List *) transformUsingClause(pstate, shape, l_cols, r_cols); + + printf("JOIN/USING transformed quals are %s\n", nodeToString(j->quals)); + + alias = nconc(nconc(alias, listCopy(l_shape)), listCopy(r_shape)); + shape = nconc(nconc(shape, l_shape), r_shape); + + printf("JOIN/USING shaped table is %s\n", nodeToString(shape)); + printf("JOIN/USING alias list is %s\n", nodeToString(alias)); + + pstate->p_shape = shape; + pstate->p_alias = alias; + } + + /* otherwise, must be an expression from an ON clause... */ + else + { + j->quals = (List *) lcons(j->quals, NIL); + } + + pstate->p_join_quals = (Node *) j->quals; + +#if 0 if (qual == NULL) elog(ERROR, "JOIN/ON not supported in this context"); + printf("Table aliases are %s\n", nodeToString(*aliasList)); +#endif + +#if 0 if (*qual == NULL) - *qual = lfirst(j->quals); + { +#endif + +#if 0 + /* merge qualified join clauses... */ + if (j->quals != NULL) + { + if (*qual != NULL) + { + A_Expr *a = makeNode(A_Expr); + + a->oper = AND; + a->opname = NULL; + a->lexpr = (Node *) *qual; + a->rexpr = (Node *) j->quals; + + *qual = (Node *)a; + } + else + { + *qual = (Node *)j->quals; + } + } +#endif + +#if 0 + } else + { elog(ERROR, "Multiple JOIN/ON clauses not handled (internal error)"); + *qual = lappend(*qual, j->quals); + } +#endif /* * if we are transforming this node back into a FROM list, * then we will need to replace the node with two nodes. * Will need access to the previous list item to change * the link pointer to reference these new nodes. Try - * accumulating and returning a new list. - thomas - * 1999-01-08 Not doing this yet though! + * accumulating and returning a new list. + * - thomas 1999-01-08 Not doing this yet though! */ } else if ((j->jointype == LEFT) || (j->jointype == RIGHT) || (j->jointype == FULL)) - elog(ERROR, "OUTER JOIN is not implemented"); + elog(ERROR, "OUTER JOIN is not yet supported"); else elog(ERROR, "Unrecognized JOIN clause; tag is %d (internal error)", j->jointype); @@ -350,7 +722,7 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual) elog(ERROR, "parseFromClause: unexpected FROM clause node (internal error)" "\n\t%s", nodeToString(n)); } -} +} /* parseFromClause() */ /* diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 76e28fbfec..365378f407 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.67 2000/01/26 05:56:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.68 2000/02/15 03:37:47 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -144,12 +144,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) Node *rexpr = transformExpr(pstate, a->rexpr, precedence); if (exprType(lexpr) != BOOLOID) - elog(ERROR, "left-hand side of AND is type '%s', not bool", - typeidTypeName(exprType(lexpr))); + elog(ERROR, "left-hand side of AND is type '%s', not '%s'", + typeidTypeName(exprType(lexpr)), typeidTypeName(BOOLOID)); if (exprType(rexpr) != BOOLOID) - elog(ERROR, "right-hand side of AND is type '%s', not bool", - typeidTypeName(exprType(rexpr))); + elog(ERROR, "right-hand side of AND is type '%s', not '%s'", + typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID)); expr->typeOid = BOOLOID; expr->opType = AND_EXPR; @@ -164,11 +164,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) Node *rexpr = transformExpr(pstate, a->rexpr, precedence); if (exprType(lexpr) != BOOLOID) - elog(ERROR, "left-hand side of OR is type '%s', not bool", - typeidTypeName(exprType(lexpr))); + elog(ERROR, "left-hand side of OR is type '%s', not '%s'", + typeidTypeName(exprType(lexpr)), typeidTypeName(BOOLOID)); if (exprType(rexpr) != BOOLOID) - elog(ERROR, "right-hand side of OR is type '%s', not bool", - typeidTypeName(exprType(rexpr))); + elog(ERROR, "right-hand side of OR is type '%s', not '%s'", + typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID)); expr->typeOid = BOOLOID; expr->opType = OR_EXPR; expr->args = makeList(lexpr, rexpr, -1); @@ -181,8 +181,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) Node *rexpr = transformExpr(pstate, a->rexpr, precedence); if (exprType(rexpr) != BOOLOID) - elog(ERROR, "argument to NOT is type '%s', not bool", - typeidTypeName(exprType(rexpr))); + elog(ERROR, "argument to NOT is type '%s', not '%s'", + typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID)); expr->typeOid = BOOLOID; expr->opType = NOT_EXPR; expr->args = makeList(rexpr, -1); @@ -223,11 +223,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) pstate->p_hasSubLinks = true; qtrees = parse_analyze(lcons(sublink->subselect, NIL), pstate); if (length(qtrees) != 1) - elog(ERROR, "parser: bad query in subselect"); + elog(ERROR, "Bad query in subselect"); qtree = (Query *) lfirst(qtrees); if (qtree->commandType != CMD_SELECT || qtree->resultRelation != 0) - elog(ERROR, "parser: bad query in subselect"); + elog(ERROR, "Bad query in subselect"); sublink->subselect = (Node *) qtree; if (sublink->subLinkType == EXISTS_SUBLINK) @@ -247,11 +247,11 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) */ if (tlist == NIL || ((TargetEntry *) lfirst(tlist))->resdom->resjunk) - elog(ERROR, "parser: subselect must have a field"); + elog(ERROR, "Subselect must have a field"); while ((tlist = lnext(tlist)) != NIL) { if (! ((TargetEntry *) lfirst(tlist))->resdom->resjunk) - elog(ERROR, "parser: subselect must have only one field"); + elog(ERROR, "Subselect must have only one field"); } /* EXPR needs no lefthand or combining operator. * These fields should be NIL already, but make sure. @@ -274,7 +274,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) /* Combining operators other than =/<> is dubious... */ if (length(left_list) != 1 && strcmp(op, "=") != 0 && strcmp(op, "<>") != 0) - elog(ERROR, "parser: '%s' is not usable for row comparison", + elog(ERROR, "Row comparison cannot use '%s'", op); sublink->oper = NIL; @@ -297,7 +297,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) continue; if (left_list == NIL) - elog(ERROR, "parser: Subselect has too many fields."); + elog(ERROR, "Subselect has too many fields"); lexpr = lfirst(left_list); left_list = lnext(left_list); @@ -308,7 +308,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) opform = (Form_pg_operator) GETSTRUCT(optup); if (opform->oprresult != BOOLOID) - elog(ERROR, "parser: '%s' must return 'bool' to be used with quantified predicate subquery", op); + elog(ERROR, "'%s' result type of '%s' must return '%s'" + " to be used with quantified predicate subquery", + op, typeidTypeName(opform->oprresult), + typeidTypeName(BOOLOID)); newop = makeOper(oprid(optup),/* opno */ InvalidOid, /* opid */ @@ -318,7 +321,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) sublink->oper = lappend(sublink->oper, newop); } if (left_list != NIL) - elog(ERROR, "parser: Subselect has too few fields."); + elog(ERROR, "Subselect has too few fields"); } result = (Node *) expr; break; @@ -430,7 +433,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) } else { - elog(ERROR, "CASE/ELSE unable to convert to type %s", + elog(ERROR, "CASE/ELSE unable to convert to type '%s'", typeidTypeName(ptype)); } } @@ -457,7 +460,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) } else { - elog(ERROR, "CASE/WHEN unable to convert to type %s", + elog(ERROR, "CASE/WHEN unable to convert to type '%s'", typeidTypeName(ptype)); } } @@ -519,8 +522,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence) } default: /* should not reach here */ - elog(ERROR, "transformExpr: does not know how to transform node %d", - nodeTag(expr)); + elog(ERROR, "transformExpr: does not know how to transform node %d" + " (internal error)", nodeTag(expr)); break; } @@ -566,18 +569,22 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence) if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL) { /* Convert it to a fully qualified Attr, and transform that */ +#ifndef DISABLE_JOIN_SYNTAX + Attr *att = makeAttr(rte->ref->relname, ident->name); +#else Attr *att = makeNode(Attr); att->relname = rte->refname; att->paramNo = NULL; att->attrs = lcons(makeString(ident->name), NIL); +#endif att->indirection = ident->indirection; return transformAttr(pstate, att, precedence); } } if (result == NULL) - elog(ERROR, "attribute '%s' not found", ident->name); + elog(ERROR, "Attribute '%s' not found", ident->name); return result; } @@ -631,7 +638,7 @@ exprType(Node *expr) TargetEntry *tent; if (! qtree || ! IsA(qtree, Query)) - elog(ERROR, "exprType: can't get type for untransformed sublink"); + elog(ERROR, "Cannot get type for untransformed sublink"); tent = (TargetEntry *) lfirst(qtree->targetList); type = tent->resdom->restype; } @@ -653,7 +660,7 @@ exprType(Node *expr) type = UNKNOWNOID; break; default: - elog(ERROR, "exprType: don't know how to get type for %d node", + elog(ERROR, "Do not know how to get type for %d node", nodeTag(expr)); break; } @@ -728,7 +735,7 @@ parser_typecast_constant(Value *expr, TypeName *typename) break; default: elog(ERROR, - "parser_typecast_constant: cannot cast this expression to type '%s'", + "Cannot cast this expression to type '%s'", typename->name); } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 9686317944..06afc86790 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.68 2000/01/26 05:56:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.69 2000/02/15 03:37:47 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -283,6 +283,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, if (IsA(first_arg, Ident) && ((Ident *) first_arg)->isRel) { RangeTblEntry *rte; + AttrNumber attnum; Ident *ident = (Ident *) first_arg; /* @@ -293,7 +294,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, rte = refnameRangeTableEntry(pstate, refname); if (rte == NULL) { - rte = addRangeTableEntry(pstate, refname, refname, + rte = addRangeTableEntry(pstate, refname, + makeAttr(refname, NULL), FALSE, FALSE, TRUE); #ifdef WARN_FROM elog(NOTICE,"Adding missing FROM-clause entry%s for table %s", @@ -304,12 +306,53 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, relname = rte->relname; relid = rte->relid; + attnum = InvalidAttrNumber; /* * 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. + * Look through the explicit column list first, since we + * now allow column aliases. + * - thomas 2000-02-07 */ - if (get_attnum(relid, funcname) != InvalidAttrNumber) + if (rte->ref->attrs != NULL) + { + List *c; + /* start counting attributes/columns from one. + * zero is reserved for InvalidAttrNumber. + * - thomas 2000-01-27 + */ + int i = 1; + foreach (c, rte->ref->attrs) + { + char *colname = strVal(lfirst(c)); + /* found a match? */ + if (strcmp(colname, funcname) == 0) + { + char *basename = get_attname(relid, i); + + if (basename != NULL) + { + funcname = basename; + attnum = i; + } + /* attnum was initialized to InvalidAttrNumber + * earlier, so no need to reset it if the + * above test fails. - thomas 2000-02-07 + */ + break; + } + i++; + } + if (attnum == InvalidAttrNumber) + attnum = specialAttNum(funcname); + } + else + { + attnum = get_attnum(relid, funcname); + } + + if (attnum != InvalidAttrNumber) { return (Node *) make_var(pstate, relid, @@ -474,7 +517,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, rte = refnameRangeTableEntry(pstate, refname); if (rte == NULL) { - rte = addRangeTableEntry(pstate, refname, refname, + rte = addRangeTableEntry(pstate, refname, + makeAttr(refname, NULL), FALSE, FALSE, TRUE); #ifdef WARN_FROM elog(NOTICE,"Adding missing FROM-clause entry%s for table %s", @@ -485,7 +529,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, relname = rte->relname; - vnum = refnameRangeTablePosn(pstate, rte->refname, NULL); + vnum = refnameRangeTablePosn(pstate, rte->ref->relname, NULL); /* * for func(relname), the param to the function is the tuple @@ -593,7 +637,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, if (attisset) { if (!strcmp(funcname, "*")) - funcnode->func_tlist = expandAll(pstate, relname, refname, curr_resno); + funcnode->func_tlist = expandAll(pstate, relname, + makeAttr(refname, NULL), + curr_resno); else { funcnode->func_tlist = setup_tlist(funcname, argrelid); diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 4e72a7c029..02a3cd2de0 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.34 2000/01/26 05:56:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.35 2000/02/15 03:37:47 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -65,6 +65,39 @@ static char *attnum_type[SPECIALS] = { "cid", }; +/* refnameRangeTableEntries() + * Given refname, return a list of range table entries + * This is possible with JOIN syntax, where tables in a join + * acquire the same reference name + * - thomas 2000-01-20 + */ +List * +refnameRangeTableEntries(ParseState *pstate, char *refname); + +List * +refnameRangeTableEntries(ParseState *pstate, char *refname) +{ + List *rteList = NULL; + List *temp; + + while (pstate != NULL) + { + foreach(temp, pstate->p_rtable) + { + RangeTblEntry *rte = lfirst(temp); + + if (strcmp(rte->ref->relname, refname) == 0) + rteList = lappend(rteList, rte); + } + /* only allow correlated columns in WHERE clause */ + if (pstate->p_in_where_clause) + pstate = pstate->parentParseState; + else + break; + } + return rteList; +} + /* given refname, return a pointer to the range table entry */ RangeTblEntry * refnameRangeTableEntry(ParseState *pstate, char *refname) @@ -77,7 +110,11 @@ refnameRangeTableEntry(ParseState *pstate, char *refname) { RangeTblEntry *rte = lfirst(temp); +#ifndef DISABLE_JOIN_SYNTAX + if (strcmp(rte->ref->relname, refname) == 0) +#else if (!strcmp(rte->refname, refname)) +#endif return rte; } /* only allow correlated columns in WHERE clause */ @@ -106,7 +143,11 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up) { RangeTblEntry *rte = lfirst(temp); +#ifndef DISABLE_JOIN_SYNTAX + if (strcmp(rte->ref->relname, refname) == 0) +#else if (!strcmp(rte->refname, refname)) +#endif return index; index++; } @@ -143,24 +184,52 @@ colnameRangeTableEntry(ParseState *pstate, char *colname) foreach(et, rtable) { + RangeTblEntry *rte_candidate = NULL; RangeTblEntry *rte = lfirst(et); /* only consider RTEs mentioned in FROM or UPDATE/DELETE */ if (!rte->inFromCl && rte != pstate->p_target_rangetblentry) continue; - if (get_attnum(rte->relid, colname) != InvalidAttrNumber) + if (rte->ref->attrs != NULL) { - if (rte_result != NULL) + List *c; + foreach (c, rte->ref->attrs) { - if (!pstate->p_is_insert || - rte != pstate->p_target_rangetblentry) - elog(ERROR, "Column '%s' is ambiguous", colname); + if (strcmp(strVal(lfirst(c)), colname) == 0) + { + if (rte_candidate != NULL) + elog(ERROR, "Column '%s' is ambiguous" + " (internal error)", colname); + rte_candidate = rte; + } } - else - rte_result = rte; } + + /* Even if we have an attribute list in the RTE, + * look for the column here anyway. This is the only + * way we will find implicit columns like "oid". + * - thomas 2000-02-07 + */ + if ((rte_candidate == NULL) + && (get_attnum(rte->relid, colname) != InvalidAttrNumber)) + { + rte_candidate = rte; + } + + if (rte_candidate == NULL) + continue; + + if (rte_result != NULL) + { + if (!pstate->p_is_insert || + rte != pstate->p_target_rangetblentry) + elog(ERROR, "Column '%s' is ambiguous", colname); + } + else + rte_result = rte; } + /* only allow correlated columns in WHERE clause */ if (pstate->p_in_where_clause && rte_result == NULL) pstate = pstate->parentParseState; @@ -177,45 +246,65 @@ colnameRangeTableEntry(ParseState *pstate, char *colname) RangeTblEntry * addRangeTableEntry(ParseState *pstate, char *relname, - char *refname, + Attr *ref, bool inh, bool inFromCl, bool inJoinSet) { - Relation relation; - RangeTblEntry *rte; - int sublevels_up; + Relation rel; + RangeTblEntry *rte; + int maxattrs; + int sublevels_up; + int varattno; + /* Look for an existing rte, if available... */ if (pstate != NULL) { - int rt_index = refnameRangeTablePosn(pstate, refname, - &sublevels_up); + int rt_index = refnameRangeTablePosn(pstate, ref->relname, + &sublevels_up); if (rt_index != 0 && (!inFromCl || sublevels_up == 0)) { - if (!strcmp(refname, "*CURRENT*") || !strcmp(refname, "*NEW*")) + if (!strcmp(ref->relname, "*CURRENT*") || !strcmp(ref->relname, "*NEW*")) return (RangeTblEntry *) nth(rt_index - 1, pstate->p_rtable); - elog(ERROR, "Table name '%s' specified more than once", refname); + elog(ERROR, "Table name '%s' specified more than once", ref->relname); } } rte = makeNode(RangeTblEntry); - rte->relname = pstrdup(relname); - rte->refname = pstrdup(refname); + rte->relname = relname; + rte->ref = ref; /* Get the rel's OID. This access also ensures that we have an * up-to-date relcache entry for the rel. We don't need to keep * it open, however. + * Since this is open anyway, let's check that the number of column + * aliases is reasonable. + * - Thomas 2000-02-04 */ - relation = heap_openr(relname, AccessShareLock); - rte->relid = RelationGetRelid(relation); - heap_close(relation, AccessShareLock); + rel = heap_openr(relname, AccessShareLock); + rte->relid = RelationGetRelid(rel); + maxattrs = RelationGetNumberOfAttributes(rel); + if (maxattrs < length(ref->attrs)) + elog(ERROR, "Table '%s' has %d columns available but %d columns specified", + relname, maxattrs, length(ref->attrs)); + + /* fill in any unspecified alias columns */ + for (varattno = length(ref->attrs); varattno < maxattrs; varattno++) + { + char *attrname; + + attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname)); + ref->attrs = lappend(ref->attrs, makeString(attrname)); + } + heap_close(rel, AccessShareLock); /* - * Flags: this RTE should be expanded to include descendant tables, - * this RTE is in the FROM clause, this RTE should be included in - * the planner's final join. + * Flags: + * - this RTE should be expanded to include descendant tables, + * - this RTE is in the FROM clause, + * - this RTE should be included in the planner's final join. */ rte->inh = inh; rte->inFromCl = inFromCl; @@ -231,23 +320,71 @@ addRangeTableEntry(ParseState *pstate, return rte; } +/* expandTable() + * Populates an Attr with table name and column names + * This is similar to expandAll(), but does not create an RTE + * if it does not already exist. + * - thomas 2000-01-19 + */ +Attr * +expandTable(ParseState *pstate, char *refname, bool getaliases) +{ + Attr *attr; + RangeTblEntry *rte; + Relation rel; + int varattno, + maxattrs; + + rte = refnameRangeTableEntry(pstate, refname); + + if (getaliases && (rte != NULL) && (rte->ref != NULL) + && (length(rte->ref->attrs) > 0)) + { + return rte->ref; + } + + if (rte != NULL) + rel = heap_open(rte->relid, AccessShareLock); + else + rel = heap_openr(refname, AccessShareLock); + + if (rel == NULL) + elog(ERROR, "Relation '%s' not found", refname); + + maxattrs = RelationGetNumberOfAttributes(rel); + + attr = makeAttr(refname, NULL); + + for (varattno = 0; varattno < maxattrs; varattno++) + { + char *attrname; + + attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname)); + attr->attrs = lappend(attr->attrs, makeString(attrname)); + } + + heap_close(rel, AccessShareLock); + + return attr; +} + /* * expandAll - * makes a list of attributes */ List * -expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno) +expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno) { - List *te_list = NIL; - RangeTblEntry *rte; - Relation rel; - int varattno, - maxattrs; + List *te_list = NIL; + RangeTblEntry *rte; + Relation rel; + int varattno, + maxattrs; - rte = refnameRangeTableEntry(pstate, refname); + rte = refnameRangeTableEntry(pstate, ref->relname); if (rte == NULL) { - rte = addRangeTableEntry(pstate, relname, refname, + rte = addRangeTableEntry(pstate, relname, ref, FALSE, FALSE, TRUE); #ifdef WARN_FROM elog(NOTICE,"Adding missing FROM-clause entry%s for table %s", @@ -262,12 +399,19 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno) for (varattno = 0; varattno < maxattrs; varattno++) { - char *attrname; - Var *varnode; - TargetEntry *te = makeNode(TargetEntry); + char *attrname; + char *label; + Var *varnode; + TargetEntry *te = makeNode(TargetEntry); attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname)); - varnode = make_var(pstate, rte->relid, refname, attrname); + + /* varattno is zero-based, so check that length() is always greater */ + if (length(rte->ref->attrs) > varattno) + label = pstrdup(strVal(nth(varattno, rte->ref->attrs))); + else + label = attrname; + varnode = make_var(pstate, rte->relid, relname, attrname); /* * Even if the elements making up a set are complex, the set @@ -277,7 +421,7 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno) te->resdom = makeResdom((AttrNumber) (*this_resno)++, varnode->vartype, varnode->vartypmod, - attrname, + label, (Index) 0, (Oid) 0, false); @@ -306,16 +450,32 @@ attnameAttNum(Relation rd, char *a) if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) return i + 1; - for (i = 0; i < SPECIALS; i++) - if (!strcmp(special_attr[i].field, a)) - return special_attr[i].code; + if ((i = specialAttNum(a)) != InvalidAttrNumber) + return i; /* on failure */ elog(ERROR, "Relation '%s' does not have attribute '%s'", RelationGetRelationName(rd), a); - return 0; /* lint */ + return InvalidAttrNumber; /* lint */ } +/* specialAttNum() + * Check attribute name to see if it is "special", e.g. "oid". + * - thomas 2000-02-07 + */ +int +specialAttNum(char *a) +{ + int i; + + for (i = 0; i < SPECIALS; i++) + if (!strcmp(special_attr[i].field, a)) + return special_attr[i].code; + + return InvalidAttrNumber; +} + + /* * Given range variable, return whether attribute of this name * is a set. @@ -372,3 +532,8 @@ attnumTypeId(Relation rd, int attid) */ return rd->rd_att->attrs[attid - 1]->atttypid; } + + + + + diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 653afe7058..9d00e1789e 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.54 2000/01/26 05:56:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.55 2000/02/15 03:37:47 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -105,8 +105,35 @@ transformTargetList(ParseState *pstate, List *targetlist) * Target item is a single '*', expand all tables * (eg. SELECT * FROM emp) */ - p_target = nconc(p_target, - ExpandAllTables(pstate)); + if (pstate->p_shape != NULL) + { + List *s, *a; + int i; + + Assert(length(pstate->p_shape) == length(pstate->p_alias)); + + s = pstate->p_shape; + a = pstate->p_alias; + for (i = 0; i < length(pstate->p_shape); i++) + { + TargetEntry *te; + char *colname; + Attr *shape = lfirst(s); + Attr *alias = lfirst(a); + + Assert(IsA(shape, Attr) && IsA(alias, Attr)); + + colname = strVal(lfirst(alias->attrs)); + te = transformTargetEntry(pstate, (Node *) shape, + NULL, colname, false); + p_target = lappend(p_target, te); + s = lnext(s); + a = lnext(a); + } + } + else + p_target = nconc(p_target, + ExpandAllTables(pstate)); } else if (att->attrs != NIL && strcmp(strVal(lfirst(att->attrs)), "*") == 0) @@ -116,9 +143,8 @@ transformTargetList(ParseState *pstate, List *targetlist) * (eg. SELECT emp.*, dname FROM emp, dept) */ p_target = nconc(p_target, - expandAll(pstate, - att->relname, - att->relname, + expandAll(pstate, att->relname, + makeAttr(att->relname, NULL), &pstate->p_last_resno)); } else @@ -192,12 +218,18 @@ updateTargetListEntry(ParseState *pstate, */ if (indirection) { +#ifndef DISABLE_JOIN_SYNTAX + Attr *att = makeAttr(pstrdup(RelationGetRelationName(rd)), colname); +#else Attr *att = makeNode(Attr); +#endif Node *arrayBase; ArrayRef *aref; +#ifdef DISABLE_JOIN_SYNTAX att->relname = pstrdup(RelationGetRelationName(rd)); att->attrs = lcons(makeString(colname), NIL); +#endif arrayBase = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno, EXPR_COLUMN_FIRST); @@ -355,10 +387,9 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) return cols; } -/* - * ExpandAllTables - - * turns '*' (in the target list) into a list of attributes - * (of all relations in the range table) +/* ExpandAllTables() + * Turns '*' (in the target list) into a list of attributes + * (of all relations in the range table) */ static List * ExpandAllTables(ParseState *pstate) @@ -378,7 +409,7 @@ ExpandAllTables(ParseState *pstate) /* SELECT *; */ if (rtable == NIL) - elog(ERROR, "Wildcard with no tables specified."); + elog(ERROR, "Wildcard with no tables specified not allowed"); foreach(rt, rtable) { @@ -393,7 +424,7 @@ ExpandAllTables(ParseState *pstate) continue; target = nconc(target, - expandAll(pstate, rte->relname, rte->refname, + expandAll(pstate, rte->ref->relname, rte->ref, &pstate->p_last_resno)); } return target; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c4e3149160..d3c2534505 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * out of its tuple * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.39 2000/01/15 22:43:24 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.40 2000/02/15 03:37:56 thomas Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -922,9 +922,9 @@ get_select_query_def(Query *query, deparse_context *context) continue; rte = (RangeTblEntry *) lfirst(l); - if (!strcmp(rte->refname, "*NEW*")) + if (!strcmp(rte->ref->relname, "*NEW*")) continue; - if (!strcmp(rte->refname, "*CURRENT*")) + if (!strcmp(rte->ref->relname, "*CURRENT*")) continue; rt_constonly = FALSE; @@ -980,10 +980,10 @@ get_select_query_def(Query *query, deparse_context *context) { rte = (RangeTblEntry *) lfirst(l); - if (!strcmp(rte->refname, "*NEW*")) + if (!strcmp(rte->ref->relname, "*NEW*")) continue; - if (!strcmp(rte->refname, "*CURRENT*")) + if (!strcmp(rte->ref->relname, "*CURRENT*")) continue; appendStringInfo(buf, sep); @@ -991,9 +991,19 @@ get_select_query_def(Query *query, deparse_context *context) appendStringInfo(buf, "%s%s", quote_identifier(rte->relname), inherit_marker(rte)); - if (strcmp(rte->relname, rte->refname) != 0) + if (strcmp(rte->relname, rte->ref->relname) != 0) + { + List *col; appendStringInfo(buf, " %s", - quote_identifier(rte->refname)); + quote_identifier(rte->ref->relname)); + appendStringInfo(buf, " ("); + foreach (col, rte->ref->attrs) + { + if (col != lfirst(rte->ref->attrs)) + appendStringInfo(buf, ", "); + appendStringInfo(buf, "%s", strVal(col)); + } + } } } } @@ -1071,9 +1081,9 @@ get_insert_query_def(Query *query, deparse_context *context) continue; rte = (RangeTblEntry *) lfirst(l); - if (!strcmp(rte->refname, "*NEW*")) + if (!strcmp(rte->ref->relname, "*NEW*")) continue; - if (!strcmp(rte->refname, "*CURRENT*")) + if (!strcmp(rte->ref->relname, "*CURRENT*")) continue; rt_constonly = FALSE; @@ -1241,13 +1251,13 @@ get_rule_expr(Node *node, deparse_context *context) if (context->varprefix) { - if (!strcmp(rte->refname, "*NEW*")) + if (!strcmp(rte->ref->relname, "*NEW*")) appendStringInfo(buf, "new."); - else if (!strcmp(rte->refname, "*CURRENT*")) + else if (!strcmp(rte->ref->relname, "*CURRENT*")) appendStringInfo(buf, "old."); else appendStringInfo(buf, "%s.", - quote_identifier(rte->refname)); + quote_identifier(rte->ref->relname)); } appendStringInfo(buf, "%s", quote_identifier(get_attribute_name(rte->relid, diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 2b3c5339c6..f934eb5e63 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: makefuncs.h,v 1.22 2000/01/26 05:58:16 momjian Exp $ + * $Id: makefuncs.h,v 1.23 2000/02/15 03:38:13 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -46,4 +46,7 @@ extern Const *makeConst(Oid consttype, bool constisset, bool constiscast); +extern Attr * +makeAttr(char *relname, char *attname); + #endif /* MAKEFUNC_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 288e7f96b8..6eb47618c5 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.97 2000/01/27 18:11:44 tgl Exp $ + * $Id: parsenodes.h,v 1.98 2000/02/15 03:38:14 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -1031,7 +1031,7 @@ typedef struct RangeVar { NodeTag type; RelExpr *relExpr; /* the relation expression */ - char *name; /* the name to be referenced (optional) */ + Attr *name; /* the name to be referenced (optional) */ } RangeVar; /* @@ -1064,9 +1064,11 @@ typedef struct JoinExpr { NodeTag type; int jointype; - RangeVar *larg; - Node *rarg; - List *quals; + bool isNatural; /* Natural join? Will need to shape table */ + Node *larg; /* RangeVar or join expression */ + Node *rarg; /* RangeVar or join expression */ + Attr *alias; /* table and column aliases, if any */ + List *quals; /* qualifiers on join, if any */ } JoinExpr; @@ -1122,8 +1124,10 @@ typedef struct RangeTblEntry { NodeTag type; char *relname; /* real name of the relation */ - char *refname; /* the reference name (as specified in the - * FROM clause) */ +// char *refname; /* reference name (given in FROM clause) */ +#ifndef DISABLE_JOIN_SYNTAX + Attr *ref; /* reference names (given in FROM clause) */ +#endif Oid relid; /* OID of the relation */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause */ diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h index 235a02bc4c..58b8fc60fa 100644 --- a/src/include/parser/parse_clause.h +++ b/src/include/parser/parse_clause.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_clause.h,v 1.15 2000/01/27 18:11:47 tgl Exp $ + * $Id: parse_clause.h,v 1.16 2000/02/15 03:38:28 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -16,10 +16,9 @@ #include "parser/parse_node.h" -extern void makeRangeTable(ParseState *pstate, List *frmList, Node **qual); +extern void makeRangeTable(ParseState *pstate, List *frmList); extern void setTargetTable(ParseState *pstate, char *relname); -extern Node *transformWhereClause(ParseState *pstate, Node *where, - Node *using); +extern Node *transformWhereClause(ParseState *pstate, Node *where); extern List *transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist); extern List *transformSortClause(ParseState *pstate, List *orderlist, diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 4ba502ebaf..16641b530a 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_node.h,v 1.17 2000/01/26 05:58:27 momjian Exp $ + * $Id: parse_node.h,v 1.18 2000/02/15 03:38:29 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,11 @@ #include "nodes/parsenodes.h" #include "utils/rel.h" -/* state information used during parse analysis */ +/* State information used during parse analysis + * p_join_quals is a list of qualification expressions + * found in the FROM clause. Needs to be available later + * to merge with other qualifiers from the WHERE clause. + */ typedef struct ParseState { int p_last_resno; @@ -30,6 +34,9 @@ typedef struct ParseState bool p_in_where_clause; Relation p_target_relation; RangeTblEntry *p_target_rangetblentry; + List *p_shape; + List *p_alias; + Node *p_join_quals; } ParseState; extern ParseState *make_parsestate(ParseState *parentParseState); diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index b9fe0b1b77..5ba5db3f9d 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_relation.h,v 1.14 2000/01/26 05:58:27 momjian Exp $ + * $Id: parse_relation.h,v 1.15 2000/02/15 03:38:29 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -18,17 +18,20 @@ extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate, char *refname); extern int refnameRangeTablePosn(ParseState *pstate, - char *refname, int *sublevels_up); + char *refname, + int *sublevels_up); extern RangeTblEntry *colnameRangeTableEntry(ParseState *pstate, char *colname); extern RangeTblEntry *addRangeTableEntry(ParseState *pstate, - char *relname, - char *refname, - bool inh, - bool inFromCl, - bool inJoinSet); -extern List *expandAll(ParseState *pstate, char *relname, char *refname, - int *this_resno); + char *relname, + Attr *ref, + bool inh, + bool inFromCl, + bool inJoinSet); +extern Attr *expandTable(ParseState *pstate, char *refname, bool getaliases); +extern List *expandAll(ParseState *pstate, char *relname, Attr *ref, + int *this_resno); extern int attnameAttNum(Relation rd, char *a); +extern int specialAttNum(char *a); extern bool attnameIsSet(Relation rd, char *name); extern int attnumAttNelems(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h index 979ebf327e..3f5e09cc1d 100644 --- a/src/include/parser/parsetree.h +++ b/src/include/parser/parsetree.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsetree.h,v 1.8 2000/01/26 05:58:27 momjian Exp $ + * $Id: parsetree.h,v 1.9 2000/02/15 03:38:29 thomas Exp $ * *------------------------------------------------------------------------- */ @@ -39,8 +39,8 @@ */ #define rt_relname(rt_entry) \ - ((!strcmp(((rt_entry)->refname),"*CURRENT*") ||\ - !strcmp(((rt_entry)->refname),"*NEW*")) ? ((rt_entry)->refname) : \ + ((!strcmp(((rt_entry)->ref->relname),"*CURRENT*") ||\ + !strcmp(((rt_entry)->ref->relname),"*NEW*")) ? ((rt_entry)->ref->relname) : \ ((char *)(rt_entry)->relname)) /* -- 2.11.0