-<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.169 2006/07/06 01:46:37 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.170 2006/08/21 00:57:23 tgl Exp $ -->
<chapter id="datatype">
<title id="datatype-title">Data Types</title>
<programlisting>
CREATE SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq;
CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
- <replaceable class="parameter">colname</replaceable> integer DEFAULT nextval('<replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq') NOT NULL
+ <replaceable class="parameter">colname</replaceable> integer NOT NULL DEFAULT nextval('<replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq')
);
+ALTER SEQUENCE <replaceable class="parameter">tablename</replaceable>_<replaceable class="parameter">colname</replaceable>_seq OWNED BY <replaceable class="parameter">tablename</replaceable>.<replaceable class="parameter">colname</replaceable>;
</programlisting>
Thus, we have created an integer column and arranged for its default
values to be assigned from a sequence generator. A <literal>NOT NULL</>
constraint is applied to ensure that a null value cannot be explicitly
- inserted, either. In most cases you would also want to attach a
+ inserted, either. (In most cases you would also want to attach a
<literal>UNIQUE</> or <literal>PRIMARY KEY</> constraint to prevent
duplicate values from being inserted by accident, but this is
- not automatic.
+ not automatic.) Lastly, the sequence is marked as <quote>owned by</>
+ the column, so that it will be dropped if the column or table is dropped.
</para>
<note>
<para>
The sequence created for a <type>serial</type> column is
- automatically dropped when the owning column is dropped, and
- cannot be dropped otherwise. (This was not true in
- <productname>PostgreSQL</productname> releases before 7.3. Note
- that this automatic drop linkage will not occur for a sequence
- created by reloading a dump from a pre-7.3 database; the dump
- file does not contain the information needed to establish the
- dependency link.) Furthermore, this dependency between sequence
- and column is made only for the <type>serial</> column itself. If
- any other columns reference the sequence (perhaps by manually
- calling the <function>nextval</> function), they will be broken
- if the sequence is removed. Using a <type>serial</> column's sequence
- in such a fashion is considered bad form; if you wish to feed several
- columns from the same sequence generator, create the sequence as an
- independent object.
+ automatically dropped when the owning column is dropped.
+ You can drop the sequence without dropping the column, but this
+ will force removal of the column default expression.
</para>
</sect2>
</sect1>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.330 2006/08/17 23:04:03 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.331 2006/08/21 00:57:23 tgl Exp $ -->
<chapter id="functions">
<title>Functions and Operators</title>
<para>
<function>pg_get_serial_sequence</function> fetches the name of the
- sequence associated with a <type>serial</> or <type>bigserial</>
- column. The name is suitably formatted for passing to the sequence
- functions (see <xref linkend="functions-sequence">). NULL is
- returned if the column does not have an associated sequence.
+ sequence associated with a column, or NULL if there is no sequence
+ associated with the column. The result is suitably formatted for passing
+ to the sequence functions (see <xref linkend="functions-sequence">).
+ This association can be modified or removed with <command>ALTER SEQUENCE
+ OWNED BY</>. (The function probably should have been called
+ <function>pg_get_owned_sequence</function>; its name reflects the fact
+ that it's typically used with <type>serial</> or <type>bigserial</>
+ columns.)
</para>
<para>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_sequence.sgml,v 1.13 2005/11/01 21:09:50 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_sequence.sgml,v 1.14 2006/08/21 00:57:24 tgl Exp $
PostgreSQL documentation
-->
ALTER SEQUENCE <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ]
[ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
[ RESTART [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
+ [ OWNED BY { <replaceable class="parameter">table</replaceable>.<replaceable class="parameter">column</replaceable> | NONE } ]
ALTER SEQUENCE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
</synopsis>
</refsynopsisdiv>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>OWNED BY</literal> <replaceable class="parameter">table</replaceable>.<replaceable class="parameter">column</replaceable></term>
+ <term><literal>OWNED BY NONE</literal></term>
+ <listitem>
+ <para>
+ The <literal>OWNED BY</literal> option causes the sequence to be
+ associated with a specific table column, such that if that column
+ (or its whole table) is dropped, the sequence will be automatically
+ dropped as well. If specified, this association replaces any
+ previously specified association for the sequence. The specified
+ table must have the same owner and be in the same schema as the
+ sequence.
+ Specifying <literal>OWNED BY NONE</literal> removes any existing
+ association, making the sequence <quote>free-standing</>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><replaceable class="parameter">new_schema</replaceable></term>
<listitem>
<para>
To avoid blocking of concurrent transactions that obtain numbers from the
- same sequence, <command>ALTER SEQUENCE</command> is never rolled back;
- the changes take effect immediately and are not reversible.
+ same sequence, <command>ALTER SEQUENCE</command>'s effects on the sequence
+ generation parameters are never rolled back;
+ those changes take effect immediately and are not reversible. However,
+ the <literal>OWNED BY</> and <literal>SET SCHEMA</> clauses are ordinary
+ catalog updates and can be rolled back.
</para>
<para>
<function>nextval</> results in backends,
other than the current one, that have preallocated (cached) sequence
values. They will use up all cached values prior to noticing the changed
- sequence parameters. The current backend will be affected immediately.
+ sequence generation parameters. The current backend will be affected
+ immediately.
</para>
<para>
<para>
<command>ALTER SEQUENCE</command> conforms to the <acronym>SQL</acronym>
standard,
- except for the <literal>SET SCHEMA</literal> variant, which is a
- <productname>PostgreSQL</productname> extension.
+ except for the <literal>OWNED BY</> and <literal>SET SCHEMA</literal>
+ clauses, which are <productname>PostgreSQL</productname> extensions.
</para>
</refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-createsequence" endterm="sql-createsequence-title"></member>
+ <member><xref linkend="sql-dropsequence" endterm="sql-dropsequence-title"></member>
+ </simplelist>
+ </refsect1>
+
</refentry>
<!-- Keep this comment at the end of the file
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.88 2006/08/03 20:57:06 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.89 2006/08/21 00:57:24 tgl Exp $
PostgreSQL documentation
-->
<listitem>
<para>
This form moves the table into another schema. Associated indexes,
- constraints, and SERIAL-column sequences are moved as well.
+ constraints, and sequences owned by table columns are moved as well.
</para>
</listitem>
</varlistentry>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_sequence.sgml,v 1.43 2005/11/01 21:09:50 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_sequence.sgml,v 1.44 2006/08/21 00:57:24 tgl Exp $
PostgreSQL documentation
-->
CREATE [ TEMPORARY | TEMP ] SEQUENCE <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ]
[ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
[ START [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
+ [ OWNED BY { <replaceable class="parameter">table</replaceable>.<replaceable class="parameter">column</replaceable> | NONE } ]
</synopsis>
</refsynopsisdiv>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>OWNED BY</literal> <replaceable class="parameter">table</replaceable>.<replaceable class="parameter">column</replaceable></term>
+ <term><literal>OWNED BY NONE</literal></term>
+ <listitem>
+ <para>
+ The <literal>OWNED BY</literal> option causes the sequence to be
+ associated with a specific table column, such that if that column
+ (or its whole table) is dropped, the sequence will be automatically
+ dropped as well. The specified table must have the same owner and be in
+ the same schema as the sequence.
+ <literal>OWNED BY NONE</literal>, the default, specifies that there
+ is no such association.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<command>CREATE SEQUENCE</command> conforms to the <acronym>SQL</acronym>
standard, with the following exceptions:
<itemizedlist>
- <listitem><para>The standard's <literal>AS <data type></literal> expression is not supported.</para></listitem>
- <listitem><para>Obtaining the next value is done using the <function>nextval()</> function instead of the standard's <command>NEXT VALUE FOR</command> expression.</para></listitem>
+ <listitem>
+ <para>
+ The standard's <literal>AS <data type></literal> expression is not
+ supported.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Obtaining the next value is done using the <function>nextval()</>
+ function instead of the standard's <command>NEXT VALUE FOR</command>
+ expression.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The <literal>OWNED BY</> clause is a <productname>PostgreSQL</>
+ extension.
+ </para>
+ </listitem>
</itemizedlist>
</para>
</refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-altersequence" endterm="sql-altersequence-title"></member>
+ <member><xref linkend="sql-dropsequence" endterm="sql-dropsequence-title"></member>
+ </simplelist>
+ </refsect1>
+
</refentry>
<!-- Keep this comment at the end of the file
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/drop_sequence.sgml,v 1.24 2005/11/19 17:39:44 adunstan Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_sequence.sgml,v 1.25 2006/08/21 00:57:24 tgl Exp $
PostgreSQL documentation
-->
<simplelist type="inline">
<member><xref linkend="sql-createsequence" endterm="sql-createsequence-title"></member>
+ <member><xref linkend="sql-altersequence" endterm="sql-altersequence-title"></member>
</simplelist>
</refsect1>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.21 2006/07/11 17:26:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.22 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return count;
}
-/*
- * objectIsInternalDependency -- return whether the specified object
- * is listed as an internal dependency for some other object.
- *
- * This is used to implement DROP/REASSIGN OWNED. We cannot invoke
- * performDeletion blindly, because it may try to drop or modify an internal-
- * dependent object before the "main" object, so we need to skip the first
- * object and expect it to be automatically dropped when the main object is
- * dropped.
- */
-bool
-objectIsInternalDependency(Oid classId, Oid objectId)
-{
- Relation depRel;
- ScanKeyData key[2];
- SysScanDesc scan;
- HeapTuple tup;
- bool isdep = false;
-
- depRel = heap_open(DependRelationId, AccessShareLock);
-
- ScanKeyInit(&key[0],
- Anum_pg_depend_classid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(classId));
- ScanKeyInit(&key[1],
- Anum_pg_depend_objid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(objectId));
-
- scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 2, key);
-
- while (HeapTupleIsValid(tup = systable_getnext(scan)))
- {
- Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
-
- if (depForm->deptype == DEPENDENCY_INTERNAL)
- {
- /* No need to keep scanning */
- isdep = true;
- break;
- }
- }
-
- systable_endscan(scan);
-
- heap_close(depRel, AccessShareLock);
-
- return isdep;
-}
-
/*
* Adjust dependency record(s) to point to a different object of the same type
*
return count;
}
+/*
+ * Detect whether a sequence is marked as "owned" by a column
+ *
+ * An ownership marker is an AUTO dependency from the sequence to the
+ * column. If we find one, store the identity of the owning column
+ * into *tableId and *colId and return TRUE; else return FALSE.
+ *
+ * Note: if there's more than one such pg_depend entry then you get
+ * a random one of them returned into the out parameters. This should
+ * not happen, though.
+ */
+bool
+sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId)
+{
+ bool ret = false;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = heap_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(seqId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == RelationRelationId &&
+ depform->deptype == DEPENDENCY_AUTO)
+ {
+ *tableId = depform->refobjid;
+ *colId = depform->refobjsubid;
+ ret = true;
+ break; /* no need to keep scanning */
+ }
+ }
+
+ systable_endscan(scan);
+
+ heap_close(depRel, AccessShareLock);
+
+ return ret;
+}
+
+/*
+ * Remove any existing "owned" markers for the specified sequence.
+ *
+ * Note: we don't provide a special function to install an "owned"
+ * marker; just use recordDependencyOn().
+ */
+void
+markSequenceUnowned(Oid seqId)
+{
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = heap_open(DependRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(seqId));
+
+ scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid((tup = systable_getnext(scan))))
+ {
+ Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
+
+ if (depform->refclassid == RelationRelationId &&
+ depform->deptype == DEPENDENCY_AUTO)
+ {
+ simple_heap_delete(depRel, &tup->t_self);
+ }
+ }
+
+ systable_endscan(scan);
+
+ heap_close(depRel, RowExclusiveLock);
+}
+
/*
* isObjectPinned()
*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.13 2006/08/20 21:56:16 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.14 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/genam.h"
#include "access/heapam.h"
#include "access/xact.h"
+#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_authid.h"
* Get the database Id that should be used in pg_shdepend, given the OID
* of the catalog containing the object. For shared objects, it's 0
* (InvalidOid); for all other objects, it's the current database Id.
- *
- * XXX it's awfully tempting to hard-wire this instead of doing a syscache
- * lookup ... but resist the temptation, unless you can prove it's a
- * bottleneck.
*/
static Oid
classIdGetDbId(Oid classId)
{
Oid dbId;
- HeapTuple tup;
-
- tup = SearchSysCache(RELOID,
- ObjectIdGetDatum(classId),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for relation %u", classId);
- if (((Form_pg_class) GETSTRUCT(tup))->relisshared)
+ if (IsSharedRelation(classId))
dbId = InvalidOid;
else
dbId = MyDatabaseId;
- ReleaseSysCache(tup);
-
return dbId;
}
* Drop the objects owned by any one of the given RoleIds. If a role has
* access to an object, the grant will be removed as well (but the object
* will not, of course.)
+ *
+ * We can revoke grants immediately while doing the scan, but drops are
+ * saved up and done all at once with performMultipleDeletions. This
+ * is necessary so that we don't get failures from trying to delete
+ * interdependent objects in the wrong order.
*/
void
shdepDropOwned(List *roleids, DropBehavior behavior)
InternalGrant istmt;
Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
- /* We only operate on objects on the current database */
+ /* We only operate on objects in the current database */
if (sdepForm->dbid != MyDatabaseId)
continue;
switch (sdepForm->classid)
{
case RelationRelationId:
- {
- /* is it a sequence or non-sequence? */
- Form_pg_class pg_class_tuple;
- HeapTuple tuple;
-
- tuple = SearchSysCache(RELOID,
- ObjectIdGetDatum(sdepForm->objid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for relation %u",
- sdepForm->objid);
- pg_class_tuple = (Form_pg_class) GETSTRUCT(tuple);
- if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
- istmt.objtype = ACL_OBJECT_SEQUENCE;
- else
- istmt.objtype = ACL_OBJECT_RELATION;
- ReleaseSysCache(tuple);
- }
+ /* it's OK to use RELATION for a sequence */
+ istmt.objtype = ACL_OBJECT_RELATION;
break;
case DatabaseRelationId:
istmt.objtype = ACL_OBJECT_DATABASE;
ExecGrantStmt_oids(&istmt);
break;
case SHARED_DEPENDENCY_OWNER:
- /* Save it for later deleting it */
+ /* Save it for deletion below */
obj.classId = sdepForm->classid;
obj.objectId = sdepForm->objid;
obj.objectSubId = 0;
-
add_exact_object_address(&obj, deleteobjs);
break;
}
{
Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
- /* We only operate on objects on the current database */
+ /* We only operate on objects in the current database */
if (sdepForm->dbid != MyDatabaseId)
continue;
if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
continue;
- /*
- * If there's a regular (non-shared) dependency on this object
- * marked with DEPENDENCY_INTERNAL, skip this object. We will
- * alter the referencer object instead.
- */
- if (objectIsInternalDependency(sdepForm->classid, sdepForm->objid))
- continue;
-
- /* Issue the appropiate ALTER OWNER call */
+ /* Issue the appropriate ALTER OWNER call */
switch (sdepForm->classid)
{
case ConversionRelationId:
break;
case RelationRelationId:
- ATExecChangeOwner(sdepForm->objid, newrole, false);
+ /*
+ * Pass recursing = true so that we don't fail on
+ * indexes, owned sequences, etc when we happen
+ * to visit them before their parent table.
+ */
+ ATExecChangeOwner(sdepForm->objid, newrole, true);
break;
case ProcedureRelationId:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.138 2006/07/31 20:09:00 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.139 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include "access/transam.h"
#include "access/xact.h"
+#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "nodes/makefuncs.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
static Relation open_share_lock(SeqTable seq);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
-static void init_params(List *options, Form_pg_sequence new, bool isInit);
+static void init_params(List *options, bool isInit,
+ Form_pg_sequence new, List **owned_by);
static void do_setval(Oid relid, int64 next, bool iscalled);
+static void process_owned_by(Relation seqrel, List *owned_by);
+
/*
* DefineSequence
DefineSequence(CreateSeqStmt *seq)
{
FormData_pg_sequence new;
+ List *owned_by;
CreateStmt *stmt = makeNode(CreateStmt);
Oid seqoid;
Relation rel;
NameData name;
/* Check and set all option values */
- init_params(seq->options, &new, true);
+ init_params(seq->options, true, &new, &owned_by);
/*
* Create relation (and fill *null & *value)
coldef->raw_default = NULL;
coldef->cooked_default = NULL;
coldef->constraints = NIL;
- coldef->support = NULL;
null[i - 1] = ' ';
UnlockReleaseBuffer(buf);
+ /* process OWNED BY if given */
+ if (owned_by)
+ process_owned_by(rel, owned_by);
+
heap_close(rel, NoLock);
}
Page page;
Form_pg_sequence seq;
FormData_pg_sequence new;
+ List *owned_by;
/* open and AccessShareLock sequence */
relid = RangeVarGetRelid(stmt->sequence, false);
memcpy(&new, seq, sizeof(FormData_pg_sequence));
/* Check and set new values */
- init_params(stmt->options, &new, false);
+ init_params(stmt->options, false, &new, &owned_by);
/* Now okay to update the on-disk tuple */
memcpy(seq, &new, sizeof(FormData_pg_sequence));
UnlockReleaseBuffer(buf);
+ /* process OWNED BY if given */
+ if (owned_by)
+ process_owned_by(seqrel, owned_by);
+
relation_close(seqrel, NoLock);
}
/*
* init_params: process the options list of CREATE or ALTER SEQUENCE,
- * and store the values into appropriate fields of *new.
+ * and store the values into appropriate fields of *new. Also set
+ * *owned_by to any OWNED BY option, or to NIL if there is none.
*
* If isInit is true, fill any unspecified options with default values;
* otherwise, do not change existing options that aren't explicitly overridden.
*/
static void
-init_params(List *options, Form_pg_sequence new, bool isInit)
+init_params(List *options, bool isInit,
+ Form_pg_sequence new, List **owned_by)
{
DefElem *last_value = NULL;
DefElem *increment_by = NULL;
DefElem *is_cycled = NULL;
ListCell *option;
+ *owned_by = NIL;
+
foreach(option, options)
{
DefElem *defel = (DefElem *) lfirst(option);
errmsg("conflicting or redundant options")));
is_cycled = defel;
}
+ else if (strcmp(defel->defname, "owned_by") == 0)
+ {
+ if (*owned_by)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ *owned_by = defGetQualifiedName(defel);
+ }
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
new->cache_value = 1;
}
+/*
+ * Process an OWNED BY option for CREATE/ALTER SEQUENCE
+ *
+ * Ownership permissions on the sequence are already checked,
+ * but if we are establishing a new owned-by dependency, we must
+ * enforce that the referenced table has the same owner and namespace
+ * as the sequence.
+ */
+static void
+process_owned_by(Relation seqrel, List *owned_by)
+{
+ int nnames;
+ Relation tablerel;
+ AttrNumber attnum;
+
+ nnames = list_length(owned_by);
+ Assert(nnames > 0);
+ if (nnames == 1)
+ {
+ /* Must be OWNED BY NONE */
+ if (strcmp(strVal(linitial(owned_by)), "none") != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid OWNED BY option"),
+ errhint("Specify OWNED BY table.column or OWNED BY NONE.")));
+ tablerel = NULL;
+ attnum = 0;
+ }
+ else
+ {
+ List *relname;
+ char *attrname;
+ RangeVar *rel;
+
+ /* Separate relname and attr name */
+ relname = list_truncate(list_copy(owned_by), nnames - 1);
+ attrname = strVal(lfirst(list_tail(owned_by)));
+
+ /* Open and lock rel to ensure it won't go away meanwhile */
+ rel = makeRangeVarFromNameList(relname);
+ tablerel = relation_openrv(rel, AccessShareLock);
+
+ /* Must be a regular table */
+ if (tablerel->rd_rel->relkind != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("referenced relation \"%s\" is not a table",
+ RelationGetRelationName(tablerel))));
+
+ /* We insist on same owner and schema */
+ if (seqrel->rd_rel->relowner != tablerel->rd_rel->relowner)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("sequence must have same owner as table it is owned by")));
+ if (RelationGetNamespace(seqrel) != RelationGetNamespace(tablerel))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("sequence must be in same schema as table it is owned by")));
+
+ /* Now, fetch the attribute number from the system cache */
+ attnum = get_attnum(RelationGetRelid(tablerel), attrname);
+ if (attnum == InvalidAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ attrname, RelationGetRelationName(tablerel))));
+ }
+
+ /*
+ * OK, we are ready to update pg_depend. First remove any existing
+ * AUTO dependencies for the sequence, then optionally add a new one.
+ */
+ markSequenceUnowned(RelationGetRelid(seqrel));
+
+ if (tablerel)
+ {
+ ObjectAddress refobject,
+ depobject;
+
+ refobject.classId = RelationRelationId;
+ refobject.objectId = RelationGetRelid(tablerel);
+ refobject.objectSubId = attnum;
+ depobject.classId = RelationRelationId;
+ depobject.objectId = RelationGetRelid(seqrel);
+ depobject.objectSubId = 0;
+ recordDependencyOn(&depobject, &refobject, DEPENDENCY_AUTO);
+ }
+
+ /* Done, but hold lock until commit */
+ if (tablerel)
+ relation_close(tablerel, NoLock);
+}
+
void
seq_redo(XLogRecPtr lsn, XLogRecord *record)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.199 2006/08/03 20:57:06 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.200 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
ColumnDef *colDef);
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
-static void add_column_support_dependency(Oid relid, int32 attnum,
- RangeVar *support);
static void ATExecDropNotNull(Relation rel, const char *colName);
static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
const char *colName);
* work unless we have a pre-existing relation. So, the transformation has
* to be postponed to this final step of CREATE TABLE.
*
- * Another task that's conveniently done at this step is to add dependency
- * links between columns and supporting relations (such as SERIAL
- * sequences).
- *
* First, scan schema to find new column defaults.
*/
rawDefaults = NIL;
rawEnt->raw_default = colDef->raw_default;
rawDefaults = lappend(rawDefaults, rawEnt);
}
-
- /* Create dependency for supporting relation for this column */
- if (colDef->support != NULL)
- add_column_support_dependency(relationId, attnum, colDef->support);
}
/*
def->raw_default = NULL;
def->cooked_default = NULL;
def->constraints = NIL;
- def->support = NULL;
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
return attmap;
}
-/* Generate a map for change_varattnos_of_a_node from a tupledesc and a list of
- * ColumnDefs */
-
+/*
+ * Generate a map for change_varattnos_of_a_node from a tupledesc and a list of
+ * ColumnDefs
+ */
AttrNumber *
varattnos_map_schema(TupleDesc old, List *schema)
{
/* Child should see column as singly inherited */
colDefChild->inhcount = 1;
colDefChild->is_local = false;
- /* and don't make a support dependency on the child */
- colDefChild->support = NULL;
ATOneLevelRecursion(wqueue, rel, childCmd);
}
* Add needed dependency entries for the new column.
*/
add_column_datatype_dependency(myrelid, i, attribute->atttypid);
- if (colDef->support != NULL)
- add_column_support_dependency(myrelid, i, colDef->support);
}
/*
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
-/*
- * Install a dependency for a column's supporting relation (serial sequence).
- */
-static void
-add_column_support_dependency(Oid relid, int32 attnum, RangeVar *support)
-{
- ObjectAddress colobject,
- suppobject;
-
- colobject.classId = RelationRelationId;
- colobject.objectId = relid;
- colobject.objectSubId = attnum;
- suppobject.classId = RelationRelationId;
- suppobject.objectId = RangeVarGetRelid(support, false);
- suppobject.objectSubId = 0;
- recordDependencyOn(&suppobject, &colobject, DEPENDENCY_INTERNAL);
-}
-
/*
* ALTER TABLE ALTER COLUMN DROP NOT NULL
*/
/*
* ALTER TABLE OWNER
*
- * recursing is true if we are recursing from a table to its indexes or
- * toast table. We don't allow the ownership of those things to be
- * changed separately from the parent table. Also, we can skip permission
+ * recursing is true if we are recursing from a table to its indexes,
+ * sequences, or toast table. We don't allow the ownership of those things to
+ * be changed separately from the parent table. Also, we can skip permission
* checks (this is necessary not just an optimization, else we'd fail to
* handle toast tables properly).
*/
{
case RELKIND_RELATION:
case RELKIND_VIEW:
- case RELKIND_SEQUENCE:
/* ok to change owner */
break;
case RELKIND_INDEX:
newOwnerId = tuple_class->relowner;
}
break;
+ case RELKIND_SEQUENCE:
+ if (!recursing &&
+ tuple_class->relowner != newOwnerId)
+ {
+ /* if it's an owned sequence, disallow changing it by itself */
+ Oid tableId;
+ int32 colId;
+
+ if (sequenceIsOwned(relationOid, &tableId, &colId))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot change owner of sequence \"%s\"",
+ NameStr(tuple_class->relname)),
+ errdetail("Sequence \"%s\" is linked to table \"%s\".",
+ NameStr(tuple_class->relname),
+ get_rel_name(tableId))));
+ }
+ break;
case RELKIND_TOASTVALUE:
if (recursing)
break;
HeapTuple tup;
/*
- * SERIAL sequences are those having an internal dependency on one of the
+ * SERIAL sequences are those having an auto dependency on one of the
* table's columns (we don't care *which* column, exactly).
*/
depRel = heap_open(DependRelationId, AccessShareLock);
Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
Relation seqRel;
- /* skip dependencies other than internal dependencies on columns */
+ /* skip dependencies other than auto dependencies on columns */
if (depForm->refobjsubid == 0 ||
depForm->classid != RelationRelationId ||
depForm->objsubid != 0 ||
- depForm->deptype != DEPENDENCY_INTERNAL)
+ depForm->deptype != DEPENDENCY_AUTO)
continue;
/* Use relation_open just in case it's an index */
}
/* We don't need to close the sequence while we alter it. */
- ATExecChangeOwner(depForm->objid, newOwnerId, false);
+ ATExecChangeOwner(depForm->objid, newOwnerId, true);
/* Now we can close it. Keep the lock till end of transaction. */
relation_close(seqRel, NoLock);
rel = heap_openrv(relation, AccessExclusiveLock);
+ relid = RelationGetRelid(rel);
+ oldNspOid = RelationGetNamespace(rel);
+
/* heap_openrv allows TOAST, but we don't want to */
if (rel->rd_rel->relkind == RELKIND_TOASTVALUE)
ereport(ERROR,
errmsg("\"%s\" is a TOAST relation",
RelationGetRelationName(rel))));
- relid = RelationGetRelid(rel);
- oldNspOid = RelationGetNamespace(rel);
+ /* if it's an owned sequence, disallow moving it by itself */
+ if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+ {
+ Oid tableId;
+ int32 colId;
+
+ if (sequenceIsOwned(relid, &tableId, &colId))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot move an owned sequence into another schema"),
+ errdetail("Sequence \"%s\" is linked to table \"%s\".",
+ RelationGetRelationName(rel),
+ get_rel_name(tableId))));
+ }
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
HeapTuple tup;
/*
- * SERIAL sequences are those having an internal dependency on one of the
+ * SERIAL sequences are those having an auto dependency on one of the
* table's columns (we don't care *which* column, exactly).
*/
depRel = heap_open(DependRelationId, AccessShareLock);
Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
Relation seqRel;
- /* skip dependencies other than internal dependencies on columns */
+ /* skip dependencies other than auto dependencies on columns */
if (depForm->refobjsubid == 0 ||
depForm->classid != RelationRelationId ||
depForm->objsubid != 0 ||
- depForm->deptype != DEPENDENCY_INTERNAL)
+ depForm->deptype != DEPENDENCY_AUTO)
continue;
/* Use relation_open just in case it's an index */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.96 2006/07/13 16:49:14 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.97 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
def->raw_default = NULL;
def->cooked_default = NULL;
def->constraints = NIL;
- def->support = NULL;
attrList = lappend(attrList, def);
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.347 2006/08/12 02:52:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.348 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COPY_NODE_FIELD(raw_default);
COPY_STRING_FIELD(cooked_default);
COPY_NODE_FIELD(constraints);
- COPY_NODE_FIELD(support);
return newnode;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.281 2006/08/12 02:52:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.282 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COMPARE_NODE_FIELD(raw_default);
COMPARE_STRING_FIELD(cooked_default);
COMPARE_NODE_FIELD(constraints);
- COMPARE_NODE_FIELD(support);
return true;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.50 2006/03/14 22:48:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.51 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
return funcexpr;
}
+
+/*
+ * makeDefElem -
+ * build a DefElem node
+ */
+DefElem *
+makeDefElem(char *name, Node *arg)
+{
+ DefElem *res = makeNode(DefElem);
+
+ res->defname = name;
+ res->arg = arg;
+ return res;
+}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.282 2006/08/12 02:52:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.283 2006/08/21 00:57:24 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_NODE_FIELD(raw_default);
WRITE_STRING_FIELD(cooked_default);
WRITE_NODE_FIELD(constraints);
- WRITE_NODE_FIELD(support);
}
static void
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.346 2006/08/12 20:05:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.347 2006/08/21 00:57:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
}
+ /*
+ * transformIndexConstraints wants cxt.alist to contain only index
+ * statements, so transfer anything we already have into extras_after
+ * immediately.
+ */
+ *extras_after = list_concat(cxt.alist, *extras_after);
+ cxt.alist = NIL;
+
Assert(stmt->constraints == NIL);
/*
A_Const *snamenode;
FuncCall *funccallnode;
CreateSeqStmt *seqstmt;
+ AlterSeqStmt *altseqstmt;
+ List *attnamelist;
/*
* Determine namespace and name to use for the sequence.
cxt->blist = lappend(cxt->blist, seqstmt);
/*
- * Mark the ColumnDef so that during execution, an appropriate
- * dependency will be added from the sequence to the column.
+ * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
+ * as owned by this column, and add it to the list of things to be
+ * done after this CREATE/ALTER TABLE.
*/
- column->support = makeRangeVar(snamespace, sname);
+ altseqstmt = makeNode(AlterSeqStmt);
+ altseqstmt->sequence = makeRangeVar(snamespace, sname);
+ attnamelist = list_make3(makeString(snamespace),
+ makeString(cxt->relation->relname),
+ makeString(column->colname));
+ altseqstmt->options = list_make1(makeDefElem("owned_by",
+ (Node *) attnamelist));
+
+ cxt->alist = lappend(cxt->alist, altseqstmt);
/*
* Create appropriate constraints for SERIAL. We do this in full,
def->raw_default = NULL;
def->cooked_default = NULL;
def->constraints = NIL;
- def->support = NULL;
/*
* Add to column list
* XXX in ALTER TABLE case, it'd be nice to look for duplicate
* pre-existing indexes, too.
*/
- cxt->alist = NIL;
+ Assert(cxt->alist == NIL);
if (cxt->pkey != NULL)
{
/* Make sure we keep the PKEY index in preference to others... */
}
}
+ /*
+ * transformIndexConstraints wants cxt.alist to contain only index
+ * statements, so transfer anything we already have into extras_after
+ * immediately.
+ */
+ *extras_after = list_concat(cxt.alist, *extras_after);
+ cxt.alist = NIL;
+
/* Postprocess index and FK constraints */
transformIndexConstraints(pstate, &cxt);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.556 2006/08/12 18:58:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.557 2006/08/21 00:57:25 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
static Node *makeFloatConst(char *str);
static Node *makeAConst(Value *v);
static Node *makeRowNullTest(NullTestType test, RowExpr *row);
-static DefElem *makeDefElem(char *name, Node *arg);
static A_Const *makeBoolAConst(bool state);
static FuncCall *makeOverlaps(List *largs, List *rargs, int location);
static void check_qualified_name(List *names);
n->raw_default = NULL;
n->cooked_default = NULL;
n->constraints = NIL;
- n->support = NULL;
$$ = (Node *)n;
}
;
{
$$ = makeDefElem("minvalue", NULL);
}
+ | OWNED BY any_name
+ {
+ $$ = makeDefElem("owned_by", (Node *)$3);
+ }
| START opt_with NumericOnly
{
$$ = makeDefElem("start", (Node *)$3);
return n;
}
-/* makeDefElem()
- * Create a DefElem node and set contents.
- * Could be moved to nodes/makefuncs.c if this is useful elsewhere.
- */
-static DefElem *
-makeDefElem(char *name, Node *arg)
-{
- DefElem *f = makeNode(DefElem);
- f->defname = name;
- f->arg = arg;
- return f;
-}
-
/* makeBoolAConst()
* Create an A_Const node and initialize to a boolean constant.
*/
* ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.231 2006/08/12 02:52:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.232 2006/08/21 00:57:25 tgl Exp $
**********************************************************************/
#include "postgres.h"
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
/*
- * We assume any internal dependency of a relation on a column must be
+ * We assume any auto dependency of a relation on a column must be
* what we are looking for.
*/
if (deprec->classid == RelationRelationId &&
deprec->objsubid == 0 &&
- deprec->deptype == DEPENDENCY_INTERNAL)
+ deprec->deptype == DEPENDENCY_AUTO)
{
sequenceId = deprec->objid;
break;
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.446 2006/08/04 18:32:15 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.447 2006/08/21 00:57:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Note: in this phase we should collect only a minimal amount of
* information about each table, basically just enough to decide if it is
* interesting. We must fetch all tables in this phase because otherwise
- * we cannot correctly identify inherited columns, serial columns, etc.
+ * we cannot correctly identify inherited columns, owned sequences, etc.
*/
if (g_fout->remoteVersion >= 80200)
{
/*
* Left join to pick up dependency info linking sequences to their
- * serial column, if any
+ * owning column, if any (note this dependency is AUTO as of 8.2)
*/
appendPQExpBuffer(query,
"SELECT c.tableoid, c.oid, relname, "
"(c.relkind = '%c' and "
"d.classid = c.tableoid and d.objid = c.oid and "
"d.objsubid = 0 and "
- "d.refclassid = c.tableoid and d.deptype = 'i') "
+ "d.refclassid = c.tableoid and d.deptype = 'a') "
"where relkind in ('%c', '%c', '%c', '%c') "
"order by c.oid",
username_subquery,
{
/*
* Left join to pick up dependency info linking sequences to their
- * serial column, if any
+ * owning column, if any
*/
appendPQExpBuffer(query,
"SELECT c.tableoid, c.oid, relname, "
{
/*
* Left join to pick up dependency info linking sequences to their
- * serial column, if any
+ * owning column, if any
*/
appendPQExpBuffer(query,
"SELECT c.tableoid, c.oid, relname, "
/* other fields were zeroed above */
/*
- * Decide whether we want to dump this table. Sequences owned by
- * serial columns are never dumpable on their own; we will transpose
- * their owning table's dump flag to them below.
+ * Decide whether we want to dump this table.
*/
if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
tblinfo[i].dobj.dump = false;
- else if (OidIsValid(tblinfo[i].owning_tab))
- tblinfo[i].dobj.dump = false;
else
selectDumpableTable(&tblinfo[i]);
tblinfo[i].interesting = tblinfo[i].dobj.dump;
}
PQclear(res);
+
+ /*
+ * Force sequences that are "owned" by table columns to be dumped
+ * whenever their owning table is being dumped.
+ */
+ for (i = 0; i < ntups; i++)
+ {
+ TableInfo *seqinfo = &tblinfo[i];
+ int j;
+
+ if (!OidIsValid(seqinfo->owning_tab))
+ continue; /* not an owned sequence */
+ if (seqinfo->dobj.dump)
+ continue; /* no need to search */
+
+ /* can't use findTableByOid yet, unfortunately */
+ for (j = 0; j < ntups; j++)
+ {
+ if (tblinfo[j].dobj.catId.oid == seqinfo->owning_tab)
+ {
+ if (tblinfo[j].dobj.dump)
+ {
+ seqinfo->interesting = true;
+ seqinfo->dobj.dump = true;
+ }
+ break;
+ }
+ }
+ }
+
destroyPQExpBuffer(query);
destroyPQExpBuffer(delqry);
destroyPQExpBuffer(lockquery);
getTableAttrs(TableInfo *tblinfo, int numTables)
{
int i,
- j,
- k;
+ j;
PQExpBuffer q = createPQExpBuffer();
int i_attnum;
int i_attname;
tbinfo->typstorage = (char *) malloc(ntups * sizeof(char));
tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
- tbinfo->attisserial = (bool *) malloc(ntups * sizeof(bool));
tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
- tbinfo->attisserial[j] = false; /* fix below */
tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
tbinfo->attrdefs[j] = NULL; /* fix below */
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
}
PQclear(res);
}
-
- /*
- * Check to see if any columns are serial columns. Our first quick
- * filter is that it must be integer or bigint with a default. If so,
- * we scan to see if we found a sequence linked to this column. If we
- * did, mark the column and sequence appropriately.
- */
- for (j = 0; j < ntups; j++)
- {
- /*
- * Note assumption that format_type will show these types as
- * exactly "integer" and "bigint" regardless of schema path. This
- * is correct in 7.3 but needs to be watched.
- */
- if (strcmp(tbinfo->atttypnames[j], "integer") != 0 &&
- strcmp(tbinfo->atttypnames[j], "bigint") != 0)
- continue;
- if (tbinfo->attrdefs[j] == NULL)
- continue;
- for (k = 0; k < numTables; k++)
- {
- TableInfo *seqinfo = &tblinfo[k];
-
- if (OidIsValid(seqinfo->owning_tab) &&
- seqinfo->owning_tab == tbinfo->dobj.catId.oid &&
- seqinfo->owning_col == j + 1)
- {
- /*
- * Found a match. Copy the table's interesting and
- * dumpable flags to the sequence.
- */
- tbinfo->attisserial[j] = true;
- seqinfo->interesting = tbinfo->interesting;
- seqinfo->dobj.dump = tbinfo->dobj.dump;
- break;
- }
- }
- }
}
destroyPQExpBuffer(q);
/* Attribute type */
if (g_fout->remoteVersion >= 70100)
{
- char *typname = tbinfo->atttypnames[j];
-
- if (tbinfo->attisserial[j])
- {
- if (strcmp(typname, "integer") == 0)
- typname = "serial";
- else if (strcmp(typname, "bigint") == 0)
- typname = "bigserial";
- }
- appendPQExpBuffer(q, "%s", typname);
+ appendPQExpBuffer(q, "%s",
+ tbinfo->atttypnames[j]);
}
else
{
}
/*
- * Default value --- suppress if inherited, serial, or to be
+ * Default value --- suppress if inherited or to be
* printed separately.
*/
if (tbinfo->attrdefs[j] != NULL &&
!tbinfo->inhAttrDef[j] &&
- !tbinfo->attisserial[j] &&
!tbinfo->attrdefs[j]->separate)
appendPQExpBuffer(q, " DEFAULT %s",
tbinfo->attrdefs[j]->adef_expr);
/*
* Not Null constraint --- suppress if inherited
- *
- * Note: we could suppress this for serial columns since
- * SERIAL implies NOT NULL. We choose not to for forward
- * compatibility, since there has been some talk of making
- * SERIAL not imply NOT NULL, in which case the explicit
- * specification would be needed.
*/
if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
appendPQExpBuffer(q, " NOT NULL");
if (!tbinfo->dobj.dump || !adinfo->separate || dataOnly)
return;
- /* Don't print inherited or serial defaults, either */
- if (tbinfo->inhAttrDef[adnum - 1] || tbinfo->attisserial[adnum - 1])
+ /* Don't print inherited defaults, either */
+ if (tbinfo->inhAttrDef[adnum - 1])
return;
q = createPQExpBuffer();
/*
* The logic we use for restoring sequences is as follows:
*
- * Add a basic CREATE SEQUENCE statement (use last_val for start if called
- * is false, else use min_val for start_val). Skip this if the sequence
- * came from a SERIAL column.
+ * Add a CREATE SEQUENCE statement as part of a "schema" dump
+ * (use last_val for start if called is false, else use min_val for
+ * start_val). Also, if the sequence is owned by a column, add an
+ * ALTER SEQUENCE SET OWNED command for it.
*
- * Add a 'SETVAL(seq, last_val, iscalled)' at restore-time iff we load
- * data. We do this for serial sequences too.
+ * Add a 'SETVAL(seq, last_val, iscalled)' as part of a "data" dump.
*/
-
- if (!dataOnly && !OidIsValid(tbinfo->owning_tab))
+ if (!dataOnly)
{
resetPQExpBuffer(delqry);
false, "SEQUENCE", query->data, delqry->data, NULL,
tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
NULL, NULL);
+
+ /*
+ * If the sequence is owned by a table column, emit the ALTER for it
+ * as a separate TOC entry immediately following the sequence's own
+ * entry. It's OK to do this rather than using full sorting logic,
+ * because the dependency that tells us it's owned will have forced
+ * the table to be created first. We can't just include the ALTER
+ * in the TOC entry because it will fail if we haven't reassigned
+ * the sequence owner to match the table's owner.
+ *
+ * We need not schema-qualify the table reference because both
+ * sequence and table must be in the same schema.
+ */
+ if (OidIsValid(tbinfo->owning_tab))
+ {
+ TableInfo *owning_tab = findTableByOid(tbinfo->owning_tab);
+
+ if (owning_tab)
+ {
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "ALTER SEQUENCE %s",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(query, " OWNED BY %s",
+ fmtId(owning_tab->dobj.name));
+ appendPQExpBuffer(query, ".%s;\n",
+ fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
+
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ tbinfo->dobj.name,
+ tbinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname,
+ false, "SEQUENCE OWNED BY", query->data, "", NULL,
+ &(tbinfo->dobj.dumpId), 1,
+ NULL, NULL);
+ }
+ }
+
+ /* Dump Sequence Comments */
+ resetPQExpBuffer(query);
+ appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
+ dumpComment(fout, query->data,
+ tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+ tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
}
if (!schemaOnly)
{
- TableInfo *owning_tab;
-
resetPQExpBuffer(query);
appendPQExpBuffer(query, "SELECT pg_catalog.setval(");
-
- /*
- * If this is a SERIAL sequence, then use the pg_get_serial_sequence
- * function to avoid hard-coding the sequence name. Note that this
- * implicitly assumes that the sequence and its owning table are in
- * the same schema, because we don't schema-qualify the reference.
- */
- if (OidIsValid(tbinfo->owning_tab) &&
- (owning_tab = findTableByOid(tbinfo->owning_tab)) != NULL)
- {
- appendPQExpBuffer(query, "pg_catalog.pg_get_serial_sequence(");
- appendStringLiteralAH(query, fmtId(owning_tab->dobj.name), fout);
- appendPQExpBuffer(query, ", ");
- appendStringLiteralAH(query, owning_tab->attnames[tbinfo->owning_col - 1], fout);
- appendPQExpBuffer(query, ")");
- }
- else
- appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
+ appendStringLiteralAH(query, fmtId(tbinfo->dobj.name), fout);
appendPQExpBuffer(query, ", %s, %s);\n",
last, (called ? "true" : "false"));
NULL, NULL);
}
- if (!dataOnly)
- {
- /* Dump Sequence Comments */
- resetPQExpBuffer(query);
- appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
- dumpComment(fout, query->data,
- tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
- tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
- }
-
PQclear(res);
destroyPQExpBuffer(query);
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.128 2006/08/01 18:05:04 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.129 2006/08/21 00:57:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool hasoids; /* does it have OIDs? */
int ncheck; /* # of CHECK expressions */
int ntrig; /* # of triggers */
- /* these two are set only if table is a SERIAL column's sequence: */
+ /* these two are set only if table is a sequence owned by a column: */
Oid owning_tab; /* OID of table owning sequence */
int owning_col; /* attr # of column owning sequence */
char *typstorage; /* type storage scheme */
bool *attisdropped; /* true if attr is dropped; don't dump it */
bool *attislocal; /* true if attr has local definition */
- bool *attisserial; /* true if attr is serial or bigserial */
/*
* Note: we need to store per-attribute notnull, default, and constraint
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.351 2006/08/19 01:36:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.352 2006/08/21 00:57:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200608181
+#define CATALOG_VERSION_NO 200608191
#endif
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.26 2006/08/20 21:56:16 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.27 2006/08/21 00:57:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int32 objectSubId; /* Subitem within the object (column of table) */
} ObjectAddress;
-/* expansible list of ObjectAddresses */
+/* expansible list of ObjectAddresses (private in dependency.c) */
typedef struct ObjectAddresses ObjectAddresses;
/*
Oid refClassId, Oid oldRefObjectId,
Oid newRefObjectId);
-extern bool objectIsInternalDependency(Oid classId, Oid objectId);
+extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
+
+extern void markSequenceUnowned(Oid seqId);
/* in pg_shdepend.c */
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.55 2006/03/14 22:48:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.56 2006/08/21 00:57:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype,
List *args, CoercionForm fformat);
+extern DefElem *makeDefElem(char *name, Node *arg);
+
#endif /* MAKEFUNC_H */
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.323 2006/08/12 20:05:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.324 2006/08/21 00:57:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* parsetree produced by gram.y, but transformCreateStmt will remove
* the item and set raw_default instead. CONSTR_DEFAULT items
* should not appear in any subsequent processing.
- *
- * The "support" field, if not null, denotes a supporting relation that
- * should be linked by an internal dependency to the column. Currently
- * this is only used to link a SERIAL column's sequence to the column.
*/
typedef struct ColumnDef
{
Node *raw_default; /* default value (untransformed parse tree) */
char *cooked_default; /* nodeToString representation */
List *constraints; /* other constraints on column */
- RangeVar *support; /* supporting relation, if any */
} ColumnDef;
/*
NOTICE: drop cascades to view alter2.v1
NOTICE: drop cascades to rule _RETURN on view alter2.v1
NOTICE: drop cascades to sequence alter2.t1_f1_seq
-NOTICE: drop cascades to table alter2.t1 column f1
+NOTICE: drop cascades to default for table alter2.t1 column f1
NOTICE: drop cascades to table alter2.t1
NOTICE: drop cascades to constraint t1_f2_check on table alter2.t1
ERROR: permission denied to reassign objects
-- this one is allowed
DROP OWNED BY regression_user0;
-CREATE TABLE deptest1 ();
+CREATE TABLE deptest1 (f1 int unique);
+NOTICE: CREATE TABLE / UNIQUE will create implicit index "deptest1_f1_key" for table "deptest1"
GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION;
SET SESSION AUTHORIZATION regression_user1;
CREATE TABLE deptest (a serial primary key, b text);
CREATE TABLE deptest (a serial primary key, b text);
NOTICE: CREATE TABLE will create implicit sequence "deptest_a_seq" for serial column "deptest.a"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" for table "deptest"
+CREATE TABLE deptest2 (f1 int);
+-- make a serial column the hard way
+CREATE SEQUENCE ss1;
+ALTER TABLE deptest2 ALTER f1 SET DEFAULT nextval('ss1');
+ALTER SEQUENCE ss1 OWNED BY deptest2.f1;
RESET SESSION AUTHORIZATION;
REASSIGN OWNED BY regression_user1 TO regression_user2;
\dt deptest
NOTICE: drop cascades to view test_schema_1.abc_view
NOTICE: drop cascades to rule _RETURN on view test_schema_1.abc_view
NOTICE: drop cascades to table test_schema_1.abc
+NOTICE: drop cascades to default for table test_schema_1.abc column a
-- verify that the objects were dropped
SELECT COUNT(*) FROM pg_class WHERE relnamespace =
(SELECT oid FROM pg_namespace WHERE nspname = 'test_schema_1');
NOTICE: CREATE TABLE will create implicit sequence "t1_f1_seq" for serial column "t1.f1"
-- Both drops should fail, but with different error messages:
DROP SEQUENCE t1_f1_seq;
-ERROR: cannot drop sequence t1_f1_seq because table t1 column f1 requires it
-HINT: You may drop table t1 column f1 instead.
+NOTICE: default for table t1 column f1 depends on sequence t1_f1_seq
+ERROR: cannot drop sequence t1_f1_seq because other objects depend on it
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
DROP SEQUENCE myseq2;
NOTICE: default for table t1 column f2 depends on sequence myseq2
ERROR: cannot drop sequence myseq2 because other objects depend on it
-- this one is allowed
DROP OWNED BY regression_user0;
-CREATE TABLE deptest1 ();
+CREATE TABLE deptest1 (f1 int unique);
GRANT ALL ON deptest1 TO regression_user1 WITH GRANT OPTION;
SET SESSION AUTHORIZATION regression_user1;
SET SESSION AUTHORIZATION regression_user1;
CREATE TABLE deptest (a serial primary key, b text);
+
+CREATE TABLE deptest2 (f1 int);
+-- make a serial column the hard way
+CREATE SEQUENCE ss1;
+ALTER TABLE deptest2 ALTER f1 SET DEFAULT nextval('ss1');
+ALTER SEQUENCE ss1 OWNED BY deptest2.f1;
RESET SESSION AUTHORIZATION;
REASSIGN OWNED BY regression_user1 TO regression_user2;
\dt deptest
+
-- doesn't work: grant still exists
DROP USER regression_user1;
DROP OWNED BY regression_user1;