Commit 077db40f authored by Tom Lane's avatar Tom Lane

ALTER TABLE rewrite. New cool stuff:

* ALTER ... ADD COLUMN with defaults and NOT NULL constraints works per SQL
spec.  A default is implemented by rewriting the table with the new value
stored in each row.

* ALTER COLUMN TYPE.  You can change a column's datatype to anything you
want, so long as you can specify how to convert the old value.  Rewrites
the table.  (Possible future improvement: optimize no-op conversions such
as varchar(N) to varchar(N+1).)

* Multiple ALTER actions in a single ALTER TABLE command.  You can perform
any number of column additions, type changes, and constraint additions with
only one pass over the table contents.

Basic documentation provided in ALTER TABLE ref page, but some more docs
work is needed.

Original patch from Rod Taylor, additional work from Tom Lane.
parent 3e3cb0a1
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.68 2004/03/24 09:49:20 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.69 2004/05/05 04:48:45 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -21,31 +21,26 @@ PostgreSQL documentation ...@@ -21,31 +21,26 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ] ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ] <replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ] ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable class="PARAMETER">new_column</replaceable>
ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
where <replaceable class="PARAMETER">action</replaceable> is one of:
ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
DROP [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ] DROP [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ] ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TYPE <replaceable class="PARAMETER">type</replaceable> [ USING <replaceable class="PARAMETER">expression</replaceable> ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET DEFAULT <replaceable class="PARAMETER">expression</replaceable> | DROP DEFAULT } ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET DEFAULT <replaceable class="PARAMETER">expression</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ] ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET | DROP } NOT NULL
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
SET WITHOUT OIDS
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable
class="PARAMETER">new_column</replaceable>
ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
ADD <replaceable class="PARAMETER">table_constraint</replaceable> ADD <replaceable class="PARAMETER">table_constraint</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ] DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
ALTER TABLE <replaceable class="PARAMETER">name</replaceable> SET WITHOUT OIDS
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable> CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -81,6 +76,23 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> ...@@ -81,6 +76,23 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>ALTER COLUMN TYPE</literal></term>
<listitem>
<para>
This form changes the type of a column of a table. Indexes and
simple table constraints involving the column will be automatically
converted to use the new column type by reparsing the originally
supplied expression. The optional <literal>USING</literal>
clause specifies how to compute the new column value from the old;
if omitted, the default conversion is the same as an assignment
cast from old data type to new. A <literal>USING</literal>
clause must be provided if there is no implicit or assignment
cast from old to new type.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>SET</literal>/<literal>DROP DEFAULT</literal></term> <term><literal>SET</literal>/<literal>DROP DEFAULT</literal></term>
<listitem> <listitem>
...@@ -147,53 +159,42 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> ...@@ -147,53 +159,42 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><literal>SET WITHOUT OIDS</literal></term> <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term>
<listitem> <listitem>
<para> <para>
This form removes the <literal>oid</literal> system column from the This form adds a new constraint to a table using the same syntax as
table. This is exactly equivalent to <xref linkend="SQL-CREATETABLE" endterm="SQL-CREATETABLE-TITLE">.
<literal>DROP COLUMN oid RESTRICT</literal>,
except that it will not complain if there is already no
<literal>oid</literal> column.
</para>
<para>
Note that there is no variant of <command>ALTER TABLE</command>
that allows OIDs to be restored to a table once they have been
removed.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><literal>RENAME</literal></term> <term><literal>DROP CONSTRAINT</literal></term>
<listitem> <listitem>
<para> <para>
The <literal>RENAME</literal> forms change the name of a table This form drops constraints on a table.
(or an index, sequence, or view) or the name of an individual column in Currently, constraints on tables are not required to have unique
a table. There is no effect on the stored data. names, so there may be more than one constraint matching the specified
name. All matching constraints will be dropped.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term> <term><literal>SET WITHOUT OIDS</literal></term>
<listitem> <listitem>
<para> <para>
This form adds a new constraint to a table using the same syntax as This form removes the <literal>oid</literal> system column from the
<xref linkend="SQL-CREATETABLE" endterm="SQL-CREATETABLE-TITLE">. table. This is exactly equivalent to
<literal>DROP COLUMN oid RESTRICT</literal>,
except that it will not complain if there is already no
<literal>oid</literal> column.
</para> </para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>DROP CONSTRAINT</literal></term>
<listitem>
<para> <para>
This form drops constraints on a table. Note that there is no variant of <command>ALTER TABLE</command>
Currently, constraints on tables are not required to have unique that allows OIDs to be restored to a table once they have been
names, so there may be more than one constraint matching the specified removed.
name. All such constraints will be dropped.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -212,15 +213,34 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> ...@@ -212,15 +213,34 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
<term><literal>CLUSTER</literal></term> <term><literal>CLUSTER</literal></term>
<listitem> <listitem>
<para> <para>
This form marks a table for future <xref linkend="SQL-CLUSTER" endterm="sql-cluster-title"> This form selects the default controlling index for future <xref linkend="SQL-CLUSTER" endterm="sql-cluster-title">
operations. operations.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>RENAME</literal></term>
<listitem>
<para>
The <literal>RENAME</literal> forms change the name of a table
(or an index, sequence, or view) or the name of an individual column in
a table. There is no effect on the stored data.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</para> </para>
<para>
All the actions except <literal>RENAME</literal> can be combined into
a list of multiple alterations to apply in parallel. For example, it
is possible to add several columns and/or alter the type of several
columns in a single command. This is particularly useful with large
tables, since only one pass over the table need be made.
</para>
<para> <para>
You must own the table to use <command>ALTER TABLE</>; except for You must own the table to use <command>ALTER TABLE</>; except for
<command>ALTER TABLE OWNER</>, which may only be executed by a superuser. <command>ALTER TABLE OWNER</>, which may only be executed by a superuser.
...@@ -262,7 +282,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> ...@@ -262,7 +282,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
<term><replaceable class="PARAMETER">type</replaceable></term> <term><replaceable class="PARAMETER">type</replaceable></term>
<listitem> <listitem>
<para> <para>
Data type of the new column. Data type of the new column, or new data type for an existing
column.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -352,16 +373,27 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> ...@@ -352,16 +373,27 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</para> </para>
<para> <para>
In the current implementation of <literal>ADD COLUMN</literal>, When a column is added with <literal>ADD COLUMN</literal>, all existing
default and <literal>NOT NULL</> clauses for the new column are not supported. rows in the table are initialized with the column's default value
The new column always comes into being with all values null. (NULL if no <literal>DEFAULT</> clause is specified).
You can use the <literal>SET DEFAULT</literal> form </para>
of <command>ALTER TABLE</command> to set the default afterward.
(You may also want to update the already existing rows to the <para>
new default value, using Adding a column with a non-null default or changing the type of an
<xref linkend="sql-update" endterm="sql-update-title">.) existing column will require the entire table to be rewritten. This
If you want to mark the column non-null, use the <literal>SET NOT NULL</> may take a significant amount of time for a large table; and it will
form after you've entered non-null values for the column in all rows. temporarily require double the disk space.
</para>
<para>
Adding a <literal>CHECK</> or <literal>NOT NULL</> constraint requires
scanning the table to verify that existing rows meet the constraint.
</para>
<para>
The main reason for providing the option to specify multiple changes
in a single <command>ALTER TABLE</> is that multiple table scans or
rewrites can thereby be combined into a single pass over the table.
</para> </para>
<para> <para>
...@@ -381,9 +413,9 @@ VACUUM FULL table; ...@@ -381,9 +413,9 @@ VACUUM FULL table;
</para> </para>
<para> <para>
If a table has any descendant tables, it is not permitted to add If a table has any descendant tables, it is not permitted to add,
or rename a column in the parent table without doing the same to rename, or change the type of a column in the parent table without doing
the descendants. That is, <command>ALTER TABLE ONLY</command> the same to the descendants. That is, <command>ALTER TABLE ONLY</command>
will be rejected. This ensures that the descendants always have will be rejected. This ensures that the descendants always have
columns matching the parent. columns matching the parent.
</para> </para>
...@@ -427,6 +459,15 @@ ALTER TABLE distributors DROP COLUMN address RESTRICT; ...@@ -427,6 +459,15 @@ ALTER TABLE distributors DROP COLUMN address RESTRICT;
</programlisting> </programlisting>
</para> </para>
<para>
To change the types of two existing columns in one operation:
<programlisting>
ALTER TABLE distributors
ALTER COLUMN address TYPE varchar(80),
ALTER COLUMN name TYPE varchar(100);
</programlisting>
</para>
<para> <para>
To rename an existing column: To rename an existing column:
<programlisting> <programlisting>
...@@ -493,15 +534,11 @@ ALTER TABLE distributors ADD PRIMARY KEY (dist_id); ...@@ -493,15 +534,11 @@ ALTER TABLE distributors ADD PRIMARY KEY (dist_id);
<title>Compatibility</title> <title>Compatibility</title>
<para> <para>
The <literal>ADD COLUMN</literal> form conforms with the SQL The <literal>ADD</literal>, <literal>DROP</>, and <literal>SET DEFAULT</>
standard, with the exception that it does not support defaults and forms conform with the SQL standard. The other forms are
not-null constraints, as explained above. The <literal>ALTER
COLUMN</literal> form is in full conformance.
</para>
<para>
The clauses to rename tables, columns, indexes, views, and sequences are
<productname>PostgreSQL</productname> extensions of the SQL standard. <productname>PostgreSQL</productname> extensions of the SQL standard.
Also, the ability to specify more than one manipulation in a single
<command>ALTER TABLE</> command is an extension.
</para> </para>
<para> <para>
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.65 2004/03/23 19:35:16 tgl Exp $ * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.66 2004/05/05 04:48:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -241,7 +241,9 @@ Boot_DeclareIndexStmt: ...@@ -241,7 +241,9 @@ Boot_DeclareIndexStmt:
LexIDStr($3), LexIDStr($3),
LexIDStr($7), LexIDStr($7),
$9, $9,
false, false, false, NULL, NIL); NULL, NIL,
false, false, false,
false, false, true, false);
do_end(); do_end();
} }
; ;
...@@ -255,7 +257,9 @@ Boot_DeclareUniqueIndexStmt: ...@@ -255,7 +257,9 @@ Boot_DeclareUniqueIndexStmt:
LexIDStr($4), LexIDStr($4),
LexIDStr($8), LexIDStr($8),
$10, $10,
true, false, false, NULL, NIL); NULL, NIL,
true, false, false,
false, false, true, false);
do_end(); do_end();
} }
; ;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.34 2003/11/29 19:51:42 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.35 2004/05/05 04:48:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -48,25 +48,6 @@ ...@@ -48,25 +48,6 @@
#include "utils/syscache.h" #include "utils/syscache.h"
/* This enum covers all system catalogs whose OIDs can appear in classid. */
typedef enum ObjectClasses
{
OCLASS_CLASS, /* pg_class */
OCLASS_PROC, /* pg_proc */
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
OCLASS_LANGUAGE, /* pg_language */
OCLASS_OPERATOR, /* pg_operator */
OCLASS_OPCLASS, /* pg_opclass */
OCLASS_REWRITE, /* pg_rewrite */
OCLASS_TRIGGER, /* pg_trigger */
OCLASS_SCHEMA, /* pg_namespace */
MAX_OCLASS /* MUST BE LAST */
} ObjectClasses;
/* expansible list of ObjectAddresses */ /* expansible list of ObjectAddresses */
typedef struct typedef struct
{ {
...@@ -113,7 +94,7 @@ static bool find_expr_references_walker(Node *node, ...@@ -113,7 +94,7 @@ static bool find_expr_references_walker(Node *node,
static void eliminate_duplicate_dependencies(ObjectAddresses *addrs); static void eliminate_duplicate_dependencies(ObjectAddresses *addrs);
static int object_address_comparator(const void *a, const void *b); static int object_address_comparator(const void *a, const void *b);
static void init_object_addresses(ObjectAddresses *addrs); static void init_object_addresses(ObjectAddresses *addrs);
static void add_object_address(ObjectClasses oclass, Oid objectId, int32 subId, static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
ObjectAddresses *addrs); ObjectAddresses *addrs);
static void add_exact_object_address(const ObjectAddress *object, static void add_exact_object_address(const ObjectAddress *object,
ObjectAddresses *addrs); ObjectAddresses *addrs);
...@@ -121,8 +102,6 @@ static bool object_address_present(const ObjectAddress *object, ...@@ -121,8 +102,6 @@ static bool object_address_present(const ObjectAddress *object,
ObjectAddresses *addrs); ObjectAddresses *addrs);
static void term_object_addresses(ObjectAddresses *addrs); static void term_object_addresses(ObjectAddresses *addrs);
static void init_object_classes(void); static void init_object_classes(void);
static ObjectClasses getObjectClass(const ObjectAddress *object);
static char *getObjectDescription(const ObjectAddress *object);
static void getRelationDescription(StringInfo buffer, Oid relid); static void getRelationDescription(StringInfo buffer, Oid relid);
...@@ -1238,7 +1217,7 @@ init_object_addresses(ObjectAddresses *addrs) ...@@ -1238,7 +1217,7 @@ init_object_addresses(ObjectAddresses *addrs)
* by catalog OID. * by catalog OID.
*/ */
static void static void
add_object_address(ObjectClasses oclass, Oid objectId, int32 subId, add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
ObjectAddresses *addrs) ObjectAddresses *addrs)
{ {
ObjectAddress *item; ObjectAddress *item;
...@@ -1350,7 +1329,7 @@ init_object_classes(void) ...@@ -1350,7 +1329,7 @@ init_object_classes(void)
* This function is needed just because some of the system catalogs do * This function is needed just because some of the system catalogs do
* not have hardwired-at-compile-time OIDs. * not have hardwired-at-compile-time OIDs.
*/ */
static ObjectClasses ObjectClass
getObjectClass(const ObjectAddress *object) getObjectClass(const ObjectAddress *object)
{ {
/* Easy for the bootstrapped catalogs... */ /* Easy for the bootstrapped catalogs... */
...@@ -1435,7 +1414,7 @@ getObjectClass(const ObjectAddress *object) ...@@ -1435,7 +1414,7 @@ getObjectClass(const ObjectAddress *object)
* *
* The result is a palloc'd string. * The result is a palloc'd string.
*/ */
static char * char *
getObjectDescription(const ObjectAddress *object) getObjectDescription(const ObjectAddress *object)
{ {
StringInfoData buffer; StringInfoData buffer;
...@@ -1447,18 +1426,18 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1447,18 +1426,18 @@ getObjectDescription(const ObjectAddress *object)
case OCLASS_CLASS: case OCLASS_CLASS:
getRelationDescription(&buffer, object->objectId); getRelationDescription(&buffer, object->objectId);
if (object->objectSubId != 0) if (object->objectSubId != 0)
appendStringInfo(&buffer, " column %s", appendStringInfo(&buffer, gettext(" column %s"),
get_relid_attribute_name(object->objectId, get_relid_attribute_name(object->objectId,
object->objectSubId)); object->objectSubId));
break; break;
case OCLASS_PROC: case OCLASS_PROC:
appendStringInfo(&buffer, "function %s", appendStringInfo(&buffer, gettext("function %s"),
format_procedure(object->objectId)); format_procedure(object->objectId));
break; break;
case OCLASS_TYPE: case OCLASS_TYPE:
appendStringInfo(&buffer, "type %s", appendStringInfo(&buffer, gettext("type %s"),
format_type_be(object->objectId)); format_type_be(object->objectId));
break; break;
...@@ -1488,7 +1467,7 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1488,7 +1467,7 @@ getObjectDescription(const ObjectAddress *object)
castForm = (Form_pg_cast) GETSTRUCT(tup); castForm = (Form_pg_cast) GETSTRUCT(tup);
appendStringInfo(&buffer, "cast from %s to %s", appendStringInfo(&buffer, gettext("cast from %s to %s"),
format_type_be(castForm->castsource), format_type_be(castForm->castsource),
format_type_be(castForm->casttarget)); format_type_be(castForm->casttarget));
...@@ -1525,13 +1504,13 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1525,13 +1504,13 @@ getObjectDescription(const ObjectAddress *object)
if (OidIsValid(con->conrelid)) if (OidIsValid(con->conrelid))
{ {
appendStringInfo(&buffer, "constraint %s on ", appendStringInfo(&buffer, gettext("constraint %s on "),
NameStr(con->conname)); NameStr(con->conname));
getRelationDescription(&buffer, con->conrelid); getRelationDescription(&buffer, con->conrelid);
} }
else else
{ {
appendStringInfo(&buffer, "constraint %s", appendStringInfo(&buffer, gettext("constraint %s"),
NameStr(con->conname)); NameStr(con->conname));
} }
...@@ -1550,7 +1529,7 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1550,7 +1529,7 @@ getObjectDescription(const ObjectAddress *object)
if (!HeapTupleIsValid(conTup)) if (!HeapTupleIsValid(conTup))
elog(ERROR, "cache lookup failed for conversion %u", elog(ERROR, "cache lookup failed for conversion %u",
object->objectId); object->objectId);
appendStringInfo(&buffer, "conversion %s", appendStringInfo(&buffer, gettext("conversion %s"),
NameStr(((Form_pg_conversion) GETSTRUCT(conTup))->conname)); NameStr(((Form_pg_conversion) GETSTRUCT(conTup))->conname));
ReleaseSysCache(conTup); ReleaseSysCache(conTup);
break; break;
...@@ -1587,7 +1566,7 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1587,7 +1566,7 @@ getObjectDescription(const ObjectAddress *object)
colobject.objectId = attrdef->adrelid; colobject.objectId = attrdef->adrelid;
colobject.objectSubId = attrdef->adnum; colobject.objectSubId = attrdef->adnum;
appendStringInfo(&buffer, "default for %s", appendStringInfo(&buffer, gettext("default for %s"),
getObjectDescription(&colobject)); getObjectDescription(&colobject));
systable_endscan(adscan); systable_endscan(adscan);
...@@ -1605,14 +1584,14 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1605,14 +1584,14 @@ getObjectDescription(const ObjectAddress *object)
if (!HeapTupleIsValid(langTup)) if (!HeapTupleIsValid(langTup))
elog(ERROR, "cache lookup failed for language %u", elog(ERROR, "cache lookup failed for language %u",
object->objectId); object->objectId);
appendStringInfo(&buffer, "language %s", appendStringInfo(&buffer, gettext("language %s"),
NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname)); NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname));
ReleaseSysCache(langTup); ReleaseSysCache(langTup);
break; break;
} }
case OCLASS_OPERATOR: case OCLASS_OPERATOR:
appendStringInfo(&buffer, "operator %s", appendStringInfo(&buffer, gettext("operator %s"),
format_operator(object->objectId)); format_operator(object->objectId));
break; break;
...@@ -1632,16 +1611,6 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1632,16 +1611,6 @@ getObjectDescription(const ObjectAddress *object)
object->objectId); object->objectId);
opcForm = (Form_pg_opclass) GETSTRUCT(opcTup); opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
/* Qualify the name if not visible in search path */
if (OpclassIsVisible(object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(opcForm->opcnamespace);
appendStringInfo(&buffer, "operator class %s",
quote_qualified_identifier(nspname,
NameStr(opcForm->opcname)));
amTup = SearchSysCache(AMOID, amTup = SearchSysCache(AMOID,
ObjectIdGetDatum(opcForm->opcamid), ObjectIdGetDatum(opcForm->opcamid),
0, 0, 0); 0, 0, 0);
...@@ -1650,7 +1619,15 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1650,7 +1619,15 @@ getObjectDescription(const ObjectAddress *object)
opcForm->opcamid); opcForm->opcamid);
amForm = (Form_pg_am) GETSTRUCT(amTup); amForm = (Form_pg_am) GETSTRUCT(amTup);
appendStringInfo(&buffer, " for %s", /* Qualify the name if not visible in search path */
if (OpclassIsVisible(object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(opcForm->opcnamespace);
appendStringInfo(&buffer, gettext("operator class %s for %s"),
quote_qualified_identifier(nspname,
NameStr(opcForm->opcname)),
NameStr(amForm->amname)); NameStr(amForm->amname));
ReleaseSysCache(amTup); ReleaseSysCache(amTup);
...@@ -1684,7 +1661,7 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1684,7 +1661,7 @@ getObjectDescription(const ObjectAddress *object)
rule = (Form_pg_rewrite) GETSTRUCT(tup); rule = (Form_pg_rewrite) GETSTRUCT(tup);
appendStringInfo(&buffer, "rule %s on ", appendStringInfo(&buffer, gettext("rule %s on "),
NameStr(rule->rulename)); NameStr(rule->rulename));
getRelationDescription(&buffer, rule->ev_class); getRelationDescription(&buffer, rule->ev_class);
...@@ -1719,7 +1696,7 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1719,7 +1696,7 @@ getObjectDescription(const ObjectAddress *object)
trig = (Form_pg_trigger) GETSTRUCT(tup); trig = (Form_pg_trigger) GETSTRUCT(tup);
appendStringInfo(&buffer, "trigger %s on ", appendStringInfo(&buffer, gettext("trigger %s on "),
NameStr(trig->tgname)); NameStr(trig->tgname));
getRelationDescription(&buffer, trig->tgrelid); getRelationDescription(&buffer, trig->tgrelid);
...@@ -1736,7 +1713,7 @@ getObjectDescription(const ObjectAddress *object) ...@@ -1736,7 +1713,7 @@ getObjectDescription(const ObjectAddress *object)
if (!nspname) if (!nspname)
elog(ERROR, "cache lookup failed for namespace %u", elog(ERROR, "cache lookup failed for namespace %u",
object->objectId); object->objectId);
appendStringInfo(&buffer, "schema %s", nspname); appendStringInfo(&buffer, gettext("schema %s"), nspname);
break; break;
} }
...@@ -1780,40 +1757,40 @@ getRelationDescription(StringInfo buffer, Oid relid) ...@@ -1780,40 +1757,40 @@ getRelationDescription(StringInfo buffer, Oid relid)
switch (relForm->relkind) switch (relForm->relkind)
{ {
case RELKIND_RELATION: case RELKIND_RELATION:
appendStringInfo(buffer, "table %s", appendStringInfo(buffer, gettext("table %s"),
relname); relname);
break; break;
case RELKIND_INDEX: case RELKIND_INDEX:
appendStringInfo(buffer, "index %s", appendStringInfo(buffer, gettext("index %s"),
relname); relname);
break; break;
case RELKIND_SPECIAL: case RELKIND_SPECIAL:
appendStringInfo(buffer, "special system relation %s", appendStringInfo(buffer, gettext("special system relation %s"),
relname); relname);
break; break;
case RELKIND_SEQUENCE: case RELKIND_SEQUENCE:
appendStringInfo(buffer, "sequence %s", appendStringInfo(buffer, gettext("sequence %s"),
relname); relname);
break; break;
case RELKIND_UNCATALOGED: case RELKIND_UNCATALOGED:
appendStringInfo(buffer, "uncataloged table %s", appendStringInfo(buffer, gettext("uncataloged table %s"),
relname); relname);
break; break;
case RELKIND_TOASTVALUE: case RELKIND_TOASTVALUE:
appendStringInfo(buffer, "toast table %s", appendStringInfo(buffer, gettext("toast table %s"),
relname); relname);
break; break;
case RELKIND_VIEW: case RELKIND_VIEW:
appendStringInfo(buffer, "view %s", appendStringInfo(buffer, gettext("view %s"),
relname); relname);
break; break;
case RELKIND_COMPOSITE_TYPE: case RELKIND_COMPOSITE_TYPE:
appendStringInfo(buffer, "composite type %s", appendStringInfo(buffer, gettext("composite type %s"),
relname); relname);
break; break;
default: default:
/* shouldn't get here */ /* shouldn't get here */
appendStringInfo(buffer, "relation %s", appendStringInfo(buffer, gettext("relation %s"),
relname); relname);
break; break;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.262 2004/04/01 21:28:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.263 2004/05/05 04:48:45 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -72,7 +72,6 @@ static void AddNewRelationType(const char *typeName, ...@@ -72,7 +72,6 @@ static void AddNewRelationType(const char *typeName,
char new_rel_kind, char new_rel_kind,
Oid new_type_oid); Oid new_type_oid);
static void RelationRemoveInheritance(Relation relation); static void RelationRemoveInheritance(Relation relation);
static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
static void StoreRelCheck(Relation rel, char *ccname, char *ccbin); static void StoreRelCheck(Relation rel, char *ccname, char *ccbin);
static void StoreConstraints(Relation rel, TupleDesc tupdesc); static void StoreConstraints(Relation rel, TupleDesc tupdesc);
static void SetRelationNumChecks(Relation rel, int numchecks); static void SetRelationNumChecks(Relation rel, int numchecks);
...@@ -1246,7 +1245,7 @@ heap_drop_with_catalog(Oid rid) ...@@ -1246,7 +1245,7 @@ heap_drop_with_catalog(Oid rid)
* Store a default expression for column attnum of relation rel. * Store a default expression for column attnum of relation rel.
* The expression must be presented as a nodeToString() string. * The expression must be presented as a nodeToString() string.
*/ */
static void void
StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin) StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin)
{ {
Node *expr; Node *expr;
...@@ -1483,16 +1482,20 @@ StoreConstraints(Relation rel, TupleDesc tupdesc) ...@@ -1483,16 +1482,20 @@ StoreConstraints(Relation rel, TupleDesc tupdesc)
* will be processed only if they are CONSTR_CHECK type and contain a "raw" * will be processed only if they are CONSTR_CHECK type and contain a "raw"
* expression. * expression.
* *
* Returns a list of CookedConstraint nodes that shows the cooked form of
* the default and constraint expressions added to the relation.
*
* NB: caller should have opened rel with AccessExclusiveLock, and should * NB: caller should have opened rel with AccessExclusiveLock, and should
* hold that lock till end of transaction. Also, we assume the caller has * hold that lock till end of transaction. Also, we assume the caller has
* done a CommandCounterIncrement if necessary to make the relation's catalog * done a CommandCounterIncrement if necessary to make the relation's catalog
* tuples visible. * tuples visible.
*/ */
void List *
AddRelationRawConstraints(Relation rel, AddRelationRawConstraints(Relation rel,
List *rawColDefaults, List *rawColDefaults,
List *rawConstraints) List *rawConstraints)
{ {
List *cookedConstraints = NIL;
char *relname = RelationGetRelationName(rel); char *relname = RelationGetRelationName(rel);
TupleDesc tupleDesc; TupleDesc tupleDesc;
TupleConstr *oldconstr; TupleConstr *oldconstr;
...@@ -1504,6 +1507,7 @@ AddRelationRawConstraints(Relation rel, ...@@ -1504,6 +1507,7 @@ AddRelationRawConstraints(Relation rel,
int constr_name_ctr = 0; int constr_name_ctr = 0;
List *listptr; List *listptr;
Node *expr; Node *expr;
CookedConstraint *cooked;
/* /*
* Get info about existing constraints. * Get info about existing constraints.
...@@ -1544,7 +1548,15 @@ AddRelationRawConstraints(Relation rel, ...@@ -1544,7 +1548,15 @@ AddRelationRawConstraints(Relation rel,
expr = cookDefault(pstate, colDef->raw_default, expr = cookDefault(pstate, colDef->raw_default,
atp->atttypid, atp->atttypmod, atp->atttypid, atp->atttypmod,
NameStr(atp->attname)); NameStr(atp->attname));
StoreAttrDefault(rel, colDef->attnum, nodeToString(expr)); StoreAttrDefault(rel, colDef->attnum, nodeToString(expr));
cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
cooked->contype = CONSTR_DEFAULT;
cooked->name = NULL;
cooked->attnum = colDef->attnum;
cooked->expr = expr;
cookedConstraints = lappend(cookedConstraints, cooked);
} }
/* /*
...@@ -1672,6 +1684,13 @@ AddRelationRawConstraints(Relation rel, ...@@ -1672,6 +1684,13 @@ AddRelationRawConstraints(Relation rel,
StoreRelCheck(rel, ccname, nodeToString(expr)); StoreRelCheck(rel, ccname, nodeToString(expr));
numchecks++; numchecks++;
cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
cooked->contype = CONSTR_CHECK;
cooked->name = ccname;
cooked->attnum = 0;
cooked->expr = expr;
cookedConstraints = lappend(cookedConstraints, cooked);
} }
/* /*
...@@ -1682,6 +1701,8 @@ AddRelationRawConstraints(Relation rel, ...@@ -1682,6 +1701,8 @@ AddRelationRawConstraints(Relation rel,
* (This is critical if we added defaults but not constraints.) * (This is critical if we added defaults but not constraints.)
*/ */
SetRelationNumChecks(rel, numchecks); SetRelationNumChecks(rel, numchecks);
return cookedConstraints;
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.228 2004/02/15 21:01:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.229 2004/05/05 04:48:45 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -470,7 +470,8 @@ index_create(Oid heapRelationId, ...@@ -470,7 +470,8 @@ index_create(Oid heapRelationId,
Oid *classObjectId, Oid *classObjectId,
bool primary, bool primary,
bool isconstraint, bool isconstraint,
bool allow_system_table_mods) bool allow_system_table_mods,
bool skip_build)
{ {
Relation heapRelation; Relation heapRelation;
Relation indexRelation; Relation indexRelation;
...@@ -721,21 +722,31 @@ index_create(Oid heapRelationId, ...@@ -721,21 +722,31 @@ index_create(Oid heapRelationId,
* If this is bootstrap (initdb) time, then we don't actually fill in * If this is bootstrap (initdb) time, then we don't actually fill in
* the index yet. We'll be creating more indexes and classes later, * the index yet. We'll be creating more indexes and classes later,
* so we delay filling them in until just before we're done with * so we delay filling them in until just before we're done with
* bootstrapping. Otherwise, we call the routine that constructs the * bootstrapping. Similarly, if the caller specified skip_build then
* index. * filling the index is delayed till later (ALTER TABLE can save work
* in some cases with this). Otherwise, we call the AM routine that
* constructs the index.
* *
* In normal processing mode, the heap and index relations are closed by * In normal processing mode, the heap and index relations are closed,
* index_build() --- but we continue to hold the ShareLock on the heap * but we continue to hold the ShareLock on the heap and the exclusive
* and the exclusive lock on the index that we acquired above, until * lock on the index that we acquired above, until end of transaction.
* end of transaction.
*/ */
if (IsBootstrapProcessingMode()) if (IsBootstrapProcessingMode())
{ {
index_register(heapRelationId, indexoid, indexInfo); index_register(heapRelationId, indexoid, indexInfo);
/* XXX shouldn't we close the heap and index rels here? */ /* XXX shouldn't we close the heap and index rels here? */
} }
else if (skip_build)
{
/* caller is responsible for filling the index later on */
relation_close(indexRelation, NoLock);
heap_close(heapRelation, NoLock);
}
else else
{
index_build(heapRelation, indexRelation, indexInfo); index_build(heapRelation, indexRelation, indexInfo);
/* index_build closes the passed rels */
}
return indexoid; return indexoid;
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.120 2004/03/23 19:35:16 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.121 2004/05/05 04:48:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -64,11 +64,7 @@ typedef struct ...@@ -64,11 +64,7 @@ typedef struct
static void cluster_rel(RelToCluster *rv, bool recheck); static void cluster_rel(RelToCluster *rv, bool recheck);
static Oid make_new_heap(Oid OIDOldHeap, const char *NewName);
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
static List *get_indexattr_list(Relation OldHeap, Oid OldIndex);
static void rebuild_indexes(Oid OIDOldHeap, List *indexes);
static void swap_relfilenodes(Oid r1, Oid r2);
static List *get_tables_to_cluster(MemoryContext cluster_context); static List *get_tables_to_cluster(MemoryContext cluster_context);
...@@ -479,7 +475,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid) ...@@ -479,7 +475,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
/* /*
* Create the new table that we will fill with correctly-ordered data. * Create the new table that we will fill with correctly-ordered data.
*/ */
static Oid Oid
make_new_heap(Oid OIDOldHeap, const char *NewName) make_new_heap(Oid OIDOldHeap, const char *NewName)
{ {
TupleDesc OldHeapDesc, TupleDesc OldHeapDesc,
...@@ -578,7 +574,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) ...@@ -578,7 +574,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
* Get the necessary info about the indexes of the relation and * Get the necessary info about the indexes of the relation and
* return a list of IndexAttrs structures. * return a list of IndexAttrs structures.
*/ */
static List * List *
get_indexattr_list(Relation OldHeap, Oid OldIndex) get_indexattr_list(Relation OldHeap, Oid OldIndex)
{ {
List *indexes = NIL; List *indexes = NIL;
...@@ -621,7 +617,7 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex) ...@@ -621,7 +617,7 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
* Create new indexes and swap the filenodes with old indexes. Then drop * Create new indexes and swap the filenodes with old indexes. Then drop
* the new index (carrying the old index filenode along). * the new index (carrying the old index filenode along).
*/ */
static void void
rebuild_indexes(Oid OIDOldHeap, List *indexes) rebuild_indexes(Oid OIDOldHeap, List *indexes)
{ {
List *elem; List *elem;
...@@ -646,10 +642,15 @@ rebuild_indexes(Oid OIDOldHeap, List *indexes) ...@@ -646,10 +642,15 @@ rebuild_indexes(Oid OIDOldHeap, List *indexes)
* matter: after the filenode swap the index will keep the * matter: after the filenode swap the index will keep the
* constraint status of the old index. * constraint status of the old index.
*/ */
newIndexOID = index_create(OIDOldHeap, newIndexName, newIndexOID = index_create(OIDOldHeap,
attrs->indexInfo, attrs->accessMethodOID, newIndexName,
attrs->classOID, false, attrs->indexInfo,
false, allowSystemTableMods); attrs->accessMethodOID,
attrs->classOID,
false,
false,
allowSystemTableMods,
false);
CommandCounterIncrement(); CommandCounterIncrement();
/* Swap the filenodes. */ /* Swap the filenodes. */
...@@ -698,7 +699,7 @@ rebuild_indexes(Oid OIDOldHeap, List *indexes) ...@@ -698,7 +699,7 @@ rebuild_indexes(Oid OIDOldHeap, List *indexes)
* Also swap any TOAST links, so that the toast data moves along with * Also swap any TOAST links, so that the toast data moves along with
* the main-table data. * the main-table data.
*/ */
static void void
swap_relfilenodes(Oid r1, Oid r2) swap_relfilenodes(Oid r1, Oid r2)
{ {
Relation relRelation, Relation relRelation,
...@@ -789,9 +790,9 @@ swap_relfilenodes(Oid r1, Oid r2) ...@@ -789,9 +790,9 @@ swap_relfilenodes(Oid r1, Oid r2)
* their new owning relations. Otherwise the wrong one will get * their new owning relations. Otherwise the wrong one will get
* dropped ... * dropped ...
* *
* NOTE: for now, we can assume the new table will have a TOAST table if * NOTE: it is possible that only one table has a toast table; this
* and only if the old one does. This logic might need work if we get * can happen in CLUSTER if there were dropped columns in the old table,
* smarter about dropped columns. * and in ALTER TABLE when adding or changing type of columns.
* *
* NOTE: at present, a TOAST table's only dependency is the one on its * NOTE: at present, a TOAST table's only dependency is the one on its
* owning table. If more are ever created, we'd need to use something * owning table. If more are ever created, we'd need to use something
...@@ -804,35 +805,43 @@ swap_relfilenodes(Oid r1, Oid r2) ...@@ -804,35 +805,43 @@ swap_relfilenodes(Oid r1, Oid r2)
toastobject; toastobject;
long count; long count;
if (!(relform1->reltoastrelid && relform2->reltoastrelid))
elog(ERROR, "expected both swapped tables to have TOAST tables");
/* Delete old dependencies */ /* Delete old dependencies */
count = deleteDependencyRecordsFor(RelOid_pg_class, if (relform1->reltoastrelid)
relform1->reltoastrelid); {
if (count != 1) count = deleteDependencyRecordsFor(RelOid_pg_class,
elog(ERROR, "expected one dependency record for TOAST table, found %ld", relform1->reltoastrelid);
count); if (count != 1)
count = deleteDependencyRecordsFor(RelOid_pg_class, elog(ERROR, "expected one dependency record for TOAST table, found %ld",
relform2->reltoastrelid); count);
if (count != 1) }
elog(ERROR, "expected one dependency record for TOAST table, found %ld", if (relform2->reltoastrelid)
count); {
count = deleteDependencyRecordsFor(RelOid_pg_class,
relform2->reltoastrelid);
if (count != 1)
elog(ERROR, "expected one dependency record for TOAST table, found %ld",
count);
}
/* Register new dependencies */ /* Register new dependencies */
baseobject.classId = RelOid_pg_class; baseobject.classId = RelOid_pg_class;
baseobject.objectId = r1;
baseobject.objectSubId = 0; baseobject.objectSubId = 0;
toastobject.classId = RelOid_pg_class; toastobject.classId = RelOid_pg_class;
toastobject.objectId = relform1->reltoastrelid;
toastobject.objectSubId = 0; toastobject.objectSubId = 0;
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); if (relform1->reltoastrelid)
{
baseobject.objectId = r2; baseobject.objectId = r1;
toastobject.objectId = relform2->reltoastrelid; toastobject.objectId = relform1->reltoastrelid;
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
}
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL); if (relform2->reltoastrelid)
{
baseobject.objectId = r2;
toastobject.objectId = relform2->reltoastrelid;
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
}
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.117 2003/12/28 21:57:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.118 2004/05/05 04:48:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/prep.h" #include "optimizer/prep.h"
#include "parser/analyze.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
...@@ -38,38 +39,62 @@ ...@@ -38,38 +39,62 @@
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
/* non-export function prototypes */ /* non-export function prototypes */
static void CheckPredicate(Expr *predicate); static void CheckPredicate(Expr *predicate);
static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
List *attList, List *attList,
Oid relId, Oid relId,
char *accessMethodName, Oid accessMethodId); char *accessMethodName, Oid accessMethodId,
bool isconstraint);
static Oid GetIndexOpClass(List *opclass, Oid attrType, static Oid GetIndexOpClass(List *opclass, Oid attrType,
char *accessMethodName, Oid accessMethodId); char *accessMethodName, Oid accessMethodId);
static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId); static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);
static char *CreateIndexName(const char *table_name, const char *column_name,
const char *label, Oid inamespace);
static bool relationHasPrimaryKey(Relation rel);
/* /*
* DefineIndex * DefineIndex
* Creates a new index. * Creates a new index.
* *
* 'attributeList' is a list of IndexElem specifying columns and expressions * 'heapRelation': the relation the index will apply to.
* 'indexRelationName': the name for the new index, or NULL to indicate
* that a nonconflicting default name should be picked.
* 'accessMethodName': name of the AM to use.
* 'attributeList': a list of IndexElem specifying columns and expressions
* to index on. * to index on.
* 'predicate' is the qual specified in the where clause. * 'predicate': the partial-index condition, or NULL if none.
* 'rangetable' is needed to interpret the predicate. * 'rangetable': needed to interpret the predicate.
* 'unique': make the index enforce uniqueness.
* 'primary': mark the index as a primary key in the catalogs.
* 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
* so build a pg_constraint entry for it.
* 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
* 'check_rights': check for CREATE rights in the namespace. (This should
* be true except when ALTER is deleting/recreating an index.)
* 'skip_build': make the catalog entries but leave the index file empty;
* it will be filled later.
* 'quiet': suppress the NOTICE chatter ordinarily provided for constraints.
*/ */
void void
DefineIndex(RangeVar *heapRelation, DefineIndex(RangeVar *heapRelation,
char *indexRelationName, char *indexRelationName,
char *accessMethodName, char *accessMethodName,
List *attributeList, List *attributeList,
Expr *predicate,
List *rangetable,
bool unique, bool unique,
bool primary, bool primary,
bool isconstraint, bool isconstraint,
Expr *predicate, bool is_alter_table,
List *rangetable) bool check_rights,
bool skip_build,
bool quiet)
{ {
Oid *classObjectId; Oid *classObjectId;
Oid accessMethodId; Oid accessMethodId;
...@@ -111,15 +136,13 @@ DefineIndex(RangeVar *heapRelation, ...@@ -111,15 +136,13 @@ DefineIndex(RangeVar *heapRelation,
relationId = RelationGetRelid(rel); relationId = RelationGetRelid(rel);
namespaceId = RelationGetNamespace(rel); namespaceId = RelationGetNamespace(rel);
heap_close(rel, NoLock);
/* /*
* Verify we (still) have CREATE rights in the rel's namespace. * Verify we (still) have CREATE rights in the rel's namespace.
* (Presumably we did when the rel was created, but maybe not * (Presumably we did when the rel was created, but maybe not
* anymore.) Skip check if bootstrapping, since permissions machinery * anymore.) Skip check if caller doesn't want it. Also skip check
* may not be working yet. * if bootstrapping, since permissions machinery may not be working yet.
*/ */
if (!IsBootstrapProcessingMode()) if (check_rights && !IsBootstrapProcessingMode())
{ {
AclResult aclresult; AclResult aclresult;
...@@ -130,6 +153,27 @@ DefineIndex(RangeVar *heapRelation, ...@@ -130,6 +153,27 @@ DefineIndex(RangeVar *heapRelation,
get_namespace_name(namespaceId)); get_namespace_name(namespaceId));
} }
/*
* Select name for index if caller didn't specify
*/
if (indexRelationName == NULL)
{
if (primary)
indexRelationName = CreateIndexName(RelationGetRelationName(rel),
NULL,
"pkey",
namespaceId);
else
{
IndexElem *iparam = (IndexElem *) lfirst(attributeList);
indexRelationName = CreateIndexName(RelationGetRelationName(rel),
iparam->name,
"key",
namespaceId);
}
}
/* /*
* look up the access method, verify it can handle the requested * look up the access method, verify it can handle the requested
* features * features
...@@ -177,13 +221,33 @@ DefineIndex(RangeVar *heapRelation, ...@@ -177,13 +221,33 @@ DefineIndex(RangeVar *heapRelation,
CheckPredicate(predicate); CheckPredicate(predicate);
/* /*
* Check that all of the attributes in a primary key are marked as not * Extra checks when creating a PRIMARY KEY index.
* null, otherwise attempt to ALTER TABLE .. SET NOT NULL
*/ */
if (primary) if (primary)
{ {
List *cmds;
List *keys; List *keys;
/*
* If ALTER TABLE, check that there isn't already a PRIMARY KEY.
* In CREATE TABLE, we have faith that the parser rejected multiple
* pkey clauses; and CREATE INDEX doesn't have a way to say
* PRIMARY KEY, so it's no problem either.
*/
if (is_alter_table &&
relationHasPrimaryKey(rel))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("multiple primary keys for table \"%s\" are not allowed",
RelationGetRelationName(rel))));
}
/*
* Check that all of the attributes in a primary key are marked as not
* null, otherwise attempt to ALTER TABLE .. SET NOT NULL
*/
cmds = NIL;
foreach(keys, attributeList) foreach(keys, attributeList)
{ {
IndexElem *key = (IndexElem *) lfirst(keys); IndexElem *key = (IndexElem *) lfirst(keys);
...@@ -203,29 +267,43 @@ DefineIndex(RangeVar *heapRelation, ...@@ -203,29 +267,43 @@ DefineIndex(RangeVar *heapRelation,
{ {
if (!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull) if (!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull)
{ {
/* /* Add a subcommand to make this one NOT NULL */
* Try to make it NOT NULL. AlterTableCmd *cmd = makeNode(AlterTableCmd);
*
* XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade cmd->subtype = AT_SetNotNull;
* to child tables? Currently, since the PRIMARY KEY cmd->name = key->name;
* itself doesn't cascade, we don't cascade the
* notnull constraint either; but this is pretty cmds = lappend(cmds, cmd);
* debatable.
*/
AlterTableAlterColumnSetNotNull(relationId, false,
key->name);
} }
ReleaseSysCache(atttuple); ReleaseSysCache(atttuple);
} }
else else
{ {
/* This shouldn't happen if parser did its job ... */ /*
* This shouldn't happen during CREATE TABLE, but can
* happen during ALTER TABLE. Keep message in sync with
* transformIndexConstraints() in parser/analyze.c.
*/
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" named in key does not exist", errmsg("column \"%s\" named in key does not exist",
key->name))); key->name)));
} }
} }
/*
* XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade
* to child tables? Currently, since the PRIMARY KEY
* itself doesn't cascade, we don't cascade the
* notnull constraint(s) either; but this is pretty debatable.
*
* XXX: possible future improvement: when being called from
* ALTER TABLE, it would be more efficient to merge this with
* the outer ALTER TABLE, so as to avoid two scans. But that
* seems to complicate DefineIndex's API unduly.
*/
if (cmds)
AlterTableInternal(relationId, cmds, false);
} }
/* /*
...@@ -242,11 +320,26 @@ DefineIndex(RangeVar *heapRelation, ...@@ -242,11 +320,26 @@ DefineIndex(RangeVar *heapRelation,
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
ComputeIndexAttrs(indexInfo, classObjectId, attributeList, ComputeIndexAttrs(indexInfo, classObjectId, attributeList,
relationId, accessMethodName, accessMethodId); relationId, accessMethodName, accessMethodId,
isconstraint);
heap_close(rel, NoLock);
/*
* Report index creation if appropriate (delay this till after most
* of the error checks)
*/
if (isconstraint && !quiet)
ereport(NOTICE,
(errmsg("%s %s will create implicit index \"%s\" for table \"%s\"",
is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
primary ? "PRIMARY KEY" : "UNIQUE",
indexRelationName, RelationGetRelationName(rel))));
index_create(relationId, indexRelationName, index_create(relationId, indexRelationName,
indexInfo, accessMethodId, classObjectId, indexInfo, accessMethodId, classObjectId,
primary, isconstraint, allowSystemTableMods); primary, isconstraint,
allowSystemTableMods, skip_build);
/* /*
* We update the relation's pg_class tuple even if it already has * We update the relation's pg_class tuple even if it already has
...@@ -303,7 +396,8 @@ ComputeIndexAttrs(IndexInfo *indexInfo, ...@@ -303,7 +396,8 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
List *attList, /* list of IndexElem's */ List *attList, /* list of IndexElem's */
Oid relId, Oid relId,
char *accessMethodName, char *accessMethodName,
Oid accessMethodId) Oid accessMethodId,
bool isconstraint)
{ {
List *rest; List *rest;
int attn = 0; int attn = 0;
...@@ -325,10 +419,19 @@ ComputeIndexAttrs(IndexInfo *indexInfo, ...@@ -325,10 +419,19 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Assert(attribute->expr == NULL); Assert(attribute->expr == NULL);
atttuple = SearchSysCacheAttName(relId, attribute->name); atttuple = SearchSysCacheAttName(relId, attribute->name);
if (!HeapTupleIsValid(atttuple)) if (!HeapTupleIsValid(atttuple))
ereport(ERROR, {
(errcode(ERRCODE_UNDEFINED_COLUMN), /* difference in error message spellings is historical */
errmsg("column \"%s\" does not exist", if (isconstraint)
attribute->name))); ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" named in key does not exist",
attribute->name)));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" does not exist",
attribute->name)));
}
attform = (Form_pg_attribute) GETSTRUCT(atttuple); attform = (Form_pg_attribute) GETSTRUCT(atttuple);
indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum; indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
atttype = attform->atttypid; atttype = attform->atttypid;
...@@ -553,6 +656,79 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId) ...@@ -553,6 +656,79 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId)
return InvalidOid; return InvalidOid;
} }
/*
* Select a nonconflicting name for an index.
*/
static char *
CreateIndexName(const char *table_name, const char *column_name,
const char *label, Oid inamespace)
{
int pass = 0;
char *iname = NULL;
char typename[NAMEDATALEN];
/*
* The type name for makeObjectName is label, or labelN if that's
* necessary to prevent collision with existing indexes.
*/
strncpy(typename, label, sizeof(typename));
for (;;)
{
iname = makeObjectName(table_name, column_name, typename);
if (!OidIsValid(get_relname_relid(iname, inamespace)))
break;
/* found a conflict, so try a new name component */
pfree(iname);
snprintf(typename, sizeof(typename), "%s%d", label, ++pass);
}
return iname;
}
/*
* relationHasPrimaryKey -
*
* See whether an existing relation has a primary key.
*/
static bool
relationHasPrimaryKey(Relation rel)
{
bool result = false;
List *indexoidlist,
*indexoidscan;
/*
* Get the list of index OIDs for the table from the relcache, and
* look up each one in the pg_index syscache until we find one marked
* primary key (hopefully there isn't more than one such).
*/
indexoidlist = RelationGetIndexList(rel);
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirsto(indexoidscan);
HeapTuple indexTuple;
indexTuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(indexoid),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple)) /* should not happen */
elog(ERROR, "cache lookup failed for index %u", indexoid);
result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
ReleaseSysCache(indexTuple);
if (result)
break;
}
freeList(indexoidlist);
return result;
}
/* /*
* RemoveIndex * RemoveIndex
* Deletes an index. * Deletes an index.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.279 2004/03/17 20:48:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.280 2004/05/05 04:48:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1674,10 +1674,21 @@ _copyAlterTableStmt(AlterTableStmt *from) ...@@ -1674,10 +1674,21 @@ _copyAlterTableStmt(AlterTableStmt *from)
{ {
AlterTableStmt *newnode = makeNode(AlterTableStmt); AlterTableStmt *newnode = makeNode(AlterTableStmt);
COPY_SCALAR_FIELD(subtype);
COPY_NODE_FIELD(relation); COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(cmds);
return newnode;
}
static AlterTableCmd *
_copyAlterTableCmd(AlterTableCmd *from)
{
AlterTableCmd *newnode = makeNode(AlterTableCmd);
COPY_SCALAR_FIELD(subtype);
COPY_STRING_FIELD(name); COPY_STRING_FIELD(name);
COPY_NODE_FIELD(def); COPY_NODE_FIELD(def);
COPY_NODE_FIELD(transform);
COPY_SCALAR_FIELD(behavior); COPY_SCALAR_FIELD(behavior);
return newnode; return newnode;
...@@ -2773,6 +2784,9 @@ copyObject(void *from) ...@@ -2773,6 +2784,9 @@ copyObject(void *from)
case T_AlterTableStmt: case T_AlterTableStmt:
retval = _copyAlterTableStmt(from); retval = _copyAlterTableStmt(from);
break; break;
case T_AlterTableCmd:
retval = _copyAlterTableCmd(from);
break;
case T_AlterDomainStmt: case T_AlterDomainStmt:
retval = _copyAlterDomainStmt(from); retval = _copyAlterDomainStmt(from);
break; break;
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.218 2004/03/17 20:48:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.219 2004/05/05 04:48:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -710,10 +710,19 @@ _equalSetOperationStmt(SetOperationStmt *a, SetOperationStmt *b) ...@@ -710,10 +710,19 @@ _equalSetOperationStmt(SetOperationStmt *a, SetOperationStmt *b)
static bool static bool
_equalAlterTableStmt(AlterTableStmt *a, AlterTableStmt *b) _equalAlterTableStmt(AlterTableStmt *a, AlterTableStmt *b)
{ {
COMPARE_SCALAR_FIELD(subtype);
COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(cmds);
return true;
}
static bool
_equalAlterTableCmd(AlterTableCmd *a, AlterTableCmd *b)
{
COMPARE_SCALAR_FIELD(subtype);
COMPARE_STRING_FIELD(name); COMPARE_STRING_FIELD(name);
COMPARE_NODE_FIELD(def); COMPARE_NODE_FIELD(def);
COMPARE_NODE_FIELD(transform);
COMPARE_SCALAR_FIELD(behavior); COMPARE_SCALAR_FIELD(behavior);
return true; return true;
...@@ -1846,6 +1855,9 @@ equal(void *a, void *b) ...@@ -1846,6 +1855,9 @@ equal(void *a, void *b)
case T_AlterTableStmt: case T_AlterTableStmt:
retval = _equalAlterTableStmt(a, b); retval = _equalAlterTableStmt(a, b);
break; break;
case T_AlterTableCmd:
retval = _equalAlterTableCmd(a, b);
break;
case T_AlterDomainStmt: case T_AlterDomainStmt:
retval = _equalAlterDomainStmt(a, b); retval = _equalAlterDomainStmt(a, b);
break; break;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.298 2004/04/02 21:05:32 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.299 2004/05/05 04:48:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -77,7 +77,7 @@ typedef struct ...@@ -77,7 +77,7 @@ typedef struct
RangeVar *relation; /* relation to create */ RangeVar *relation; /* relation to create */
List *inhRelations; /* relations to inherit from */ List *inhRelations; /* relations to inherit from */
bool hasoids; /* does relation have an OID column? */ bool hasoids; /* does relation have an OID column? */
Oid relOid; /* OID of table, if ALTER TABLE case */ bool isalter; /* true if altering existing table */
List *columns; /* ColumnDef items */ List *columns; /* ColumnDef items */
List *ckconstraints; /* CHECK constraints */ List *ckconstraints; /* CHECK constraints */
List *fkconstraints; /* FOREIGN KEY constraints */ List *fkconstraints; /* FOREIGN KEY constraints */
...@@ -131,18 +131,17 @@ static void transformIndexConstraints(ParseState *pstate, ...@@ -131,18 +131,17 @@ static void transformIndexConstraints(ParseState *pstate,
CreateStmtContext *cxt); CreateStmtContext *cxt);
static void transformFKConstraints(ParseState *pstate, static void transformFKConstraints(ParseState *pstate,
CreateStmtContext *cxt, CreateStmtContext *cxt,
bool skipValidation,
bool isAddConstraint); bool isAddConstraint);
static void applyColumnNames(List *dst, List *src); static void applyColumnNames(List *dst, List *src);
static List *getSetColTypes(ParseState *pstate, Node *node); static List *getSetColTypes(ParseState *pstate, Node *node);
static void transformForUpdate(Query *qry, List *forUpdate); static void transformForUpdate(Query *qry, List *forUpdate);
static void transformConstraintAttrs(List *constraintList); static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column); static void transformColumnType(ParseState *pstate, ColumnDef *column);
static bool relationHasPrimaryKey(Oid relationOid);
static void release_pstate_resources(ParseState *pstate); static void release_pstate_resources(ParseState *pstate);
static FromExpr *makeFromExpr(List *fromlist, Node *quals); static FromExpr *makeFromExpr(List *fromlist, Node *quals);
static bool check_parameter_resolution_walker(Node *node, static bool check_parameter_resolution_walker(Node *node,
check_parameter_resolution_context *context); check_parameter_resolution_context *context);
static char *makeObjectName(char *name1, char *name2, char *typename);
/* /*
...@@ -346,7 +345,8 @@ transformStmt(ParseState *pstate, Node *parseTree, ...@@ -346,7 +345,8 @@ transformStmt(ParseState *pstate, Node *parseTree,
break; break;
case T_AlterTableStmt: case T_AlterTableStmt:
result = transformAlterTableStmt(pstate, (AlterTableStmt *) parseTree, result = transformAlterTableStmt(pstate,
(AlterTableStmt *) parseTree,
extras_before, extras_after); extras_before, extras_after);
break; break;
...@@ -733,8 +733,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, ...@@ -733,8 +733,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
* from the truncated characters. Currently it seems best to keep it simple, * from the truncated characters. Currently it seems best to keep it simple,
* so that the generated names are easily predictable by a person. * so that the generated names are easily predictable by a person.
*/ */
static char * char *
makeObjectName(char *name1, char *name2, char *typename) makeObjectName(const char *name1, const char *name2, const char *typename)
{ {
char *name; char *name;
int overhead = 0; /* chars needed for type and underscores */ int overhead = 0; /* chars needed for type and underscores */
...@@ -795,48 +795,6 @@ makeObjectName(char *name1, char *name2, char *typename) ...@@ -795,48 +795,6 @@ makeObjectName(char *name1, char *name2, char *typename)
return name; return name;
} }
static char *
CreateIndexName(char *table_name, char *column_name,
char *label, List *indices)
{
int pass = 0;
char *iname = NULL;
List *ilist;
char typename[NAMEDATALEN];
/*
* The type name for makeObjectName is label, or labelN if that's
* necessary to prevent collisions among multiple indexes for the same
* table. Note there is no check for collisions with already-existing
* indexes, only among the indexes we're about to create now; this
* ought to be improved someday.
*/
strncpy(typename, label, sizeof(typename));
for (;;)
{
iname = makeObjectName(table_name, column_name, typename);
foreach(ilist, indices)
{
IndexStmt *index = lfirst(ilist);
if (index->idxname != NULL &&
strcmp(iname, index->idxname) == 0)
break;
}
/* ran through entire list? then no name conflict found so done */
if (ilist == NIL)
break;
/* found a conflict, so try a new name component */
pfree(iname);
snprintf(typename, sizeof(typename), "%s%d", label, ++pass);
}
return iname;
}
/* /*
* transformCreateStmt - * transformCreateStmt -
* transforms the "create table" statement * transforms the "create table" statement
...@@ -857,7 +815,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt, ...@@ -857,7 +815,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
cxt.stmtType = "CREATE TABLE"; cxt.stmtType = "CREATE TABLE";
cxt.relation = stmt->relation; cxt.relation = stmt->relation;
cxt.inhRelations = stmt->inhRelations; cxt.inhRelations = stmt->inhRelations;
cxt.relOid = InvalidOid; cxt.isalter = false;
cxt.columns = NIL; cxt.columns = NIL;
cxt.ckconstraints = NIL; cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL; cxt.fkconstraints = NIL;
...@@ -914,7 +872,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt, ...@@ -914,7 +872,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt,
/* /*
* Postprocess foreign-key constraints. * Postprocess foreign-key constraints.
*/ */
transformFKConstraints(pstate, &cxt, false); transformFKConstraints(pstate, &cxt, true, false);
/* /*
* Output results. * Output results.
...@@ -1326,24 +1284,23 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -1326,24 +1284,23 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
index->primary = (constraint->contype == CONSTR_PRIMARY); index->primary = (constraint->contype == CONSTR_PRIMARY);
if (index->primary) if (index->primary)
{ {
/* In ALTER TABLE case, a primary index might already exist */ if (cxt->pkey != NULL)
if (cxt->pkey != NULL ||
(OidIsValid(cxt->relOid) &&
relationHasPrimaryKey(cxt->relOid)))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION), (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("multiple primary keys for table \"%s\" are not allowed", errmsg("multiple primary keys for table \"%s\" are not allowed",
cxt->relation->relname))); cxt->relation->relname)));
cxt->pkey = index; cxt->pkey = index;
/*
* In ALTER TABLE case, a primary index might already exist,
* but DefineIndex will check for it.
*/
} }
index->isconstraint = true; index->isconstraint = true;
if (constraint->name != NULL) if (constraint->name != NULL)
index->idxname = pstrdup(constraint->name); index->idxname = pstrdup(constraint->name);
else if (constraint->contype == CONSTR_PRIMARY)
index->idxname = makeObjectName(cxt->relation->relname, NULL, "pkey");
else else
index->idxname = NULL; /* will set it later */ index->idxname = NULL; /* DefineIndex will choose name */
index->relation = cxt->relation; index->relation = cxt->relation;
index->accessMethod = DEFAULT_INDEX_TYPE; index->accessMethod = DEFAULT_INDEX_TYPE;
...@@ -1431,25 +1388,14 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -1431,25 +1388,14 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
break; break;
} }
} }
else if (OidIsValid(cxt->relOid))
{
/* ALTER TABLE case: does column already exist? */
HeapTuple atttuple;
atttuple = SearchSysCacheAttName(cxt->relOid, key);
if (HeapTupleIsValid(atttuple))
{
found = true;
/* /*
* If it's not already NOT NULL, leave it to * In the ALTER TABLE case, don't complain about index keys
* DefineIndex to fix later. * not created in the command; they may well exist already.
*/ * DefineIndex will complain about them if not, and will also
ReleaseSysCache(atttuple); * take care of marking them NOT NULL.
} */
} if (!found && !cxt->isalter)
if (!found)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" named in key does not exist", errmsg("column \"%s\" named in key does not exist",
...@@ -1537,51 +1483,36 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -1537,51 +1483,36 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
indexlist = lnext(indexlist); indexlist = lnext(indexlist);
} }
/*
* Finally, select unique names for all not-previously-named indices,
* and display NOTICE messages.
*
* XXX in ALTER TABLE case, we fail to consider name collisions against
* pre-existing indexes.
*/
foreach(indexlist, cxt->alist)
{
index = lfirst(indexlist);
if (index->idxname == NULL && index->indexParams != NIL)
{
iparam = (IndexElem *) lfirst(index->indexParams);
/* we should never see an expression item here */
Assert(iparam->expr == NULL);
index->idxname = CreateIndexName(cxt->relation->relname,
iparam->name,
"key",
cxt->alist);
}
if (index->idxname == NULL) /* should not happen */
elog(ERROR, "failed to make implicit index name");
ereport(NOTICE,
(errmsg("%s / %s%s will create implicit index \"%s\" for table \"%s\"",
cxt->stmtType,
(strcmp(cxt->stmtType, "ALTER TABLE") == 0) ? "ADD " : "",
(index->primary ? "PRIMARY KEY" : "UNIQUE"),
index->idxname, cxt->relation->relname)));
}
} }
static void static void
transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt, transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
bool isAddConstraint) bool skipValidation, bool isAddConstraint)
{ {
List *fkclist;
if (cxt->fkconstraints == NIL) if (cxt->fkconstraints == NIL)
return; return;
/* /*
* For ALTER TABLE ADD CONSTRAINT, nothing to do. For CREATE TABLE or * If CREATE TABLE or adding a column with NULL default, we can safely
* ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD CONSTRAINT * skip validation of the constraint.
* command to execute after the basic command is complete. */
if (skipValidation)
{
foreach(fkclist, cxt->fkconstraints)
{
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
fkconstraint->skip_validation = true;
}
}
/*
* For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE
* ADD CONSTRAINT command to execute after the basic command is complete.
* (If called from ADD CONSTRAINT, that routine will add the FK constraints
* to its own subcommand list.)
* *
* Note: the ADD CONSTRAINT command must also execute after any index * Note: the ADD CONSTRAINT command must also execute after any index
* creation commands. Thus, this should run after * creation commands. Thus, this should run after
...@@ -1591,22 +1522,22 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt, ...@@ -1591,22 +1522,22 @@ transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt,
if (!isAddConstraint) if (!isAddConstraint)
{ {
AlterTableStmt *alterstmt = makeNode(AlterTableStmt); AlterTableStmt *alterstmt = makeNode(AlterTableStmt);
List *fkclist;
alterstmt->subtype = 'c'; /* preprocessed add constraint */
alterstmt->relation = cxt->relation; alterstmt->relation = cxt->relation;
alterstmt->name = NULL; alterstmt->cmds = NIL;
alterstmt->def = (Node *) cxt->fkconstraints;
/* Don't need to scan the table contents in this case */
foreach(fkclist, cxt->fkconstraints) foreach(fkclist, cxt->fkconstraints)
{ {
FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist); FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
AlterTableCmd *altercmd = makeNode(AlterTableCmd);
fkconstraint->skip_validation = true; altercmd->subtype = AT_ProcessedConstraint;
altercmd->name = NULL;
altercmd->def = (Node *) fkconstraint;
alterstmt->cmds = lappend(alterstmt->cmds, altercmd);
} }
cxt->alist = lappend(cxt->alist, (Node *) alterstmt); cxt->alist = lappend(cxt->alist, alterstmt);
} }
} }
...@@ -2554,111 +2485,158 @@ static Query * ...@@ -2554,111 +2485,158 @@ static Query *
transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt, transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt,
List **extras_before, List **extras_after) List **extras_before, List **extras_after)
{ {
Relation rel;
CreateStmtContext cxt; CreateStmtContext cxt;
Query *qry; Query *qry;
List *lcmd,
*l;
List *newcmds = NIL;
bool skipValidation = true;
AlterTableCmd *newcmd;
cxt.stmtType = "ALTER TABLE";
cxt.relation = stmt->relation;
cxt.inhRelations = NIL;
cxt.isalter = true;
cxt.hasoids = false; /* need not be right */
cxt.columns = NIL;
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
/* /*
* The only subtypes that currently require parse transformation * The only subtypes that currently require parse transformation
* handling are 'A'dd column and Add 'C'onstraint. These largely * handling are ADD COLUMN and ADD CONSTRAINT. These largely
* re-use code from CREATE TABLE. * re-use code from CREATE TABLE.
*
* If we need to do any parse transformation, get exclusive lock on the
* relation to make sure it won't change before we execute the
* command.
*/ */
switch (stmt->subtype) foreach(lcmd, stmt->cmds)
{ {
case 'A': AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
cxt.stmtType = "ALTER TABLE";
cxt.relation = stmt->relation;
cxt.inhRelations = NIL;
cxt.relOid = RelationGetRelid(rel);
cxt.hasoids = SearchSysCacheExists(ATTNUM,
ObjectIdGetDatum(cxt.relOid),
Int16GetDatum(ObjectIdAttributeNumber),
0, 0);
cxt.columns = NIL;
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
Assert(IsA(stmt->def, ColumnDef));
transformColumnDefinition(pstate, &cxt,
(ColumnDef *) stmt->def);
transformIndexConstraints(pstate, &cxt);
transformFKConstraints(pstate, &cxt, false);
((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
*extras_before = nconc(*extras_before, cxt.blist);
*extras_after = nconc(cxt.alist, *extras_after);
heap_close(rel, NoLock); /* close rel, keep lock */
break;
case 'C': switch (cmd->subtype)
rel = heap_openrv(stmt->relation, AccessExclusiveLock); {
case AT_AddColumn:
cxt.stmtType = "ALTER TABLE"; {
cxt.relation = stmt->relation; ColumnDef *def = (ColumnDef *) cmd->def;
cxt.inhRelations = NIL;
cxt.relOid = RelationGetRelid(rel);
cxt.hasoids = SearchSysCacheExists(ATTNUM,
ObjectIdGetDatum(cxt.relOid),
Int16GetDatum(ObjectIdAttributeNumber),
0, 0);
cxt.columns = NIL;
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
if (IsA(stmt->def, Constraint))
transformTableConstraint(pstate, &cxt,
(Constraint *) stmt->def);
else if (IsA(stmt->def, FkConstraint))
cxt.fkconstraints = lappend(cxt.fkconstraints, stmt->def);
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(stmt->def));
transformIndexConstraints(pstate, &cxt); Assert(IsA(cmd->def, ColumnDef));
transformFKConstraints(pstate, &cxt, true); transformColumnDefinition(pstate, &cxt,
(ColumnDef *) cmd->def);
Assert(cxt.columns == NIL); /*
/* fkconstraints should be put into my own stmt in this case */ * If the column has a non-null default, we can't skip
stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints); * validation of foreign keys.
*extras_before = nconc(*extras_before, cxt.blist); */
*extras_after = nconc(cxt.alist, *extras_after); if (((ColumnDef *) cmd->def)->raw_default != NULL)
skipValidation = false;
heap_close(rel, NoLock); /* close rel, keep lock */ newcmds = lappend(newcmds, cmd);
break;
case 'c': /*
* Convert an ADD COLUMN ... NOT NULL constraint to a separate
* command
*/
if (def->is_not_null)
{
/* Remove NOT NULL from AddColumn */
def->is_not_null = false;
/* Add as a separate AlterTableCmd */
newcmd = makeNode(AlterTableCmd);
newcmd->subtype = AT_SetNotNull;
newcmd->name = pstrdup(def->colname);
newcmds = lappend(newcmds, newcmd);
}
/* /*
* Already-transformed ADD CONSTRAINT, so just make it look * All constraints are processed in other ways.
* like the standard case. * Remove the original list
*/ */
stmt->subtype = 'C'; def->constraints = NIL;
break;
default: break;
break; }
case AT_AddConstraint:
/* The original AddConstraint cmd node doesn't go to newcmds */
if (IsA(cmd->def, Constraint))
transformTableConstraint(pstate, &cxt,
(Constraint *) cmd->def);
else if (IsA(cmd->def, FkConstraint))
{
cxt.fkconstraints = lappend(cxt.fkconstraints, cmd->def);
skipValidation = false;
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(cmd->def));
break;
case AT_ProcessedConstraint:
/*
* Already-transformed ADD CONSTRAINT, so just make it look
* like the standard case.
*/
cmd->subtype = AT_AddConstraint;
newcmds = lappend(newcmds, cmd);
break;
default:
newcmds = lappend(newcmds, cmd);
break;
}
} }
/* Postprocess index and FK constraints */
transformIndexConstraints(pstate, &cxt);
transformFKConstraints(pstate, &cxt, skipValidation, true);
/*
* Push any index-creation commands into the ALTER, so that
* they can be scheduled nicely by tablecmds.c.
*/
foreach(l, cxt.alist)
{
Node *idxstmt = (Node *) lfirst(l);
Assert(IsA(idxstmt, IndexStmt));
newcmd = makeNode(AlterTableCmd);
newcmd->subtype = AT_AddIndex;
newcmd->def = idxstmt;
newcmds = lappend(newcmds, newcmd);
}
cxt.alist = NIL;
/* Append any CHECK or FK constraints to the commands list */
foreach(l, cxt.ckconstraints)
{
newcmd = makeNode(AlterTableCmd);
newcmd->subtype = AT_AddConstraint;
newcmd->def = (Node *) lfirst(l);
newcmds = lappend(newcmds, newcmd);
}
foreach(l, cxt.fkconstraints)
{
newcmd = makeNode(AlterTableCmd);
newcmd->subtype = AT_AddConstraint;
newcmd->def = (Node *) lfirst(l);
newcmds = lappend(newcmds, newcmd);
}
/* Update statement's commands list */
stmt->cmds = newcmds;
qry = makeNode(Query); qry = makeNode(Query);
qry->commandType = CMD_UTILITY; qry->commandType = CMD_UTILITY;
qry->utilityStmt = (Node *) stmt; qry->utilityStmt = (Node *) stmt;
*extras_before = nconc(*extras_before, cxt.blist);
*extras_after = nconc(cxt.alist, *extras_after);
return qry; return qry;
} }
...@@ -2946,51 +2924,6 @@ transformForUpdate(Query *qry, List *forUpdate) ...@@ -2946,51 +2924,6 @@ transformForUpdate(Query *qry, List *forUpdate)
} }
/*
* relationHasPrimaryKey -
*
* See whether an existing relation has a primary key.
*/
static bool
relationHasPrimaryKey(Oid relationOid)
{
bool result = false;
Relation rel;
List *indexoidlist,
*indexoidscan;
rel = heap_open(relationOid, AccessShareLock);
/*
* Get the list of index OIDs for the table from the relcache, and
* look up each one in the pg_index syscache until we find one marked
* primary key (hopefully there isn't more than one such).
*/
indexoidlist = RelationGetIndexList(rel);
foreach(indexoidscan, indexoidlist)
{
Oid indexoid = lfirsto(indexoidscan);
HeapTuple indexTuple;
indexTuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(indexoid),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple)) /* should not happen */
elog(ERROR, "cache lookup failed for index %u", indexoid);
result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
ReleaseSysCache(indexTuple);
if (result)
break;
}
freeList(indexoidlist);
heap_close(rel, AccessShareLock);
return result;
}
/* /*
* Preprocess a list of column constraint clauses * Preprocess a list of column constraint clauses
* to attach constraint attributes to their primary constraint nodes * to attach constraint attributes to their primary constraint nodes
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.452 2004/04/21 00:34:18 momjian Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.453 2004/05/05 04:48:46 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -158,9 +158,12 @@ static void doNegateFloat(Value *v); ...@@ -158,9 +158,12 @@ static void doNegateFloat(Value *v);
%type <node> select_no_parens select_with_parens select_clause %type <node> select_no_parens select_with_parens select_clause
simple_select simple_select
%type <node> alter_column_default opclass_item %type <node> alter_column_default opclass_item alter_using
%type <ival> add_drop %type <ival> add_drop
%type <node> alter_table_cmd
%type <list> alter_table_cmds
%type <dbehavior> opt_drop_behavior %type <dbehavior> opt_drop_behavior
%type <list> createdb_opt_list copy_opt_list %type <list> createdb_opt_list copy_opt_list
...@@ -199,7 +202,7 @@ static void doNegateFloat(Value *v); ...@@ -199,7 +202,7 @@ static void doNegateFloat(Value *v);
%type <range> qualified_name OptConstrFromTable %type <range> qualified_name OptConstrFromTable
%type <str> all_Op MathOp opt_name SpecialRuleRelation %type <str> all_Op MathOp SpecialRuleRelation
%type <str> iso_level opt_encoding %type <str> iso_level opt_encoding
%type <node> grantee %type <node> grantee
...@@ -1127,127 +1130,139 @@ CheckPointStmt: ...@@ -1127,127 +1130,139 @@ CheckPointStmt:
*****************************************************************************/ *****************************************************************************/
AlterTableStmt: AlterTableStmt:
/* ALTER TABLE <relation> ADD [COLUMN] <coldef> */ ALTER TABLE relation_expr alter_table_cmds
ALTER TABLE relation_expr ADD opt_column columnDef
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableStmt *n = makeNode(AlterTableStmt);
n->subtype = 'A';
n->relation = $3; n->relation = $3;
n->def = $6; n->cmds = $4;
$$ = (Node *)n;
}
;
alter_table_cmds:
alter_table_cmd { $$ = makeList1($1); }
| alter_table_cmds ',' alter_table_cmd { $$ = lappend($1, $3); }
;
alter_table_cmd:
/* ALTER TABLE <relation> ADD [COLUMN] <coldef> */
ADD opt_column columnDef
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_AddColumn;
n->def = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> {SET DEFAULT <expr>|DROP DEFAULT} */ /* ALTER TABLE <relation> ALTER [COLUMN] <colname> {SET DEFAULT <expr>|DROP DEFAULT} */
| ALTER TABLE relation_expr ALTER opt_column ColId alter_column_default | ALTER opt_column ColId alter_column_default
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'T'; n->subtype = AT_ColumnDefault;
n->relation = $3; n->name = $3;
n->name = $6; n->def = $4;
n->def = $7;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> DROP NOT NULL */ /* ALTER TABLE <relation> ALTER [COLUMN] <colname> DROP NOT NULL */
| ALTER TABLE relation_expr ALTER opt_column ColId DROP NOT NULL_P | ALTER opt_column ColId DROP NOT NULL_P
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'N'; n->subtype = AT_DropNotNull;
n->relation = $3; n->name = $3;
n->name = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET NOT NULL */ /* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET NOT NULL */
| ALTER TABLE relation_expr ALTER opt_column ColId SET NOT NULL_P | ALTER opt_column ColId SET NOT NULL_P
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'n'; n->subtype = AT_SetNotNull;
n->relation = $3; n->name = $3;
n->name = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STATISTICS <IntegerOnly> */ /* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STATISTICS <IntegerOnly> */
| ALTER TABLE relation_expr ALTER opt_column ColId SET STATISTICS IntegerOnly | ALTER opt_column ColId SET STATISTICS IntegerOnly
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'S'; n->subtype = AT_SetStatistics;
n->relation = $3; n->name = $3;
n->name = $6; n->def = (Node *) $6;
n->def = (Node *) $9;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */ /* ALTER TABLE <relation> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */
| ALTER TABLE relation_expr ALTER opt_column ColId | ALTER opt_column ColId SET STORAGE ColId
SET STORAGE ColId
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'M'; n->subtype = AT_SetStorage;
n->relation = $3; n->name = $3;
n->name = $6; n->def = (Node *) makeString($6);
n->def = (Node *) makeString($9);
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */ /* ALTER TABLE <relation> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */
| ALTER TABLE relation_expr DROP opt_column ColId opt_drop_behavior | DROP opt_column ColId opt_drop_behavior
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'D'; n->subtype = AT_DropColumn;
n->relation = $3; n->name = $3;
n->name = $6; n->behavior = $4;
n->behavior = $7; $$ = (Node *)n;
}
/*
* ALTER TABLE <relation> ALTER [COLUMN] <colname> TYPE <typename>
* [ USING <expression> ]
*/
| ALTER opt_column ColId TYPE_P Typename alter_using
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_AlterColumnType;
n->name = $3;
n->def = (Node *) $5;
n->transform = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> ADD CONSTRAINT ... */ /* ALTER TABLE <relation> ADD CONSTRAINT ... */
| ALTER TABLE relation_expr ADD TableConstraint | ADD TableConstraint
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'C'; n->subtype = AT_AddConstraint;
n->relation = $3; n->def = $2;
n->def = $5;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */ /* ALTER TABLE <relation> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */
| ALTER TABLE relation_expr DROP CONSTRAINT name opt_drop_behavior | DROP CONSTRAINT name opt_drop_behavior
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'X'; n->subtype = AT_DropConstraint;
n->relation = $3; n->name = $3;
n->name = $6; n->behavior = $4;
n->behavior = $7;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <relation> SET WITHOUT OIDS */ /* ALTER TABLE <relation> SET WITHOUT OIDS */
| ALTER TABLE relation_expr SET WITHOUT OIDS | SET WITHOUT OIDS
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->relation = $3; n->subtype = AT_DropOids;
n->subtype = 'o';
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <name> CREATE TOAST TABLE */ /* ALTER TABLE <name> CREATE TOAST TABLE -- ONLY */
| ALTER TABLE qualified_name CREATE TOAST TABLE | CREATE TOAST TABLE
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'E'; n->subtype = AT_ToastTable;
$3->inhOpt = INH_NO;
n->relation = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <name> OWNER TO UserId */ /* ALTER TABLE <name> OWNER TO UserId */
| ALTER TABLE qualified_name OWNER TO UserId | OWNER TO UserId
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'U'; n->subtype = AT_ChangeOwner;
$3->inhOpt = INH_NO; n->name = $3;
n->relation = $3;
n->name = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <name> CLUSTER ON <indexname> */ /* ALTER TABLE <name> CLUSTER ON <indexname> */
| ALTER TABLE qualified_name CLUSTER ON name | CLUSTER ON name
{ {
AlterTableStmt *n = makeNode(AlterTableStmt); AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = 'L'; n->subtype = AT_ClusterOn;
n->relation = $3; n->name = $3;
n->name = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
...@@ -1261,7 +1276,7 @@ alter_column_default: ...@@ -1261,7 +1276,7 @@ alter_column_default:
else else
$$ = $3; $$ = $3;
} }
| DROP DEFAULT { $$ = NULL; } | DROP DEFAULT { $$ = NULL; }
; ;
opt_drop_behavior: opt_drop_behavior:
...@@ -1270,6 +1285,10 @@ opt_drop_behavior: ...@@ -1270,6 +1285,10 @@ opt_drop_behavior:
| /* EMPTY */ { $$ = DROP_RESTRICT; /* default */ } | /* EMPTY */ { $$ = DROP_RESTRICT; /* default */ }
; ;
alter_using:
USING a_expr { $$ = $2; }
| /* EMPTY */ { $$ = NULL; }
;
/***************************************************************************** /*****************************************************************************
* *
...@@ -3525,16 +3544,22 @@ RenameStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' RENAME TO name ...@@ -3525,16 +3544,22 @@ RenameStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' RENAME TO name
n->newname = $6; n->newname = $6;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER TABLE relation_expr RENAME opt_column opt_name TO name | ALTER TABLE relation_expr RENAME TO name
{ {
RenameStmt *n = makeNode(RenameStmt); RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_TABLE;
n->relation = $3;
n->subname = NULL;
n->newname = $6;
$$ = (Node *)n;
}
| ALTER TABLE relation_expr RENAME opt_column name TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_COLUMN;
n->relation = $3; n->relation = $3;
n->subname = $6; n->subname = $6;
n->newname = $8; n->newname = $8;
if ($6 == NULL)
n->renameType = OBJECT_TABLE;
else
n->renameType = OBJECT_COLUMN;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ALTER TRIGGER name ON relation_expr RENAME TO name | ALTER TRIGGER name ON relation_expr RENAME TO name
...@@ -3556,10 +3581,6 @@ RenameStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' RENAME TO name ...@@ -3556,10 +3581,6 @@ RenameStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' RENAME TO name
} }
; ;
opt_name: name { $$ = $1; }
| /*EMPTY*/ { $$ = NULL; }
;
opt_column: COLUMN { $$ = COLUMN; } opt_column: COLUMN { $$ = COLUMN; }
| /*EMPTY*/ { $$ = 0; } | /*EMPTY*/ { $$ = 0; }
; ;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.213 2004/04/22 02:58:20 momjian Exp $ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.214 2004/05/05 04:48:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,7 +43,6 @@ ...@@ -43,7 +43,6 @@
#include "commands/view.h" #include "commands/view.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "parser/parse_clause.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "rewrite/rewriteDefine.h" #include "rewrite/rewriteDefine.h"
...@@ -497,126 +496,8 @@ ProcessUtility(Node *parsetree, ...@@ -497,126 +496,8 @@ ProcessUtility(Node *parsetree,
ExecRenameStmt((RenameStmt *) parsetree); ExecRenameStmt((RenameStmt *) parsetree);
break; break;
/* various Alter Table forms */
case T_AlterTableStmt: case T_AlterTableStmt:
{ AlterTable((AlterTableStmt *) parsetree);
AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
Oid relid;
relid = RangeVarGetRelid(stmt->relation, false);
/*
* Some or all of these functions are recursive to cover
* inherited things, so permission checks are done there.
*/
switch (stmt->subtype)
{
case 'A': /* ADD COLUMN */
/*
* Recursively add column to table and, if
* requested, to descendants
*/
AlterTableAddColumn(relid,
interpretInhOption(stmt->relation->inhOpt),
(ColumnDef *) stmt->def);
break;
case 'T': /* ALTER COLUMN DEFAULT */
/*
* Recursively alter column default for table and,
* if requested, for descendants
*/
AlterTableAlterColumnDefault(relid,
interpretInhOption(stmt->relation->inhOpt),
stmt->name,
stmt->def);
break;
case 'N': /* ALTER COLUMN DROP NOT NULL */
AlterTableAlterColumnDropNotNull(relid,
interpretInhOption(stmt->relation->inhOpt),
stmt->name);
break;
case 'n': /* ALTER COLUMN SET NOT NULL */
AlterTableAlterColumnSetNotNull(relid,
interpretInhOption(stmt->relation->inhOpt),
stmt->name);
break;
case 'S': /* ALTER COLUMN STATISTICS */
case 'M': /* ALTER COLUMN STORAGE */
/*
* Recursively alter column statistics for table
* and, if requested, for descendants
*/
AlterTableAlterColumnFlags(relid,
interpretInhOption(stmt->relation->inhOpt),
stmt->name,
stmt->def,
&(stmt->subtype));
break;
case 'D': /* DROP COLUMN */
/*
* Recursively drop column from table and, if
* requested, from descendants
*/
AlterTableDropColumn(relid,
interpretInhOption(stmt->relation->inhOpt),
false,
stmt->name,
stmt->behavior);
break;
case 'C': /* ADD CONSTRAINT */
/*
* Recursively add constraint to table and, if
* requested, to descendants
*/
AlterTableAddConstraint(relid,
interpretInhOption(stmt->relation->inhOpt),
(List *) stmt->def);
break;
case 'X': /* DROP CONSTRAINT */
/*
* Recursively drop constraint from table and, if
* requested, from descendants
*/
AlterTableDropConstraint(relid,
interpretInhOption(stmt->relation->inhOpt),
stmt->name,
stmt->behavior);
break;
case 'E': /* CREATE TOAST TABLE */
AlterTableCreateToastTable(relid, false);
break;
case 'U': /* ALTER OWNER */
/* check that we are the superuser */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter owner")));
/* get_usesysid raises an error if no such user */
AlterTableOwner(relid,
get_usesysid(stmt->name));
break;
case 'L': /* CLUSTER ON */
AlterTableClusterOn(relid, stmt->name);
break;
case 'o': /* SET WITHOUT OIDS */
AlterTableAlterOids(relid,
false,
interpretInhOption(stmt->relation->inhOpt),
DROP_RESTRICT);
break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) stmt->subtype);
break;
}
}
break; break;
case T_AlterDomainStmt: case T_AlterDomainStmt:
...@@ -736,11 +617,15 @@ ProcessUtility(Node *parsetree, ...@@ -736,11 +617,15 @@ ProcessUtility(Node *parsetree,
stmt->idxname, /* index name */ stmt->idxname, /* index name */
stmt->accessMethod, /* am name */ stmt->accessMethod, /* am name */
stmt->indexParams, /* parameters */ stmt->indexParams, /* parameters */
(Expr *) stmt->whereClause,
stmt->rangetable,
stmt->unique, stmt->unique,
stmt->primary, stmt->primary,
stmt->isconstraint, stmt->isconstraint,
(Expr *) stmt->whereClause, false, /* is_alter_table */
stmt->rangetable); true, /* check_rights */
false, /* skip_build */
false); /* quiet */
} }
break; break;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.163 2004/03/17 20:48:42 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.164 2004/05/05 04:48:46 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -149,15 +149,16 @@ static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_c ...@@ -149,15 +149,16 @@ static char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_c
static char *deparse_expression_pretty(Node *expr, List *dpcontext, static char *deparse_expression_pretty(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit, bool forceprefix, bool showimplicit,
int prettyFlags, int startIndent); int prettyFlags, int startIndent);
static text *pg_do_getviewdef(Oid viewoid, int prettyFlags); static char *pg_get_viewdef_worker(Oid viewoid, int prettyFlags);
static void decompile_column_index_array(Datum column_index_array, Oid relId, static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf); StringInfo buf);
static Datum pg_get_ruledef_worker(Oid ruleoid, int prettyFlags); static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static Datum pg_get_indexdef_worker(Oid indexrelid, int colno, static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
int prettyFlags); int prettyFlags);
static Datum pg_get_constraintdef_worker(Oid constraintId, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
static Datum pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags);
int prettyFlags); static char *pg_get_expr_worker(text *expr, Oid relid, char *relname,
int prettyFlags);
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
int prettyFlags); int prettyFlags);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
...@@ -208,6 +209,7 @@ static char *generate_relation_name(Oid relid); ...@@ -208,6 +209,7 @@ static char *generate_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes); static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static void print_operator_name(StringInfo buf, List *opname); static void print_operator_name(StringInfo buf, List *opname);
static text *string_to_text(char *str);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ") #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
...@@ -223,7 +225,7 @@ pg_get_ruledef(PG_FUNCTION_ARGS) ...@@ -223,7 +225,7 @@ pg_get_ruledef(PG_FUNCTION_ARGS)
{ {
Oid ruleoid = PG_GETARG_OID(0); Oid ruleoid = PG_GETARG_OID(0);
return pg_get_ruledef_worker(ruleoid, 0); PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, 0)));
} }
...@@ -235,21 +237,24 @@ pg_get_ruledef_ext(PG_FUNCTION_ARGS) ...@@ -235,21 +237,24 @@ pg_get_ruledef_ext(PG_FUNCTION_ARGS)
int prettyFlags; int prettyFlags;
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
return pg_get_ruledef_worker(ruleoid, prettyFlags); PG_RETURN_TEXT_P(string_to_text(pg_get_ruledef_worker(ruleoid, prettyFlags)));
} }
static Datum static char *
pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
{ {
text *ruledef;
Datum args[1]; Datum args[1];
char nulls[1]; char nulls[1];
int spirc; int spirc;
HeapTuple ruletup; HeapTuple ruletup;
TupleDesc rulettc; TupleDesc rulettc;
StringInfoData buf; StringInfoData buf;
int len;
/*
* Do this first so that string is alloc'd in outer context not SPI's.
*/
initStringInfo(&buf);
/* /*
* Connect to SPI manager * Connect to SPI manager
...@@ -283,39 +288,24 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags) ...@@ -283,39 +288,24 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
if (spirc != SPI_OK_SELECT) if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid); elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
if (SPI_processed != 1) if (SPI_processed != 1)
appendStringInfo(&buf, "-");
else
{ {
if (SPI_finish() != SPI_OK_FINISH) /*
elog(ERROR, "SPI_finish failed"); * Get the rules definition and put it into executors memory
ruledef = palloc(VARHDRSZ + 1); */
VARATT_SIZEP(ruledef) = VARHDRSZ + 1; ruletup = SPI_tuptable->vals[0];
VARDATA(ruledef)[0] = '-'; rulettc = SPI_tuptable->tupdesc;
PG_RETURN_TEXT_P(ruledef); make_ruledef(&buf, ruletup, rulettc, prettyFlags);
} }
ruletup = SPI_tuptable->vals[0];
rulettc = SPI_tuptable->tupdesc;
/*
* Get the rules definition and put it into executors memory
*/
initStringInfo(&buf);
make_ruledef(&buf, ruletup, rulettc, prettyFlags);
len = buf.len + VARHDRSZ;
ruledef = SPI_palloc(len);
VARATT_SIZEP(ruledef) = len;
memcpy(VARDATA(ruledef), buf.data, buf.len);
pfree(buf.data);
/* /*
* Disconnect from SPI manager * Disconnect from SPI manager
*/ */
if (SPI_finish() != SPI_OK_FINISH) if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed"); elog(ERROR, "SPI_finish failed");
/* return buf.data;
* Easy - isn't it?
*/
PG_RETURN_TEXT_P(ruledef);
} }
...@@ -329,10 +319,8 @@ pg_get_viewdef(PG_FUNCTION_ARGS) ...@@ -329,10 +319,8 @@ pg_get_viewdef(PG_FUNCTION_ARGS)
{ {
/* By OID */ /* By OID */
Oid viewoid = PG_GETARG_OID(0); Oid viewoid = PG_GETARG_OID(0);
text *ruledef;
ruledef = pg_do_getviewdef(viewoid, 0); PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, 0)));
PG_RETURN_TEXT_P(ruledef);
} }
...@@ -342,12 +330,10 @@ pg_get_viewdef_ext(PG_FUNCTION_ARGS) ...@@ -342,12 +330,10 @@ pg_get_viewdef_ext(PG_FUNCTION_ARGS)
/* By OID */ /* By OID */
Oid viewoid = PG_GETARG_OID(0); Oid viewoid = PG_GETARG_OID(0);
bool pretty = PG_GETARG_BOOL(1); bool pretty = PG_GETARG_BOOL(1);
text *ruledef;
int prettyFlags; int prettyFlags;
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
ruledef = pg_do_getviewdef(viewoid, prettyFlags); PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags)));
PG_RETURN_TEXT_P(ruledef);
} }
Datum Datum
...@@ -357,14 +343,12 @@ pg_get_viewdef_name(PG_FUNCTION_ARGS) ...@@ -357,14 +343,12 @@ pg_get_viewdef_name(PG_FUNCTION_ARGS)
text *viewname = PG_GETARG_TEXT_P(0); text *viewname = PG_GETARG_TEXT_P(0);
RangeVar *viewrel; RangeVar *viewrel;
Oid viewoid; Oid viewoid;
text *ruledef;
viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname, viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname,
"get_viewdef")); "get_viewdef"));
viewoid = RangeVarGetRelid(viewrel, false); viewoid = RangeVarGetRelid(viewrel, false);
ruledef = pg_do_getviewdef(viewoid, 0); PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, 0)));
PG_RETURN_TEXT_P(ruledef);
} }
...@@ -377,31 +361,32 @@ pg_get_viewdef_name_ext(PG_FUNCTION_ARGS) ...@@ -377,31 +361,32 @@ pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
int prettyFlags; int prettyFlags;
RangeVar *viewrel; RangeVar *viewrel;
Oid viewoid; Oid viewoid;
text *ruledef;
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname, viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname,
"get_viewdef")); "get_viewdef"));
viewoid = RangeVarGetRelid(viewrel, false); viewoid = RangeVarGetRelid(viewrel, false);
ruledef = pg_do_getviewdef(viewoid, prettyFlags); PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags)));
PG_RETURN_TEXT_P(ruledef);
} }
/* /*
* Common code for by-OID and by-name variants of pg_get_viewdef * Common code for by-OID and by-name variants of pg_get_viewdef
*/ */
static text * static char *
pg_do_getviewdef(Oid viewoid, int prettyFlags) pg_get_viewdef_worker(Oid viewoid, int prettyFlags)
{ {
text *ruledef;
Datum args[2]; Datum args[2];
char nulls[2]; char nulls[2];
int spirc; int spirc;
HeapTuple ruletup; HeapTuple ruletup;
TupleDesc rulettc; TupleDesc rulettc;
StringInfoData buf; StringInfoData buf;
int len;
/*
* Do this first so that string is alloc'd in outer context not SPI's.
*/
initStringInfo(&buf);
/* /*
* Connect to SPI manager * Connect to SPI manager
...@@ -437,7 +422,6 @@ pg_do_getviewdef(Oid viewoid, int prettyFlags) ...@@ -437,7 +422,6 @@ pg_do_getviewdef(Oid viewoid, int prettyFlags)
spirc = SPI_execp(plan_getviewrule, args, nulls, 2); spirc = SPI_execp(plan_getviewrule, args, nulls, 2);
if (spirc != SPI_OK_SELECT) if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid); elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
initStringInfo(&buf);
if (SPI_processed != 1) if (SPI_processed != 1)
appendStringInfo(&buf, "Not a view"); appendStringInfo(&buf, "Not a view");
else else
...@@ -449,11 +433,6 @@ pg_do_getviewdef(Oid viewoid, int prettyFlags) ...@@ -449,11 +433,6 @@ pg_do_getviewdef(Oid viewoid, int prettyFlags)
rulettc = SPI_tuptable->tupdesc; rulettc = SPI_tuptable->tupdesc;
make_viewdef(&buf, ruletup, rulettc, prettyFlags); make_viewdef(&buf, ruletup, rulettc, prettyFlags);
} }
len = buf.len + VARHDRSZ;
ruledef = SPI_palloc(len);
VARATT_SIZEP(ruledef) = len;
memcpy(VARDATA(ruledef), buf.data, buf.len);
pfree(buf.data);
/* /*
* Disconnect from SPI manager * Disconnect from SPI manager
...@@ -461,7 +440,7 @@ pg_do_getviewdef(Oid viewoid, int prettyFlags) ...@@ -461,7 +440,7 @@ pg_do_getviewdef(Oid viewoid, int prettyFlags)
if (SPI_finish() != SPI_OK_FINISH) if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed"); elog(ERROR, "SPI_finish failed");
return ruledef; return buf.data;
} }
/* ---------- /* ----------
...@@ -472,10 +451,8 @@ Datum ...@@ -472,10 +451,8 @@ Datum
pg_get_triggerdef(PG_FUNCTION_ARGS) pg_get_triggerdef(PG_FUNCTION_ARGS)
{ {
Oid trigid = PG_GETARG_OID(0); Oid trigid = PG_GETARG_OID(0);
text *trigdef;
HeapTuple ht_trig; HeapTuple ht_trig;
Form_pg_trigger trigrec; Form_pg_trigger trigrec;
int len;
StringInfoData buf; StringInfoData buf;
Relation tgrel; Relation tgrel;
ScanKeyData skey[1]; ScanKeyData skey[1];
...@@ -597,21 +574,12 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) ...@@ -597,21 +574,12 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
/* We deliberately do not put semi-colon at end */ /* We deliberately do not put semi-colon at end */
appendStringInfo(&buf, ")"); appendStringInfo(&buf, ")");
/* /* Clean up */
* Create the result as a TEXT datum, and free working data
*/
len = buf.len + VARHDRSZ;
trigdef = (text *) palloc(len);
VARATT_SIZEP(trigdef) = len;
memcpy(VARDATA(trigdef), buf.data, buf.len);
pfree(buf.data);
systable_endscan(tgscan); systable_endscan(tgscan);
heap_close(tgrel, AccessShareLock); heap_close(tgrel, AccessShareLock);
PG_RETURN_TEXT_P(trigdef); PG_RETURN_TEXT_P(string_to_text(buf.data));
} }
/* ---------- /* ----------
...@@ -627,7 +595,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -627,7 +595,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
{ {
Oid indexrelid = PG_GETARG_OID(0); Oid indexrelid = PG_GETARG_OID(0);
return pg_get_indexdef_worker(indexrelid, 0, 0); PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0, 0)));
} }
Datum Datum
...@@ -639,13 +607,19 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS) ...@@ -639,13 +607,19 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
int prettyFlags; int prettyFlags;
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
return pg_get_indexdef_worker(indexrelid, colno, prettyFlags); PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno, prettyFlags)));
}
/* Internal version that returns a palloc'd C string */
char *
pg_get_indexdef_string(Oid indexrelid)
{
return pg_get_indexdef_worker(indexrelid, 0, 0);
} }
static Datum static char *
pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags) pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
{ {
text *indexdef;
HeapTuple ht_idx; HeapTuple ht_idx;
HeapTuple ht_idxrel; HeapTuple ht_idxrel;
HeapTuple ht_am; HeapTuple ht_am;
...@@ -655,7 +629,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags) ...@@ -655,7 +629,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
List *indexprs; List *indexprs;
List *context; List *context;
Oid indrelid; Oid indrelid;
int len;
int keyno; int keyno;
Oid keycoltype; Oid keycoltype;
StringInfoData buf; StringInfoData buf;
...@@ -817,21 +790,12 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags) ...@@ -817,21 +790,12 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
} }
} }
/* /* Clean up */
* Create the result as a TEXT datum, and free working data
*/
len = buf.len + VARHDRSZ;
indexdef = (text *) palloc(len);
VARATT_SIZEP(indexdef) = len;
memcpy(VARDATA(indexdef), buf.data, buf.len);
pfree(buf.data);
ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idx);
ReleaseSysCache(ht_idxrel); ReleaseSysCache(ht_idxrel);
ReleaseSysCache(ht_am); ReleaseSysCache(ht_am);
PG_RETURN_TEXT_P(indexdef); return buf.data;
} }
...@@ -846,7 +810,8 @@ pg_get_constraintdef(PG_FUNCTION_ARGS) ...@@ -846,7 +810,8 @@ pg_get_constraintdef(PG_FUNCTION_ARGS)
{ {
Oid constraintId = PG_GETARG_OID(0); Oid constraintId = PG_GETARG_OID(0);
return pg_get_constraintdef_worker(constraintId, 0); PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId,
false, 0)));
} }
Datum Datum
...@@ -857,16 +822,22 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS) ...@@ -857,16 +822,22 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
int prettyFlags; int prettyFlags;
prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
return pg_get_constraintdef_worker(constraintId, prettyFlags); PG_RETURN_TEXT_P(string_to_text(pg_get_constraintdef_worker(constraintId,
false, prettyFlags)));
} }
/* Internal version that returns a palloc'd C string */
char *
pg_get_constraintdef_string(Oid constraintId)
{
return pg_get_constraintdef_worker(constraintId, true, 0);
}
static Datum static char *
pg_get_constraintdef_worker(Oid constraintId, int prettyFlags) pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
int prettyFlags)
{ {
text *result;
StringInfoData buf; StringInfoData buf;
int len;
Relation conDesc; Relation conDesc;
SysScanDesc conscan; SysScanDesc conscan;
ScanKeyData skey[1]; ScanKeyData skey[1];
...@@ -894,6 +865,13 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags) ...@@ -894,6 +865,13 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags)
initStringInfo(&buf); initStringInfo(&buf);
if (fullCommand && OidIsValid(conForm->conrelid))
{
appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
generate_relation_name(conForm->conrelid),
quote_identifier(NameStr(conForm->conname)));
}
switch (conForm->contype) switch (conForm->contype)
{ {
case CONSTRAINT_FOREIGN: case CONSTRAINT_FOREIGN:
...@@ -1087,18 +1065,11 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags) ...@@ -1087,18 +1065,11 @@ pg_get_constraintdef_worker(Oid constraintId, int prettyFlags)
break; break;
} }
/* Record the results */
len = buf.len + VARHDRSZ;
result = (text *) palloc(len);
VARATT_SIZEP(result) = len;
memcpy(VARDATA(result), buf.data, buf.len);
/* Cleanup */ /* Cleanup */
pfree(buf.data);
systable_endscan(conscan); systable_endscan(conscan);
heap_close(conDesc, AccessShareLock); heap_close(conDesc, AccessShareLock);
PG_RETURN_TEXT_P(result); return buf.data;
} }
...@@ -1157,7 +1128,7 @@ pg_get_expr(PG_FUNCTION_ARGS) ...@@ -1157,7 +1128,7 @@ pg_get_expr(PG_FUNCTION_ARGS)
if (relname == NULL) if (relname == NULL)
PG_RETURN_NULL(); /* should we raise an error? */ PG_RETURN_NULL(); /* should we raise an error? */
return pg_get_expr_worker(expr, relid, relname, 0); PG_RETURN_TEXT_P(string_to_text(pg_get_expr_worker(expr, relid, relname, 0)));
} }
Datum Datum
...@@ -1176,13 +1147,12 @@ pg_get_expr_ext(PG_FUNCTION_ARGS) ...@@ -1176,13 +1147,12 @@ pg_get_expr_ext(PG_FUNCTION_ARGS)
if (relname == NULL) if (relname == NULL)
PG_RETURN_NULL(); /* should we raise an error? */ PG_RETURN_NULL(); /* should we raise an error? */
return pg_get_expr_worker(expr, relid, relname, prettyFlags); PG_RETURN_TEXT_P(string_to_text(pg_get_expr_worker(expr, relid, relname, prettyFlags)));
} }
static Datum static char *
pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags) pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags)
{ {
text *result;
Node *node; Node *node;
List *context; List *context;
char *exprstr; char *exprstr;
...@@ -1200,11 +1170,7 @@ pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags) ...@@ -1200,11 +1170,7 @@ pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags)
str = deparse_expression_pretty(node, context, false, false, str = deparse_expression_pretty(node, context, false, false,
prettyFlags, 0); prettyFlags, 0);
/* Pass the result back as TEXT */ return str;
result = DatumGetTextP(DirectFunctionCall1(textin,
CStringGetDatum(str)));
PG_RETURN_TEXT_P(result);
} }
...@@ -4364,3 +4330,25 @@ print_operator_name(StringInfo buf, List *opname) ...@@ -4364,3 +4330,25 @@ print_operator_name(StringInfo buf, List *opname)
appendStringInfo(buf, "%s)", strVal(lfirst(opname))); appendStringInfo(buf, "%s)", strVal(lfirst(opname)));
} }
} }
/*
* Given a C string, produce a TEXT datum.
*
* We assume that the input was palloc'd and may be freed.
*/
static text *
string_to_text(char *str)
{
text *result;
int slen = strlen(str);
int tlen;
tlen = slen + VARHDRSZ;
result = (text *) palloc(tlen);
VARATT_SIZEP(result) = tlen;
memcpy(VARDATA(result), str, slen);
pfree(str);
return result;
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.11 2003/11/29 22:40:58 pgsql Exp $ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.12 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#include "nodes/parsenodes.h" /* for DropBehavior */ #include "nodes/parsenodes.h" /* for DropBehavior */
/* /*----------
* Precise semantics of a dependency relationship are specified by the * Precise semantics of a dependency relationship are specified by the
* DependencyType code (which is stored in a "char" field in pg_depend, * DependencyType code (which is stored in a "char" field in pg_depend,
* so we assign ASCII-code values to the enumeration members). * so we assign ASCII-code values to the enumeration members).
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
* contain zeroes. * contain zeroes.
* *
* Other dependency flavors may be needed in future. * Other dependency flavors may be needed in future.
*----------
*/ */
typedef enum DependencyType typedef enum DependencyType
...@@ -79,6 +80,28 @@ typedef struct ObjectAddress ...@@ -79,6 +80,28 @@ typedef struct ObjectAddress
} ObjectAddress; } ObjectAddress;
/*
* This enum covers all system catalogs whose OIDs can appear in classId.
*/
typedef enum ObjectClass
{
OCLASS_CLASS, /* pg_class */
OCLASS_PROC, /* pg_proc */
OCLASS_TYPE, /* pg_type */
OCLASS_CAST, /* pg_cast */
OCLASS_CONSTRAINT, /* pg_constraint */
OCLASS_CONVERSION, /* pg_conversion */
OCLASS_DEFAULT, /* pg_attrdef */
OCLASS_LANGUAGE, /* pg_language */
OCLASS_OPERATOR, /* pg_operator */
OCLASS_OPCLASS, /* pg_opclass */
OCLASS_REWRITE, /* pg_rewrite */
OCLASS_TRIGGER, /* pg_trigger */
OCLASS_SCHEMA, /* pg_namespace */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
/* in dependency.c */ /* in dependency.c */
extern void performDeletion(const ObjectAddress *object, extern void performDeletion(const ObjectAddress *object,
...@@ -96,6 +119,10 @@ extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, ...@@ -96,6 +119,10 @@ extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
DependencyType behavior, DependencyType behavior,
DependencyType self_behavior); DependencyType self_behavior);
extern ObjectClass getObjectClass(const ObjectAddress *object);
extern char *getObjectDescription(const ObjectAddress *object);
/* in pg_depend.c */ /* in pg_depend.c */
extern void recordDependencyOn(const ObjectAddress *depender, extern void recordDependencyOn(const ObjectAddress *depender,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.65 2004/03/23 19:35:17 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.66 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,6 +27,14 @@ typedef struct RawColumnDefault ...@@ -27,6 +27,14 @@ typedef struct RawColumnDefault
* tree) */ * tree) */
} RawColumnDefault; } RawColumnDefault;
typedef struct CookedConstraint
{
ConstrType contype; /* CONSTR_DEFAULT or CONSTR_CHECK */
char *name; /* name, or NULL if none */
AttrNumber attnum; /* which attr (only for DEFAULT) */
Node *expr; /* transformed default or check expr */
} CookedConstraint;
extern Relation heap_create(const char *relname, extern Relation heap_create(const char *relname,
Oid relnamespace, Oid relnamespace,
TupleDesc tupDesc, TupleDesc tupDesc,
...@@ -52,10 +60,12 @@ extern void heap_truncate(Oid rid); ...@@ -52,10 +60,12 @@ extern void heap_truncate(Oid rid);
extern void heap_truncate_check_FKs(Relation rel); extern void heap_truncate_check_FKs(Relation rel);
extern void AddRelationRawConstraints(Relation rel, extern List *AddRelationRawConstraints(Relation rel,
List *rawColDefaults, List *rawColDefaults,
List *rawConstraints); List *rawConstraints);
extern void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin);
extern Node *cookDefault(ParseState *pstate, extern Node *cookDefault(ParseState *pstate,
Node *raw_default, Node *raw_default,
Oid atttypid, Oid atttypid,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.54 2003/11/29 22:40:58 pgsql Exp $ * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.55 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -37,7 +37,8 @@ extern Oid index_create(Oid heapRelationId, ...@@ -37,7 +37,8 @@ extern Oid index_create(Oid heapRelationId,
Oid *classObjectId, Oid *classObjectId,
bool primary, bool primary,
bool isconstraint, bool isconstraint,
bool allow_system_table_mods); bool allow_system_table_mods,
bool skip_build);
extern void index_drop(Oid indexId); extern void index_drop(Oid indexId);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.20 2003/11/29 22:40:59 pgsql Exp $ * $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.21 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,5 +20,9 @@ ...@@ -20,5 +20,9 @@
extern void cluster(ClusterStmt *stmt); extern void cluster(ClusterStmt *stmt);
extern void rebuild_relation(Relation OldHeap, Oid indexOid); extern void rebuild_relation(Relation OldHeap, Oid indexOid);
extern Oid make_new_heap(Oid OIDOldHeap, const char *NewName);
extern List *get_indexattr_list(Relation OldHeap, Oid OldIndex);
extern void rebuild_indexes(Oid OIDOldHeap, List *indexes);
extern void swap_relfilenodes(Oid r1, Oid r2);
#endif /* CLUSTER_H */ #endif /* CLUSTER_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.54 2004/02/21 00:34:53 tgl Exp $ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.55 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,11 +22,15 @@ extern void DefineIndex(RangeVar *heapRelation, ...@@ -22,11 +22,15 @@ extern void DefineIndex(RangeVar *heapRelation,
char *indexRelationName, char *indexRelationName,
char *accessMethodName, char *accessMethodName,
List *attributeList, List *attributeList,
Expr *predicate,
List *rangetable,
bool unique, bool unique,
bool primary, bool primary,
bool isconstraint, bool isconstraint,
Expr *predicate, bool is_alter_table,
List *rangetable); bool check_rights,
bool skip_build,
bool quiet);
extern void RemoveIndex(RangeVar *relation, DropBehavior behavior); extern void RemoveIndex(RangeVar *relation, DropBehavior behavior);
extern void ReindexIndex(RangeVar *indexRelation, bool force); extern void ReindexIndex(RangeVar *indexRelation, bool force);
extern void ReindexTable(RangeVar *relation, bool force); extern void ReindexTable(RangeVar *relation, bool force);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.15 2004/03/23 19:35:17 tgl Exp $ * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.16 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,46 +16,17 @@ ...@@ -16,46 +16,17 @@
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
extern void AlterTableAddColumn(Oid myrelid, bool recurse, ColumnDef *colDef);
extern void AlterTableAlterColumnDropNotNull(Oid myrelid, bool recurse, extern Oid DefineRelation(CreateStmt *stmt, char relkind);
const char *colName);
extern void AlterTableAlterColumnSetNotNull(Oid myrelid, bool recurse,
const char *colName);
extern void AlterTableAlterColumnDefault(Oid myrelid, bool recurse,
const char *colName,
Node *newDefault);
extern void AlterTableAlterColumnFlags(Oid myrelid, bool recurse,
const char *colName,
Node *flagValue, const char *flagType);
extern void AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
const char *colName,
DropBehavior behavior);
extern void AlterTableAddConstraint(Oid myrelid, bool recurse, extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior);
List *newConstraints);
extern void AlterTableDropConstraint(Oid myrelid, bool recurse, extern void AlterTable(AlterTableStmt *stmt);
const char *constrName,
DropBehavior behavior);
extern void AlterTableClusterOn(Oid relOid, const char *indexName); extern void AlterTableInternal(Oid relid, List *cmds, bool recurse);
extern void AlterTableCreateToastTable(Oid relOid, bool silent); extern void AlterTableCreateToastTable(Oid relOid, bool silent);
extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId);
extern void AlterTableAlterOids(Oid myrelid, bool setOid, bool recurse,
DropBehavior behavior);
extern Oid DefineRelation(CreateStmt *stmt, char relkind);
extern void RemoveRelation(const RangeVar *relation, DropBehavior behavior);
extern void TruncateRelation(const RangeVar *relation); extern void TruncateRelation(const RangeVar *relation);
extern void renameatt(Oid myrelid, extern void renameatt(Oid myrelid,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.152 2004/04/01 21:28:46 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.153 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -200,6 +200,7 @@ typedef enum NodeTag ...@@ -200,6 +200,7 @@ typedef enum NodeTag
T_UpdateStmt, T_UpdateStmt,
T_SelectStmt, T_SelectStmt,
T_AlterTableStmt, T_AlterTableStmt,
T_AlterTableCmd,
T_AlterDomainStmt, T_AlterDomainStmt,
T_SetOperationStmt, T_SetOperationStmt,
T_GrantStmt, T_GrantStmt,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.254 2004/03/11 01:47:41 ishii Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.255 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -762,44 +762,58 @@ typedef enum DropBehavior ...@@ -762,44 +762,58 @@ typedef enum DropBehavior
/* ---------------------- /* ----------------------
* Alter Table * Alter Table
*
* The fields are used in different ways by the different variants of
* this command.
* ---------------------- * ----------------------
*/ */
typedef struct AlterTableStmt typedef struct AlterTableStmt
{ {
NodeTag type; NodeTag type;
char subtype; /*------------
* A = add column
* T = alter column default
* N = alter column drop not null
* n = alter column set not null
* S = alter column statistics
* M = alter column storage
* D = drop column
* C = add constraint
* c = pre-processed add constraint
* (local in parser/analyze.c)
* X = drop constraint
* E = create toast table
* U = change owner
* L = CLUSTER ON
* o = DROP OIDS
*------------
*/
RangeVar *relation; /* table to work on */ RangeVar *relation; /* table to work on */
List *cmds; /* list of subcommands */
} AlterTableStmt;
typedef enum AlterTableType
{
AT_AddColumn, /* add column */
AT_ColumnDefault, /* alter column default */
AT_DropNotNull, /* alter column drop not null */
AT_SetNotNull, /* alter column set not null */
AT_SetStatistics, /* alter column statistics */
AT_SetStorage, /* alter column storage */
AT_DropColumn, /* drop column */
AT_DropColumnRecurse, /* internal to commands/tablecmds.c */
AT_AddIndex, /* add index */
AT_ReAddIndex, /* internal to commands/tablecmds.c */
AT_AddConstraint, /* add constraint */
AT_ProcessedConstraint, /* pre-processed add constraint
* (local in parser/analyze.c) */
AT_DropConstraint, /* drop constraint */
AT_DropConstraintQuietly, /* drop constraint, no error/warning
* (local in commands/tablecmds.c) */
AT_AlterColumnType, /* alter column type */
AT_ToastTable, /* create toast table */
AT_ChangeOwner, /* change owner */
AT_ClusterOn, /* CLUSTER ON */
AT_DropOids /* SET WITHOUT OIDS */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
{
NodeTag type;
AlterTableType subtype; /* Type of table alteration to apply */
char *name; /* column or constraint name to act on, or char *name; /* column or constraint name to act on, or
* new owner */ * new owner */
Node *def; /* definition of new column or constraint */ Node *def; /* definition of new column, column type,
* index, or constraint */
Node *transform; /* transformation expr for ALTER TYPE */
DropBehavior behavior; /* RESTRICT or CASCADE for DROP cases */ DropBehavior behavior; /* RESTRICT or CASCADE for DROP cases */
} AlterTableStmt; } AlterTableCmd;
/* ---------------------- /* ----------------------
* Alter Domain * Alter Domain
* *
* The fields are used in different ways by the different variants of * The fields are used in different ways by the different variants of
* this command. Subtypes should match AlterTable subtypes where possible. * this command.
* ---------------------- * ----------------------
*/ */
typedef struct AlterDomainStmt typedef struct AlterDomainStmt
...@@ -814,7 +828,7 @@ typedef struct AlterDomainStmt ...@@ -814,7 +828,7 @@ typedef struct AlterDomainStmt
* U = change owner * U = change owner
*------------ *------------
*/ */
List *typename; /* table to work on */ List *typename; /* domain to work on */
char *name; /* column or constraint name to act on, or char *name; /* column or constraint name to act on, or
* new owner */ * new owner */
Node *def; /* definition of default or constraint */ Node *def; /* definition of default or constraint */
...@@ -922,6 +936,8 @@ typedef struct CreateStmt ...@@ -922,6 +936,8 @@ typedef struct CreateStmt
* Definitions for plain (non-FOREIGN KEY) constraints in CreateStmt * Definitions for plain (non-FOREIGN KEY) constraints in CreateStmt
* *
* XXX probably these ought to be unified with FkConstraints at some point? * XXX probably these ought to be unified with FkConstraints at some point?
* To this end we include CONSTR_FOREIGN in the ConstrType enum, even though
* the parser does not generate it.
* *
* For constraints that use expressions (CONSTR_DEFAULT, CONSTR_CHECK) * For constraints that use expressions (CONSTR_DEFAULT, CONSTR_CHECK)
* we may have the expression in either "raw" form (an untransformed * we may have the expression in either "raw" form (an untransformed
...@@ -944,6 +960,7 @@ typedef enum ConstrType /* types of constraints */ ...@@ -944,6 +960,7 @@ typedef enum ConstrType /* types of constraints */
CONSTR_NOTNULL, CONSTR_NOTNULL,
CONSTR_DEFAULT, CONSTR_DEFAULT,
CONSTR_CHECK, CONSTR_CHECK,
CONSTR_FOREIGN,
CONSTR_PRIMARY, CONSTR_PRIMARY,
CONSTR_UNIQUE, CONSTR_UNIQUE,
CONSTR_ATTR_DEFERRABLE, /* attributes for previous constraint node */ CONSTR_ATTR_DEFERRABLE, /* attributes for previous constraint node */
...@@ -1291,7 +1308,7 @@ typedef struct FetchStmt ...@@ -1291,7 +1308,7 @@ typedef struct FetchStmt
typedef struct IndexStmt typedef struct IndexStmt
{ {
NodeTag type; NodeTag type;
char *idxname; /* name of the index */ char *idxname; /* name of new index, or NULL for default */
RangeVar *relation; /* relation to build index on */ RangeVar *relation; /* relation to build index on */
char *accessMethod; /* name of access method (eg. btree) */ char *accessMethod; /* name of access method (eg. btree) */
List *indexParams; /* a list of IndexElem */ List *indexParams; /* a list of IndexElem */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.25 2004/01/23 02:13:12 neilc Exp $ * $PostgreSQL: pgsql/src/include/parser/analyze.h,v 1.26 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,6 +22,9 @@ extern List *parse_analyze_varparams(Node *parseTree, Oid **paramTypes, ...@@ -22,6 +22,9 @@ extern List *parse_analyze_varparams(Node *parseTree, Oid **paramTypes,
extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState); extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt); extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
extern char *makeObjectName(const char *name1, const char *name2,
const char *typename);
extern void CheckSelectForUpdate(Query *qry); extern void CheckSelectForUpdate(Query *qry);
#endif /* ANALYZE_H */ #endif /* ANALYZE_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.236 2004/04/01 21:28:46 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.237 2004/05/05 04:48:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -463,9 +463,11 @@ extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS); ...@@ -463,9 +463,11 @@ extern Datum pg_get_viewdef_name(PG_FUNCTION_ARGS);
extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS); extern Datum pg_get_viewdef_name_ext(PG_FUNCTION_ARGS);
extern Datum pg_get_indexdef(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef(PG_FUNCTION_ARGS);
extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_indexdef_ext(PG_FUNCTION_ARGS);
extern char *pg_get_indexdef_string(Oid indexrelid);
extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS); extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS);
extern Datum pg_get_constraintdef(PG_FUNCTION_ARGS); extern Datum pg_get_constraintdef(PG_FUNCTION_ARGS);
extern Datum pg_get_constraintdef_ext(PG_FUNCTION_ARGS); extern Datum pg_get_constraintdef_ext(PG_FUNCTION_ARGS);
extern char *pg_get_constraintdef_string(Oid constraintId);
extern Datum pg_get_userbyid(PG_FUNCTION_ARGS); extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
extern Datum pg_get_expr(PG_FUNCTION_ARGS); extern Datum pg_get_expr(PG_FUNCTION_ARGS);
extern Datum pg_get_expr_ext(PG_FUNCTION_ARGS); extern Datum pg_get_expr_ext(PG_FUNCTION_ARGS);
......
...@@ -7,7 +7,7 @@ COMMENT ON TABLE tmp_wrong IS 'table comment'; ...@@ -7,7 +7,7 @@ COMMENT ON TABLE tmp_wrong IS 'table comment';
ERROR: relation "tmp_wrong" does not exist ERROR: relation "tmp_wrong" does not exist
COMMENT ON TABLE tmp IS 'table comment'; COMMENT ON TABLE tmp IS 'table comment';
COMMENT ON TABLE tmp IS NULL; COMMENT ON TABLE tmp IS NULL;
ALTER TABLE tmp ADD COLUMN a int4; ALTER TABLE tmp ADD COLUMN a int4 default 3;
ALTER TABLE tmp ADD COLUMN b name; ALTER TABLE tmp ADD COLUMN b name;
ALTER TABLE tmp ADD COLUMN c text; ALTER TABLE tmp ADD COLUMN c text;
ALTER TABLE tmp ADD COLUMN d float8; ALTER TABLE tmp ADD COLUMN d float8;
...@@ -419,7 +419,6 @@ create table atacc1 ( test int ); ...@@ -419,7 +419,6 @@ create table atacc1 ( test int );
insert into atacc1 (test) values (NULL); insert into atacc1 (test) values (NULL);
-- add a primary key (fails) -- add a primary key (fails)
alter table atacc1 add constraint atacc_test1 primary key (test); alter table atacc1 add constraint atacc_test1 primary key (test);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "atacc_test1" for table "atacc1"
ERROR: column "test" contains null values ERROR: column "test" contains null values
insert into atacc1 (test) values (3); insert into atacc1 (test) values (3);
drop table atacc1; drop table atacc1;
...@@ -1143,3 +1142,96 @@ select f3,max(f1) from foo group by f3; ...@@ -1143,3 +1142,96 @@ select f3,max(f1) from foo group by f3;
zz | qq zz | qq
(1 row) (1 row)
-- Simple tests for alter table column type
alter table foo alter f1 TYPE integer; -- fails
ERROR: column "f1" cannot be cast to type "pg_catalog.int4"
alter table foo alter f1 TYPE varchar(10);
create table anothertab (atcol1 serial8, atcol2 boolean,
constraint anothertab_chk check (atcol1 <= 3));
NOTICE: CREATE TABLE will create implicit sequence "anothertab_atcol1_seq" for "serial" column "anothertab.atcol1"
insert into anothertab (atcol1, atcol2) values (default, true);
insert into anothertab (atcol1, atcol2) values (default, false);
select * from anothertab;
atcol1 | atcol2
--------+--------
1 | t
2 | f
(2 rows)
alter table anothertab alter column atcol1 type boolean; -- fails
ERROR: column "atcol1" cannot be cast to type "pg_catalog.bool"
alter table anothertab alter column atcol1 type integer;
select * from anothertab;
atcol1 | atcol2
--------+--------
1 | t
2 | f
(2 rows)
insert into anothertab (atcol1, atcol2) values (45, null); -- fails
ERROR: new row for relation "anothertab" violates check constraint "anothertab_chk"
insert into anothertab (atcol1, atcol2) values (default, null);
select * from anothertab;
atcol1 | atcol2
--------+--------
1 | t
2 | f
3 |
(3 rows)
alter table anothertab alter column atcol2 type text
using case when atcol2 is true then 'IT WAS TRUE'
when atcol2 is false then 'IT WAS FALSE'
else 'IT WAS NULL!' end;
select * from anothertab;
atcol1 | atcol2
--------+--------------
1 | IT WAS TRUE
2 | IT WAS FALSE
3 | IT WAS NULL!
(3 rows)
alter table anothertab alter column atcol1 type boolean
using case when atcol1 % 2 = 0 then true else false end; -- fails
ERROR: default for column "atcol1" cannot be cast to type "pg_catalog.bool"
alter table anothertab alter column atcol1 drop default;
alter table anothertab alter column atcol1 type boolean
using case when atcol1 % 2 = 0 then true else false end; -- fails
ERROR: operator does not exist: boolean <= integer
HINT: No operator matches the given name and argument type(s). You may need to add explicit type casts.
alter table anothertab drop constraint anothertab_chk;
alter table anothertab alter column atcol1 type boolean
using case when atcol1 % 2 = 0 then true else false end;
select * from anothertab;
atcol1 | atcol2
--------+--------------
f | IT WAS TRUE
t | IT WAS FALSE
f | IT WAS NULL!
(3 rows)
drop table anothertab;
create table another (f1 int, f2 text);
insert into another values(1, 'one');
insert into another values(2, 'two');
insert into another values(3, 'three');
select * from another;
f1 | f2
----+-------
1 | one
2 | two
3 | three
(3 rows)
alter table another
alter f1 type text using f2 || ' more',
alter f2 type bigint using f1 * 10;
select * from another;
f1 | f2
------------+----
one more | 10
two more | 20
three more | 30
(3 rows)
drop table another;
...@@ -145,6 +145,28 @@ SELECT * FROM FKTABLE; ...@@ -145,6 +145,28 @@ SELECT * FROM FKTABLE;
| | 8 | | 8
(5 rows) (5 rows)
-- Try altering the column type where foreign keys are involved
ALTER TABLE PKTABLE ALTER COLUMN ptest1 TYPE bigint;
ALTER TABLE FKTABLE ALTER COLUMN ftest1 TYPE bigint;
SELECT * FROM PKTABLE;
ptest1 | ptest2 | ptest3
--------+--------+---------
1 | 3 | Test1-2
3 | 6 | Test3
4 | 8 | Test4
1 | 4 | Test2
(4 rows)
SELECT * FROM FKTABLE;
ftest1 | ftest2 | ftest3
--------+--------+--------
1 | 3 | 5
3 | 6 | 12
| | 0
| | 4
| | 8
(5 rows)
DROP TABLE PKTABLE CASCADE; DROP TABLE PKTABLE CASCADE;
NOTICE: drop cascades to constraint constrname on table fktable NOTICE: drop cascades to constraint constrname on table fktable
DROP TABLE FKTABLE; DROP TABLE FKTABLE;
......
...@@ -613,3 +613,12 @@ SELECT * FROM inhf; /* Single entry with value 'text' */ ...@@ -613,3 +613,12 @@ SELECT * FROM inhf; /* Single entry with value 'text' */
text text
(1 row) (1 row)
-- Test changing the type of inherited columns
insert into d values('test','one','two','three');
alter table a alter column aa type integer using bit_length(aa);
select * from d;
aa | bb | cc | dd
----+-----+-----+-------
32 | one | two | three
(1 row)
...@@ -9,7 +9,7 @@ COMMENT ON TABLE tmp_wrong IS 'table comment'; ...@@ -9,7 +9,7 @@ COMMENT ON TABLE tmp_wrong IS 'table comment';
COMMENT ON TABLE tmp IS 'table comment'; COMMENT ON TABLE tmp IS 'table comment';
COMMENT ON TABLE tmp IS NULL; COMMENT ON TABLE tmp IS NULL;
ALTER TABLE tmp ADD COLUMN a int4; ALTER TABLE tmp ADD COLUMN a int4 default 3;
ALTER TABLE tmp ADD COLUMN b name; ALTER TABLE tmp ADD COLUMN b name;
...@@ -918,3 +918,60 @@ select * from foo; ...@@ -918,3 +918,60 @@ select * from foo;
update foo set f3 = 'zz'; update foo set f3 = 'zz';
select * from foo; select * from foo;
select f3,max(f1) from foo group by f3; select f3,max(f1) from foo group by f3;
-- Simple tests for alter table column type
alter table foo alter f1 TYPE integer; -- fails
alter table foo alter f1 TYPE varchar(10);
create table anothertab (atcol1 serial8, atcol2 boolean,
constraint anothertab_chk check (atcol1 <= 3));
insert into anothertab (atcol1, atcol2) values (default, true);
insert into anothertab (atcol1, atcol2) values (default, false);
select * from anothertab;
alter table anothertab alter column atcol1 type boolean; -- fails
alter table anothertab alter column atcol1 type integer;
select * from anothertab;
insert into anothertab (atcol1, atcol2) values (45, null); -- fails
insert into anothertab (atcol1, atcol2) values (default, null);
select * from anothertab;
alter table anothertab alter column atcol2 type text
using case when atcol2 is true then 'IT WAS TRUE'
when atcol2 is false then 'IT WAS FALSE'
else 'IT WAS NULL!' end;
select * from anothertab;
alter table anothertab alter column atcol1 type boolean
using case when atcol1 % 2 = 0 then true else false end; -- fails
alter table anothertab alter column atcol1 drop default;
alter table anothertab alter column atcol1 type boolean
using case when atcol1 % 2 = 0 then true else false end; -- fails
alter table anothertab drop constraint anothertab_chk;
alter table anothertab alter column atcol1 type boolean
using case when atcol1 % 2 = 0 then true else false end;
select * from anothertab;
drop table anothertab;
create table another (f1 int, f2 text);
insert into another values(1, 'one');
insert into another values(2, 'two');
insert into another values(3, 'three');
select * from another;
alter table another
alter f1 type text using f2 || ' more',
alter f2 type bigint using f1 * 10;
select * from another;
drop table another;
...@@ -97,6 +97,12 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2; ...@@ -97,6 +97,12 @@ UPDATE PKTABLE SET ptest1=1 WHERE ptest1=2;
-- Check FKTABLE for update of matched row -- Check FKTABLE for update of matched row
SELECT * FROM FKTABLE; SELECT * FROM FKTABLE;
-- Try altering the column type where foreign keys are involved
ALTER TABLE PKTABLE ALTER COLUMN ptest1 TYPE bigint;
ALTER TABLE FKTABLE ALTER COLUMN ftest1 TYPE bigint;
SELECT * FROM PKTABLE;
SELECT * FROM FKTABLE;
DROP TABLE PKTABLE CASCADE; DROP TABLE PKTABLE CASCADE;
DROP TABLE FKTABLE; DROP TABLE FKTABLE;
......
...@@ -142,3 +142,10 @@ CREATE TABLE inhf (LIKE inhx, LIKE inhx); /* Throw error */ ...@@ -142,3 +142,10 @@ CREATE TABLE inhf (LIKE inhx, LIKE inhx); /* Throw error */
CREATE TABLE inhf (LIKE inhx INCLUDING DEFAULTS); CREATE TABLE inhf (LIKE inhx INCLUDING DEFAULTS);
INSERT INTO inhf DEFAULT VALUES; INSERT INTO inhf DEFAULT VALUES;
SELECT * FROM inhf; /* Single entry with value 'text' */ SELECT * FROM inhf; /* Single entry with value 'text' */
-- Test changing the type of inherited columns
insert into d values('test','one','two','three');
alter table a alter column aa type integer using bit_length(aa);
select * from d;
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