mirror of
https://github.com/postgres/postgres.git
synced 2025-11-12 05:01:15 +03:00
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.
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
@@ -892,6 +892,78 @@ DeleteAttributeTuples(Oid relid)
|
||||
heap_close(attrel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/* this shouldn't happen, but let's just check... */
|
||||
if (i != relnatts)
|
||||
for (;;)
|
||||
{
|
||||
colindex++;
|
||||
if (colindex > relnatts)
|
||||
break;
|
||||
if (!reln->rd_att->attrs[colindex - 1]->attisdropped)
|
||||
rellogcols++;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -373,6 +377,11 @@ DoCopy(const CopyStmt *stmt)
|
||||
elog(ERROR, "COPY: table \"%s\" does not have OIDs",
|
||||
RelationGetRelationName(rel));
|
||||
|
||||
/*
|
||||
* Generate or convert list of attributes to process
|
||||
*/
|
||||
attnumlist = CopyGetAttnums(rel, attnamelist);
|
||||
|
||||
/*
|
||||
* Set up variables to avoid per-attribute overhead.
|
||||
*/
|
||||
@@ -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++)
|
||||
/* 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);
|
||||
|
||||
/* if column not specified, use default value if one exists */
|
||||
if (!intMember(i + 1, attnumlist))
|
||||
{
|
||||
List *l;
|
||||
int p = 0;
|
||||
bool specified = false;
|
||||
|
||||
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)
|
||||
defexprs[num_defaults] = build_column_default(rel, i + 1);
|
||||
if (defexprs[num_defaults] != NULL)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 (file_has_oids)
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else if (!attr[attnum-1]->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[attnum-1] = 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[attnum-1] = fetch_att(&datumBuf, true, fld_size);
|
||||
}
|
||||
|
||||
for (i = 0; i < (int) fld_count; 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[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);
|
||||
}
|
||||
|
||||
nulls[i] = ' ';
|
||||
}
|
||||
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;
|
||||
List *attnums = NIL;
|
||||
|
||||
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");
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
if (!found)
|
||||
elog(ERROR, "COPY: Specified column \"%s\" does not exist",
|
||||
colName);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return attnums;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -516,6 +516,19 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
ColumnDef *def;
|
||||
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?
|
||||
*/
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -1329,6 +1368,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
|
||||
*
|
||||
@@ -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)
|
||||
return i + 1;
|
||||
|
||||
if ((i = specialAttNum(a)) != InvalidAttrNumber)
|
||||
{
|
||||
if (i != ObjectIdAttributeNumber || rd->rd_rel->relhasoids)
|
||||
return i;
|
||||
Form_pg_attribute att = rd->rd_att->attrs[i];
|
||||
|
||||
if (namestrcmp(&(att->attname), attname) == 0 && !att->attisdropped)
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
if (sysColOK)
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
37
src/backend/utils/cache/lsyscache.c
vendored
37
src/backend/utils/cache/lsyscache.c
vendored
@@ -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
|
||||
*
|
||||
|
||||
67
src/backend/utils/cache/syscache.c
vendored
67
src/backend/utils/cache/syscache.c
vendored
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user