aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_relation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r--src/backend/parser/parse_relation.c247
1 files changed, 206 insertions, 41 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 4e72a7c029b..02a3cd2de09 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;
}
+
+
+
+
+