cluster.c 10.2 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * cluster.c
4
 *	  Paul Brown's implementation of cluster index.
5
 *
6 7 8 9 10 11
 *	  I am going to use the rename function as a model for this in the
 *	  parser and executor, and the vacuum code as an example in this
 *	  file. As I go - in contrast to the rest of postgres - there will
 *	  be BUCKETS of comments. This is to allow reviewers to understand
 *	  my (probably bogus) assumptions about the way this works.
 *														[pbrown '94]
12
 *
B
Add:  
Bruce Momjian 已提交
13 14
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994-5, Regents of the University of California
15 16 17
 *
 *
 * IDENTIFICATION
18
 *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.57 2000/07/04 06:11:27 tgl Exp $
19 20 21
 *
 *-------------------------------------------------------------------------
 */
22

23
#include "postgres.h"
24

25
#include "access/genam.h"
B
Bruce Momjian 已提交
26 27
#include "access/heapam.h"
#include "catalog/heap.h"
28
#include "catalog/index.h"
B
Bruce Momjian 已提交
29
#include "catalog/pg_index.h"
B
Bruce Momjian 已提交
30
#include "catalog/pg_proc.h"
31 32
#include "commands/cluster.h"
#include "commands/rename.h"
33
#include "miscadmin.h"
B
Bruce Momjian 已提交
34 35
#include "utils/builtins.h"
#include "utils/syscache.h"
36

37
static Relation copy_heap(Oid OIDOldHeap);
38 39
static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap);
static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
40

41 42 43
/*
 * cluster
 *
44 45 46
 *	 Check that the relation is a relation in the appropriate user
 *	 ACL. I will use the same security that limits users on the
 *	 renamerel() function.
47
 *
48 49
 *	 Check that the index specified is appropriate for the task
 *	 ( ie it's an index over this relation ). This is trickier.
50
 *
51 52 53 54 55 56 57
 *	 Create a list of all the other indicies on this relation. Because
 *	 the cluster will wreck all the tids, I'll need to destroy bogus
 *	 indicies. The user will have to re-create them. Not nice, but
 *	 I'm not a nice guy. The alternative is to try some kind of post
 *	 destroy re-build. This may be possible. I'll check out what the
 *	 index create functiond want in the way of paramaters. On the other
 *	 hand, re-creating n indicies may blow out the space.
58
 *
59 60 61 62 63 64
 *	 Create new (temporary) relations for the base heap and the new
 *	 index.
 *
 *	 Exclusively lock the relations.
 *
 *	 Create new clustered index and base heap relation.
65 66 67
 *
 */
void
68
cluster(char *oldrelname, char *oldindexname)
69
{
70 71 72
	Oid			OIDOldHeap,
				OIDOldIndex,
				OIDNewHeap;
73

74 75 76
	Relation	OldHeap,
				OldIndex;
	Relation	NewHeap;
77

78 79 80 81
	char		NewIndexName[NAMEDATALEN];
	char		NewHeapName[NAMEDATALEN];
	char		saveoldrelname[NAMEDATALEN];
	char		saveoldindexname[NAMEDATALEN];
82 83

	/*
84 85
	 * Copy the arguments into local storage, because they are probably
	 * in palloc'd storage that will go away when we commit a transaction.
86 87 88 89 90 91
	 */
	strcpy(saveoldrelname, oldrelname);
	strcpy(saveoldindexname, oldindexname);

	/*
	 * Like vacuum, cluster spans transactions, so I'm going to handle it
92 93
	 * in the same way: commit and restart transactions where needed.
	 *
94 95
	 * We grab exclusive access to the target rel and index for the duration
	 * of the initial transaction.
96 97
	 */

98
	OldHeap = heap_openr(saveoldrelname, AccessExclusiveLock);
99
	OIDOldHeap = RelationGetRelid(OldHeap);
100

101
	OldIndex = index_openr(saveoldindexname); /* Open old index relation	*/
102 103
	LockRelation(OldIndex, AccessExclusiveLock);
	OIDOldIndex = RelationGetRelid(OldIndex);
104

105 106 107 108
	/*
	 * XXX Should check that index is in fact an index on this relation?
	 */

109
	heap_close(OldHeap, NoLock);/* do NOT give up the locks */
110 111 112 113 114 115 116 117
	index_close(OldIndex);

	/*
	 * I need to build the copies of the heap and the index. The Commit()
	 * between here is *very* bogus. If someone is appending stuff, they
	 * will get the lock after being blocked and add rows which won't be
	 * present in the new table. Bleagh! I'd be best to try and ensure
	 * that no-one's in the tables for the entire duration of this process
118
	 * with a pg_vlock.  XXX Isn't the above comment now invalid?
119 120
	 */
	NewHeap = copy_heap(OIDOldHeap);
121
	OIDNewHeap = RelationGetRelid(NewHeap);
122
	strcpy(NewHeapName, RelationGetRelationName(NewHeap));
123 124 125 126 127 128 129 130 131 132 133

	/* To make the new heap visible (which is until now empty). */
	CommandCounterIncrement();

	rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);

	/* To flush the filled new heap (and the statistics about it). */
	CommandCounterIncrement();

	/* Create new index over the tuples of the new heap. */
	copy_index(OIDOldIndex, OIDNewHeap);
