Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
a03c0d93
Commit
a03c0d93
authored
Dec 30, 2002
by
Tom Lane
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Code review for CLUSTER ALL patch. Fix bogus locking, incorrect transaction
stop/start nesting, other infelicities.
parent
2e1f2c31
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
286 additions
and
288 deletions
+286
-288
doc/src/sgml/ref/cluster.sgml
doc/src/sgml/ref/cluster.sgml
+13
-11
src/backend/commands/cluster.c
src/backend/commands/cluster.c
+257
-245
src/backend/commands/tablecmds.c
src/backend/commands/tablecmds.c
+6
-11
src/backend/tcop/utility.c
src/backend/tcop/utility.c
+3
-11
src/include/commands/cluster.h
src/include/commands/cluster.h
+6
-9
src/test/regress/expected/cluster.out
src/test/regress/expected/cluster.out
+1
-1
No files found.
doc/src/sgml/ref/cluster.sgml
View file @
a03c0d93
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/cluster.sgml,v 1.2
2 2002/11/18 17:12:06 momjian
Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/cluster.sgml,v 1.2
3 2002/12/30 18:42:12 tgl
Exp $
PostgreSQL documentation
-->
...
...
@@ -108,14 +108,16 @@ CLUSTER
<para>
When a table is clustered, <productname>PostgreSQL</productname>
remembers on which index it was clustered.
In calls to
remembers on which index it was clustered.
The form
<command>CLUSTER <replaceable class="parameter">tablename</replaceable></command>,
the table is clustered
on the same index that it was clustered before.
re-clusters the table
on the same index that it was clustered before.
</para>
<para>
A simple <command>CLUSTER</command> clusters all the tables in the database
that the calling user owns and uses the saved cluster information. This
<command>CLUSTER</command> without any parameter re-clusters all the tables
in the
current database that the calling user owns, or all tables if called
by a superuser. (Never-clustered tables are not touched.) This
form of <command>CLUSTER</command> cannot be called from inside a
transaction or function.
</para>
...
...
@@ -157,15 +159,15 @@ CLUSTER
</para>
<para>
<command>CLUSTER</command> preserves GRANT, inheritance, index, foreign
key, and other ancillary information about the table.
<command>CLUSTER</command> preserves GRANT, inheritance, index, foreign
key, and other ancillary information about the table.
</para>
<para>
Because <command>CLUSTER</command> remembers the clustering information,
one can cluster the tables one wants clustered manually the first time, and
setup a timed event similar to <command>VACUUM</command> so that the tables
are periodically and automatically
clustered.
Because <command>CLUSTER</command> remembers the clustering information,
one can cluster the tables one wants clustered manually the first time, and
setup a timed event similar to <command>VACUUM</command> so that the tables
are periodically re-
clustered.
</para>
<para>
...
...
src/backend/commands/cluster.c
View file @
a03c0d93
...
...
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.10
2 2002/12/06 05:00:10 momjian
Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.10
3 2002/12/30 18:42:13 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -36,6 +36,7 @@
#include "utils/syscache.h"
#include "utils/relcache.h"
/*
* We need one of these structs for each index in the relation to be
* clustered. It's basically the data needed by index_create() so
...
...
@@ -51,7 +52,8 @@ typedef struct
bool
isclustered
;
}
IndexAttrs
;
/* This struct is used to pass around the information on tables to be
/*
* This struct is used to pass around the information on tables to be
* clustered. We need this so we can make a list of them when invoked without
* a specific table/index pair.
*/
...
...
@@ -59,21 +61,174 @@ typedef struct
{
Oid
tableOid
;
Oid
indexOid
;
bool
isPrevious
;
}
relToCluster
;
}
RelToCluster
;
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
recreate_indexattr
(
Oid
OIDOldHeap
,
List
*
indexes
);
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
void
cluster_rel
(
relToCluster
*
rv
);
static
bool
check_cluster_ownership
(
Oid
relOid
);
static
List
*
get_tables_to_cluster
(
AclId
owner
);
static
bool
check_cluster_permitted
(
Oid
relOid
);
static
List
*
get_tables_to_cluster
(
MemoryContext
cluster_context
);
/*---------------------------------------------------------------------------
* This cluster code allows for clustering multiple tables at once. Because
* of this, we cannot just run everything on a single transaction, or we
* would be forced to acquire exclusive locks on all the tables being
* clustered, simultaneously --- very likely leading to deadlock.
*
* To solve this we follow a similar strategy to VACUUM code,
* clustering each relation in a separate transaction. For this to work,
* we need to:
* - provide a separate memory context so that we can pass information in
* a way that survives across transactions
* - start a new transaction every time a new relation is clustered
* - check for validity of the information on to-be-clustered relations,
* as someone might have deleted a relation behind our back, or
* clustered one on a different index
* - end the transaction
*
* The single-relation case does not have any such overhead.
*
* We also allow a relation being specified without index. In that case,
* the indisclustered bit will be looked up, and an ERROR will be thrown
* if there is no index with the bit set.
*---------------------------------------------------------------------------
*/
void
cluster
(
ClusterStmt
*
stmt
)
{
if
(
stmt
->
relation
!=
NULL
)
{
/* This is the single-relation case. */
Oid
tableOid
,
indexOid
=
InvalidOid
;
Relation
rel
;
RelToCluster
rvtc
;
/* Find and lock the table */
tableOid
=
RangeVarGetRelid
(
stmt
->
relation
,
false
);
rel
=
heap_open
(
tableOid
,
AccessExclusiveLock
);
/* Check permissions */
if
(
!
check_cluster_permitted
(
tableOid
))
elog
(
ERROR
,
"CLUSTER: You do not own relation %s"
,
stmt
->
relation
->
relname
);
if
(
stmt
->
indexname
==
NULL
)
{
List
*
index
;
/* We need to find the index that has indisclustered set. */
foreach
(
index
,
RelationGetIndexList
(
rel
))
{
HeapTuple
idxtuple
;
Form_pg_index
indexForm
;
indexOid
=
lfirsti
(
index
);
idxtuple
=
SearchSysCache
(
INDEXRELID
,
ObjectIdGetDatum
(
indexOid
),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
idxtuple
))
elog
(
ERROR
,
"Cache lookup failed for index %u"
,
indexOid
);
indexForm
=
(
Form_pg_index
)
GETSTRUCT
(
idxtuple
);
if
(
indexForm
->
indisclustered
)
{
ReleaseSysCache
(
idxtuple
);
break
;
}
ReleaseSysCache
(
idxtuple
);
indexOid
=
InvalidOid
;
}
if
(
!
OidIsValid
(
indexOid
))
elog
(
ERROR
,
"CLUSTER: No previously clustered index found on table
\"
%s
\"
"
,
stmt
->
relation
->
relname
);
}
else
{
/* The index is expected to be in the same namespace as the relation. */
indexOid
=
get_relname_relid
(
stmt
->
indexname
,
rel
->
rd_rel
->
relnamespace
);
if
(
!
OidIsValid
(
indexOid
))
elog
(
ERROR
,
"CLUSTER: cannot find index
\"
%s
\"
for table
\"
%s
\"
"
,
stmt
->
indexname
,
stmt
->
relation
->
relname
);
}
rvtc
.
tableOid
=
tableOid
;
rvtc
.
indexOid
=
indexOid
;
/* close relation, keep lock till commit */
heap_close
(
rel
,
NoLock
);
/* Do the job */
cluster_rel
(
&
rvtc
,
false
);
}
else
{
/*
* This is the "multi relation" case. We need to cluster all tables
* that have some index with indisclustered set.
*/
MemoryContext
cluster_context
;
List
*
rv
,
*
rvs
;
/*
* We cannot run this form of CLUSTER inside a user transaction block;
* we'd be holding locks way too long.
*/
PreventTransactionChain
((
void
*
)
stmt
,
"CLUSTER"
);
/*
* Create special memory context for cross-transaction storage.
*
* Since it is a child of QueryContext, it will go away even in case
* of error.
*/
cluster_context
=
AllocSetContextCreate
(
QueryContext
,
"Cluster"
,
ALLOCSET_DEFAULT_MINSIZE
,
ALLOCSET_DEFAULT_INITSIZE
,
ALLOCSET_DEFAULT_MAXSIZE
);
/*
* Build the list of relations to cluster. Note that this lives in
* cluster_context.
*/
rvs
=
get_tables_to_cluster
(
cluster_context
);
/* Commit to get out of starting transaction */
CommitTransactionCommand
(
true
);
/* Ok, now that we've got them all, cluster them one by one */
foreach
(
rv
,
rvs
)
{
RelToCluster
*
rvtc
=
(
RelToCluster
*
)
lfirst
(
rv
);
/* Start a new transaction for each relation. */
StartTransactionCommand
(
true
);
SetQuerySnapshot
();
/* might be needed for functional index */
cluster_rel
(
rvtc
,
true
);
CommitTransactionCommand
(
true
);
}
/* Start a new transaction for the cleanup work. */
StartTransactionCommand
(
true
);
static
MemoryContext
cluster_context
=
NULL
;
/* Clean up working storage */
MemoryContextDelete
(
cluster_context
);
}
}
/*
* cluster
* cluster
_rel
*
* This clusters the table by creating a new, clustered table and
* swapping the relfilenodes of the new table and the old table, so
...
...
@@ -85,45 +240,52 @@ static MemoryContext cluster_context = NULL;
* same way we do for the relation. Since we are effectively bulk-loading
* the new table, it's better to create the indexes afterwards than to fill
* them incrementally while we load the table.
*
* Since we may open a new transaction for each relation, we have to
* check that the relation still is what we think it is.
*/
void
cluster_rel
(
relToCluster
*
rvtc
)
static
void
cluster_rel
(
RelToCluster
*
rvtc
,
bool
recheck
)
{
Relation
OldHeap
,
OldIndex
;
List
*
indexes
;
/* Check for user-requested abort. */
CHECK_FOR_INTERRUPTS
();
/* Check if the relation and index still exist before opening them
/*
* Since we may open a new transaction for each relation, we have to
* check that the relation still is what we think it is.
*
* If this is a single-transaction CLUSTER, we can skip these tests.
* We *must* skip the one on indisclustered since it would reject an
* attempt to cluster a not-previously-clustered index.
*/
if
(
!
SearchSysCacheExists
(
RELOID
,
ObjectIdGetDatum
(
rvtc
->
tableOid
),
0
,
0
,
0
)
||
if
(
recheck
)
{
HeapTuple
tuple
;
Form_pg_index
indexForm
;
/*
* Check if the relation and index still exist before opening them
*/
if
(
!
SearchSysCacheExists
(
RELOID
,
ObjectIdGetDatum
(
rvtc
->
tableOid
),
0
,
0
,
0
)
||
!
SearchSysCacheExists
(
RELOID
,
ObjectIdGetDatum
(
rvtc
->
indexOid
),
0
,
0
,
0
))
return
;
/* Check that the user still owns the relation */
if
(
!
check_cluster_ownership
(
rvtc
->
tableOid
))
return
;
return
;
/* Check that the index is still the one with indisclustered set.
* If this is a standalone cluster, skip this test.
*/
if
(
rvtc
->
isPrevious
)
{
HeapTuple
tuple
;
Form_pg_index
indexForm
;
/* Check that the user still owns the relation */
if
(
!
check_cluster_permitted
(
rvtc
->
tableOid
))
return
;
/*
* Check that the index is still the one with indisclustered set.
*/
tuple
=
SearchSysCache
(
INDEXRELID
,
ObjectIdGetDatum
(
rvtc
->
indexOid
),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
tuple
))
return
;
/* could have gone away... */
indexForm
=
(
Form_pg_index
)
GETSTRUCT
(
tuple
);
if
(
!
indexForm
->
indisclustered
)
{
...
...
@@ -135,7 +297,8 @@ cluster_rel(relToCluster *rvtc)
/*
* We grab exclusive access to the target rel and index for the
* duration of the transaction.
* duration of the transaction. (This is redundant for the single-
* transaction case, since cluster() already did it.)
*/
OldHeap
=
heap_open
(
rvtc
->
tableOid
,
AccessExclusiveLock
);
...
...
@@ -162,29 +325,43 @@ cluster_rel(relToCluster *rvtc)
elog
(
ERROR
,
"CLUSTER: cannot cluster system relation
\"
%s
\"
"
,
RelationGetRelationName
(
OldHeap
));
/* Save the information of all indexes on the relation. */
indexes
=
get_indexattr_list
(
OldHeap
,
rvtc
->
indexOid
);
/* Drop relcache refcnts, but do NOT give up the locks */
/* Drop relcache refcnt on OldIndex, but keep lock */
index_close
(
OldIndex
);
heap_close
(
OldHeap
,
NoLock
);
/* rebuild_rel does all the dirty work */
rebuild_rel
(
rvtc
->
tableOid
,
rvtc
->
indexOid
,
indexes
,
true
);
/* rebuild_relation does all the dirty work */
rebuild_relation
(
OldHeap
,
rvtc
->
indexOid
);
/* NB: rebuild_relation does heap_close() on OldHeap */
}
/*
* rebuild_relation: rebuild an existing relation
*
* This is shared code between CLUSTER and TRUNCATE. In the TRUNCATE
* case, the new relation is built and left empty. In the CLUSTER case,
* it is filled with data read from the old relation in the order specified
* by the index.
*
* OldHeap: table to rebuild --- must be opened and exclusive-locked!
* indexOid: index to cluster by, or InvalidOid in TRUNCATE case
*
* NB: this routine closes OldHeap at the right time; caller should not.
*/
void
rebuild_rel
(
Oid
tableOid
,
Oid
indexOid
,
List
*
indexes
,
bool
dataCopy
)
rebuild_rel
ation
(
Relation
OldHeap
,
Oid
indexOid
)
{
Oid
tableOid
=
RelationGetRelid
(
OldHeap
);
List
*
indexes
;
Oid
OIDNewHeap
;
char
NewHeapName
[
NAMEDATALEN
];
ObjectAddress
object
;
/*
* If dataCopy is true, we assume that we will be basing the
* copy off an index for cluster operations.
*/
Assert
(
!
dataCopy
||
OidIsValid
(
indexOid
));
/* Save the information about all indexes on the relation. */
indexes
=
get_indexattr_list
(
OldHeap
,
indexOid
);
/* Close relcache entry, but keep lock until transaction commit */
heap_close
(
OldHeap
,
NoLock
);
/*
* Create the new heap, using a temporary name in the same namespace
* as the existing table. NOTE: there is some risk of collision with
...
...
@@ -204,7 +381,7 @@ rebuild_rel(Oid tableOid, Oid indexOid, List *indexes, bool dataCopy)
/*
* Copy the heap data into the new table in the desired order.
*/
if
(
dataCopy
)
if
(
OidIsValid
(
indexOid
)
)
copy_heap_data
(
OIDNewHeap
,
tableOid
,
indexOid
);
/* To make the new heap's data visible (probably not needed?). */
...
...
@@ -230,9 +407,9 @@ rebuild_rel(Oid tableOid, Oid indexOid, List *indexes, bool dataCopy)
/*
* Recreate each index on the relation. We do not need
* CommandCounterIncrement() because re
create_indexattr
does it.
* CommandCounterIncrement() because re
build_indexes
does it.
*/
re
create_indexattr
(
tableOid
,
indexes
);
re
build_indexes
(
tableOid
,
indexes
);
}
/*
...
...
@@ -335,7 +512,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
* Get the necessary info about the indexes of the relation and
* return a list of IndexAttrs structures.
*/
List
*
static
List
*
get_indexattr_list
(
Relation
OldHeap
,
Oid
OldIndex
)
{
List
*
indexes
=
NIL
;
...
...
@@ -366,7 +543,8 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
palloc
(
sizeof
(
Oid
)
*
attrs
->
indexInfo
->
ii_NumIndexAttrs
);
memcpy
(
attrs
->
classOID
,
indexForm
->
indclass
,
sizeof
(
Oid
)
*
attrs
->
indexInfo
->
ii_NumIndexAttrs
);
attrs
->
isclustered
=
(
OldIndex
==
indexOID
);
/* We adjust the isclustered attribute to correct new state */
attrs
->
isclustered
=
(
indexOID
==
OldIndex
);
/* Name and access method of each index come from pg_class */
classTuple
=
SearchSysCache
(
RELOID
,
...
...
@@ -397,7 +575,7 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
* the new index (carrying the old index filenode along).
*/
static
void
re
create_indexattr
(
Oid
OIDOldHeap
,
List
*
indexes
)
re
build_indexes
(
Oid
OIDOldHeap
,
List
*
indexes
)
{
List
*
elem
;
...
...
@@ -629,235 +807,69 @@ swap_relfilenodes(Oid r1, Oid r2)
heap_close
(
relRelation
,
RowExclusiveLock
);
}
/*---------------------------------------------------------------------------
* This cluster code allows for clustering multiple tables at once. Because
* of this, we cannot just run everything on a single transaction, or we
* would be forced to acquire exclusive locks on all the tables being
* clustered. To solve this we follow a similar strategy to VACUUM code,
* clustering each relation in a separate transaction. For this to work,
* we need to:
* - provide a separate memory context so that we can pass information in
* a way that trascends transactions
* - start a new transaction every time a new relation is clustered
* - check for validity of the information on to-be-clustered relations,
* as someone might have deleted a relation behind our back, or
* clustered one on a different index
* - end the transaction
*
* The single relation code does not have any overhead.
*
* We also allow a relation being specified without index. In that case,
* the indisclustered bit will be looked up, and an ERROR will be thrown
* if there is no index with the bit set.
*---------------------------------------------------------------------------
*/
void
cluster
(
ClusterStmt
*
stmt
)
{
/* This is the single relation case. */
if
(
stmt
->
relation
!=
NULL
)
{
Oid
indexOid
=
InvalidOid
,
tableOid
;
relToCluster
rvtc
;
HeapTuple
tuple
;
Form_pg_class
classForm
;
tableOid
=
RangeVarGetRelid
(
stmt
->
relation
,
false
);
if
(
!
check_cluster_ownership
(
tableOid
))
elog
(
ERROR
,
"CLUSTER: You do not own relation %s"
,
stmt
->
relation
->
relname
);
tuple
=
SearchSysCache
(
RELOID
,
ObjectIdGetDatum
(
tableOid
),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
tuple
))
elog
(
ERROR
,
"Cache lookup failed for relation %u"
,
tableOid
);
classForm
=
(
Form_pg_class
)
GETSTRUCT
(
tuple
);
if
(
stmt
->
indexname
==
NULL
)
{
List
*
index
;
Relation
rel
=
RelationIdGetRelation
(
tableOid
);
HeapTuple
ituple
=
NULL
,
idxtuple
=
NULL
;
/* We need to fetch the index that has indisclustered set. */
foreach
(
index
,
RelationGetIndexList
(
rel
))
{
Form_pg_index
indexForm
;
indexOid
=
lfirsti
(
index
);
ituple
=
SearchSysCache
(
RELOID
,
ObjectIdGetDatum
(
indexOid
),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
ituple
))
elog
(
ERROR
,
"Cache lookup failed for relation %u"
,
indexOid
);
idxtuple
=
SearchSysCache
(
INDEXRELID
,
ObjectIdGetDatum
(
HeapTupleGetOid
(
ituple
)),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
idxtuple
))
elog
(
ERROR
,
"Cache lookup failed for index %u"
,
HeapTupleGetOid
(
ituple
));
indexForm
=
(
Form_pg_index
)
GETSTRUCT
(
idxtuple
);
if
(
indexForm
->
indisclustered
)
break
;
indexOid
=
InvalidOid
;
}
if
(
indexOid
==
InvalidOid
)
elog
(
ERROR
,
"CLUSTER: No previously clustered index found on table %s"
,
stmt
->
relation
->
relname
);
RelationClose
(
rel
);
ReleaseSysCache
(
ituple
);
ReleaseSysCache
(
idxtuple
);
}
else
{
/* The index is expected to be in the same namespace as the relation. */
indexOid
=
get_relname_relid
(
stmt
->
indexname
,
classForm
->
relnamespace
);
}
ReleaseSysCache
(
tuple
);
/* XXX Maybe the namespace should be reported as well */
if
(
!
OidIsValid
(
indexOid
))
elog
(
ERROR
,
"CLUSTER: cannot find index
\"
%s
\"
for table
\"
%s
\"
"
,
stmt
->
indexname
,
stmt
->
relation
->
relname
);
rvtc
.
tableOid
=
tableOid
;
rvtc
.
indexOid
=
indexOid
;
rvtc
.
isPrevious
=
false
;
/* Do the job */
cluster_rel
(
&
rvtc
);
}
else
{
/*
* This is the "no relation" case. We need to cluster all tables
* that have some index with indisclustered set.
*/
relToCluster
*
rvtc
;
List
*
rv
,
*
rvs
;
/*
* We cannot run CLUSTER inside a user transaction block; if we were inside
* a transaction, then our commit- and start-transaction-command calls
* would not have the intended effect!
*/
if
(
IsTransactionBlock
())
elog
(
ERROR
,
"CLUSTER cannot run inside a BEGIN/END block"
);
/* Running CLUSTER from a function would free the function context */
if
(
!
MemoryContextContains
(
QueryContext
,
stmt
))
elog
(
ERROR
,
"CLUSTER cannot be called from a function"
);
/*
* Create special memory context for cross-transaction storage.
*
* Since it is a child of QueryContext, it will go away even in case
* of error.
*/
cluster_context
=
AllocSetContextCreate
(
QueryContext
,
"Cluster"
,
ALLOCSET_DEFAULT_MINSIZE
,
ALLOCSET_DEFAULT_INITSIZE
,
ALLOCSET_DEFAULT_MAXSIZE
);
/*
* Build the list of relations to cluster. Note that this lives in
* cluster_context.
*/
rvs
=
get_tables_to_cluster
(
GetUserId
());
/* Ok, now that we've got them all, cluster them one by one */
foreach
(
rv
,
rvs
)
{
rvtc
=
(
relToCluster
*
)
lfirst
(
rv
);
/* Start a new transaction for this relation. */
StartTransactionCommand
(
true
);
cluster_rel
(
rvtc
);
CommitTransactionCommand
(
true
);
}
}
/* Start a new transaction for the cleanup work. */
StartTransactionCommand
(
true
);
/* Clean up working storage */
if
(
stmt
->
relation
==
NULL
)
{
MemoryContextDelete
(
cluster_context
);
cluster_context
=
NULL
;
}
}
/* Checks if the user owns the relation. Superusers
* are allowed to cluster any table.
/*
* Checks if the user is allowed to cluster (ie, owns) the relation.
* Superusers are allowed to cluster any table.
*/
bool
check_cluster_
ownership
(
Oid
relOid
)
static
bool
check_cluster_
permitted
(
Oid
relOid
)
{
/* Superusers bypass this check */
return
pg_class_ownercheck
(
relOid
,
GetUserId
());
}
/* Get a list of tables that the current user owns and
/*
* Get a list of tables that the current user owns and
* have indisclustered set. Return the list in a List * of rvsToCluster
* with the tableOid and the indexOid on which the table is already
* clustered.
*/
List
*
get_tables_to_cluster
(
AclId
owner
)
static
List
*
get_tables_to_cluster
(
MemoryContext
cluster_context
)
{
Relation
indRelation
;
HeapScanDesc
scan
;
ScanKeyData
entry
;
HeapTuple
indexTuple
;
Form_pg_index
index
;
relToCluster
*
rvtc
;
MemoryContext
old_context
;
RelToCluster
*
rvtc
;
List
*
rvs
=
NIL
;
/*
* Get all indexes that have indisclustered set
. System
*
relations or nailed-in relations cannot ever have
*
indisclustered set, because CLUSTER will refuse to
*
set it when
called with one of them as argument.
* Get all indexes that have indisclustered set
and are owned by
*
appropriate user. System relations or nailed-in relations cannot ever
*
have indisclustered set, because CLUSTER will refuse to set it when
* called with one of them as argument.
*/
indRelation
=
relation_openr
(
IndexRelationName
,
RowExclusiveLock
);
ScanKeyEntryInitialize
(
&
entry
,
0
,
Anum_pg_index_indisclustered
,
F_BOOLEQ
,
true
);
indRelation
=
relation_openr
(
IndexRelationName
,
AccessShareLock
);
ScanKeyEntryInitialize
(
&
entry
,
0
,
Anum_pg_index_indisclustered
,
F_BOOLEQ
,
BoolGetDatum
(
true
));
scan
=
heap_beginscan
(
indRelation
,
SnapshotNow
,
1
,
&
entry
);
while
((
indexTuple
=
heap_getnext
(
scan
,
ForwardScanDirection
))
!=
NULL
)
{
MemoryContext
old_context
=
NULL
;
index
=
(
Form_pg_index
)
GETSTRUCT
(
indexTuple
);
if
(
!
check_cluster_
ownership
(
index
->
indrelid
))
if
(
!
check_cluster_
permitted
(
index
->
indrelid
))
continue
;
/*
* We have to build the
struc
t in a different memory context so
* We have to build the
lis
t in a different memory context so
* it will survive the cross-transaction processing
*/
old_context
=
MemoryContextSwitchTo
(
cluster_context
);
rvtc
=
(
relToCluster
*
)
palloc
(
sizeof
(
relToCluster
));
rvtc
->
indexOid
=
index
->
indexrelid
;
rvtc
=
(
RelToCluster
*
)
palloc
(
sizeof
(
RelToCluster
));
rvtc
->
tableOid
=
index
->
indrelid
;
rvtc
->
i
sPrevious
=
true
;
rvs
=
lcons
(
(
void
*
)
rvtc
,
rvs
);
rvtc
->
i
ndexOid
=
index
->
indexrelid
;
rvs
=
lcons
(
rvtc
,
rvs
);
MemoryContextSwitchTo
(
old_context
);
}
heap_endscan
(
scan
);
/*
* Release the lock on pg_index. We will check the indexes
* later again.
*
*/
relation_close
(
indRelation
,
RowExclusiveLock
);
relation_close
(
indRelation
,
AccessShareLock
);
return
rvs
;
}
src/backend/commands/tablecmds.c
View file @
a03c0d93
...
...
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.6
2 2002/12/16 18:39:22
tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.6
3 2002/12/30 18:42:14
tgl Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -355,7 +355,7 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior)
* Removes all the rows from a relation.
*
* Note: This routine only does safety and permissions checks;
* rebuild_rel in cluster.c does the actual work.
* rebuild_rel
ation
in cluster.c does the actual work.
*/
void
TruncateRelation
(
const
RangeVar
*
relation
)
...
...
@@ -366,7 +366,6 @@ TruncateRelation(const RangeVar *relation)
Relation
fkeyRel
;
SysScanDesc
fkeyScan
;
HeapTuple
tuple
;
List
*
indexes
;
/* Grab exclusive lock in preparation for truncate */
rel
=
heap_openrv
(
relation
,
AccessExclusiveLock
);
...
...
@@ -433,17 +432,13 @@ TruncateRelation(const RangeVar *relation)
systable_endscan
(
fkeyScan
);
heap_close
(
fkeyRel
,
AccessShareLock
);
/* Save the information of all indexes on the relation. */
indexes
=
get_indexattr_list
(
rel
,
InvalidOid
);
/* Keep the lock until transaction commit */
heap_close
(
rel
,
NoLock
);
/*
* Do the real work using the same technique as cluster, but
* without the
code copy
portion
* without the
data-copying
portion
*/
rebuild_rel
(
relid
,
InvalidOid
,
indexes
,
false
);
rebuild_relation
(
rel
,
InvalidOid
);
/* NB: rebuild_relation does heap_close() */
}
/*----------
...
...
src/backend/tcop/utility.c
View file @
a03c0d93
...
...
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.18
6 2002/12/30 15:31:48 momjian
Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.18
7 2002/12/30 18:42:16 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -187,7 +187,6 @@ ProcessUtility(Node *parsetree,
CommandDest
dest
,
char
*
completionTag
)
{
if
(
completionTag
)
completionTag
[
0
]
=
'\0'
;
...
...
@@ -195,7 +194,6 @@ ProcessUtility(Node *parsetree,
{
/*
* ******************************** transactions ********************************
*
*/
case
T_TransactionStmt
:
{
...
...
@@ -742,11 +740,7 @@ ProcessUtility(Node *parsetree,
break
;
case
T_ClusterStmt
:
{
ClusterStmt
*
stmt
=
(
ClusterStmt
*
)
parsetree
;
cluster
(
stmt
);
}
cluster
((
ClusterStmt
*
)
parsetree
);
break
;
case
T_VacuumStmt
:
...
...
@@ -874,7 +868,6 @@ ProcessUtility(Node *parsetree,
switch
(
stmt
->
reindexType
)
{
char
*
relname
;
case
INDEX
:
CheckOwnership
(
stmt
->
relation
,
false
);
ReindexIndex
(
stmt
->
relation
,
stmt
->
force
);
...
...
@@ -884,8 +877,7 @@ ProcessUtility(Node *parsetree,
ReindexTable
(
stmt
->
relation
,
stmt
->
force
);
break
;
case
DATABASE
:
relname
=
(
char
*
)
stmt
->
name
;
ReindexDatabase
(
relname
,
stmt
->
force
,
false
);
ReindexDatabase
(
stmt
->
name
,
stmt
->
force
,
false
);
break
;
}
break
;
...
...
src/include/commands/cluster.h
View file @
a03c0d93
...
...
@@ -6,22 +6,19 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* $Id: cluster.h,v 1.1
7 2002/11/23 04:05:52 momjian
Exp $
* $Id: cluster.h,v 1.1
8 2002/12/30 18:42:16 tgl
Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef CLUSTER_H
#define CLUSTER_H
#include <nodes/parsenodes.h>
/*
* functions
*/
extern
void
cluster
(
ClusterStmt
*
stmt
);
#include "nodes/parsenodes.h"
#include "utils/rel.h"
extern
List
*
get_indexattr_list
(
Relation
OldHeap
,
Oid
OldIndex
);
extern
void
rebuild_rel
(
Oid
tableOid
,
Oid
indexOid
,
List
*
indexes
,
bool
dataCopy
);
extern
void
cluster
(
ClusterStmt
*
stmt
);
extern
void
rebuild_relation
(
Relation
OldHeap
,
Oid
indexOid
);
#endif
/* CLUSTER_H */
src/test/regress/expected/cluster.out
View file @
a03c0d93
...
...
@@ -304,7 +304,7 @@ INSERT INTO clstr_3 VALUES (2);
INSERT INTO clstr_3 VALUES (1);
-- "CLUSTER <tablename>" on a table that hasn't been clustered
CLUSTER clstr_2;
ERROR: CLUSTER: No previously clustered index found on table
clstr_2
ERROR: CLUSTER: No previously clustered index found on table
"clstr_2"
CLUSTER clstr_1_pkey ON clstr_1;
CLUSTER clstr_2_pkey ON clstr_2;
SELECT * FROM clstr_1 UNION ALL
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment