Commit 38bb77a5 authored by Tom Lane's avatar Tom Lane

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.
parent 5e6528ad
......@@ -829,8 +829,9 @@ dblink_replace_text(PG_FUNCTION_ARGS)
left_text = DatumGetTextP(DirectFunctionCall3(text_substr, PointerGetDatum(buf_text), 1, DatumGetInt32(DirectFunctionCall2(textpos, PointerGetDatum(buf_text), PointerGetDatum(from_sub_text))) - 1));
right_text = DatumGetTextP(DirectFunctionCall3(text_substr, PointerGetDatum(buf_text), DatumGetInt32(DirectFunctionCall2(textpos, PointerGetDatum(buf_text), PointerGetDatum(from_sub_text))) + from_sub_text_len, -1));
appendStringInfo(str, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(left_text))));
appendStringInfo(str, to_sub_str);
appendStringInfo(str, "%s",
DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(left_text))));
appendStringInfo(str, "%s", to_sub_str);
pfree(buf_text);
pfree(left_text);
......@@ -838,7 +839,8 @@ dblink_replace_text(PG_FUNCTION_ARGS)
curr_posn = DatumGetInt32(DirectFunctionCall2(textpos, PointerGetDatum(buf_text), PointerGetDatum(from_sub_text)));
}
appendStringInfo(str, DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(buf_text))));
appendStringInfo(str, "%s",
DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(buf_text))));
pfree(buf_text);
ret_str = pstrdup(str->data);
......@@ -1013,10 +1015,11 @@ get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
TupleDesc tupdesc;
int natts;
StringInfo str = makeStringInfo();
char *sql = NULL;
char *val = NULL;
char *sql;
char *val;
int16 key;
unsigned int i;
int i;
bool needComma;
/*
* Open relation using relid
......@@ -1029,12 +1032,19 @@ get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
tuple = get_tuple_of_interest(relid, pkattnums, pknumatts, src_pkattvals);
appendStringInfo(str, "INSERT INTO %s(", quote_ident_cstr(relname));
needComma = false;
for (i = 0; i < natts; i++)
{
if (i > 0)
if (tupdesc->attrs[i]->attisdropped)
continue;
if (needComma)
appendStringInfo(str, ",");
appendStringInfo(str, NameStr(tupdesc->attrs[i]->attname));
appendStringInfo(str, "%s",
quote_ident_cstr(NameStr(tupdesc->attrs[i]->attname)));
needComma = true;
}
appendStringInfo(str, ") VALUES(");
......@@ -1042,9 +1052,13 @@ get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
/*
* remember attvals are 1 based
*/
needComma = false;
for (i = 0; i < natts; i++)
{
if (i > 0)
if (tupdesc->attrs[i]->attisdropped)
continue;
if (needComma)
appendStringInfo(str, ",");
if (tgt_pkattvals != NULL)
......@@ -1059,11 +1073,12 @@ get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
if (val != NULL)
{
appendStringInfo(str, quote_literal_cstr(val));
appendStringInfo(str, "%s", quote_literal_cstr(val));
pfree(val);
}
else
appendStringInfo(str, "NULL");
needComma = true;
}
appendStringInfo(str, ")");
......@@ -1083,9 +1098,9 @@ get_sql_delete(Oid relid, int16 *pkattnums, int16 pknumatts, char **tgt_pkattval
TupleDesc tupdesc;
int natts;
StringInfo str = makeStringInfo();
char *sql = NULL;
char *val = NULL;
unsigned int i;
char *sql;
char *val;
int i;
/*
* Open relation using relid
......@@ -1103,21 +1118,24 @@ get_sql_delete(Oid relid, int16 *pkattnums, int16 pknumatts, char **tgt_pkattval
if (i > 0)
appendStringInfo(str, " AND ");
appendStringInfo(str, NameStr(tupdesc->attrs[pkattnum - 1]->attname));
appendStringInfo(str, "%s",
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname)));
if (tgt_pkattvals != NULL)
val = pstrdup(tgt_pkattvals[i]);
else
{
elog(ERROR, "Target key array must not be NULL");
val = NULL; /* keep compiler quiet */
}
if (val != NULL)
{
appendStringInfo(str, "=");
appendStringInfo(str, quote_literal_cstr(val));
appendStringInfo(str, " = %s", quote_literal_cstr(val));
pfree(val);
}
else
appendStringInfo(str, "IS NULL");
appendStringInfo(str, " IS NULL");
}
sql = pstrdup(str->data);
......@@ -1137,10 +1155,11 @@ get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
TupleDesc tupdesc;
int natts;
StringInfo str = makeStringInfo();
char *sql = NULL;
char *val = NULL;
char *sql;
char *val;
int16 key;
int i;
bool needComma;
/*
* Open relation using relid
......@@ -1154,13 +1173,17 @@ get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
appendStringInfo(str, "UPDATE %s SET ", quote_ident_cstr(relname));
needComma = false;
for (i = 0; i < natts; i++)
{
if (i > 0)
appendStringInfo(str, ",");
if (tupdesc->attrs[i]->attisdropped)
continue;
if (needComma)
appendStringInfo(str, ", ");
appendStringInfo(str, NameStr(tupdesc->attrs[i]->attname));
appendStringInfo(str, "=");
appendStringInfo(str, "%s = ",
quote_ident_cstr(NameStr(tupdesc->attrs[i]->attname)));
if (tgt_pkattvals != NULL)
key = get_attnum_pk_pos(pkattnums, pknumatts, i + 1);
......@@ -1174,11 +1197,12 @@ get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
if (val != NULL)
{
appendStringInfo(str, quote_literal_cstr(val));
appendStringInfo(str, "%s", quote_literal_cstr(val));
pfree(val);
}
else
appendStringInfo(str, "NULL");
needComma = true;
}
appendStringInfo(str, " WHERE ");
......@@ -1190,7 +1214,8 @@ get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
if (i > 0)
appendStringInfo(str, " AND ");
appendStringInfo(str, NameStr(tupdesc->attrs[pkattnum - 1]->attname));
appendStringInfo(str, "%s",
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname)));
if (tgt_pkattvals != NULL)
val = pstrdup(tgt_pkattvals[i]);
......@@ -1199,12 +1224,11 @@ get_sql_update(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattval
if (val != NULL)
{
appendStringInfo(str, "=");
appendStringInfo(str, quote_literal_cstr(val));
appendStringInfo(str, " = %s", quote_literal_cstr(val));
pfree(val);
}
else
appendStringInfo(str, "IS NULL");
appendStringInfo(str, " IS NULL");
}
sql = pstrdup(str->data);
......@@ -1297,7 +1321,7 @@ get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_p
* Build sql statement to look up tuple of interest
* Use src_pkattvals as the criteria.
*/
appendStringInfo(str, "SELECT * from %s WHERE ", relname);
appendStringInfo(str, "SELECT * FROM %s WHERE ", quote_ident_cstr(relname));
for (i = 0; i < pknumatts; i++)
{
......@@ -1306,17 +1330,17 @@ get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_p
if (i > 0)
appendStringInfo(str, " AND ");
appendStringInfo(str, NameStr(tupdesc->attrs[pkattnum - 1]->attname));
appendStringInfo(str, "%s",
quote_ident_cstr(NameStr(tupdesc->attrs[pkattnum - 1]->attname)));
val = pstrdup(src_pkattvals[i]);
if (val != NULL)
{
appendStringInfo(str, "=");
appendStringInfo(str, quote_literal_cstr(val));
appendStringInfo(str, " = %s", quote_literal_cstr(val));
pfree(val);
}
else
appendStringInfo(str, "IS NULL");
appendStringInfo(str, " IS NULL");
}
sql = pstrdup(str->data);
......
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
$Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.50 2002/07/31 17:19:49 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.51 2002/08/02 18:15:04 tgl Exp $
-->
<chapter id="catalogs">
......@@ -810,6 +810,17 @@
</entry>
</row>
<row>
<entry>attisdropped</entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>
This column has been dropped and is no longer valid. A dropped
column is still physically present in the table, but is
ignored by the parser and so cannot be accessed via SQL.
</entry>
</row>
</tbody>
</tgroup>
</table>
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.47 2002/07/31 17:19:50 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.48 2002/08/02 18:15:04 tgl Exp $
PostgreSQL documentation
-->
......@@ -23,6 +23,8 @@ PostgreSQL documentation
<synopsis>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
DROP [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET DEFAULT <replaceable class="PARAMETER">value</replaceable> | DROP DEFAULT }
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
......@@ -126,6 +128,26 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend on the dropped column
or constraint (for example, views referencing the column).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the column or constraint if there are any dependent
objects. This is the default behavior.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
......@@ -186,6 +208,19 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
</listitem>
</varlistentry>
<varlistentry>
<term>DROP COLUMN</term>
<listitem>
<para>
This form drops a column from a table. Note that indexes and
table constraints involving the column will be automatically
dropped as well. You will need to say <literal>CASCADE</> if
anything outside the table depends on the column --- for example,
foreign key references, views, etc.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>SET/DROP DEFAULT</term>
<listitem>
......@@ -317,6 +352,22 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
form after you've entered non-null values for the column in all rows.
</para>
<para>
The <literal>DROP COLUMN</literal> command does not physically remove
the column, but simply makes it invisible to SQL operations. Subsequent
inserts and updates of the table will store a NULL for the column.
Thus, dropping a column is quick but it will not immediately reduce the
on-disk size of your table, as the space occupied
by the dropped column is not reclaimed. The space will be
reclaimed over time as existing rows are updated.
To reclaim the space at once, do a dummy <command>UPDATE</> of all rows
and then vacuum, as in:
<programlisting>
UPDATE table SET col = col;
VACUUM FULL table;
</programlisting>
</para>
<para>
Changing any part of the schema of a system
catalog is not permitted.
......@@ -342,6 +393,13 @@ ALTER TABLE distributors ADD COLUMN address VARCHAR(30);
</programlisting>
</para>
<para>
To drop a column from a table:
<programlisting>
ALTER TABLE distributors DROP COLUMN address RESTRICT;
</programlisting>
</para>
<para>
To rename an existing column:
<programlisting>
......@@ -420,38 +478,6 @@ ALTER TABLE distributors ADD PRIMARY KEY (dist_id);
The <literal>ALTER COLUMN</literal> form is in full compliance.
</para>
<para>
SQL92 specifies some additional capabilities for <command>ALTER TABLE</command>
statement which are not yet directly supported by <productname>PostgreSQL</productname>:
<variablelist>
<varlistentry>
<term>
<synopsis>
ALTER TABLE <replaceable class="PARAMETER">table</replaceable> DROP [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { RESTRICT | CASCADE }
</synopsis>
</term>
<listitem>
<para>
Removes a column from a table.
Currently, to remove an existing column the table must be
recreated and reloaded:
<programlisting>
CREATE TABLE temp AS SELECT did, city FROM distributors;
DROP TABLE distributors;
CREATE TABLE distributors (
did DECIMAL(3) DEFAULT 1,
name VARCHAR(40) NOT NULL
);
INSERT INTO distributors SELECT * FROM temp;
DROP TABLE temp;
</programlisting>
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
The clauses to rename tables, columns, indexes, and sequences are
<productname>PostgreSQL</productname> extensions from SQL92.
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.34 2002/07/30 16:55:05 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.35 2002/08/02 18:15:04 tgl Exp $
PostgreSQL documentation
-->
......@@ -21,16 +21,14 @@ PostgreSQL documentation
<date>1999-12-11</date>
</refsynopsisdivinfo>
<synopsis>
COPY <replaceable class="parameter">table</replaceable>
[ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
COPY <replaceable class="parameter">table</replaceable> [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
FROM { '<replaceable class="parameter">filename</replaceable>' | <filename>stdin</filename> }
[ [ WITH ]
[ BINARY ]
[ OIDS ]
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
[ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ] ]
COPY <replaceable class="parameter">table</replaceable>
[ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
COPY <replaceable class="parameter">table</replaceable> [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
TO { '<replaceable class="parameter">filename</replaceable>' | <filename>stdout</filename> }
[ [ WITH ]
[ BINARY ]
......@@ -201,10 +199,10 @@ ERROR: <replaceable>reason</replaceable>
<para>
If a list of columns is specified, <command>COPY</command> will
only copy the data in the specified columns to or from the table.
If there are any columns in the table that are not in the table,
<command>COPY FROM</command> will insert the default value for
that column.
only copy the data in the specified columns to or from the file.
If there are any columns in the table that are not in the file,
<command>COPY FROM</command> will insert the default values for
those columns.
</para>
<para>
......@@ -266,8 +264,8 @@ ERROR: <replaceable>reason</replaceable>
</para>
<para>
<command>COPY FROM</command> will invoke any triggers or check
constraints. However, it will not invoke rules.
<command>COPY FROM</command> will invoke any triggers and check
constraints on the destination table. However, it will not invoke rules.
</para>
<para>
......@@ -330,12 +328,9 @@ ERROR: <replaceable>reason</replaceable>
The attribute values themselves are strings generated by the
output function, or acceptable to the input function, of each
attribute's data type. The specified null-value string is used in
place of attributes that are NULL. When using <command>COPY
FROM</command> without a column list, each row of the input file
must contain data for each attribute in the table: no missing data
is allowed. Similarly, <command>COPY FROM</command> will raise
an error if it encounters any data in the input file that would
not be inserted into the table: extra data is not allowed.
place of attributes that are NULL.
<command>COPY FROM</command> will raise an error if any line of the
input file contains more or fewer columns than are expected.
</para>
<para>
If OIDS is specified, the OID is read or written as the first column,
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.144 2002/07/29 22:14:10 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.145 2002/08/02 18:15:04 tgl Exp $
-->
<appendix id="release">
......@@ -24,6 +24,8 @@ CDATA means the content is "SGML-free", so you can write without
worries about funny characters.
-->
<literallayout><![CDATA[
COPY accepts a list of columns to copy
ALTER TABLE DROP COLUMN
CREATE OPERATOR CLASS/DROP OPERATOR CLASS
CREATE CAST/DROP CAST
Sequences created by SERIAL column definitions now auto-drop with the column
......
......@@ -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);
}
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);
}
......
......@@ -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 */
......
This diff is collapsed.
......@@ -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);
......
This diff is collapsed.
......@@ -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)
{
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;
......
......@@ -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;
}
......@@ -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
*
......
......@@ -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
*
......
......@@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.278 2002/07/31 17:19:52 tgl Exp $
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.279 2002/08/02 18:15:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -863,13 +863,15 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
{
appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;",
fmtQualifiedId(tbinfo->relnamespace->nspname,
classname),column_list);
classname),
column_list);
}
else
{
appendPQExpBuffer(q, "COPY %s %s TO stdout;",
fmtQualifiedId(tbinfo->relnamespace->nspname,
classname), column_list);
classname),
column_list);
}
res = PQexec(g_conn, q->data);
if (!res ||
......@@ -1193,10 +1195,13 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
if (!dumpData)
{
/* Dump/restore using COPY */
const char *column_list;
dumpFn = dumpClasses_nodumpData;
column_list = fmtCopyColumnList(&(tblinfo[i]));
sprintf(copyBuf, "COPY %s %s %sFROM stdin;\n",
fmtQualifiedId(tblinfo[i].relnamespace->nspname,tblinfo[i].relname),
fmtCopyColumnList(&(tblinfo[i])),
fmtId(tblinfo[i].relname, force_quotes),
column_list,
(oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
copyStmt = copyBuf;
}
......@@ -2347,6 +2352,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int i_attstattarget;
int i_attnotnull;
int i_atthasdef;
int i_attisdropped;
PGresult *res;
int ntups;
bool hasdefaults;
......@@ -2386,7 +2392,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
if (g_fout->remoteVersion >= 70300)
{
appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, attstattarget, "
"attnotnull, atthasdef, "
"attnotnull, atthasdef, attisdropped, "
"pg_catalog.format_type(atttypid,atttypmod) as atttypname "
"from pg_catalog.pg_attribute a "
"where attrelid = '%s'::pg_catalog.oid "
......@@ -2402,7 +2408,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
* explicitly set or was just a default.
*/
appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, -1 as attstattarget, "
"attnotnull, atthasdef, "
"attnotnull, atthasdef, false as attisdropped, "
"format_type(atttypid,atttypmod) as atttypname "
"from pg_attribute a "
"where attrelid = '%s'::oid "
......@@ -2414,7 +2420,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
{
/* format_type not available before 7.1 */
appendPQExpBuffer(q, "SELECT attnum, attname, atttypmod, -1 as attstattarget, "
"attnotnull, atthasdef, "
"attnotnull, atthasdef, false as attisdropped, "
"(select typname from pg_type where oid = atttypid) as atttypname "
"from pg_attribute a "
"where attrelid = '%s'::oid "
......@@ -2439,12 +2445,14 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_attstattarget = PQfnumber(res, "attstattarget");
i_attnotnull = PQfnumber(res, "attnotnull");
i_atthasdef = PQfnumber(res, "atthasdef");
i_attisdropped = PQfnumber(res, "attisdropped");
tblinfo[i].numatts = ntups;
tblinfo[i].attnames = (char **) malloc(ntups * sizeof(char *));
tblinfo[i].atttypnames = (char **) malloc(ntups * sizeof(char *));
tblinfo[i].atttypmod = (int *) malloc(ntups * sizeof(int));
tblinfo[i].attstattarget = (int *) malloc(ntups * sizeof(int));
tblinfo[i].attisdropped = (bool *) malloc(ntups * sizeof(bool));
tblinfo[i].notnull = (bool *) malloc(ntups * sizeof(bool));
tblinfo[i].adef_expr = (char **) malloc(ntups * sizeof(char *));
tblinfo[i].inhAttrs = (bool *) malloc(ntups * sizeof(bool));
......@@ -2458,6 +2466,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tblinfo[i].atttypnames[j] = strdup(PQgetvalue(res, j, i_atttypname));
tblinfo[i].atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
tblinfo[i].attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
tblinfo[i].attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
tblinfo[i].notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
tblinfo[i].adef_expr[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
......@@ -4999,8 +5008,8 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
actual_atts = 0;
for (j = 0; j < tbinfo->numatts; j++)
{
/* Is this one of the table's own attrs ? */
if (!tbinfo->inhAttrs[j])
/* Is this one of the table's own attrs, and not dropped ? */
if (!tbinfo->inhAttrs[j] && !tbinfo->attisdropped[j])
{
/* Format properly if not first attr */
if (actual_atts > 0)
......@@ -5161,7 +5170,8 @@ dumpOneTable(Archive *fout, TableInfo *tbinfo, TableInfo *g_tblinfo)
*/
for (j = 0; j < tbinfo->numatts; j++)
{
if (tbinfo->attstattarget[j] >= 0)
if (tbinfo->attstattarget[j] >= 0 &&
!tbinfo->attisdropped[j])
{
appendPQExpBuffer(q, "ALTER TABLE %s ",
fmtId(tbinfo->relname, force_quotes));
......@@ -6274,7 +6284,7 @@ fmtQualifiedId(const char *schema, const char *id)
}
/*
* return a column list clause for the qualified relname.
* return a column list clause for the given relation.
* returns an empty string if the remote server is older than
* 7.3.
*/
......@@ -6284,9 +6294,11 @@ fmtCopyColumnList(const TableInfo* ti)
static PQExpBuffer q = NULL;
int numatts = ti->numatts;
char** attnames = ti->attnames;
bool* attisdropped = ti->attisdropped;
bool needComma;
int i;
if (g_fout->remoteVersion < 70300 )
if (g_fout->remoteVersion < 70300)
return "";
if (q) /* first time through? */
......@@ -6295,15 +6307,18 @@ fmtCopyColumnList(const TableInfo* ti)
q = createPQExpBuffer();
resetPQExpBuffer(q);
appendPQExpBuffer(q,"(");
appendPQExpBuffer(q, "(");
needComma = false;
for (i = 0; i < numatts; i++)
{
if( i > 0 )
appendPQExpBuffer(q,",");
appendPQExpBuffer(q, fmtId(attnames[i], force_quotes));
if (attisdropped[i])
continue;
if (needComma)
appendPQExpBuffer(q, ",");
appendPQExpBuffer(q, "%s", fmtId(attnames[i], force_quotes));
needComma = true;
}
appendPQExpBuffer(q, ")");
return q->data;
}
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_dump.h,v 1.93 2002/07/31 17:19:53 tgl Exp $
* $Id: pg_dump.h,v 1.94 2002/08/02 18:15:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -128,6 +128,7 @@ typedef struct _tableInfo
* all interesting tables so that we can tell which constraints were
* inherited.
*/
bool *attisdropped; /* true if attr is dropped; don't dump it */
bool *notnull; /* Not null constraints on attributes */
char **adef_expr; /* DEFAULT expressions */
bool *inhAttrs; /* true if each attribute is inherited */
......
......@@ -3,7 +3,7 @@
*
* Copyright 2000 by PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.56 2002/07/20 05:57:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.57 2002/08/02 18:15:08 tgl Exp $
*/
#include "postgres_fe.h"
#include "describe.h"
......@@ -536,7 +536,7 @@ describeTableDetails(const char *name, bool desc)
appendPQExpBuffer(&buf, "\nFROM pg_class c, pg_attribute a");
if (tableinfo.relkind == 'i')
appendPQExpBuffer(&buf, ", pg_index i");
appendPQExpBuffer(&buf, "\nWHERE c.relname = '%s'\n AND a.attnum > 0 AND a.attrelid = c.oid", name);
appendPQExpBuffer(&buf, "\nWHERE c.relname = '%s'\n AND a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid", name);
if (tableinfo.relkind == 'i')
appendPQExpBuffer(&buf, " AND a.attrelid = i.indexrelid");
appendPQExpBuffer(&buf, "\nORDER BY a.attnum");
......
......@@ -3,7 +3,7 @@
*
* Copyright 2000 by PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.53 2002/07/31 17:19:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.54 2002/08/02 18:15:09 tgl Exp $
*/
/*----------------------------------------------------------------------
......@@ -156,7 +156,7 @@ pgsql_thing_t words_after_create[] = {
#define Query_for_list_of_tables words_after_create[9].query
#define Query_for_list_of_indexes words_after_create[4].query
#define Query_for_list_of_databases words_after_create[1].query
#define Query_for_list_of_attributes "SELECT a.attname FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and substr(a.attname,1,%d)='%s' and c.relname='%s'"
#define Query_for_list_of_attributes "SELECT a.attname FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and not a.attisdropped and substr(a.attname,1,%d)='%s' and c.relname='%s'"
#define Query_for_list_of_users words_after_create[14].query
/* A couple of macros to ease typing. You can use these to complete the given
......
......@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.143 2002/07/25 10:07:12 ishii Exp $
* $Id: catversion.h,v 1.144 2002/08/02 18:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200207251
#define CATALOG_VERSION_NO 200208011
#endif
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: heap.h,v 1.54 2002/07/15 16:33:31 tgl Exp $
* $Id: heap.h,v 1.55 2002/08/02 18:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -63,6 +63,7 @@ extern int RemoveRelConstraints(Relation rel, const char *constrName,
extern void DeleteRelationTuple(Oid relid);
extern void DeleteAttributeTuples(Oid relid);
extern void RemoveAttributeById(Oid relid, AttrNumber attnum);
extern void RemoveAttrDefault(Oid relid, AttrNumber attnum,
DropBehavior behavior, bool complain);
extern void RemoveAttrDefaultById(Oid attrdefId);
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_class.h,v 1.69 2002/07/24 19:11:12 petere Exp $
* $Id: pg_class.h,v 1.70 2002/08/02 18:15:09 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
......@@ -136,7 +136,7 @@ typedef FormData_pg_class *Form_pg_class;
DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 f f r 20 0 0 0 0 0 t f f f _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 15 0 0 0 0 0 f f f f _null_ ));
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 f f r 16 0 0 0 0 0 f f f f _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 f f r 15 0 0 0 0 0 t f f f _null_ ));
DESCR("");
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: executor.h,v 1.72 2002/07/20 15:12:55 tgl Exp $
* $Id: executor.h,v 1.73 2002/08/02 18:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -159,7 +159,6 @@ extern void ExecAssignScanType(CommonScanState *csstate,
TupleDesc tupDesc, bool shouldFree);
extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
CommonScanState *csstate);
extern Form_pg_attribute ExecGetTypeInfo(Relation relDesc);
extern ExprContext *MakeExprContext(TupleTableSlot *slot,
MemoryContext queryContext);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_relation.h,v 1.34 2002/06/20 20:29:51 momjian Exp $
* $Id: parse_relation.h,v 1.35 2002/08/02 18:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -58,7 +58,7 @@ extern RangeTblEntry *addImplicitRTE(ParseState *pstate, RangeVar *relation);
extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
List **colnames, List **colvars);
extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
extern int attnameAttNum(Relation rd, char *a);
extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK);
extern Name attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid);
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: lsyscache.h,v 1.55 2002/07/12 18:43:19 tgl Exp $
* $Id: lsyscache.h,v 1.56 2002/08/02 18:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -18,9 +18,8 @@
extern bool op_in_opclass(Oid opno, Oid opclass);
extern bool op_requires_recheck(Oid opno, Oid opclass);
extern char *get_attname(Oid relid, AttrNumber attnum);
extern AttrNumber get_attnum(Oid relid, char *attname);
extern AttrNumber get_attnum(Oid relid, const char *attname);
extern Oid get_atttype(Oid relid, AttrNumber attnum);
extern bool get_attisset(Oid relid, char *attname);
extern int32 get_atttypmod(Oid relid, AttrNumber attnum);
extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
Oid *typid, int32 *typmod);
......
......@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: syscache.h,v 1.51 2002/07/25 10:07:13 ishii Exp $
* $Id: syscache.h,v 1.52 2002/08/02 18:15:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -78,6 +78,10 @@ extern bool SearchSysCacheExists(int cacheId,
extern Oid GetSysCacheOid(int cacheId,
Datum key1, Datum key2, Datum key3, Datum key4);
extern HeapTuple SearchSysCacheAttName(Oid relid, const char *attname);
extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname);
extern bool SearchSysCacheExistsAttName(Oid relid, const char *attname);
extern Datum SysCacheGetAttr(int cacheId, HeapTuple tup,
AttrNumber attributeNumber, bool *isNull);
......
......@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.42 2002/06/15 19:54:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.43 2002/08/02 18:15:09 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
......@@ -1037,10 +1037,7 @@ plpgsql_parse_dblwordtype(char *string)
/*
* Fetch the named table field and it's type
*/
attrtup = SearchSysCache(ATTNAME,
ObjectIdGetDatum(classOid),
PointerGetDatum(word2),
0, 0);
attrtup = SearchSysCacheAttName(classOid, word2);
if (!HeapTupleIsValid(attrtup))
{
ReleaseSysCache(classtup);
......
This diff is collapsed.
......@@ -29,26 +29,44 @@ COPY x (b, d) from stdin;
COPY x (a, b, c, d, e) from stdin;
-- non-existent column in column list: should fail
COPY x (xyz) from stdin;
ERROR: COPY: Specified column "xyz" does not exist
ERROR: Relation "x" has no column "xyz"
-- too many columns in column list: should fail
COPY x (a, b, c, d, e, d, c) from stdin;
ERROR: COPY: Too many columns specified
ERROR: Attribute "d" specified more than once
-- missing data: should fail
COPY x from stdin;
ERROR: copy: line 1, COPY TEXT: Missing data for attribute 1
ERROR: copy: line 1, Missing data for column "b"
lost synchronization with server, resetting connection
COPY x from stdin;
ERROR: copy: line 1, COPY TEXT: Missing data for attribute 4
ERROR: copy: line 1, Missing data for column "e"
lost synchronization with server, resetting connection
COPY x from stdin;
ERROR: copy: line 1, COPY TEXT: Missing data for attribute 4
ERROR: copy: line 1, Missing data for column "e"
lost synchronization with server, resetting connection
-- extra data: should fail
COPY x from stdin;
ERROR: copy: line 1, COPY TEXT: Extra data encountered
ERROR: copy: line 1, Extra data after last expected column
lost synchronization with server, resetting connection
-- various COPY options: delimiters, oids, NULL string
COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
-- check results of copy in
SELECT * FROM x;
a | b | c | d | e
-------+----+-------+--------+----------------------
10000 | 21 | 31 | 41 | before trigger fired
10001 | 22 | 32 | 42 | before trigger fired
10002 | 23 | 33 | 43 | before trigger fired
10003 | 24 | 34 | 44 | before trigger fired
10004 | 25 | 35 | 45 | before trigger fired
10005 | 26 | 36 | 46 | before trigger fired
6 | | 45 | 80 | before trigger fired
1 | 1 | stuff | test_1 | after trigger fired
2 | 2 | stuff | test_2 | after trigger fired
3 | 3 | stuff | test_3 | after trigger fired
4 | 4 | stuff | test_4 | after trigger fired
5 | 5 | stuff | test_5 | after trigger fired
(12 rows)
-- COPY w/ oids on a table w/o oids should fail
CREATE TABLE no_oids (
a int,
......@@ -61,6 +79,7 @@ COPY no_oids FROM stdin WITH OIDS;
ERROR: COPY: table "no_oids" does not have OIDs
COPY no_oids TO stdout WITH OIDS;
ERROR: COPY: table "no_oids" does not have OIDs
-- check copy out
COPY x TO stdout;
10000 21 31 41 before trigger fired
10001 22 32 42 before trigger fired
......@@ -87,6 +106,19 @@ stuff after trigger fired
stuff after trigger fired
stuff after trigger fired
stuff after trigger fired
COPY x (b, e) TO stdout WITH NULL 'I''m null';
21 before trigger fired
22 before trigger fired
23 before trigger fired
24 before trigger fired
25 before trigger fired
26 before trigger fired
I'm null before trigger fired
1 after trigger fired
2 after trigger fired
3 after trigger fired
4 after trigger fired
5 after trigger fired
DROP TABLE x;
DROP FUNCTION fn_x_before();
DROP FUNCTION fn_x_after();
......@@ -571,3 +571,163 @@ select * from def_view_test;
drop rule def_view_test_ins on def_view_test;
drop view def_view_test;
drop table def_test;
-- alter table / drop column tests
-- try altering system catalogs, should fail
alter table pg_class drop column relname;
-- try altering non-existent table, should fail
alter table foo drop column bar;
-- test dropping columns
create table atacc1 (a int4 not null, b int4, c int4 not null, d int4);
insert into atacc1 values (1, 2, 3, 4);
alter table atacc1 drop a;
alter table atacc1 drop a;
-- SELECTs
select * from atacc1;
select * from atacc1 order by a;
select * from atacc1 order by "........pg.dropped.1........";
select * from atacc1 group by a;
select * from atacc1 group by "........pg.dropped.1........";
select atacc1.* from atacc1;
select a from atacc1;
select atacc1.a from atacc1;
select b,c,d from atacc1;
select a,b,c,d from atacc1;
select * from atacc1 where a = 1;
select "........pg.dropped.1........" from atacc1;
select atacc1."........pg.dropped.1........" from atacc1;
select "........pg.dropped.1........",b,c,d from atacc1;
select * from atacc1 where "........pg.dropped.1........" = 1;
-- UPDATEs
update atacc1 set a = 3;
update atacc1 set b = 2 where a = 3;
update atacc1 set "........pg.dropped.1........" = 3;
update atacc1 set b = 2 where "........pg.dropped.1........" = 3;
-- INSERTs
insert into atacc1 values (10, 11, 12, 13);
insert into atacc1 values (default, 11, 12, 13);
insert into atacc1 values (11, 12, 13);
insert into atacc1 (a) values (10);
insert into atacc1 (a) values (default);
insert into atacc1 (a,b,c,d) values (10,11,12,13);
insert into atacc1 (a,b,c,d) values (default,11,12,13);
insert into atacc1 (b,c,d) values (11,12,13);
insert into atacc1 ("........pg.dropped.1........") values (10);
insert into atacc1 ("........pg.dropped.1........") values (default);
insert into atacc1 ("........pg.dropped.1........",b,c,d) values (10,11,12,13);
insert into atacc1 ("........pg.dropped.1........",b,c,d) values (default,11,12,13);
-- DELETEs
delete from atacc1 where a = 3;
delete from atacc1 where "........pg.dropped.1........" = 3;
delete from atacc1;
-- try dropping a non-existent column, should fail
alter table atacc1 drop bar;
-- try dropping the oid column, should fail
alter table atacc1 drop oid;
-- try creating a view and altering that, should fail
create view myview as select * from atacc1;
select * from myview;
alter table myview drop d;
drop view myview;
-- test some commands to make sure they fail on the dropped column
analyze atacc1(a);
analyze atacc1("........pg.dropped.1........");
vacuum analyze atacc1(a);
vacuum analyze atacc1("........pg.dropped.1........");
comment on column atacc1.a is 'testing';
comment on column atacc1."........pg.dropped.1........" is 'testing';
alter table atacc1 alter a set storage plain;
alter table atacc1 alter "........pg.dropped.1........" set storage plain;
alter table atacc1 alter a set statistics 0;
alter table atacc1 alter "........pg.dropped.1........" set statistics 0;
alter table atacc1 alter a set default 3;
alter table atacc1 alter "........pg.dropped.1........" set default 3;
alter table atacc1 alter a drop default;
alter table atacc1 alter "........pg.dropped.1........" drop default;
alter table atacc1 alter a set not null;
alter table atacc1 alter "........pg.dropped.1........" set not null;
alter table atacc1 alter a drop not null;
alter table atacc1 alter "........pg.dropped.1........" drop not null;
alter table atacc1 rename a to x;
alter table atacc1 rename "........pg.dropped.1........" to x;
alter table atacc1 add primary key(a);
alter table atacc1 add primary key("........pg.dropped.1........");
alter table atacc1 add unique(a);
alter table atacc1 add unique("........pg.dropped.1........");
alter table atacc1 add check (a > 3);
alter table atacc1 add check ("........pg.dropped.1........" > 3);
create table atacc2 (id int4 unique);
alter table atacc1 add foreign key (a) references atacc2(id);
alter table atacc1 add foreign key ("........pg.dropped.1........") references atacc2(id);
alter table atacc2 add foreign key (id) references atacc1(a);
alter table atacc2 add foreign key (id) references atacc1("........pg.dropped.1........");
drop table atacc2;
create index "testing_idx" on atacc1(a);
create index "testing_idx" on atacc1("........pg.dropped.1........");
-- test create as and select into
insert into atacc1 values (21, 22, 23);
create table test1 as select * from atacc1;
select * from test1;
drop table test1;
select * into test2 from atacc1;
select * from test2;
drop table test2;
-- try dropping all columns
alter table atacc1 drop c;
alter table atacc1 drop d;
alter table atacc1 drop b;
select * from atacc1;
drop table atacc1;
-- test inheritance
create table parent (a int, b int, c int);
insert into parent values (1, 2, 3);
alter table parent drop a;
create table child (d varchar(255)) inherits (parent);
insert into child values (12, 13, 'testing');
select * from parent;
select * from child;
alter table parent drop c;
select * from parent;
select * from child;
drop table child;
drop table parent;
-- test copy in/out
create table test (a int4, b int4, c int4);
insert into test values (1,2,3);
alter table test drop a;
copy test to stdout;
copy test(a) to stdout;
copy test("........pg.dropped.1........") to stdout;
copy test from stdin;
10 11 12
\.
select * from test;
copy test from stdin;
21 22
\.
select * from test;
copy test(a) from stdin;
copy test("........pg.dropped.1........") from stdin;
copy test(b,c) from stdin;
31 32
\.
select * from test;
drop table test;
......@@ -76,6 +76,9 @@ COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
500000,x,45,80,90
\.
-- check results of copy in
SELECT * FROM x;
-- COPY w/ oids on a table w/o oids should fail
CREATE TABLE no_oids (
a int,
......@@ -89,8 +92,11 @@ INSERT INTO no_oids (a, b) VALUES (20, 30);
COPY no_oids FROM stdin WITH OIDS;
COPY no_oids TO stdout WITH OIDS;
-- check copy out
COPY x TO stdout;
COPY x (c, e) TO stdout;
COPY x (b, e) TO stdout WITH NULL 'I''m null';
DROP TABLE x;
DROP FUNCTION fn_x_before();
DROP FUNCTION fn_x_after();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment