aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_relation.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-08-02 18:15:10 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-08-02 18:15:10 +0000
commit38bb77a5d15aa022248488bc8c0147139ce120a9 (patch)
treed01573bceae2db61eb97421f91c6068ef8522b66 /src/backend/parser/parse_relation.c
parent5e6528adf726429463a5c1f3edf712f98d6b5f7e (diff)
downloadpostgresql-38bb77a5d15aa022248488bc8c0147139ce120a9.tar.gz
postgresql-38bb77a5d15aa022248488bc8c0147139ce120a9.zip
ALTER TABLE DROP COLUMN works. Patch by Christopher Kings-Lynne,
code review by Tom Lane. Remaining issues: functions that take or return tuple types are likely to break if one drops (or adds!) a column in the table defining the type. Need to think about what to do here. Along the way: some code review for recent COPY changes; mark system columns attnotnull = true where appropriate, per discussion a month ago.
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r--src/backend/parser/parse_relation.c151
1 files changed, 139 insertions, 12 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 78a16ea08f8..99b639d73e3 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.70 2002/06/20 20:29:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.71 2002/08/02 18:15:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,7 +37,9 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname);
static bool isForUpdate(ParseState *pstate, char *refname);
-static int specialAttNum(char *a);
+static bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
+ AttrNumber attnum);
+static int specialAttNum(const char *attname);
static void warnAutoRange(ParseState *pstate, RangeVar *relation);
@@ -267,12 +269,28 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
/*
* Scan the user column names (or aliases) for a match. Complain if
* multiple matches.
+ *
+ * Note: because eref->colnames may include names of dropped columns,
+ * we need to check for non-droppedness before accepting a match.
+ * This takes an extra cache lookup, but we can skip the lookup most
+ * of the time by exploiting the knowledge that dropped columns are
+ * assigned dummy names starting with '.', which is an unusual choice
+ * for actual column names.
+ *
+ * Should the user try to fool us by altering pg_attribute.attname
+ * for a dropped column, we'll still catch it by virtue of the checks
+ * in get_rte_attribute_type(), which is called by make_var(). That
+ * routine has to do a cache lookup anyway, so the check there is
+ * cheap.
*/
foreach(c, rte->eref->colnames)
{
attnum++;
if (strcmp(strVal(lfirst(c)), colname) == 0)
{
+ if (colname[0] == '.' && /* see note above */
+ get_rte_attribute_is_dropped(rte, attnum))
+ continue;
if (result)
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
result = (Node *) make_var(pstate, rte, attnum);
@@ -962,6 +980,9 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+ if (attr->attisdropped)
+ continue;
+
if (colnames)
{
char *label;
@@ -1051,6 +1072,9 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
+ if (attr->attisdropped)
+ continue;
+
if (colnames)
{
char *label;
@@ -1246,9 +1270,16 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
0, 0);
/* this shouldn't happen... */
if (!HeapTupleIsValid(tp))
- elog(ERROR, "Relation %s does not have attribute %d",
+ elog(ERROR, "Relation \"%s\" does not have attribute %d",
get_rel_name(rte->relid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ /*
+ * If dropped column, pretend it ain't there. See notes
+ * in scanRTEForColumn.
+ */
+ if (att_tup->attisdropped)
+ elog(ERROR, "Relation \"%s\" has no column \"%s\"",
+ get_rel_name(rte->relid), NameStr(att_tup->attname));
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
@@ -1298,6 +1329,14 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
elog(ERROR, "Relation %s does not have attribute %d",
get_rel_name(funcrelid), attnum);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ /*
+ * If dropped column, pretend it ain't there. See notes
+ * in scanRTEForColumn.
+ */
+ if (att_tup->attisdropped)
+ elog(ERROR, "Relation \"%s\" has no column \"%s\"",
+ get_rel_name(funcrelid),
+ NameStr(att_tup->attname));
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
ReleaseSysCache(tp);
@@ -1330,6 +1369,86 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
}
/*
+ * get_rte_attribute_is_dropped
+ * Check whether attempted attribute ref is to a dropped column
+ */
+static bool
+get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
+{
+ bool result;
+
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ {
+ /* Plain relation RTE --- get the attribute's type info */
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+
+ tp = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(rte->relid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ /* this shouldn't happen... */
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "Relation \"%s\" does not have attribute %d",
+ get_rel_name(rte->relid), attnum);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ result = att_tup->attisdropped;
+ ReleaseSysCache(tp);
+ }
+ break;
+ case RTE_SUBQUERY:
+ case RTE_JOIN:
+ /* Subselect and join RTEs never have dropped columns */
+ result = false;
+ break;
+ case RTE_FUNCTION:
+ {
+ /* Function RTE */
+ Oid funcrettype = exprType(rte->funcexpr);
+ Oid funcrelid = typeidTypeRelid(funcrettype);
+
+ if (OidIsValid(funcrelid))
+ {
+ /*
+ * Composite data type, i.e. a table's row type
+ * Same as ordinary relation RTE
+ */
+ HeapTuple tp;
+ Form_pg_attribute att_tup;
+
+ tp = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(funcrelid),
+ Int16GetDatum(attnum),
+ 0, 0);
+ /* this shouldn't happen... */
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "Relation %s does not have attribute %d",
+ get_rel_name(funcrelid), attnum);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ result = att_tup->attisdropped;
+ ReleaseSysCache(tp);
+ }
+ else
+ {
+ /*
+ * Must be a base data type, i.e. scalar
+ */
+ result = false;
+ }
+ }
+ break;
+ default:
+ elog(ERROR, "get_rte_attribute_is_dropped: unsupported RTE kind %d",
+ (int) rte->rtekind);
+ result = false; /* keep compiler quiet */
+ }
+
+ return result;
+}
+
+/*
* given relation and att name, return id of variable
*
* This should only be used if the relation is already
@@ -1337,23 +1456,30 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
* for access to non-opened relations.
*/
int
-attnameAttNum(Relation rd, char *a)
+attnameAttNum(Relation rd, const char *attname, bool sysColOK)
{
int i;
for (i = 0; i < rd->rd_rel->relnatts; i++)
- if (namestrcmp(&(rd->rd_att->attrs[i]->attname), a) == 0)
+ {
+ Form_pg_attribute att = rd->rd_att->attrs[i];
+
+ if (namestrcmp(&(att->attname), attname) == 0 && !att->attisdropped)
return i + 1;
+ }
- if ((i = specialAttNum(a)) != InvalidAttrNumber)
+ if (sysColOK)
{
- if (i != ObjectIdAttributeNumber || rd->rd_rel->relhasoids)
- return i;
+ if ((i = specialAttNum(attname)) != InvalidAttrNumber)
+ {
+ if (i != ObjectIdAttributeNumber || rd->rd_rel->relhasoids)
+ return i;
+ }
}
/* on failure */
- elog(ERROR, "Relation '%s' does not have attribute '%s'",
- RelationGetRelationName(rd), a);
+ elog(ERROR, "Relation \"%s\" has no column \"%s\"",
+ RelationGetRelationName(rd), attname);
return InvalidAttrNumber; /* lint */
}
@@ -1367,11 +1493,12 @@ attnameAttNum(Relation rd, char *a)
* at least in the case of "oid", which is now optional.
*/
static int
-specialAttNum(char *a)
+specialAttNum(const char *attname)
{
Form_pg_attribute sysatt;
- sysatt = SystemAttributeByName(a, true /* "oid" will be accepted */ );
+ sysatt = SystemAttributeByName(attname,
+ true /* "oid" will be accepted */ );
if (sysatt != NULL)
return sysatt->attnum;
return InvalidAttrNumber;