M
 
Marc G. Fournier 已提交
134
	snprintf(NewIndexName, NAMEDATALEN, "temp_%x", OIDOldIndex);
135 136 137 138 139 140 141 142 143

	/*
	 * make this really happen. Flush all the buffers. (Believe me, it is
	 * necessary ... ended up in a mess without it.)
	 */
	CommitTransactionCommand();
	StartTransactionCommand();

	/* Destroy old heap (along with its index) and rename new. */
144
	heap_drop_with_catalog(saveoldrelname, allowSystemTableMods);
145

B
Bruce Momjian 已提交
146 147 148
	CommitTransactionCommand();
	StartTransactionCommand();

149 150
	renamerel(NewHeapName, saveoldrelname);
	renamerel(NewIndexName, saveoldindexname);
151 152
}

153
static Relation
154 155
copy_heap(Oid OIDOldHeap)
{
156 157 158 159 160 161
	char		NewName[NAMEDATALEN];
	TupleDesc	OldHeapDesc,
				tupdesc;
	Oid			OIDNewHeap;
	Relation	NewHeap,
				OldHeap;
162 163 164 165 166

	/*
	 * Create a new heap relation with a temporary name, which has the
	 * same tuple description as the old one.
	 */
M
 
Marc G. Fournier 已提交
167
	snprintf(NewName, NAMEDATALEN, "temp_%x", OIDOldHeap);
168

169
	OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
170
	OldHeapDesc = RelationGetDescr(OldHeap);
171 172

	/*
173 174
	 * Need to make a copy of the tuple descriptor,
	 * heap_create_with_catalog modifies it.
175 176 177 178
	 */

	tupdesc = CreateTupleDescCopy(OldHeapDesc);

179
	OIDNewHeap = heap_create_with_catalog(NewName, tupdesc,
180 181
										  RELKIND_RELATION, false,
										  allowSystemTableMods);
182 183

	if (!OidIsValid(OIDNewHeap))
184
		elog(ERROR, "clusterheap: cannot create temporary heap relation\n");
185

186 187
	/* XXX why are we bothering to do this: */
	NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
188

189 190
	heap_close(NewHeap, AccessExclusiveLock);
	heap_close(OldHeap, AccessExclusiveLock);
191 192

	return NewHeap;
193 194
}

195
static void
196 197
copy_index(Oid OIDOldIndex, Oid OIDNewHeap)
{
B
Bruce Momjian 已提交
198 199 200 201 202
	Relation	OldIndex,
				NewHeap;
	HeapTuple	Old_pg_index_Tuple,
				Old_pg_index_relation_Tuple,
				pg_proc_Tuple;
203
	Form_pg_index Old_pg_index_Form;
204
	Form_pg_class Old_pg_index_relation_Form;
B
Bruce Momjian 已提交
205 206 207 208
	Form_pg_proc pg_proc_Form;
	char	   *NewIndexName;
	AttrNumber *attnumP;
	int			natts;
209
	FuncIndexInfo *finfo;
210

211
	NewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
212 213 214 215 216 217 218
	OldIndex = index_open(OIDOldIndex);

	/*
	 * OK. Create a new (temporary) index for the one that's already here.
	 * To do this I get the info from pg_index, re-build the FunctInfo if
	 * I have to, and add a new index with a temporary name.
	 */
219
	Old_pg_index_Tuple = SearchSysCacheTuple(INDEXRELID,
220
							ObjectIdGetDatum(RelationGetRelid(OldIndex)),
B
Bruce Momjian 已提交
221
											 0, 0, 0);
222 223

	Assert(Old_pg_index_Tuple);
224
	Old_pg_index_Form = (Form_pg_index) GETSTRUCT(Old_pg_index_Tuple);
225

226
	Old_pg_index_relation_Tuple = SearchSysCacheTuple(RELOID,
227
							ObjectIdGetDatum(RelationGetRelid(OldIndex)),
B
Bruce Momjian 已提交
228
													  0, 0, 0);
229 230

	Assert(Old_pg_index_relation_Tuple);
231
	Old_pg_index_relation_Form = (Form_pg_class) GETSTRUCT(Old_pg_index_relation_Tuple);
232

M
 
Marc G. Fournier 已提交
233
	/* Set the name. */
234
	NewIndexName = palloc(NAMEDATALEN); /* XXX */
M
 
Marc G. Fournier 已提交
235
	snprintf(NewIndexName, NAMEDATALEN, "temp_%x", OIDOldIndex);
236 237 238 239 240 241 242

	/*
	 * Ugly as it is, the only way I have of working out the number of
	 * attribues is to count them. Mostly there'll be just one but I've
	 * got to be sure.
	 */
	for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0;
B
Bruce Momjian 已提交
243
		 natts < INDEX_MAX_KEYS && *attnumP != InvalidAttrNumber;
244 245 246 247 248 249 250 251 252 253 254 255
		 attnumP++, natts++);

	/*
	 * If this is a functional index, I need to rebuild the functional
	 * component to pass it to the defining procedure.
	 */
	if (Old_pg_index_Form->indproc != InvalidOid)
	{
		finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo));
		FIgetnArgs(finfo) = natts;
		FIgetProcOid(finfo) = Old_pg_index_Form->indproc;

