aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-07-11 23:13:58 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-07-11 23:13:58 +0000
commitaf4de81469d08cc76b4da14c0b05894ce97738e2 (patch)
tree8e667420385fefd2748a70388775d9818fcb5ed9
parent08d89db34d83e7e7ef4e2742ceda8b4c6c369869 (diff)
downloadpostgresql-af4de81469d08cc76b4da14c0b05894ce97738e2.tar.gz
postgresql-af4de81469d08cc76b4da14c0b05894ce97738e2.zip
ALTER TABLE SET TABLESPACE. Gavin Sherry, some rework by Tom Lane.
-rw-r--r--doc/src/sgml/ref/alter_table.sgml100
-rw-r--r--src/backend/commands/cluster.c43
-rw-r--r--src/backend/commands/tablecmds.c288
-rw-r--r--src/backend/parser/gram.y10
-rw-r--r--src/include/commands/cluster.h7
-rw-r--r--src/include/nodes/parsenodes.h7
6 files changed, 392 insertions, 63 deletions
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index ef8b57e0ebe..263e5024b62 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.72 2004/06/02 21:04:40 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.73 2004/07/11 23:13:51 tgl Exp $
PostgreSQL documentation
-->
@@ -39,10 +39,11 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
ADD <replaceable class="PARAMETER">table_constraint</replaceable>
DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
- SET WITHOUT OIDS
- OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
SET WITHOUT CLUSTER
+ SET WITHOUT OIDS
+ OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
+ SET TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable>
</synopsis>
</refsynopsisdiv>
@@ -182,6 +183,29 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</varlistentry>
<varlistentry>
+ <term><literal>CLUSTER</literal></term>
+ <listitem>
+ <para>
+ This form selects the default index for future
+ <xref linkend="SQL-CLUSTER" endterm="sql-cluster-title">
+ operations. It does not actually re-cluster the table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET WITHOUT CLUSTER</literal></term>
+ <listitem>
+ <para>
+ This form removes the most recently used
+ <xref linkend="SQL-CLUSTER" endterm="sql-cluster-title">
+ index specification from the table. This affects
+ future cluster operations that don't specify an index.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>SET WITHOUT OIDS</literal></term>
<listitem>
<para>
@@ -211,29 +235,20 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</varlistentry>
<varlistentry>
- <term><literal>CLUSTER</literal></term>
+ <term><literal>SET TABLESPACE</literal></term>
<listitem>
<para>
- This form selects the default index for future
- <xref linkend="SQL-CLUSTER" endterm="sql-cluster-title">
- operations.
+ This form changes the table's tablespace to the specified tablespace and
+ moves the data file(s) associated with the table to the new tablespace.
+ Indexes on the table, if any, are not moved; but they can be moved
+ separately with additional <literal>SET TABLESPACE</literal> commands.
+ See also
+ <xref linkend="SQL-CREATETABLESPACE" endterm="sql-createtablespace-title">.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><literal>SET WITHOUT CLUSTER</literal></term>
- <listitem>
- <para>
- This form removes the most recently used
- <xref linkend="SQL-CLUSTER" endterm="sql-cluster-title">
- index specification from the table. This affects
- future cluster operations that don't specify an index.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
<term><literal>RENAME</literal></term>
<listitem>
<para>
@@ -293,29 +308,29 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</varlistentry>
<varlistentry>
- <term><replaceable class="PARAMETER">type</replaceable></term>
+ <term><replaceable class="PARAMETER">new_column</replaceable></term>
<listitem>
<para>
- Data type of the new column, or new data type for an existing
- column.
+ New name for an existing column.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><replaceable class="PARAMETER">new_column</replaceable></term>
+ <term><replaceable class="PARAMETER">new_name</replaceable></term>
<listitem>
<para>
- New name for an existing column.
+ New name for the table.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><replaceable class="PARAMETER">new_name</replaceable></term>
+ <term><replaceable class="PARAMETER">type</replaceable></term>
<listitem>
<para>
- New name for the table.
+ Data type of the new column, or new data type for an existing
+ column.
</para>
</listitem>
</varlistentry>
@@ -339,10 +354,21 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</varlistentry>
<varlistentry>
- <term><replaceable class="PARAMETER">new_owner</replaceable></term>
+ <term><literal>CASCADE</literal></term>
<listitem>
<para>
- The user name of the new owner of the table.
+ Automatically drop objects that depend on the dropped column
+ or constraint (for example, views referencing the column).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Refuse to drop the column or constraint if there are any dependent
+ objects. This is the default behavior.
</para>
</listitem>
</varlistentry>
@@ -357,21 +383,19 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</varlistentry>
<varlistentry>
- <term><literal>CASCADE</literal></term>
+ <term><replaceable class="PARAMETER">new_owner</replaceable></term>
<listitem>
<para>
- Automatically drop objects that depend on the dropped column
- or constraint (for example, views referencing the column).
+ The user name of the new owner of the table.
</para>
</listitem>
</varlistentry>
<varlistentry>
- <term><literal>RESTRICT</literal></term>
+ <term><replaceable class="PARAMETER">tablespace_name</replaceable></term>
<listitem>
<para>
- Refuse to drop the column or constraint if there are any dependent
- objects. This is the default behavior.
+ The tablespace name to which the table will be moved.
</para>
</listitem>
</varlistentry>
@@ -551,6 +575,14 @@ ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zip
ALTER TABLE distributors ADD PRIMARY KEY (dist_id);
</programlisting>
</para>
+
+ <para>
+ To move a table to a different tablespace:
+<programlisting>
+ALTER TABLE distributors SET TABLESPACE fasttablespace;
+</programlisting>
+ </para>
+
</refsect1>
<refsect1>
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index d81771c7a04..391504fb37d 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.126 2004/06/18 06:13:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.127 2004/07/11 23:13:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -485,6 +485,7 @@ static void
rebuild_relation(Relation OldHeap, Oid indexOid)
{
Oid tableOid = RelationGetRelid(OldHeap);
+ Oid tableSpace = OldHeap->rd_rel->reltablespace;
Oid OIDNewHeap;
char NewHeapName[NAMEDATALEN];
ObjectAddress object;
@@ -505,7 +506,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
*/
snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", tableOid);
- OIDNewHeap = make_new_heap(tableOid, NewHeapName);
+ OIDNewHeap = make_new_heap(tableOid, NewHeapName, tableSpace);
/*
* We don't need CommandCounterIncrement() because make_new_heap did
@@ -520,8 +521,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
/* To make the new heap's data visible (probably not needed?). */
CommandCounterIncrement();
- /* Swap the relfilenodes of the old and new heaps. */
- swap_relfilenodes(tableOid, OIDNewHeap);
+ /* Swap the physical files of the old and new heaps. */
+ swap_relation_files(tableOid, OIDNewHeap);
CommandCounterIncrement();
@@ -550,7 +551,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
* Create the new table that we will fill with correctly-ordered data.
*/
Oid
-make_new_heap(Oid OIDOldHeap, const char *NewName)
+make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
{
TupleDesc OldHeapDesc,
tupdesc;
@@ -568,7 +569,7 @@ make_new_heap(Oid OIDOldHeap, const char *NewName)
OIDNewHeap = heap_create_with_catalog(NewName,
RelationGetNamespace(OldHeap),
- OldHeap->rd_rel->reltablespace,
+ NewTableSpace,
tupdesc,
OldHeap->rd_rel->relkind,
OldHeap->rd_rel->relisshared,
@@ -646,13 +647,16 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
}
/*
- * Swap the relfilenodes for two given relations.
+ * Swap the physical files of two given relations.
+ *
+ * We swap the physical identity (reltablespace and relfilenode) while
+ * keeping the same logical identities of the two relations.
*
* Also swap any TOAST links, so that the toast data moves along with
* the main-table data.
*/
void
-swap_relfilenodes(Oid r1, Oid r2)
+swap_relation_files(Oid r1, Oid r2)
{
Relation relRelation,
rel;
@@ -695,12 +699,16 @@ swap_relfilenodes(Oid r1, Oid r2)
relation_close(rel, NoLock);
/*
- * Actually swap the filenode and TOAST fields in the two tuples
+ * Actually swap the fields in the two tuples
*/
swaptemp = relform1->relfilenode;
relform1->relfilenode = relform2->relfilenode;
relform2->relfilenode = swaptemp;
+ swaptemp = relform1->reltablespace;
+ relform1->reltablespace = relform2->reltablespace;
+ relform2->reltablespace = swaptemp;
+
swaptemp = relform1->reltoastrelid;
relform1->reltoastrelid = relform2->reltoastrelid;
relform2->reltoastrelid = swaptemp;
@@ -793,13 +801,16 @@ swap_relfilenodes(Oid r1, Oid r2)
/*
* Blow away the old relcache entries now. We need this kluge because
- * relcache.c indexes relcache entries by rd_node as well as OID. It
- * will get confused if it is asked to (re)build an entry with a new
- * rd_node value when there is still another entry laying about with
- * that same rd_node value. (Fortunately, since one of the entries is
- * local in our transaction, it's sufficient to clear out our own
- * relcache this way; the problem cannot arise for other backends when
- * they see our update on the non-local relation.)
+ * relcache.c keeps a link to the smgr relation for the physical file,
+ * and that will be out of date as soon as we do CommandCounterIncrement.
+ * Whichever of the rels is the second to be cleared during cache
+ * invalidation will have a dangling reference to an already-deleted smgr
+ * relation. Rather than trying to avoid this by ordering operations
+ * just so, it's easiest to not have the relcache entries there at all.
+ * (Fortunately, since one of the entries is local in our transaction,
+ * it's sufficient to clear out our own relcache this way; the problem
+ * cannot arise for other backends when they see our update on the
+ * non-local relation.)
*/
RelationForgetRelation(r1);
RelationForgetRelation(r2);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 392822abf50..a5a7deadcfd 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.118 2004/07/01 00:50:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.119 2004/07/11 23:13:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,6 +52,7 @@
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "rewrite/rewriteHandler.h"
+#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -120,6 +121,7 @@ typedef struct AlteredTableInfo
/* Information saved by Phases 1/2 for Phase 3: */
List *constraints; /* List of NewConstraint */
List *newvals; /* List of NewColumnValue */
+ Oid newTableSpace; /* new tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
List *changedConstraintDefs; /* string definitions of same */
@@ -237,6 +239,10 @@ static void ATPostAlterTypeParse(char *cmd, List **wqueue);
static void ATExecChangeOwner(Oid relationOid, int32 newOwnerSysId);
static void ATExecClusterOn(Relation rel, const char *indexName);
static void ATExecDropCluster(Relation rel);
+static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
+ char *tablespacename);
+static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace);
+static void copy_relation_data(Relation rel, SMgrRelation dst);
static int ri_trigger_type(Oid tgfoid);
static void update_ri_trigger_args(Oid relid,
const char *oldname,
@@ -1946,6 +1952,11 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
}
pass = AT_PASS_DROP;
break;
+ case AT_SetTableSpace: /* SET TABLESPACE */
+ /* This command never recurses */
+ ATPrepSetTableSpace(tab, rel, cmd->name);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -2097,6 +2108,11 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd)
* to do the real work
*/
break;
+ case AT_SetTableSpace: /* SET TABLESPACE */
+ /*
+ * Nothing to do here; Phase 3 does the work
+ */
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -2132,6 +2148,7 @@ ATRewriteTables(List **wqueue)
/* Build a temporary relation and copy data */
Oid OIDNewHeap;
char NewHeapName[NAMEDATALEN];
+ Oid NewTableSpace;
Relation OldHeap;
ObjectAddress object;
@@ -2157,6 +2174,15 @@ ATRewriteTables(List **wqueue)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot rewrite temporary tables of other sessions")));
+ /*
+ * Select destination tablespace (same as original unless user
+ * requested a change)
+ */
+ if (tab->newTableSpace)
+ NewTableSpace = tab->newTableSpace;
+ else
+ NewTableSpace = OldHeap->rd_rel->reltablespace;
+
heap_close(OldHeap, NoLock);
/*
@@ -2170,7 +2196,7 @@ ATRewriteTables(List **wqueue)
snprintf(NewHeapName, sizeof(NewHeapName),
"pg_temp_%u", tab->relid);
- OIDNewHeap = make_new_heap(tab->relid, NewHeapName);
+ OIDNewHeap = make_new_heap(tab->relid, NewHeapName, NewTableSpace);
/*
* Copy the heap data into the new table with the desired
@@ -2179,8 +2205,8 @@ ATRewriteTables(List **wqueue)
*/
ATRewriteTable(tab, OIDNewHeap);
- /* Swap the relfilenodes of the old and new heaps. */
- swap_relfilenodes(tab->relid, OIDNewHeap);
+ /* Swap the physical files of the old and new heaps. */
+ swap_relation_files(tab->relid, OIDNewHeap);
CommandCounterIncrement();
@@ -2203,13 +2229,20 @@ ATRewriteTables(List **wqueue)
*/
reindex_relation(tab->relid, false);
}
- else if (tab->constraints != NIL)
+ else
{
/*
* Test the current data within the table against new constraints
* generated by ALTER TABLE commands, but don't rebuild data.
*/
- ATRewriteTable(tab, InvalidOid);
+ if (tab->constraints != NIL)
+ ATRewriteTable(tab, InvalidOid);
+ /*
+ * If we had SET TABLESPACE but no reason to reconstruct tuples,
+ * just do a block-by-block copy.
+ */
+ if (tab->newTableSpace)
+ ATExecSetTableSpace(tab->relid, tab->newTableSpace);
}
}
@@ -5185,6 +5218,249 @@ ATExecDropCluster(Relation rel)
mark_index_clustered(rel, InvalidOid);
}
+/*
+ * ALTER TABLE SET TABLESPACE
+ */
+static void
+ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename)
+{
+ Oid tablespaceId;
+ AclResult aclresult;
+
+ /*
+ * We do our own permission checking because we want to allow this on
+ * indexes.
+ */
+ if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ rel->rd_rel->relkind != RELKIND_INDEX)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table or index",
+ RelationGetRelationName(rel))));
+
+ /* Permissions checks */
+ if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(rel));
+
+ if (!allowSystemTableMods && IsSystemRelation(rel))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: \"%s\" is a system catalog",
+ RelationGetRelationName(rel))));
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename);
+ if (!OidIsValid(tablespaceId))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist", tablespacename)));
+
+ /* Check its permissions */
+ aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
+
+ /* Save info for Phase 3 to do the real work */
+ if (OidIsValid(tab->newTableSpace))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("multiple SET TABLESPACE subcommands are not valid")));
+ tab->newTableSpace = tablespaceId;
+}
+
+/*
+ * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
+ * rewriting to be done, so we just want to copy the data as fast as possible.
+ */
+static void
+ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)
+{
+ Relation rel;
+ Oid oldTableSpace;
+ Oid reltoastrelid;
+ Oid reltoastidxid;
+ RelFileNode newrnode;
+ SMgrRelation dstrel;
+ Relation pg_class;
+ HeapTuple tuple;
+ Form_pg_class rd_rel;
+
+ rel = relation_open(tableOid, NoLock);
+
+ /*
+ * We can never allow moving of shared or nailed-in-cache relations,
+ * because we can't support changing their reltablespace values.
+ */
+ if (rel->rd_rel->relisshared || rel->rd_isnailed)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot move system relation \"%s\"",
+ RelationGetRelationName(rel))));
+
+ /*
+ * Don't allow moving temp tables of other backends ... their
+ * local buffer manager is not going to cope.
+ */
+ if (isOtherTempNamespace(RelationGetNamespace(rel)))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot move temporary tables of other sessions")));
+
+ /*
+ * No work if no change in tablespace.
+ */
+ oldTableSpace = rel->rd_rel->reltablespace;
+ if (newTableSpace == oldTableSpace ||
+ (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
+ {
+ relation_close(rel, NoLock);
+ return;
+ }
+
+ reltoastrelid = rel->rd_rel->reltoastrelid;
+ reltoastidxid = rel->rd_rel->reltoastidxid;
+
+ /* Get a modifiable copy of the relation's pg_class row */
+ pg_class = heap_openr(RelationRelationName, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy(RELOID,
+ ObjectIdGetDatum(tableOid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", tableOid);
+ rd_rel = (Form_pg_class) GETSTRUCT(tuple);
+
+ /* create another storage file. Is it a little ugly ? */
+ /* NOTE: any conflict in relfilenode value will be caught here */
+ newrnode = rel->rd_node;
+ newrnode.spcNode = newTableSpace;
+
+ dstrel = smgropen(newrnode);
+ smgrcreate(dstrel, rel->rd_istemp, false);
+
+ /* schedule unlinking old physical file */
+ if (rel->rd_smgr == NULL)
+ rel->rd_smgr = smgropen(rel->rd_node);
+ smgrscheduleunlink(rel->rd_smgr, rel->rd_istemp);
+
+ /* copy relation data to the new physical file */
+ copy_relation_data(rel, dstrel);
+
+ /*
+ * Now drop smgr references. We need not smgrclose() the old file,
+ * since it will be dropped anyway at commit by the pending unlink.
+ * We do need to get rid of relcache's reference to it, however.
+ */
+ smgrclose(dstrel);
+ rel->rd_smgr = NULL;
+
+ /* update the pg_class row */
+ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
+ simple_heap_update(pg_class, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(pg_class, tuple);
+
+ heap_freetuple(tuple);
+
+ heap_close(pg_class, RowExclusiveLock);
+
+ relation_close(rel, NoLock);
+
+ /* Make sure the reltablespace change is visible */
+ CommandCounterIncrement();
+
+ /* Move associated toast relation and/or index, too */
+ if (OidIsValid(reltoastrelid))
+ ATExecSetTableSpace(reltoastrelid, newTableSpace);
+ if (OidIsValid(reltoastidxid))
+ ATExecSetTableSpace(reltoastidxid, newTableSpace);
+}
+
+/*
+ * Copy data, block by block
+ */
+static void
+copy_relation_data(Relation rel, SMgrRelation dst)
+{
+ SMgrRelation src = rel->rd_smgr;
+ bool use_wal;
+ BlockNumber nblocks;
+ BlockNumber blkno;
+ char buf[BLCKSZ];
+ Page page = (Page) buf;
+
+ /*
+ * Since we copy the data directly without looking at the shared buffers,
+ * we'd better first flush out any pages of the source relation that are
+ * in shared buffers. We assume no new pages will get loaded into
+ * buffers while we are holding exclusive lock on the rel.
+ */
+ FlushRelationBuffers(rel, 0);
+
+ /*
+ * We need to log the copied data in WAL iff WAL archiving is enabled
+ * AND it's not a temp rel.
+ *
+ * XXX when WAL archiving is actually supported, this test will likely
+ * need to change; and the hardwired extern is cruddy anyway ...
+ */
+ {
+ extern char XLOG_archive_dir[];
+
+ use_wal = XLOG_archive_dir[0] && !rel->rd_istemp;
+ }
+
+ nblocks = RelationGetNumberOfBlocks(rel);
+ for (blkno = 0; blkno < nblocks; blkno++)
+ {
+ smgrread(src, blkno, buf);
+
+ /* XLOG stuff */
+ if (use_wal)
+ {
+ xl_heap_newpage xlrec;
+ XLogRecPtr recptr;
+ XLogRecData rdata[2];
+
+ /* NO ELOG(ERROR) from here till newpage op is logged */
+ START_CRIT_SECTION();
+
+ xlrec.node = dst->smgr_rnode;
+ xlrec.blkno = blkno;
+
+ rdata[0].buffer = InvalidBuffer;
+ rdata[0].data = (char *) &xlrec;
+ rdata[0].len = SizeOfHeapNewpage;
+ rdata[0].next = &(rdata[1]);
+
+ rdata[1].buffer = InvalidBuffer;
+ rdata[1].data = (char *) page;
+ rdata[1].len = BLCKSZ;
+ rdata[1].next = NULL;
+
+ recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_NEWPAGE, rdata);
+
+ PageSetLSN(page, recptr);
+ PageSetSUI(page, ThisStartUpID);
+
+ END_CRIT_SECTION();
+ }
+
+ /*
+ * Now write the page. If not using WAL, say isTemp = true, to
+ * suppress duplicate fsync. If we are using WAL, it surely isn't a
+ * temp rel, so !use_wal is a sufficient condition.
+ */
+ smgrwrite(dst, blkno, buf, !use_wal);
+ }
+
+ /*
+ * If we weren't using WAL, and the rel isn't temp, we must fsync it
+ * down to disk before it's safe to commit the transaction.
+ */
+ if (!use_wal && !rel->rd_istemp)
+ smgrimmedsync(dst);
+}
/*
* ALTER TABLE CREATE TOAST TABLE
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7446bc612d5..e9af75baaa3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.465 2004/06/28 01:19:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.466 2004/07/11 23:13:54 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1286,6 +1286,14 @@ alter_table_cmd:
n->name = NULL;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> SET TABLESPACE <tablespacename> */
+ | SET TABLESPACE name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetTableSpace;
+ n->name = $3;
+ $$ = (Node *)n;
+ }
;
alter_column_default:
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 820c9521080..11e8a391817 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.23 2004/05/08 00:34:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/cluster.h,v 1.24 2004/07/11 23:13:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,7 +21,8 @@ extern void cluster(ClusterStmt *stmt);
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid);
extern void mark_index_clustered(Relation rel, Oid indexOid);
-extern Oid make_new_heap(Oid OIDOldHeap, const char *NewName);
-extern void swap_relfilenodes(Oid r1, Oid r2);
+extern Oid make_new_heap(Oid OIDOldHeap, const char *NewName,
+ Oid NewTableSpace);
+extern void swap_relation_files(Oid r1, Oid r2);
#endif /* CLUSTER_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 22d18ef4f70..3745972bd50 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.260 2004/06/25 21:55:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.261 2004/07/11 23:13:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -806,7 +806,8 @@ typedef enum AlterTableType
AT_ChangeOwner, /* change owner */
AT_ClusterOn, /* CLUSTER ON */
AT_DropCluster, /* SET WITHOUT CLUSTER */
- AT_DropOids /* SET WITHOUT OIDS */
+ AT_DropOids, /* SET WITHOUT OIDS */
+ AT_SetTableSpace /* SET TABLESPACE */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
@@ -814,7 +815,7 @@ 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
- * new owner */
+ * new owner or tablespace */
Node *def; /* definition of new column, column type,
* index, or constraint */
Node *transform; /* transformation expr for ALTER TYPE */