1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-17 17:02:08 +03:00

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.
This commit is contained in:
Thomas G. Lockhart
2000-02-15 03:38:29 +00:00
parent 92c8437d8d
commit a344a6e7b5
27 changed files with 1102 additions and 324 deletions

View File

@ -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;
}