1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-05 09:19:17 +03:00
postgres/src/backend/parser/parse_relation.c
Tom Lane 62e29fe2e7 Remove 'func_tlist' from Func expression nodes, likewise 'param_tlist'
from Param nodes, per discussion a few days ago on pghackers.  Add new
expression node type FieldSelect that implements the functionality where
it's actually needed.  Clean up some other unused fields in Func nodes
as well.
NOTE: initdb forced due to change in stored expression trees for rules.
2000-08-08 15:43:12 +00:00

556 lines
12 KiB
C

/*-------------------------------------------------------------------------
*
* parse_relation.c
* parser support routines dealing with relations
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.46 2000/08/08 15:42:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include "postgres.h"
#include "access/heapam.h"
#include "access/htup.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
/*
* Information defining the "system" attributes of every relation.
*/
static struct
{
char *attrname; /* name of system attribute */
int attrnum; /* its attribute number (always < 0) */
Oid attrtype; /* its type id */
} special_attr[] =
{
{
"ctid", SelfItemPointerAttributeNumber, TIDOID
},
{
"oid", ObjectIdAttributeNumber, OIDOID
},
{
"xmin", MinTransactionIdAttributeNumber, XIDOID
},
{
"cmin", MinCommandIdAttributeNumber, CIDOID
},
{
"xmax", MaxTransactionIdAttributeNumber, XIDOID
},
{
"cmax", MaxCommandIdAttributeNumber, CIDOID
},
{
"tableoid", TableOidAttributeNumber, OIDOID
}
};
#define SPECIALS ((int) (sizeof(special_attr)/sizeof(special_attr[0])))
#ifdef NOT_USED
/* 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
* But at the moment we aren't carrying along a full list of
* table/column aliases, so we don't have the full mechanism
* to support outer joins in place yet.
* - thomas 2000-03-04
*/
static 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->eref->relname, refname) == 0)
rteList = lappend(rteList, rte);
}
pstate = pstate->parentParseState;
}
return rteList;
}
#endif
/* given refname, return a pointer to the range table entry */
RangeTblEntry *
refnameRangeTableEntry(ParseState *pstate, char *refname)
{
List *temp;
while (pstate != NULL)
{
foreach(temp, pstate->p_rtable)
{
RangeTblEntry *rte = lfirst(temp);
if (strcmp(rte->eref->relname, refname) == 0)
return rte;
}
pstate = pstate->parentParseState;
}
return NULL;
}
/* given refname, return RT index (starting with 1) of the relation,
* and optionally get its nesting depth (0 = current). If sublevels_up
* is NULL, only consider rels at the current nesting level.
*/
int
refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
{
int index;
List *temp;
if (sublevels_up)
*sublevels_up = 0;
while (pstate != NULL)
{
index = 1;
foreach(temp, pstate->p_rtable)
{
RangeTblEntry *rte = lfirst(temp);
if (strcmp(rte->eref->relname, refname) == 0)
return index;
index++;
}
pstate = pstate->parentParseState;
if (sublevels_up)
(*sublevels_up)++;
else
break;
}
return 0;
}
/*
* returns range entry if found, else NULL
*/
RangeTblEntry *
colnameRangeTableEntry(ParseState *pstate, char *colname)
{
List *et;
List *rtable;
RangeTblEntry *rte_result = NULL;
while (pstate != NULL)
{
if (pstate->p_is_rule)
rtable = lnext(lnext(pstate->p_rtable));
else
rtable = pstate->p_rtable;
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 (rte->eref->attrs != NULL)
{
List *c;
foreach(c, rte->ref->attrs)
{
if (strcmp(strVal(lfirst(c)), colname) == 0)
{
if (rte_candidate != NULL)
elog(ERROR, "Column '%s' is ambiguous"
" (internal error)", colname);
rte_candidate = 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;
}
if (rte_result != NULL)
break; /* found */
pstate = pstate->parentParseState;
}
return rte_result;
}
/*
* put new entry in pstate p_rtable structure, or return pointer
* if pstate null
*/
RangeTblEntry *
addRangeTableEntry(ParseState *pstate,
char *relname,
Attr *ref,
bool inh,
bool inFromCl,
bool inJoinSet)
{
Relation rel;
RangeTblEntry *rte;
Attr *eref;
int maxattrs;
int sublevels_up;
int varattno;
/* Look for an existing rte, if available... */
if (pstate != NULL)
{
int rt_index = refnameRangeTablePosn(pstate, ref->relname,
&sublevels_up);
if (rt_index != 0 && (!inFromCl || sublevels_up == 0))
{
if (!strcmp(ref->relname, "*OLD*") || !strcmp(ref->relname, "*NEW*"))
return (RangeTblEntry *) nth(rt_index - 1, pstate->p_rtable);
elog(ERROR, "Table name '%s' specified more than once", ref->relname);
}
}
rte = makeNode(RangeTblEntry);
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
*/
rel = heap_openr(relname, AccessShareLock);
rte->relid = RelationGetRelid(rel);
maxattrs = RelationGetNumberOfAttributes(rel);
eref = copyObject(ref);
if (maxattrs < length(eref->attrs))
elog(ERROR, "Table '%s' has %d columns available but %d columns specified",
relname, maxattrs, length(eref->attrs));
/* fill in any unspecified alias columns */
for (varattno = length(eref->attrs); varattno < maxattrs; varattno++)
{
char *attrname;
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
eref->attrs = lappend(eref->attrs, makeString(attrname));
}
heap_close(rel, AccessShareLock);
rte->eref = eref;
/*
* 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;
rte->inJoinSet = inJoinSet;
rte->skipAcl = false; /* always starts out false */
/*
* Add completed RTE to range table list.
*/
if (pstate != NULL)
pstate->p_rtable = lappend(pstate->p_rtable, rte);
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))
return rte->eref;
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;
#ifdef _DROP_COLUMN_HACK__
if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
continue;
#endif /* _DROP_COLUMN_HACK__ */
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, Attr *ref, int *this_resno)
{
List *te_list = NIL;
RangeTblEntry *rte;
Relation rel;
int varattno,
maxattrs;
rte = refnameRangeTableEntry(pstate, ref->relname);
if (rte == NULL)
{
rte = addRangeTableEntry(pstate, relname, ref,
FALSE, FALSE, TRUE);
warnAutoRange(pstate, ref->relname);
}
rel = heap_open(rte->relid, AccessShareLock);
maxattrs = RelationGetNumberOfAttributes(rel);
for (varattno = 0; varattno < maxattrs; varattno++)
{
char *attrname;
char *label;
Var *varnode;
TargetEntry *te = makeNode(TargetEntry);
#ifdef _DROP_COLUMN_HACK__
if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
continue;
#endif /* _DROP_COLUMN_HACK__ */
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
/*
* varattno is zero-based, so check that length() is always
* greater
*/
if (length(rte->eref->attrs) > varattno)
label = pstrdup(strVal(nth(varattno, rte->eref->attrs)));
else
label = attrname;
varnode = make_var(pstate, rte->relid, relname, attrname);
/*
* Even if the elements making up a set are complex, the set
* itself is not.
*/
te->resdom = makeResdom((AttrNumber) (*this_resno)++,
varnode->vartype,
varnode->vartypmod,
label,
false);
te->expr = (Node *) varnode;
te_list = lappend(te_list, te);
}
heap_close(rel, AccessShareLock);
return te_list;
}
/*
* given relation and att name, return id of variable
*
* This should only be used if the relation is already
* heap_open()'ed. Use the cache version get_attnum()
* for access to non-opened relations.
*/
int
attnameAttNum(Relation rd, char *a)
{
int i;
for (i = 0; i < rd->rd_rel->relnatts; i++)
if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a))
return i + 1;
if ((i = specialAttNum(a)) != InvalidAttrNumber)
return i;
/* on failure */
elog(ERROR, "Relation '%s' does not have attribute '%s'",
RelationGetRelationName(rd), a);
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].attrname, a) == 0)
return special_attr[i].attrnum;
return InvalidAttrNumber;
}
#ifdef NOT_USED
/*
* Given range variable, return whether attribute of this name
* is a set.
* NOTE the ASSUMPTION here that no system attributes are, or ever
* will be, sets.
*
* This should only be used if the relation is already
* heap_open()'ed. Use the cache version get_attisset()
* for access to non-opened relations.
*/
bool
attnameIsSet(Relation rd, char *name)
{
int i;
/* First check if this is a system attribute */
for (i = 0; i < SPECIALS; i++)
{
if (strcmp(special_attr[i].attrname, name) == 0)
return false; /* no sys attr is a set */
}
return get_attisset(RelationGetRelid(rd), name);
}
#endif
#ifdef NOT_USED
/*
* This should only be used if the relation is already
* heap_open()'ed. Use the cache version
* for access to non-opened relations.
*/
int
attnumAttNelems(Relation rd, int attid)
{
return rd->rd_att->attrs[attid - 1]->attnelems;
}
#endif
/* given attribute id, return type of that attribute */
/*
* This should only be used if the relation is already
* heap_open()'ed. Use the cache version get_atttype()
* for access to non-opened relations.
*/
Oid
attnumTypeId(Relation rd, int attid)
{
if (attid < 0)
{
int i;
for (i = 0; i < SPECIALS; i++)
{
if (special_attr[i].attrnum == attid)
return special_attr[i].attrtype;
}
/* negative but not a valid system attr? */
elog(ERROR, "attnumTypeId: bogus attribute number %d", attid);
}
/*
* -1 because attid is 1-based
*/
return rd->rd_att->attrs[attid - 1]->atttypid;
}
void
warnAutoRange(ParseState *pstate, char *refname)
{
List *temp;
bool foundInFromCl = false;
foreach(temp, pstate->p_rtable)
{
RangeTblEntry *rte = lfirst(temp);
if (rte->inFromCl)
{
foundInFromCl = true;
break;
}
}
if (foundInFromCl)
elog(NOTICE, "Adding missing FROM-clause entry%s for table %s",
pstate->parentParseState != NULL ? " in subquery" : "",
refname);
}