diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/common/tupdesc.c | 9 | ||||
-rw-r--r-- | src/backend/bootstrap/bootstrap.c | 51 | ||||
-rw-r--r-- | src/backend/catalog/dependency.c | 5 | ||||
-rw-r--r-- | src/backend/catalog/heap.c | 88 | ||||
-rw-r--r-- | src/backend/catalog/pg_proc.c | 51 | ||||
-rw-r--r-- | src/backend/commands/analyze.c | 27 | ||||
-rw-r--r-- | src/backend/commands/comment.c | 6 | ||||
-rw-r--r-- | src/backend/commands/copy.c | 581 | ||||
-rw-r--r-- | src/backend/commands/indexcmds.c | 12 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 202 | ||||
-rw-r--r-- | src/backend/optimizer/prep/preptlist.c | 27 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 15 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 25 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 5 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 151 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 10 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 6 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteDefine.c | 12 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 6 | ||||
-rw-r--r-- | src/backend/utils/adt/not_in.c | 29 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 37 | ||||
-rw-r--r-- | src/backend/utils/cache/syscache.c | 67 |
22 files changed, 894 insertions, 528 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 1eb62dea91a..249be870002 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.82 2002/07/31 17:19:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.83 2002/08/02 18:15:04 tgl Exp $ * * NOTES * some of the executor utility code such as "ExecTypeFromTL" should be @@ -111,7 +111,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) for (i = 0; i < desc->natts; i++) { desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE); - memmove(desc->attrs[i], + memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE); desc->attrs[i]->attnotnull = false; @@ -146,7 +146,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) for (i = 0; i < desc->natts; i++) { desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE); - memmove(desc->attrs[i], + memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE); } @@ -263,6 +263,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) return false; if (attr1->attnotnull != attr2->attnotnull) return false; + if (attr1->attisdropped != attr2->attisdropped) + return false; } if (tupdesc1->constr != NULL) { @@ -385,6 +387,7 @@ TupleDescInitEntry(TupleDesc desc, att->attnotnull = false; att->atthasdef = false; + att->attisdropped = false; tuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(oidtypeid), diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 4300c0211da..f48c8389ce6 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.133 2002/07/20 05:16:56 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.134 2002/08/02 18:15:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -522,19 +522,6 @@ boot_openrel(char *relname) (char *) boot_reldesc->rd_att->attrs[i], ATTRIBUTE_TUPLE_SIZE); - /* Some old pg_attribute tuples might not have attisset. */ - - /* - * If the attname is attisset, don't look for it - it may not be - * defined yet. - */ - if (namestrcmp(&attrtypes[i]->attname, "attisset") == 0) - attrtypes[i]->attisset = - get_attisset(RelationGetRelid(boot_reldesc), - NameStr(attrtypes[i]->attname)); - else - attrtypes[i]->attisset = false; - { Form_pg_attribute at = attrtypes[i]; @@ -598,15 +585,19 @@ DefineAttr(char *name, char *type, int attnum) closerel(relname); } - typeoid = gettype(type); if (attrtypes[attnum] == (Form_pg_attribute) NULL) attrtypes[attnum] = AllocateAttribute(); + MemSet(attrtypes[attnum], 0, ATTRIBUTE_TUPLE_SIZE); + + namestrcpy(&attrtypes[attnum]->attname, name); + elog(DEBUG3, "column %s %s", NameStr(attrtypes[attnum]->attname), type); + attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ + + typeoid = gettype(type); + if (Typ != (struct typmap **) NULL) { attrtypes[attnum]->atttypid = Ap->am_oid; - namestrcpy(&attrtypes[attnum]->attname, name); - elog(DEBUG3, "column %s %s", NameStr(attrtypes[attnum]->attname), type); - attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ attlen = attrtypes[attnum]->attlen = Ap->am_typ.typlen; attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; attrtypes[attnum]->attstorage = Ap->am_typ.typstorage; @@ -615,9 +606,6 @@ DefineAttr(char *name, char *type, int attnum) else { attrtypes[attnum]->atttypid = Procid[typeoid].oid; - namestrcpy(&attrtypes[attnum]->attname, name); - elog(DEBUG3, "column %s %s", NameStr(attrtypes[attnum]->attname), type); - attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */ attlen = attrtypes[attnum]->attlen = Procid[typeoid].len; /* @@ -656,6 +644,23 @@ DefineAttr(char *name, char *type, int attnum) } attrtypes[attnum]->attcacheoff = -1; attrtypes[attnum]->atttypmod = -1; + /* + * Mark as "not null" if type is fixed-width and prior columns are too. + * This corresponds to case where column can be accessed directly via + * C struct declaration. + */ + if (attlen > 0) + { + int i; + + for (i = 0; i < attnum; i++) + { + if (attrtypes[i]->attlen <= 0) + break; + } + if (i == attnum) + attrtypes[attnum]->attnotnull = true; + } } @@ -896,8 +901,8 @@ gettype(char *type) * AllocateAttribute * ---------------- */ -static Form_pg_attribute /* XXX */ -AllocateAttribute() +static Form_pg_attribute +AllocateAttribute(void) { Form_pg_attribute attribute = (Form_pg_attribute) malloc(ATTRIBUTE_TUPLE_SIZE); diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index ae88c4bf055..d51c8d589a9 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.7 2002/07/29 22:14:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.8 2002/08/02 18:15:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -567,7 +567,8 @@ doDeletion(const ObjectAddress *object) else { if (object->objectSubId != 0) - elog(ERROR, "DROP COLUMN not implemented yet"); + RemoveAttributeById(object->objectId, + object->objectSubId); else heap_drop_with_catalog(object->objectId); } diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 6045c17bb6a..ec9165c55b1 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.214 2002/07/31 17:19:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.215 2002/08/02 18:15:05 tgl Exp $ * * * INTERFACE ROUTINES @@ -97,37 +97,37 @@ static void RemoveStatistics(Relation rel); static FormData_pg_attribute a1 = { 0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData), SelfItemPointerAttributeNumber, 0, -1, -1, - false, 'p', false, 'i', true, false + false, 'p', false, 'i', true, false, false }; static FormData_pg_attribute a2 = { 0, {"oid"}, OIDOID, 0, sizeof(Oid), ObjectIdAttributeNumber, 0, -1, -1, - true, 'p', false, 'i', true, false + true, 'p', false, 'i', true, false, false }; static FormData_pg_attribute a3 = { 0, {"xmin"}, XIDOID, 0, sizeof(TransactionId), MinTransactionIdAttributeNumber, 0, -1, -1, - true, 'p', false, 'i', true, false + true, 'p', false, 'i', true, false, false }; static FormData_pg_attribute a4 = { 0, {"cmin"}, CIDOID, 0, sizeof(CommandId), MinCommandIdAttributeNumber, 0, -1, -1, - true, 'p', false, 'i', true, false + true, 'p', false, 'i', true, false, false }; static FormData_pg_attribute a5 = { 0, {"xmax"}, XIDOID, 0, sizeof(TransactionId), MaxTransactionIdAttributeNumber, 0, -1, -1, - true, 'p', false, 'i', true, false + true, 'p', false, 'i', true, false, false }; static FormData_pg_attribute a6 = { 0, {"cmax"}, CIDOID, 0, sizeof(CommandId), MaxCommandIdAttributeNumber, 0, -1, -1, - true, 'p', false, 'i', true, false + true, 'p', false, 'i', true, false, false }; /* @@ -139,7 +139,7 @@ static FormData_pg_attribute a6 = { static FormData_pg_attribute a7 = { 0, {"tableoid"}, OIDOID, 0, sizeof(Oid), TableOidAttributeNumber, 0, -1, -1, - true, 'p', false, 'i', true, false + true, 'p', false, 'i', true, false, false }; static Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; @@ -893,6 +893,78 @@ DeleteAttributeTuples(Oid relid) } /* + * RemoveAttributeById + * + * This is the guts of ALTER TABLE DROP COLUMN: actually mark the attribute + * deleted in pg_attribute. (Everything else needed, such as getting rid + * of any pg_attrdef entry, is handled by dependency.c.) + */ +void +RemoveAttributeById(Oid relid, AttrNumber attnum) +{ + Relation rel; + Relation attr_rel; + HeapTuple tuple; + Form_pg_attribute attStruct; + char newattname[NAMEDATALEN]; + + /* + * Grab an exclusive lock on the target table, which we will NOT + * release until end of transaction. (In the simple case where + * we are directly dropping this column, AlterTableDropColumn already + * did this ... but when cascading from a drop of some other object, + * we may not have any lock.) + */ + rel = heap_open(relid, AccessExclusiveLock); + + attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock); + + tuple = SearchSysCacheCopy(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0, 0); + if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ + elog(ERROR, "RemoveAttributeById: Failed to find attribute %d in relation %u", + attnum, relid); + attStruct = (Form_pg_attribute) GETSTRUCT(tuple); + + /* Mark the attribute as dropped */ + attStruct->attisdropped = true; + + /* Remove any NOT NULL constraint the column may have */ + attStruct->attnotnull = false; + + /* We don't want to keep stats for it anymore */ + attStruct->attstattarget = 0; + + /* Change the column name to something that isn't likely to conflict */ + snprintf(newattname, NAMEDATALEN, "........pg.dropped.%d........", attnum); + namestrcpy(&(attStruct->attname), newattname); + + simple_heap_update(attr_rel, &tuple->t_self, tuple); + + /* keep the system catalog indices current */ + if (RelationGetForm(attr_rel)->relhasindex) + { + Relation idescs[Num_pg_attr_indices]; + + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple); + CatalogCloseIndices(Num_pg_attr_indices, idescs); + } + + /* + * Because updating the pg_attribute row will trigger a relcache flush + * for the target relation, we need not do anything else to notify + * other backends of the change. + */ + + heap_close(attr_rel, RowExclusiveLock); + + heap_close(rel, NoLock); +} + +/* * RemoveAttrDefault * * If the specified relation/attribute has a default, remove it. diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 2a11474e9f5..2624fdaf25d 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.81 2002/07/24 19:11:09 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.82 2002/08/02 18:15:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -327,9 +327,9 @@ checkretval(Oid rettype, List *queryTreeList) Oid typerelid; Oid restype; Relation reln; - Oid relid; - int relnatts; - int i; + int relnatts; /* physical number of columns in rel */ + int rellogcols; /* # of nondeleted columns in rel */ + int colindex; /* physical column index */ /* guard against empty function body; OK only if no return type */ if (queryTreeList == NIL) @@ -404,42 +404,55 @@ checkretval(Oid rettype, List *queryTreeList) /* * By here, the procedure returns a tuple or set of tuples. This part * of the typechecking is a hack. We look up the relation that is the - * declared return type, and be sure that attributes 1 .. n in the - * target list match the declared types. + * declared return type, and scan the non-deleted attributes to ensure + * that they match the datatypes of the non-resjunk columns. */ reln = heap_open(typerelid, AccessShareLock); - relid = reln->rd_id; relnatts = reln->rd_rel->relnatts; + rellogcols = 0; /* we'll count nondeleted cols as we go */ + colindex = 0; - if (tlistlen != relnatts) - elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", - format_type_be(rettype), relnatts); - - /* expect attributes 1 .. n in order */ - i = 0; foreach(tlistitem, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(tlistitem); + Form_pg_attribute attr; Oid tletype; Oid atttype; if (tle->resdom->resjunk) continue; + + do { + colindex++; + if (colindex > relnatts) + elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", + format_type_be(rettype), rellogcols); + attr = reln->rd_att->attrs[colindex - 1]; + } while (attr->attisdropped); + rellogcols++; + tletype = exprType(tle->expr); - atttype = reln->rd_att->attrs[i]->atttypid; + atttype = attr->atttypid; if (!IsBinaryCompatible(tletype, atttype)) elog(ERROR, "function declared to return %s returns %s instead of %s at column %d", format_type_be(rettype), format_type_be(tletype), format_type_be(atttype), - i + 1); - i++; + rellogcols); + } + + for (;;) + { + colindex++; + if (colindex > relnatts) + break; + if (!reln->rd_att->attrs[colindex - 1]->attisdropped) + rellogcols++; } - /* this shouldn't happen, but let's just check... */ - if (i != relnatts) + if (tlistlen != rellogcols) elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", - format_type_be(rettype), relnatts); + format_type_be(rettype), rellogcols); heap_close(reln, AccessShareLock); } diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 55069aa6feb..9844a5df0a9 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.39 2002/07/31 17:19:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.40 2002/08/02 18:15:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,7 @@ #include "commands/vacuum.h" #include "miscadmin.h" #include "parser/parse_oper.h" +#include "parser/parse_relation.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" @@ -147,7 +148,6 @@ void analyze_rel(Oid relid, VacuumStmt *vacstmt) { Relation onerel; - Form_pg_attribute *attr; int attr_cnt, tcnt, i; @@ -234,9 +234,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt) * * Note that system attributes are never analyzed. */ - attr = onerel->rd_att->attrs; - attr_cnt = onerel->rd_att->natts; - if (vacstmt->va_cols != NIL) { List *le; @@ -248,15 +245,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt) { char *col = strVal(lfirst(le)); - for (i = 0; i < attr_cnt; i++) - { - if (namestrcmp(&(attr[i]->attname), col) == 0) - break; - } - if (i >= attr_cnt) - elog(ERROR, "ANALYZE: there is no attribute %s in %s", - col, RelationGetRelationName(onerel)); - vacattrstats[tcnt] = examine_attribute(onerel, i + 1); + i = attnameAttNum(onerel, col, false); + vacattrstats[tcnt] = examine_attribute(onerel, i); if (vacattrstats[tcnt] != NULL) tcnt++; } @@ -264,12 +254,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt) } else { + attr_cnt = onerel->rd_att->natts; vacattrstats = (VacAttrStats **) palloc(attr_cnt * sizeof(VacAttrStats *)); tcnt = 0; - for (i = 0; i < attr_cnt; i++) + for (i = 1; i <= attr_cnt; i++) { - vacattrstats[tcnt] = examine_attribute(onerel, i + 1); + vacattrstats[tcnt] = examine_attribute(onerel, i); if (vacattrstats[tcnt] != NULL) tcnt++; } @@ -388,6 +379,10 @@ examine_attribute(Relation onerel, int attnum) Oid ltopr = InvalidOid; VacAttrStats *stats; + /* Don't analyze dropped columns */ + if (attr->attisdropped) + return NULL; + /* Don't analyze column if user has specified not to */ if (attr->attstattarget == 0) return NULL; diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index c5dee8f8af9..118c2c4b777 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -7,7 +7,7 @@ * Copyright (c) 1996-2001, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.53 2002/07/29 23:46:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.54 2002/08/02 18:15:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -382,8 +382,8 @@ CommentAttribute(List *qualname, char *comment) attnum = get_attnum(RelationGetRelid(relation), attrname); if (attnum == InvalidAttrNumber) - elog(ERROR, "\"%s\" is not an attribute of class \"%s\"", - attrname, RelationGetRelationName(relation)); + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + RelationGetRelationName(relation), attrname); /* Create the comment using the relation's oid */ diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 19e17ed2873..2529b728230 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.161 2002/07/30 16:55:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.162 2002/08/02 18:15:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,7 @@ #include "rewrite/rewriteHandler.h" #include "libpq/libpq.h" #include "miscadmin.h" +#include "parser/parse_relation.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "utils/acl.h" @@ -53,13 +54,15 @@ typedef enum CopyReadResult } CopyReadResult; /* non-export function prototypes */ -static void CopyTo(Relation rel, List *attlist, bool binary, bool oids, FILE *fp, char *delim, char *null_print); -static void CopyFrom(Relation rel, List *attlist, bool binary, bool oids, FILE *fp, char *delim, char *null_print); +static void CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, + FILE *fp, char *delim, char *null_print); +static void CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, + FILE *fp, char *delim, char *null_print); static Oid GetInputFunction(Oid type); static Oid GetTypeElement(Oid type); static char *CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result); static void CopyAttributeOut(FILE *fp, char *string, char *delim); -static void CopyCheckAttlist(Relation rel, List *attlist); +static List *CopyGetAttnums(Relation rel, List *attnamelist); static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0"; @@ -275,7 +278,8 @@ DoCopy(const CopyStmt *stmt) bool is_from = stmt->is_from; bool pipe = (stmt->filename == NULL); List *option; - List *attlist = stmt->attlist; + List *attnamelist = stmt->attlist; + List *attnumlist; bool binary = false; bool oids = false; char *delim = NULL; @@ -374,6 +378,11 @@ DoCopy(const CopyStmt *stmt) RelationGetRelationName(rel)); /* + * Generate or convert list of attributes to process + */ + attnumlist = CopyGetAttnums(rel, attnamelist); + + /* * Set up variables to avoid per-attribute overhead. */ initStringInfo(&attribute_buf); @@ -382,26 +391,6 @@ DoCopy(const CopyStmt *stmt) server_encoding = GetDatabaseEncoding(); #endif - if (attlist == NIL) - { - /* get list of attributes in the relation */ - TupleDesc desc = RelationGetDescr(rel); - int i; - for (i = 0; i < desc->natts; ++i) - { - Ident *id = makeNode(Ident); - id->name = NameStr(desc->attrs[i]->attname); - attlist = lappend(attlist,id); - } - } - else - { - if (binary) - elog(ERROR,"COPY: BINARY format cannot be used with specific column list"); - - CopyCheckAttlist(rel, attlist); - } - if (is_from) { /* copy from file to database */ if (rel->rd_rel->relkind != RELKIND_RELATION) @@ -442,10 +431,10 @@ DoCopy(const CopyStmt *stmt) if (S_ISDIR(st.st_mode)) { FreeFile(fp); - elog(ERROR, "COPY: %s is a directory.", filename); + elog(ERROR, "COPY: %s is a directory", filename); } } - CopyFrom(rel, attlist, binary, oids, fp, delim, null_print); + CopyFrom(rel, attnumlist, binary, oids, fp, delim, null_print); } else { /* copy from database to file */ @@ -483,7 +472,7 @@ DoCopy(const CopyStmt *stmt) */ if (filename[0] != '/') elog(ERROR, "Relative path not allowed for server side" - " COPY command."); + " COPY command"); oumask = umask((mode_t) 022); fp = AllocateFile(filename, PG_BINARY_W); @@ -498,10 +487,10 @@ DoCopy(const CopyStmt *stmt) if (S_ISDIR(st.st_mode)) { FreeFile(fp); - elog(ERROR, "COPY: %s is a directory.", filename); + elog(ERROR, "COPY: %s is a directory", filename); } } - CopyTo(rel, attlist, binary, oids, fp, delim, null_print); + CopyTo(rel, attnumlist, binary, oids, fp, delim, null_print); } if (!pipe) @@ -529,14 +518,14 @@ DoCopy(const CopyStmt *stmt) * Copy from relation TO file. */ static void -CopyTo(Relation rel, List *attlist, bool binary, bool oids, +CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, FILE *fp, char *delim, char *null_print) { HeapTuple tuple; TupleDesc tupDesc; HeapScanDesc scandesc; - int attr_count, - i; + int num_phys_attrs; + int attr_count; Form_pg_attribute *attr; FmgrInfo *out_functions; Oid *elements; @@ -544,48 +533,33 @@ CopyTo(Relation rel, List *attlist, bool binary, bool oids, int16 fld_size; char *string; Snapshot mySnapshot; - int copy_attr_count; - int *attmap; - int p = 0; List *cur; tupDesc = rel->rd_att; - attr_count = rel->rd_att->natts; - attr = rel->rd_att->attrs; - - copy_attr_count = length(attlist); - attmap = (int *) palloc(copy_attr_count * sizeof(int)); - - foreach(cur, attlist) - { - const char *currAtt = strVal(lfirst(cur)); - - for (i = 0; i < attr_count; i++) - { - if (namestrcmp(&attr[i]->attname, currAtt) == 0) - { - attmap[p++] = i; - continue; - } - } - } + attr = tupDesc->attrs; + num_phys_attrs = tupDesc->natts; + attr_count = length(attnumlist); /* + * Get info about the columns we need to process. + * * For binary copy we really only need isvarlena, but compute it * all... */ - out_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo)); - elements = (Oid *) palloc(attr_count * sizeof(Oid)); - isvarlena = (bool *) palloc(attr_count * sizeof(bool)); - for (i = 0; i < attr_count; i++) + out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); + elements = (Oid *) palloc(num_phys_attrs * sizeof(Oid)); + isvarlena = (bool *) palloc(num_phys_attrs * sizeof(bool)); + foreach(cur, attnumlist) { + int attnum = lfirsti(cur); Oid out_func_oid; - if (!getTypeOutputInfo(attr[i]->atttypid, - &out_func_oid, &elements[i], &isvarlena[i])) + if (!getTypeOutputInfo(attr[attnum-1]->atttypid, + &out_func_oid, &elements[attnum-1], + &isvarlena[attnum-1])) elog(ERROR, "COPY: couldn't lookup info for type %u", - attr[i]->atttypid); - fmgr_info(out_func_oid, &out_functions[i]); + attr[attnum-1]->atttypid); + fmgr_info(out_func_oid, &out_functions[attnum-1]); } if (binary) @@ -650,14 +624,14 @@ CopyTo(Relation rel, List *attlist, bool binary, bool oids, } } - for (i = 0; i < copy_attr_count; i++) + foreach(cur, attnumlist) { + int attnum = lfirsti(cur); Datum origvalue, value; bool isnull; - int mi = attmap[i]; - origvalue = heap_getattr(tuple, mi + 1, tupDesc, &isnull); + origvalue = heap_getattr(tuple, attnum, tupDesc, &isnull); if (!binary) { @@ -686,25 +660,25 @@ CopyTo(Relation rel, List *attlist, bool binary, bool oids, * (or for binary case, becase we must output untoasted * value). */ - if (isvarlena[mi]) + if (isvarlena[attnum-1]) value = PointerGetDatum(PG_DETOAST_DATUM(origvalue)); else value = origvalue; if (!binary) { - string = DatumGetCString(FunctionCall3(&out_functions[mi], + string = DatumGetCString(FunctionCall3(&out_functions[attnum-1], value, - ObjectIdGetDatum(elements[mi]), - Int32GetDatum(attr[mi]->atttypmod))); + ObjectIdGetDatum(elements[attnum-1]), + Int32GetDatum(attr[attnum-1]->atttypmod))); CopyAttributeOut(fp, string, delim); pfree(string); } else { - fld_size = attr[mi]->attlen; + fld_size = attr[attnum-1]->attlen; CopySendData(&fld_size, sizeof(int16), fp); - if (isvarlena[mi]) + if (isvarlena[attnum-1]) { /* varlena */ Assert(fld_size == -1); @@ -712,7 +686,7 @@ CopyTo(Relation rel, List *attlist, bool binary, bool oids, VARSIZE(value), fp); } - else if (!attr[mi]->attbyval) + else if (!attr[attnum-1]->attbyval) { /* fixed-length pass-by-reference */ Assert(fld_size > 0); @@ -767,36 +741,36 @@ CopyTo(Relation rel, List *attlist, bool binary, bool oids, * Copy FROM file to relation. */ static void -CopyFrom(Relation rel, List *attlist, bool binary, bool oids, +CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, FILE *fp, char *delim, char *null_print) { HeapTuple tuple; TupleDesc tupDesc; Form_pg_attribute *attr; - AttrNumber attr_count, copy_attr_count, def_attr_count; + AttrNumber num_phys_attrs, attr_count, num_defaults; FmgrInfo *in_functions; Oid *elements; int i; + List *cur; Oid in_func_oid; Datum *values; char *nulls; - int done = 0; + bool done = false; ResultRelInfo *resultRelInfo; EState *estate = CreateExecutorState(); /* for ExecConstraints() */ TupleTable tupleTable; TupleTableSlot *slot; bool file_has_oids; - int *attmap = NULL; - int *defmap = NULL; - Node **defexprs = NULL; /* array of default att expressions */ + int *defmap; + Node **defexprs; /* array of default att expressions */ ExprContext *econtext; /* used for ExecEvalExpr for default atts */ - ExprDoneCond isdone; + MemoryContext oldcontext = CurrentMemoryContext; tupDesc = RelationGetDescr(rel); attr = tupDesc->attrs; - attr_count = tupDesc->natts; - copy_attr_count = length(attlist); - def_attr_count = 0; + num_phys_attrs = tupDesc->natts; + attr_count = length(attnumlist); + num_defaults = 0; /* * We need a ResultRelInfo so we can use the regular executor's @@ -819,50 +793,40 @@ CopyFrom(Relation rel, List *attlist, bool binary, bool oids, slot = ExecAllocTableSlot(tupleTable); ExecSetSlotDescriptor(slot, tupDesc, false); - if (!binary) + /* + * pick up the input function and default expression (if any) for + * each attribute in the relation. (We don't actually use the + * input function if it's a binary copy.) + */ + defmap = (int *) palloc(sizeof(int) * num_phys_attrs); + defexprs = (Node **) palloc(sizeof(Node *) * num_phys_attrs); + in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); + elements = (Oid *) palloc(num_phys_attrs * sizeof(Oid)); + + for (i = 0; i < num_phys_attrs; i++) { - /* - * pick up the input function and default expression (if any) for - * each attribute in the relation. - */ - attmap = (int *) palloc(sizeof(int) * attr_count); - defmap = (int *) palloc(sizeof(int) * attr_count); - defexprs = (Node **) palloc(sizeof(Node *) * attr_count); - in_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo)); - elements = (Oid *) palloc(attr_count * sizeof(Oid)); - for (i = 0; i < attr_count; i++) - { - List *l; - int p = 0; - bool specified = false; + /* We don't need info for dropped attributes */ + if (attr[i]->attisdropped) + continue; - in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid); - fmgr_info(in_func_oid, &in_functions[i]); - elements[i] = GetTypeElement(attr[i]->atttypid); + in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid); + fmgr_info(in_func_oid, &in_functions[i]); + elements[i] = GetTypeElement(attr[i]->atttypid); - foreach(l, attlist) - { - if (namestrcmp(&attr[i]->attname, strVal(lfirst(l))) == 0) - { - attmap[p] = i; - specified = true; - continue; - } - p++; - } - - /* if column not specified, use default value if one exists */ - if (! specified) + /* if column not specified, use default value if one exists */ + if (!intMember(i + 1, attnumlist)) + { + defexprs[num_defaults] = build_column_default(rel, i + 1); + if (defexprs[num_defaults] != NULL) { - defexprs[def_attr_count] = build_column_default(rel, i + 1); - - if (defexprs[def_attr_count] != NULL) - { - defmap[def_attr_count] = i; - def_attr_count++; - } + defmap[num_defaults] = i; + num_defaults++; } } + } + + if (!binary) + { file_has_oids = oids; /* must rely on user to tell us this... */ } else @@ -898,13 +862,10 @@ CopyFrom(Relation rel, List *attlist, bool binary, bool oids, if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: bogus file header (wrong length)"); } - - in_functions = NULL; - elements = NULL; } - values = (Datum *) palloc(attr_count * sizeof(Datum)); - nulls = (char *) palloc(attr_count * sizeof(char)); + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (char *) palloc(num_phys_attrs * sizeof(char)); copy_lineno = 0; fe_eof = false; @@ -914,58 +875,77 @@ CopyFrom(Relation rel, List *attlist, bool binary, bool oids, while (!done) { bool skip_tuple; - Oid loaded_oid; + Oid loaded_oid = InvalidOid; CHECK_FOR_INTERRUPTS(); copy_lineno++; - /* Reset the per-output-tuple exprcontext */ + /* Reset the per-tuple exprcontext */ ResetPerTupleExprContext(estate); + /* Switch to and reset per-tuple memory context, too */ + MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); + MemoryContextReset(CurrentMemoryContext); + /* Initialize all values for row to NULL */ - MemSet(values, 0, attr_count * sizeof(Datum)); - MemSet(nulls, 'n', attr_count * sizeof(char)); + MemSet(values, 0, num_phys_attrs * sizeof(Datum)); + MemSet(nulls, 'n', num_phys_attrs * sizeof(char)); if (!binary) { - CopyReadResult result; + CopyReadResult result = NORMAL_ATTR; char *string; if (file_has_oids) { string = CopyReadAttribute(fp, delim, &result); - if (result == END_OF_FILE) - done = 1; - else if (string == NULL || strcmp(string, null_print) == 0) - elog(ERROR, "COPY TEXT: NULL Oid"); + if (result == END_OF_FILE && *string == '\0') + { + /* EOF at start of line: all is well */ + done = true; + break; + } + + if (strcmp(string, null_print) == 0) + elog(ERROR, "NULL Oid"); else { loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(string))); if (loaded_oid == InvalidOid) - elog(ERROR, "COPY TEXT: Invalid Oid"); + elog(ERROR, "Invalid Oid"); } } - /* - * here, we only try to read as many attributes as - * were specified. + /* + * Loop to read the user attributes on the line. */ - for (i = 0; i < copy_attr_count && !done; i++) + foreach(cur, attnumlist) { - int m = attmap[i]; + int attnum = lfirsti(cur); + int m = attnum - 1; + + /* + * If prior attr on this line was ended by newline or EOF, + * complain. + */ + if (result != NORMAL_ATTR) + elog(ERROR, "Missing data for column \"%s\"", + NameStr(attr[m]->attname)); string = CopyReadAttribute(fp, delim, &result); - /* If we got an end-of-line before we expected, bail out */ - if (result == END_OF_LINE && i < (copy_attr_count - 1)) - elog(ERROR, "COPY TEXT: Missing data for attribute %d", i + 1); + if (result == END_OF_FILE && *string == '\0' && + cur == attnumlist && !file_has_oids) + { + /* EOF at start of line: all is well */ + done = true; + break; /* out of per-attr loop */ + } - if (result == END_OF_FILE) - done = 1; - else if (strcmp(string, null_print) == 0) + if (strcmp(string, null_print) == 0) { /* we read an SQL NULL, no need to do anything */ } @@ -979,124 +959,149 @@ CopyFrom(Relation rel, List *attlist, bool binary, bool oids, } } - if (result == NORMAL_ATTR && !done) - elog(ERROR, "COPY TEXT: Extra data encountered"); + if (done) + break; /* out of per-row loop */ + + /* Complain if there are more fields on the input line */ + if (result == NORMAL_ATTR) + elog(ERROR, "Extra data after last expected column"); /* - * as above, we only try a default lookup if one is - * known to be available + * If we got some data on the line, but it was ended by EOF, + * process the line normally but set flag to exit the loop + * when we return to the top. */ - for (i = 0; i < def_attr_count && !done; i++) - { - bool isnull; - values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, - &isnull, &isdone); - - if (! isnull) - nulls[defmap[i]] = ' '; - } + if (result == END_OF_FILE) + done = true; } else - { /* binary */ + { + /* binary */ int16 fld_count, fld_size; CopyGetData(&fld_count, sizeof(int16), fp); if (CopyGetEof(fp) || fld_count == -1) - done = 1; - else { - if (fld_count <= 0 || fld_count > attr_count) - elog(ERROR, "COPY BINARY: tuple field count is %d, expected %d", - (int) fld_count, attr_count); + done = true; + break; + } + + if (fld_count != attr_count) + elog(ERROR, "COPY BINARY: tuple field count is %d, expected %d", + (int) fld_count, attr_count); + + if (file_has_oids) + { + CopyGetData(&fld_size, sizeof(int16), fp); + if (CopyGetEof(fp)) + elog(ERROR, "COPY BINARY: unexpected EOF"); + if (fld_size != (int16) sizeof(Oid)) + elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d", + (int) fld_size, (int) sizeof(Oid)); + CopyGetData(&loaded_oid, sizeof(Oid), fp); + if (CopyGetEof(fp)) + elog(ERROR, "COPY BINARY: unexpected EOF"); + if (loaded_oid == InvalidOid) + elog(ERROR, "COPY BINARY: Invalid Oid"); + } - if (file_has_oids) + i = 0; + foreach(cur, attnumlist) + { + int attnum = lfirsti(cur); + + i++; + + CopyGetData(&fld_size, sizeof(int16), fp); + if (CopyGetEof(fp)) + elog(ERROR, "COPY BINARY: unexpected EOF"); + if (fld_size == 0) + continue; /* it's NULL; nulls[attnum-1] already set */ + if (fld_size != attr[attnum-1]->attlen) + elog(ERROR, "COPY BINARY: sizeof(field %d) is %d, expected %d", + i, (int) fld_size, (int) attr[attnum-1]->attlen); + if (fld_size == -1) { - CopyGetData(&fld_size, sizeof(int16), fp); + /* varlena field */ + int32 varlena_size; + Pointer varlena_ptr; + + CopyGetData(&varlena_size, sizeof(int32), fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); - if (fld_size != (int16) sizeof(Oid)) - elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d", - (int) fld_size, (int) sizeof(Oid)); - CopyGetData(&loaded_oid, sizeof(Oid), fp); + if (varlena_size < (int32) sizeof(int32)) + elog(ERROR, "COPY BINARY: bogus varlena length"); + varlena_ptr = (Pointer) palloc(varlena_size); + VARATT_SIZEP(varlena_ptr) = varlena_size; + CopyGetData(VARDATA(varlena_ptr), + varlena_size - sizeof(int32), + fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); - if (loaded_oid == InvalidOid) - elog(ERROR, "COPY BINARY: Invalid Oid"); + values[attnum-1] = PointerGetDatum(varlena_ptr); } - - for (i = 0; i < (int) fld_count; i++) + else if (!attr[attnum-1]->attbyval) { - CopyGetData(&fld_size, sizeof(int16), fp); + /* fixed-length pass-by-reference */ + Pointer refval_ptr; + + Assert(fld_size > 0); + refval_ptr = (Pointer) palloc(fld_size); + CopyGetData(refval_ptr, fld_size, fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); - if (fld_size == 0) - continue; /* it's NULL; nulls[i] already set */ - if (fld_size != attr[i]->attlen) - elog(ERROR, "COPY BINARY: sizeof(field %d) is %d, expected %d", - i + 1, (int) fld_size, (int) attr[i]->attlen); - if (fld_size == -1) - { - /* varlena field */ - int32 varlena_size; - Pointer varlena_ptr; - - CopyGetData(&varlena_size, sizeof(int32), fp); - if (CopyGetEof(fp)) - elog(ERROR, "COPY BINARY: unexpected EOF"); - if (varlena_size < (int32) sizeof(int32)) - elog(ERROR, "COPY BINARY: bogus varlena length"); - varlena_ptr = (Pointer) palloc(varlena_size); - VARATT_SIZEP(varlena_ptr) = varlena_size; - CopyGetData(VARDATA(varlena_ptr), - varlena_size - sizeof(int32), - fp); - if (CopyGetEof(fp)) - elog(ERROR, "COPY BINARY: unexpected EOF"); - values[i] = PointerGetDatum(varlena_ptr); - } - else if (!attr[i]->attbyval) - { - /* fixed-length pass-by-reference */ - Pointer refval_ptr; - - Assert(fld_size > 0); - refval_ptr = (Pointer) palloc(fld_size); - CopyGetData(refval_ptr, fld_size, fp); - if (CopyGetEof(fp)) - elog(ERROR, "COPY BINARY: unexpected EOF"); - values[i] = PointerGetDatum(refval_ptr); - } - else - { - /* pass-by-value */ - Datum datumBuf; - - /* - * We need this horsing around because we don't - * know how shorter data values are aligned within - * a Datum. - */ - Assert(fld_size > 0 && fld_size <= sizeof(Datum)); - CopyGetData(&datumBuf, fld_size, fp); - if (CopyGetEof(fp)) - elog(ERROR, "COPY BINARY: unexpected EOF"); - values[i] = fetch_att(&datumBuf, true, fld_size); - } + values[attnum-1] = PointerGetDatum(refval_ptr); + } + else + { + /* pass-by-value */ + Datum datumBuf; - nulls[i] = ' '; + /* + * We need this horsing around because we don't + * know how shorter data values are aligned within + * a Datum. + */ + Assert(fld_size > 0 && fld_size <= sizeof(Datum)); + CopyGetData(&datumBuf, fld_size, fp); + if (CopyGetEof(fp)) + elog(ERROR, "COPY BINARY: unexpected EOF"); + values[attnum-1] = fetch_att(&datumBuf, true, fld_size); } + + nulls[attnum-1] = ' '; } } - if (done) - break; + /* + * Now compute and insert any defaults available for the + * columns not provided by the input data. Anything not + * processed here or above will remain NULL. + */ + for (i = 0; i < num_defaults; i++) + { + bool isnull; + values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, + &isnull, NULL); + if (!isnull) + nulls[defmap[i]] = ' '; + } + + /* + * And now we can form the input tuple. + */ tuple = heap_formtuple(tupDesc, values, nulls); if (oids && file_has_oids) HeapTupleSetOid(tuple, loaded_oid); + /* + * Triggers and stuff need to be invoked in query context. + */ + MemoryContextSwitchTo(oldcontext); + skip_tuple = false; /* BEFORE ROW INSERT Triggers */ @@ -1118,6 +1123,7 @@ CopyFrom(Relation rel, List *attlist, bool binary, bool oids, if (!skip_tuple) { + /* Place tuple in tuple slot */ ExecStoreTuple(tuple, slot, InvalidBuffer, false); /* @@ -1138,8 +1144,6 @@ CopyFrom(Relation rel, List *attlist, bool binary, bool oids, if (resultRelInfo->ri_TrigDesc) ExecARInsertTriggers(estate, resultRelInfo, tuple); } - - heap_freetuple(tuple); } /* @@ -1147,6 +1151,8 @@ CopyFrom(Relation rel, List *attlist, bool binary, bool oids, */ copy_lineno = 0; + MemoryContextSwitchTo(oldcontext); + pfree(values); pfree(nulls); @@ -1197,12 +1203,11 @@ GetTypeElement(Oid type) /* * Read the value of a single attribute. * - * Results are returned in the status indicator, as well as the - * return value. If a value was successfully read but there is - * more to read before EOL, NORMAL_ATTR is set and the value read - * is returned. If a value was read and we hit EOL, END_OF_LINE - * is set and the value read is returned. If we hit the EOF, - * END_OF_FILE is set and NULL is returned. + * *result is set to indicate what terminated the read: + * NORMAL_ATTR: column delimiter + * END_OF_LINE: newline + * END_OF_FILE: EOF indication + * In all cases, the string read up to the terminator is returned. * * Note: This function does not care about SQL NULL values -- it * is the caller's responsibility to check if the returned string @@ -1215,8 +1220,7 @@ static char * CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) { int c; - int delimc = (unsigned char)delim[0]; - + int delimc = (unsigned char) delim[0]; #ifdef MULTIBYTE int mblen; unsigned char s[2]; @@ -1230,7 +1234,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) attribute_buf.len = 0; attribute_buf.data[0] = '\0'; - /* set default */ + /* set default status */ *result = NORMAL_ATTR; for (;;) @@ -1239,7 +1243,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) if (c == EOF) { *result = END_OF_FILE; - return NULL; + goto copy_eof; } if (c == '\n') { @@ -1254,7 +1258,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) if (c == EOF) { *result = END_OF_FILE; - return NULL; + goto copy_eof; } switch (c) { @@ -1286,7 +1290,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) if (c == EOF) { *result = END_OF_FILE; - return NULL; + goto copy_eof; } CopyDonePeek(fp, c, false /* put back */ ); } @@ -1296,7 +1300,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) if (c == EOF) { *result = END_OF_FILE; - return NULL; + goto copy_eof; } CopyDonePeek(fp, c, false /* put back */ ); } @@ -1336,7 +1340,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) if (c != '\n') elog(ERROR, "CopyReadAttribute: end of record marker corrupted"); *result = END_OF_FILE; - return NULL; + goto copy_eof; } } appendStringInfoCharMacro(&attribute_buf, c); @@ -1353,7 +1357,7 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) if (c == EOF) { *result = END_OF_FILE; - return NULL; + goto copy_eof; } appendStringInfoCharMacro(&attribute_buf, c); } @@ -1361,6 +1365,8 @@ CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result) #endif } +copy_eof: + #ifdef MULTIBYTE if (client_encoding != server_encoding) { @@ -1468,50 +1474,51 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim) } /* - * CopyCheckAttlist: elog(ERROR,...) if the specified attlist - * is not valid for the Relation + * CopyGetAttnums - build an integer list of attnums to be copied + * + * The input attnamelist is either the user-specified column list, + * or NIL if there was none (in which case we want all the non-dropped + * columns). */ -static void -CopyCheckAttlist(Relation rel, List *attlist) +static List * +CopyGetAttnums(Relation rel, List *attnamelist) { - TupleDesc tupDesc; - int attr_count; - List *l; - - if (attlist == NIL) - return; - - tupDesc = RelationGetDescr(rel); - Assert(tupDesc != NULL); - - /* - * make sure there aren't more columns specified than are in the table - */ - attr_count = tupDesc->natts; - if (attr_count < length(attlist)) - elog(ERROR, "COPY: Too many columns specified"); + List *attnums = NIL; - /* - * make sure no columns are specified that don't exist in the table - */ - foreach(l, attlist) + if (attnamelist == NIL) { - char *colName = strVal(lfirst(l)); - bool found = false; - int i; + /* Generate default column list */ + TupleDesc tupDesc = RelationGetDescr(rel); + Form_pg_attribute *attr = tupDesc->attrs; + int attr_count = tupDesc->natts; + int i; for (i = 0; i < attr_count; i++) { - if (namestrcmp(&tupDesc->attrs[i]->attname, colName) == 0) - { - found = true; - break; - } + if (attr[i]->attisdropped) + continue; + attnums = lappendi(attnums, i + 1); + } + } + else + { + /* Validate the user-supplied list and extract attnums */ + List *l; + + foreach(l, attnamelist) + { + char *name = strVal(lfirst(l)); + int attnum; + + /* Lookup column name, elog on failure */ + /* Note we disallow system columns here */ + attnum = attnameAttNum(rel, name, false); + /* Check for duplicates */ + if (intMember(attnum, attnums)) + elog(ERROR, "Attribute \"%s\" specified more than once", name); + attnums = lappendi(attnums, attnum); } - - if (!found) - elog(ERROR, "COPY: Specified column \"%s\" does not exist", - colName); } -} + return attnums; +} diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 4049e7435c5..9edebc1e690 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.79 2002/07/29 23:46:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.80 2002/08/02 18:15:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -294,10 +294,7 @@ FuncIndexArgs(IndexInfo *indexInfo, HeapTuple tuple; Form_pg_attribute att; - tuple = SearchSysCache(ATTNAME, - ObjectIdGetDatum(relId), - PointerGetDatum(arg), - 0, 0); + tuple = SearchSysCacheAttName(relId, arg); if (!HeapTupleIsValid(tuple)) elog(ERROR, "DefineIndex: attribute \"%s\" not found", arg); att = (Form_pg_attribute) GETSTRUCT(tuple); @@ -387,10 +384,7 @@ NormIndexAttrs(IndexInfo *indexInfo, if (attribute->name == NULL) elog(ERROR, "missing attribute for define index"); - atttuple = SearchSysCache(ATTNAME, - ObjectIdGetDatum(relId), - PointerGetDatum(attribute->name), - 0, 0); + atttuple = SearchSysCacheAttName(relId, attribute->name); if (!HeapTupleIsValid(atttuple)) elog(ERROR, "DefineIndex: attribute \"%s\" not found", attribute->name); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cfcf5d5ddfd..4972c09b4a4 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.25 2002/07/31 17:19:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.26 2002/08/02 18:15:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -503,7 +503,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, /* * newattno[] will contain the child-table attribute numbers for * the attributes of this parent table. (They are not the same - * for parents after the first one.) + * for parents after the first one, nor if we have dropped columns.) */ newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber)); @@ -517,6 +517,19 @@ MergeAttributes(List *schema, List *supers, bool istemp, TypeName *typename; /* + * Ignore dropped columns in the parent. + */ + if (attribute->attisdropped) + { + /* + * change_varattnos_of_a_node asserts that this is greater than + * zero, so if anything tries to use it, we should find out. + */ + newattno[parent_attno - 1] = 0; + continue; + } + + /* * Does it conflict with some previously inherited column? */ exist_attno = findAttrByName(attributeName, inhSchema); @@ -1062,17 +1075,17 @@ renameatt(Oid relid, attrelation = heap_openr(AttributeRelationName, RowExclusiveLock); - atttup = SearchSysCacheCopy(ATTNAME, - ObjectIdGetDatum(relid), - PointerGetDatum(oldattname), - 0, 0); + atttup = SearchSysCacheCopyAttName(relid, oldattname); if (!HeapTupleIsValid(atttup)) - elog(ERROR, "renameatt: attribute \"%s\" does not exist", oldattname); + elog(ERROR, "renameatt: attribute \"%s\" does not exist", + oldattname); if (((Form_pg_attribute) GETSTRUCT(atttup))->attnum < 0) - elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname); + elog(ERROR, "renameatt: system attribute \"%s\" may not be renamed", + oldattname); /* should not already exist */ + /* this test is deliberately not attisdropped-aware */ if (SearchSysCacheExists(ATTNAME, ObjectIdGetDatum(relid), PointerGetDatum(newattname), @@ -1126,10 +1139,7 @@ renameatt(Oid relid, * Okay, look to see if any column name of the index matches the * old attribute name. */ - atttup = SearchSysCacheCopy(ATTNAME, - ObjectIdGetDatum(indexoid), - PointerGetDatum(oldattname), - 0, 0); + atttup = SearchSysCacheCopyAttName(indexoid, oldattname); if (!HeapTupleIsValid(atttup)) continue; /* Nope, so ignore it */ @@ -1634,6 +1644,10 @@ AlterTableAddColumn(Oid myrelid, elog(ERROR, "ALTER TABLE: relation \"%s\" not found", RelationGetRelationName(rel)); + /* + * this test is deliberately not attisdropped-aware, since if one tries + * to add a column matching a dropped column name, it's gonna fail anyway. + */ if (SearchSysCacheExists(ATTNAME, ObjectIdGetDatum(myrelid), PointerGetDatum(colDef->colname), @@ -1681,6 +1695,7 @@ AlterTableAddColumn(Oid myrelid, attribute->attnotnull = colDef->is_not_null; attribute->atthasdef = (colDef->raw_default != NULL || colDef->cooked_default != NULL); + attribute->attisdropped = false; ReleaseSysCache(typeTuple); @@ -1821,17 +1836,11 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, /* * get the number of the attribute */ - tuple = SearchSysCache(ATTNAME, - ObjectIdGetDatum(myrelid), - PointerGetDatum(colName), - 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"", + attnum = get_attnum(myrelid, colName); + if (attnum == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", RelationGetRelationName(rel), colName); - attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum; - ReleaseSysCache(tuple); - /* Prevent them from altering a system attribute */ if (attnum < 0) elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"", @@ -1884,10 +1893,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, */ attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock); - tuple = SearchSysCacheCopy(ATTNAME, - ObjectIdGetDatum(myrelid), - PointerGetDatum(colName), - 0, 0); + tuple = SearchSysCacheCopyAttName(myrelid, colName); if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"", RelationGetRelationName(rel), colName); @@ -1971,17 +1977,11 @@ AlterTableAlterColumnSetNotNull(Oid myrelid, /* * get the number of the attribute */ - tuple = SearchSysCache(ATTNAME, - ObjectIdGetDatum(myrelid), - PointerGetDatum(colName), - 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"", + attnum = get_attnum(myrelid, colName); + if (attnum == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", RelationGetRelationName(rel), colName); - attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum; - ReleaseSysCache(tuple); - /* Prevent them from altering a system attribute */ if (attnum < 0) elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"", @@ -2014,10 +2014,7 @@ AlterTableAlterColumnSetNotNull(Oid myrelid, */ attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock); - tuple = SearchSysCacheCopy(ATTNAME, - ObjectIdGetDatum(myrelid), - PointerGetDatum(colName), - 0, 0); + tuple = SearchSysCacheCopyAttName(myrelid, colName); if (!HeapTupleIsValid(tuple)) /* shouldn't happen */ elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"", RelationGetRelationName(rel), colName); @@ -2051,7 +2048,6 @@ AlterTableAlterColumnDefault(Oid myrelid, Node *newDefault) { Relation rel; - HeapTuple tuple; AttrNumber attnum; rel = heap_open(myrelid, AccessExclusiveLock); @@ -2106,16 +2102,15 @@ AlterTableAlterColumnDefault(Oid myrelid, /* * get the number of the attribute */ - tuple = SearchSysCache(ATTNAME, - ObjectIdGetDatum(myrelid), - PointerGetDatum(colName), - 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"", + attnum = get_attnum(myrelid, colName); + if (attnum == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", RelationGetRelationName(rel), colName); - attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum; - ReleaseSysCache(tuple); + /* Prevent them from altering a system attribute */ + if (attnum < 0) + elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"", + colName); /* * Remove any old default for the column. We use RESTRICT here for @@ -2254,10 +2249,7 @@ AlterTableAlterColumnFlags(Oid myrelid, attrelation = heap_openr(AttributeRelationName, RowExclusiveLock); - tuple = SearchSysCacheCopy(ATTNAME, - ObjectIdGetDatum(myrelid), - PointerGetDatum(colName), - 0, 0); + tuple = SearchSysCacheCopyAttName(myrelid, colName); if (!HeapTupleIsValid(tuple)) elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"", RelationGetRelationName(rel), colName); @@ -2309,7 +2301,99 @@ AlterTableDropColumn(Oid myrelid, bool inh, const char *colName, DropBehavior behavior) { - elog(ERROR, "ALTER TABLE / DROP COLUMN is not implemented"); + Relation rel; + AttrNumber attnum; + AttrNumber n; + TupleDesc tupleDesc; + bool success; + ObjectAddress object; + + rel = heap_open(myrelid, AccessExclusiveLock); + + if (rel->rd_rel->relkind != RELKIND_RELATION) + elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table", + RelationGetRelationName(rel)); + + if (!allowSystemTableMods + && IsSystemRelation(rel)) + elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog", + RelationGetRelationName(rel)); + + if (!pg_class_ownercheck(myrelid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel)); + + /* + * get the number of the attribute + */ + attnum = get_attnum(myrelid, colName); + if (attnum == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + RelationGetRelationName(rel), colName); + + /* Can't drop a system attribute */ + if (attnum < 0) + elog(ERROR, "ALTER TABLE: Cannot drop system attribute \"%s\"", + colName); + + /* + * Make sure there will be at least one user column left in the relation + * after we drop this one. Zero-length tuples tend to confuse us. + */ + tupleDesc = RelationGetDescr(rel); + + success = false; + for (n = 1; n <= tupleDesc->natts; n++) + { + Form_pg_attribute attribute = tupleDesc->attrs[n - 1]; + + if (!attribute->attisdropped && n != attnum) + { + success = true; + break; + } + } + + if (!success) + elog(ERROR, "ALTER TABLE: Cannot drop last column from table \"%s\"", + RelationGetRelationName(rel)); + + /* + * Propagate to children if desired + */ + if (inh) + { + List *child, + *children; + + /* this routine is actually in the planner */ + children = find_all_inheritors(myrelid); + + /* + * find_all_inheritors does the recursive search of the + * inheritance hierarchy, so all we have to do is process all of + * the relids in the list that it returns. + */ + foreach(child, children) + { + Oid childrelid = lfirsti(child); + + if (childrelid == myrelid) + continue; + AlterTableDropColumn(childrelid, + false, colName, behavior); + } + } + + /* + * Perform the actual deletion + */ + object.classId = RelOid_pg_class; + object.objectId = myrelid; + object.objectSubId = attnum; + + performDeletion(&object, behavior); + + heap_close(rel, NoLock); /* close rel, but keep lock! */ } @@ -2722,8 +2806,13 @@ createForeignKeyConstraint(Relation rel, Relation pkrel, foreach(l, fkconstraint->fk_attrs) { Ident *id = (Ident *) lfirst(l); + AttrNumber attno; - fkattr[i++] = get_attnum(RelationGetRelid(rel), id->name); + attno = get_attnum(RelationGetRelid(rel), id->name); + if (attno == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + RelationGetRelationName(rel), id->name); + fkattr[i++] = attno; } /* The same for the referenced primary key attrs */ @@ -2733,8 +2822,13 @@ createForeignKeyConstraint(Relation rel, Relation pkrel, foreach(l, fkconstraint->pk_attrs) { Ident *id = (Ident *) lfirst(l); + AttrNumber attno; - pkattr[i++] = get_attnum(RelationGetRelid(pkrel), id->name); + attno = get_attnum(RelationGetRelid(pkrel), id->name); + if (attno == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + RelationGetRelationName(pkrel), id->name); + pkattr[i++] = attno; } /* Now we can make the pg_constraint entry */ diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 1cc9f5af489..b7c0bac12c8 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.53 2002/06/20 20:29:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.54 2002/08/02 18:15:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -165,6 +165,8 @@ expand_targetlist(List *tlist, int command_type, * * For UPDATE, generate a Var reference to the existing value of * the attribute, so that it gets copied to the new tuple. + * But generate a NULL for dropped columns (we want to drop any + * old values). */ Oid atttype = att_tup->atttypid; int32 atttypmod = att_tup->atttypmod; @@ -182,11 +184,21 @@ expand_targetlist(List *tlist, int command_type, false); break; case CMD_UPDATE: - new_expr = (Node *) makeVar(result_relation, - attrno, - atttype, - atttypmod, - 0); + /* Insert NULLs for dropped columns */ + if (att_tup->attisdropped) + new_expr = (Node *) makeConst(atttype, + att_tup->attlen, + (Datum) 0, + true, /* isnull */ + att_tup->attbyval, + false, /* not a set */ + false); + else + new_expr = (Node *) makeVar(result_relation, + attrno, + atttype, + atttypmod, + 0); break; default: elog(ERROR, "expand_targetlist: unexpected command_type"); @@ -210,7 +222,8 @@ expand_targetlist(List *tlist, int command_type, * the end of the new tlist, making sure they have resnos higher than * the last real attribute. (Note: although the rewriter already did * such renumbering, we have to do it again here in case we are doing - * an UPDATE in an inheritance child table with more columns.) + * an UPDATE in a table with dropped columns, or an inheritance child + * table with extra columns.) */ while (tlist) { diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 367ef4a58a7..f41466dbd5c 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.74 2002/06/20 20:29:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.75 2002/08/02 18:15:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -797,9 +797,16 @@ adjust_inherited_attrs_mutator(Node *node, { var->varno = context->new_rt_index; if (var->varattno > 0) - var->varattno = get_attnum(context->new_relid, - get_attname(context->old_relid, - var->varattno)); + { + char *attname = get_attname(context->old_relid, + var->varattno); + + var->varattno = get_attnum(context->new_relid, attname); + if (var->varattno == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + get_rel_name(context->new_relid), attname); + pfree(attname); + } } return (Node *) var; } diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6337b61f2ac..e3d8ce070bc 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.239 2002/07/16 22:12:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.240 2002/08/02 18:15:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1143,6 +1143,8 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) Form_pg_attribute inhattr = rel->rd_att->attrs[count]; char *inhname = NameStr(inhattr->attname); + if (inhattr->attisdropped) + continue; if (strcmp(key->name, inhname) == 0) { found = true; @@ -1178,10 +1180,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) /* ALTER TABLE case: does column already exist? */ HeapTuple atttuple; - atttuple = SearchSysCache(ATTNAME, - ObjectIdGetDatum(cxt->relOid), - PointerGetDatum(key->name), - 0, 0); + atttuple = SearchSysCacheAttName(cxt->relOid, key->name); if (HeapTupleIsValid(atttuple)) { found = true; @@ -2369,7 +2368,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) origTarget = (ResTarget *) lfirst(origTargetList); updateTargetListEntry(pstate, tle, origTarget->name, attnameAttNum(pstate->p_target_relation, - origTarget->name), + origTarget->name, true), origTarget->indirection); origTargetList = lnext(origTargetList); } @@ -2820,11 +2819,14 @@ transformFkeyGetColType(CreateStmtContext *cxt, char *colname) inh->relname); for (count = 0; count < rel->rd_att->natts; count++) { - char *name = NameStr(rel->rd_att->attrs[count]->attname); + Form_pg_attribute inhattr = rel->rd_att->attrs[count]; + char *inhname = NameStr(inhattr->attname); - if (strcmp(name, colname) == 0) + if (inhattr->attisdropped) + continue; + if (strcmp(inhname, colname) == 0) { - result = rel->rd_att->attrs[count]->atttypid; + result = inhattr->atttypid; heap_close(rel, NoLock); return result; } @@ -2836,10 +2838,7 @@ transformFkeyGetColType(CreateStmtContext *cxt, char *colname) { HeapTuple atttuple; - atttuple = SearchSysCache(ATTNAME, - ObjectIdGetDatum(cxt->relOid), - PointerGetDatum(colname), - 0, 0); + atttuple = SearchSysCacheAttName(cxt->relOid, colname); if (HeapTupleIsValid(atttuple)) { result = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index f92bbe8ac5c..677acf9d1a3 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.132 2002/06/20 20:29:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.133 2002/08/02 18:15:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1150,6 +1150,9 @@ setup_field_select(Node *input, char *attname, Oid relid) AttrNumber attno; attno = get_attnum(relid, attname); + if (attno == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + get_rel_name(relid), attname); fselect->arg = input; fselect->fieldnum = attno; 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; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 608ca7613dc..1e51f23d704 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.85 2002/06/20 20:29:33 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.86 2002/08/02 18:15:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -385,8 +385,12 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) for (i = 0; i < numcol; i++) { - ResTarget *col = makeNode(ResTarget); + ResTarget *col; + if (attr[i]->attisdropped) + continue; + + col = makeNode(ResTarget); col->name = pstrdup(NameStr(attr[i]->attname)); col->indirection = NIL; col->val = NULL; @@ -407,7 +411,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) int attrno; /* Lookup column name, elog on failure */ - attrno = attnameAttNum(pstate->p_target_relation, name); + attrno = attnameAttNum(pstate->p_target_relation, name, false); /* Check for duplicates */ if (intMember(attrno, *attrnos)) elog(ERROR, "Attribute '%s' specified more than once", name); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 159428894e4..59c534a7ef3 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.46 2002/07/29 23:46:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.47 2002/08/02 18:15:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -85,8 +85,8 @@ LookupTypeName(const TypeName *typename) relid = RangeVarGetRelid(rel, false); attnum = get_attnum(relid, field); if (attnum == InvalidAttrNumber) - elog(ERROR, "'%s' is not an attribute of class '%s'", - field, rel->relname); + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + rel->relname, field); restype = get_atttype(relid, attnum); /* this construct should never have an array indicator */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 59d0744dcee..577ce2bd52d 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.75 2002/07/16 05:53:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.76 2002/08/02 18:15:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -257,6 +257,16 @@ DefineQueryRewrite(RuleStmt *stmt) attr = event_relation->rd_att->attrs[i - 1]; attname = NameStr(attr->attname); + /* + * Disallow dropped columns in the relation. This won't happen + * in the cases we actually care about (namely creating a view + * via CREATE TABLE then CREATE RULE). Trying to cope with it + * is much more trouble than it's worth, because we'd have to + * modify the rule to insert dummy NULLs at the right positions. + */ + if (attr->attisdropped) + elog(ERROR, "cannot convert relation containing dropped columns to view"); + if (strcmp(resdom->resname, attname) != 0) elog(ERROR, "select rule's target entry %d has different column name from %s", i, attname); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 0ae1e223baa..95ab639d0a7 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.104 2002/07/18 04:43:50 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.105 2002/08/02 18:15:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -264,6 +264,10 @@ rewriteTargetList(Query *parsetree, Relation target_relation) Form_pg_attribute att_tup = target_relation->rd_att->attrs[attrno-1]; TargetEntry *new_tle = NULL; + /* We can ignore deleted attributes */ + if (att_tup->attisdropped) + continue; + /* * Look for targetlist entries matching this attr. We match by * resno, but the resname should match too. diff --git a/src/backend/utils/adt/not_in.c b/src/backend/utils/adt/not_in.c index 5982e434da3..7c6be4533eb 100644 --- a/src/backend/utils/adt/not_in.c +++ b/src/backend/utils/adt/not_in.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.30 2002/06/20 20:29:37 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.31 2002/08/02 18:15:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,9 +28,9 @@ #include "access/heapam.h" #include "catalog/namespace.h" +#include "parser/parse_relation.h" #include "utils/builtins.h" -static int my_varattno(Relation rd, char *a); /* ---------------------------------------------------------------- * @@ -65,15 +65,10 @@ int4notin(PG_FUNCTION_ARGS) relrv = makeRangeVarFromNameList(names); /* Open the relation and get a relation descriptor */ - relation_to_scan = heap_openrv(relrv, AccessShareLock); /* Find the column to search */ - - attrid = my_varattno(relation_to_scan, attribute); - if (attrid < 0) - elog(ERROR, "int4notin: unknown attribute %s for relation %s", - attribute, RelationGetRelationName(relation_to_scan)); + attrid = attnameAttNum(relation_to_scan, attribute, true); scan_descriptor = heap_beginscan(relation_to_scan, SnapshotNow, 0, (ScanKey) NULL); @@ -118,21 +113,3 @@ oidnotin(PG_FUNCTION_ARGS) /* XXX assume oid maps to int4 */ return int4notin(fcinfo); } - -/* - * XXX - * If varattno (in parser/catalog_utils.h) ever is added to - * cinterface.a, this routine should go away - */ -static int -my_varattno(Relation rd, char *a) -{ - int i; - - for (i = 0; i < rd->rd_rel->relnatts; i++) - { - if (namestrcmp(&rd->rd_att->attrs[i]->attname, a) == 0) - return i + 1; - } - return -1; -} diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index e383ab892d1..9b5b4cd6f13 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.76 2002/07/12 18:43:18 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.77 2002/08/02 18:15:08 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -115,16 +115,15 @@ get_attname(Oid relid, AttrNumber attnum) * * Given the relation id and the attribute name, * return the "attnum" field from the attribute relation. + * + * Returns InvalidAttrNumber if the attr doesn't exist (or is dropped). */ AttrNumber -get_attnum(Oid relid, char *attname) +get_attnum(Oid relid, const char *attname) { HeapTuple tp; - tp = SearchSysCache(ATTNAME, - ObjectIdGetDatum(relid), - PointerGetDatum(attname), - 0, 0); + tp = SearchSysCacheAttName(relid, attname); if (HeapTupleIsValid(tp)) { Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); @@ -166,32 +165,6 @@ get_atttype(Oid relid, AttrNumber attnum) return InvalidOid; } -/* This routine uses the attname instead of the attnum because it - * replaces the routine find_atttype, which is called sometimes when - * only the attname, not the attno, is available. - */ -bool -get_attisset(Oid relid, char *attname) -{ - HeapTuple tp; - - tp = SearchSysCache(ATTNAME, - ObjectIdGetDatum(relid), - PointerGetDatum(attname), - 0, 0); - if (HeapTupleIsValid(tp)) - { - Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); - bool result; - - result = att_tup->attisset; - ReleaseSysCache(tp); - return result; - } - else - return false; -} - /* * get_atttypmod * diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 0e114f36968..c9b68b1d8d4 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.84 2002/07/25 10:07:12 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.85 2002/08/02 18:15:08 tgl Exp $ * * NOTES * These routines allow the parser/planner/executor to perform @@ -622,6 +622,71 @@ GetSysCacheOid(int cacheId, return result; } + +/* + * SearchSysCacheAttName + * + * This routine is equivalent to SearchSysCache on the ATTNAME cache, + * except that it will return NULL if the found attribute is marked + * attisdropped. This is convenient for callers that want to act as + * though dropped attributes don't exist. + */ +HeapTuple +SearchSysCacheAttName(Oid relid, const char *attname) +{ + HeapTuple tuple; + + tuple = SearchSysCache(ATTNAME, + ObjectIdGetDatum(relid), + CStringGetDatum(attname), + 0, 0); + if (!HeapTupleIsValid(tuple)) + return NULL; + if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped) + { + ReleaseSysCache(tuple); + return NULL; + } + return tuple; +} + +/* + * SearchSysCacheCopyAttName + * + * As above, an attisdropped-aware version of SearchSysCacheCopy. + */ +HeapTuple +SearchSysCacheCopyAttName(Oid relid, const char *attname) +{ + HeapTuple tuple, + newtuple; + + tuple = SearchSysCacheAttName(relid, attname); + if (!HeapTupleIsValid(tuple)) + return tuple; + newtuple = heap_copytuple(tuple); + ReleaseSysCache(tuple); + return newtuple; +} + +/* + * SearchSysCacheExistsAttName + * + * As above, an attisdropped-aware version of SearchSysCacheExists. + */ +bool +SearchSysCacheExistsAttName(Oid relid, const char *attname) +{ + HeapTuple tuple; + + tuple = SearchSysCacheAttName(relid, attname); + if (!HeapTupleIsValid(tuple)) + return false; + ReleaseSysCache(tuple); + return true; +} + + /* * SysCacheGetAttr * |