256
		pg_proc_Tuple = SearchSysCacheTuple(PROCOID,
257
							ObjectIdGetDatum(Old_pg_index_Form->indproc),
B
Bruce Momjian 已提交
258
											0, 0, 0);
259 260 261 262

		Assert(pg_proc_Tuple);
		pg_proc_Form = (Form_pg_proc) GETSTRUCT(pg_proc_Tuple);
		namecpy(&(finfo->funcName), &(pg_proc_Form->proname));
263
		natts = 1;				/* function result is a single column */
264 265 266 267 268 269
	}
	else
	{
		finfo = (FuncIndexInfo *) NULL;
	}

270
	index_create(RelationGetRelationName(NewHeap),
271 272 273 274 275 276 277
				 NewIndexName,
				 finfo,
				 NULL,			/* type info is in the old index */
				 Old_pg_index_relation_Form->relam,
				 natts,
				 Old_pg_index_Form->indkey,
				 Old_pg_index_Form->indclass,
278
				 (Node *) NULL,	/* XXX where's the predicate? */
279
				 Old_pg_index_Form->indislossy,
280
				 Old_pg_index_Form->indisunique,
281 282
				 Old_pg_index_Form->indisprimary,
				 allowSystemTableMods);
283

284 285
	setRelhasindexInplace(OIDNewHeap, true, false);

286 287
	index_close(OldIndex);
	heap_close(NewHeap, AccessExclusiveLock);
288 289 290
}


291
static void
292 293
rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
{
B
Bruce Momjian 已提交
294 295 296 297 298
	Relation	LocalNewHeap,
				LocalOldHeap,
				LocalOldIndex;
	IndexScanDesc ScanDesc;
	RetrieveIndexResult ScanResult;
299 300 301 302 303

	/*
	 * Open the relations I need. Scan through the OldHeap on the OldIndex
	 * and insert each tuple into the NewHeap.
	 */
304 305
	LocalNewHeap = heap_open(OIDNewHeap, AccessExclusiveLock);
	LocalOldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
306
	LocalOldIndex = index_open(OIDOldIndex);
307 308 309

	ScanDesc = index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);

B
Bruce Momjian 已提交
310
	while ((ScanResult = index_getnext(ScanDesc, ForwardScanDirection)) != NULL)
311
	{
312 313
		HeapTupleData LocalHeapTuple;
		Buffer		LocalBuffer;
314

315
		LocalHeapTuple.t_self = ScanResult->heap_iptr;
316 317
		LocalHeapTuple.t_datamcxt = NULL;
		LocalHeapTuple.t_data = NULL;
318
		heap_fetch(LocalOldHeap, SnapshotNow, &LocalHeapTuple, &LocalBuffer);
319 320 321 322 323 324 325 326 327 328 329 330 331 332
		if (LocalHeapTuple.t_data != NULL) {
			/*
			 * We must copy the tuple because heap_insert() will overwrite
			 * the commit-status fields of the tuple it's handed, and the
			 * retrieved tuple will actually be in a disk buffer!  Thus,
			 * the source relation would get trashed, which is bad news
			 * if we abort later on.  (This was a bug in releases thru 7.0)
			 */
			HeapTuple	copiedTuple = heap_copytuple(&LocalHeapTuple);

			ReleaseBuffer(LocalBuffer);
			heap_insert(LocalNewHeap, copiedTuple);
			heap_freetuple(copiedTuple);
		}
333
		pfree(ScanResult);
334
	}
335

336 337 338
	index_endscan(ScanDesc);

	index_close(LocalOldIndex);
339 340
	heap_close(LocalOldHeap, AccessExclusiveLock);
	heap_close(LocalNewHeap, AccessExclusiveLock);
341
}