indexcmds.c 22.6 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * indexcmds.c
4
 *	  POSTGRES define, extend and remove index code.
5
 *
B
Add:  
Bruce Momjian 已提交
6 7
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.40 2000/11/08 22:09:57 tgl Exp $
12 13 14
 *
 *-------------------------------------------------------------------------
 */
15

16
#include "postgres.h"
17

18 19
#include "access/genam.h"
#include "access/heapam.h"
20
#include "catalog/catalog.h"
H
Hiroshi Inoue 已提交
21
#include "catalog/catname.h"
22 23
#include "catalog/heap.h"
#include "catalog/index.h"
24
#include "catalog/pg_am.h"
25 26
#include "catalog/pg_amop.h"
#include "catalog/pg_database.h"
B
Bruce Momjian 已提交
27
#include "catalog/pg_index.h"
B
Bruce Momjian 已提交
28
#include "catalog/pg_opclass.h"
29
#include "catalog/pg_operator.h"
30
#include "catalog/pg_proc.h"
H
Hiroshi Inoue 已提交
31
#include "catalog/pg_shadow.h"
32
#include "commands/defrem.h"
33
#include "miscadmin.h"
34
#include "optimizer/clauses.h"
35
#include "optimizer/planmain.h"
B
Bruce Momjian 已提交
36 37
#include "optimizer/prep.h"
#include "parser/parsetree.h"
38
#include "parser/parse_coerce.h"
39
#include "parser/parse_func.h"
40
#include "parser/parse_type.h"
B
Bruce Momjian 已提交
41
#include "utils/builtins.h"
42
#include "utils/fmgroids.h"
B
Bruce Momjian 已提交
43
#include "utils/syscache.h"
44

45
#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args != NIL)
46 47

/* non-export function prototypes */
48
static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
49 50
static void CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid);
static void CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
51 52 53
static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
						  IndexElem *funcIndex,
						  Oid relId,
54
						  char *accessMethodName, Oid accessMethodId);
55 56 57
static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
						   List *attList,
						   Oid relId,
58 59 60
						   char *accessMethodName, Oid accessMethodId);
static Oid	GetAttrOpClass(IndexElem *attribute, Oid attrType,
						   char *accessMethodName, Oid accessMethodId);
61
static char *GetDefaultOpClass(Oid atttypid);
62 63

/*
B
Bruce Momjian 已提交
64
 * DefineIndex
65
 *		Creates a new index.
66 67
 *
 * 'attributeList' is a list of IndexElem specifying either a functional
68
 *		index or a list of attributes to index on.
69
 * 'parameterList' is a list of DefElem specified in the with clause.
70
 * 'predicate' is the qual specified in the where clause.
71
 * 'rangetable' is needed to interpret the predicate
72 73 74
 */
void
DefineIndex(char *heapRelationName,
75 76
			char *indexRelationName,
			char *accessMethodName,
77 78
			List *attributeList,
			List *parameterList,
79
			bool unique,
80
			bool primary,
81 82
			Expr *predicate,
			List *rangetable)
83
{
84 85 86
	Oid		   *classObjectId;
	Oid			accessMethodId;
	Oid			relationId;
87
	IndexInfo  *indexInfo;
88 89
	int			numberOfAttributes;
	HeapTuple	tuple;
90
	List	   *cnfPred = NIL;
91
	bool		lossy = false;
92
	List	   *pl;
93 94

	/*
95
	 * count attributes in index
96 97 98
	 */
	numberOfAttributes = length(attributeList);
	if (numberOfAttributes <= 0)
99
		elog(ERROR, "DefineIndex: must specify at least one attribute");
100 101 102
	if (numberOfAttributes > INDEX_MAX_KEYS)
		elog(ERROR, "Cannot use more than %d attributes in an index",
			 INDEX_MAX_KEYS);
103 104 105 106

	/*
	 * compute heap relation id
	 */
107
	if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid)
108
		elog(ERROR, "DefineIndex: relation \"%s\" not found",
109 110 111 112 113
			 heapRelationName);

	/*
	 * compute access method id
	 */
114 115
	tuple = SearchSysCacheTuple(AMNAME,
								PointerGetDatum(accessMethodName),
116 117
								0, 0, 0);
	if (!HeapTupleIsValid(tuple))
118
		elog(ERROR, "DefineIndex: access method \"%s\" not found",
119
			 accessMethodName);
120
	accessMethodId = tuple->t_data->t_oid;
121

122 123 124 125 126 127 128 129 130 131 132
	/*
	 * XXX Hardwired hacks to check for limitations on supported index types.
	 * We really ought to be learning this info from entries in the pg_am
	 * table, instead of having it wired in here!
	 */
	if (unique && accessMethodId != BTREE_AM_OID)
		elog(ERROR, "DefineIndex: unique indices are only available with the btree access method");

	if (numberOfAttributes > 1 && accessMethodId != BTREE_AM_OID)
		elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method");

133 134 135 136 137
	/*
	 * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
	 */
	foreach(pl, parameterList)
	{
138
		DefElem    *param = (DefElem *) lfirst(pl);
139

140
		if (!strcasecmp(param->defname, "islossy"))
141
			lossy = true;
142
		else
143
			elog(NOTICE, "Unrecognized index attribute \"%s\" ignored",
144
				 param->defname);
145 146 147 148 149 150 151 152 153 154 155 156 157
	}

	/*
	 * Convert the partial-index predicate from parsetree form to plan
	 * form, so it can be readily evaluated during index creation. Note:
	 * "predicate" comes in as a list containing (1) the predicate itself
	 * (a where_clause), and (2) a corresponding range table.
	 *
	 * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
	 */
	if (predicate != NULL && rangetable != NIL)
	{
		cnfPred = cnfify((Expr *) copyObject(predicate), true);
158
		fix_opids((Node *) cnfPred);
159 160 161
		CheckPredicate(cnfPred, rangetable, relationId);
	}

H
Hiroshi Inoue 已提交
162
	if (!IsBootstrapProcessingMode() && !IndexesAreActive(relationId, false))
163 164
		elog(ERROR, "Existing indexes are inactive. REINDEX first");

165 166 167 168 169 170 171 172
	/*
	 * Prepare arguments for index_create, primarily an IndexInfo structure
	 */
	indexInfo = makeNode(IndexInfo);
	indexInfo->ii_Predicate = (Node *) cnfPred;
	indexInfo->ii_FuncOid = InvalidOid;
	indexInfo->ii_Unique = unique;

173 174
	if (IsFuncIndex(attributeList))
	{
175
		IndexElem  *funcIndex = (IndexElem *) lfirst(attributeList);
176
		int			nargs;
177

178 179 180 181
		/* Parser should have given us only one list item, but check */
		if (numberOfAttributes != 1)
			elog(ERROR, "Functional index can only have one attribute");

182 183
		nargs = length(funcIndex->args);
		if (nargs > INDEX_MAX_KEYS)
184 185
			elog(ERROR, "Index function can take at most %d arguments",
				 INDEX_MAX_KEYS);
186

187 188
		indexInfo->ii_NumIndexAttrs = 1;
		indexInfo->ii_NumKeyAttrs = nargs;
189

190
		classObjectId = (Oid *) palloc(sizeof(Oid));
191

192 193
		FuncIndexArgs(indexInfo, classObjectId, funcIndex,
					  relationId, accessMethodName, accessMethodId);
194 195 196
	}
	else
	{
197 198
		indexInfo->ii_NumIndexAttrs = numberOfAttributes;
		indexInfo->ii_NumKeyAttrs = numberOfAttributes;
199

200
		classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
201

202 203
		NormIndexAttrs(indexInfo, classObjectId, attributeList,
					   relationId, accessMethodName, accessMethodId);
204
	}
205

206 207 208 209
	index_create(heapRelationName, indexRelationName,
				 indexInfo, accessMethodId, classObjectId,
				 lossy, primary, allowSystemTableMods);

210 211 212 213 214 215 216
	/*
	 * We update the relation's pg_class tuple even if it already has
	 * relhasindex = true.  This is needed to cause a shared-cache-inval
	 * message to be sent for the pg_class tuple, which will cause other
	 * backends to flush their relcache entries and in particular their
	 * cached lists of the indexes for this relation.
	 */
217
	setRelhasindex(relationId, true);
218 219 220 221
}


/*
B
Bruce Momjian 已提交
222
 * ExtendIndex
223
 *		Extends a partial index.
224 225
 */
void
226
ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
227
{
228 229 230 231
	Relation	heapRelation;
	Relation	indexRelation;
	Oid			accessMethodId,
				indexId,
232 233
				relationId;
	HeapTuple	tuple;
234
	Form_pg_index index;
235 236 237
	List	   *cnfPred = NIL;
	IndexInfo  *indexInfo;
	Node	   *oldPred;
238 239

	/*
240
	 * Get index's relation id and access method id from pg_class
241
	 */
242 243
	tuple = SearchSysCacheTuple(RELNAME,
								PointerGetDatum(indexRelationName),
244 245
								0, 0, 0);
	if (!HeapTupleIsValid(tuple))
246
		elog(ERROR, "ExtendIndex: index \"%s\" not found",
247
			 indexRelationName);
248
	indexId = tuple->t_data->t_oid;
249 250 251
	accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;

	/*
252
	 * Extract info from the pg_index tuple for the index
253 254 255 256
	 */
	tuple = SearchSysCacheTuple(INDEXRELID,
								ObjectIdGetDatum(indexId),
								0, 0, 0);
257
	if (!HeapTupleIsValid(tuple))
258
		elog(ERROR, "ExtendIndex: relation \"%s\" is not an index",
259
			 indexRelationName);
260
	index = (Form_pg_index) GETSTRUCT(tuple);
261 262
	Assert(index->indexrelid == indexId);
	relationId = index->indrelid;
263 264
	indexInfo = BuildIndexInfo(tuple);
	oldPred = indexInfo->ii_Predicate;
265 266

	if (oldPred == NULL)
267
		elog(ERROR, "ExtendIndex: \"%s\" is not a partial index",
268 269 270 271 272
			 indexRelationName);

	/*
	 * Convert the extension predicate from parsetree form to plan form,
	 * so it can be readily evaluated during index creation. Note:
273 274 275 276 277
	 * "predicate" comes in two parts (1) the predicate expression itself,
	 * and (2) a corresponding range table.
	 *
	 * XXX I think this code is broken --- index_build expects a single
	 * expression not a list --- tgl Jul 00
278 279 280 281
	 */
	if (rangetable != NIL)
	{
		cnfPred = cnfify((Expr *) copyObject(predicate), true);
282
		fix_opids((Node *) cnfPred);
283 284 285
		CheckPredicate(cnfPred, rangetable, relationId);
	}

286 287
	/* pass new predicate to index_build */
	indexInfo->ii_Predicate = (Node *) cnfPred;
288

289
	/* Open heap and index rels, and get suitable locks */
290
	heapRelation = heap_open(relationId, ShareLock);
291 292
	indexRelation = index_open(indexId);

293 294 295 296 297
	/* Obtain exclusive lock on it, just to be sure */
	LockRelation(indexRelation, AccessExclusiveLock);

	InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
					  indexRelation, accessMethodId);
298

299
	index_build(heapRelation, indexRelation, indexInfo, oldPred);
300 301

	/* heap and index rels are closed as a side-effect of index_build */
302 303 304 305 306
}


/*
 * CheckPredicate
307 308 309 310 311
 *		Checks that the given list of partial-index predicates refer
 *		(via the given range table) only to the given base relation oid,
 *		and that they're in a form the planner can handle, i.e.,
 *		boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
 *		has to be on the left).
312 313 314
 */

static void
315
CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
316
{
317
	List	   *item;
318 319 320

	foreach(item, predList)
		CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
321 322 323
}

static void
324
CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
325
{
326 327
	List	   *clauses = NIL,
			   *clause;
328 329 330 331 332 333

	if (is_opclause(predicate))
	{
		CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
		return;
	}
334
	else if (or_clause(predicate) || and_clause(predicate))
335 336
		clauses = ((Expr *) predicate)->args;
	else
337
		elog(ERROR, "Unsupported partial-index predicate expression type");
338 339 340

	foreach(clause, clauses)
		CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
341 342 343
}

static void
344
CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
345
{
346 347
	Var		   *pred_var;
	Const	   *pred_const;
348 349 350 351 352 353 354

	pred_var = (Var *) get_leftop(predicate);
	pred_const = (Const *) get_rightop(predicate);

	if (!IsA(predicate->oper, Oper) ||
		!IsA(pred_var, Var) ||
		!IsA(pred_const, Const))
355
		elog(ERROR, "Unsupported partial-index predicate clause type");
356 357

	if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
358
		elog(ERROR,
359
		 "Partial-index predicates may refer only to the base relation");
360 361 362
}


363
static void
364 365 366
FuncIndexArgs(IndexInfo *indexInfo,
			  Oid *classOidP,
			  IndexElem *funcIndex,
367 368 369
			  Oid relId,
			  char *accessMethodName,
			  Oid accessMethodId)
370
{
371 372
	Oid			argTypes[FUNC_MAX_ARGS];
	List	   *arglist;
373 374 375 376 377 378
	int			nargs = 0;
	int			i;
	Oid			funcid;
	Oid			rettype;
	bool		retset;
	Oid		   *true_typeids;
379 380

	/*
381 382
	 * process the function arguments, which are a list of T_String
	 * (someday ought to allow more general expressions?)
383 384
	 *
	 * Note caller already checked that list is not too long.
385
	 */
386
	MemSet(argTypes, 0, sizeof(argTypes));
387

388
	foreach(arglist, funcIndex->args)
389
	{
390
		char	   *arg = strVal(lfirst(arglist));
391
		HeapTuple	tuple;
392
		Form_pg_attribute att;
393 394 395

		tuple = SearchSysCacheTuple(ATTNAME,
									ObjectIdGetDatum(relId),
396 397
									PointerGetDatum(arg),
									0, 0);
398
		if (!HeapTupleIsValid(tuple))
399
			elog(ERROR, "DefineIndex: attribute \"%s\" not found", arg);
400
		att = (Form_pg_attribute) GETSTRUCT(tuple);
401

402 403 404
		indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum;
		argTypes[nargs] = att->atttypid;
		nargs++;
405
	}
406 407 408

	/* ----------------
	 * Lookup the function procedure to get its OID and result type.
409
	 *
410 411 412 413 414 415
	 * We rely on parse_func.c to find the correct function in the
	 * possible presence of binary-compatible types.  However, parse_func
	 * may do too much: it will accept a function that requires run-time
	 * coercion of input types, and the executor is not currently set up
	 * to support that.  So, check to make sure that the selected function
	 * has exact-match or binary-compatible input types.
416 417
	 * ----------------
	 */
418 419 420 421 422 423 424 425
	if (! func_get_detail(funcIndex->name, nargs, argTypes,
						  &funcid, &rettype, &retset, &true_typeids))
		func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);

	if (retset)
		elog(ERROR, "DefineIndex: cannot index on a function returning a set");

	for (i = 0; i < nargs; i++)
426
	{
427 428 429 430
		if (argTypes[i] != true_typeids[i] &&
			! IS_BINARY_COMPATIBLE(argTypes[i], true_typeids[i]))
			func_error("DefineIndex", funcIndex->name, nargs, argTypes,
					   "Index function must be binary-compatible with table datatype");
431 432
	}

433 434
	/* Process opclass, using func return type as default type */

435
	classOidP[0] = GetAttrOpClass(funcIndex, rettype,
436
								  accessMethodName, accessMethodId);
437

438
	/* OK, return results */
439

440 441 442
	indexInfo->ii_FuncOid = funcid;
	/* Need to do the fmgr function lookup now, too */
	fmgr_info(funcid, & indexInfo->ii_FuncInfo);
443 444
}

445
static void
446
NormIndexAttrs(IndexInfo *indexInfo,
447
			   Oid *classOidP,
448
			   List *attList,	/* list of IndexElem's */
449 450 451
			   Oid relId,
			   char *accessMethodName,
			   Oid accessMethodId)
452
{
453
	List	   *rest;
454
	int			attn = 0;
M
Marc G. Fournier 已提交
455

456 457 458
	/*
	 * process attributeList
	 */
459
	foreach(rest, attList)
460
	{
461
		IndexElem  *attribute = (IndexElem *) lfirst(rest);
462
		HeapTuple	atttuple;
463
		Form_pg_attribute attform;
464 465

		if (attribute->name == NULL)
466
			elog(ERROR, "missing attribute for define index");
467

468
		atttuple = SearchSysCacheTupleCopy(ATTNAME,
469
										   ObjectIdGetDatum(relId),
470
										PointerGetDatum(attribute->name),
471
										   0, 0);
472
		if (!HeapTupleIsValid(atttuple))
473
			elog(ERROR, "DefineIndex: attribute \"%s\" not found",
474
				 attribute->name);
475
		attform = (Form_pg_attribute) GETSTRUCT(atttuple);
476

477
		indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
478

479 480
		classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid,
										 accessMethodName, accessMethodId);
481

482
		heap_freetuple(atttuple);
483
		attn++;
484 485 486 487
	}
}

static Oid
488 489
GetAttrOpClass(IndexElem *attribute, Oid attrType,
			   char *accessMethodName, Oid accessMethodId)
490
{
491 492 493
	Relation	relation;
	HeapScanDesc scan;
	ScanKeyData entry[2];
494
	HeapTuple	tuple;
495 496 497
	Oid			opClassId,
				oprId;
	bool		doTypeCheck = true;
498 499 500 501 502 503

	if (attribute->class == NULL)
	{
		/* no operator class specified, so find the default */
		attribute->class = GetDefaultOpClass(attrType);
		if (attribute->class == NULL)
504 505
			elog(ERROR, "DefineIndex: type %s has no default operator class",
				 typeidTypeName(attrType));
506 507
		/* assume we need not check type compatibility */
		doTypeCheck = false;
508 509 510 511 512 513
	}

	tuple = SearchSysCacheTuple(CLANAME,
								PointerGetDatum(attribute->class),
								0, 0, 0);
	if (!HeapTupleIsValid(tuple))
514
		elog(ERROR, "DefineIndex: opclass \"%s\" not found",
515
			 attribute->class);
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
	opClassId = tuple->t_data->t_oid;

	/*
	 * Assume the opclass is supported by this index access method
	 * if we can find at least one relevant entry in pg_amop.
	 */
	ScanKeyEntryInitialize(&entry[0], 0,
						   Anum_pg_amop_amopid,
						   F_OIDEQ,
						   ObjectIdGetDatum(accessMethodId));
	ScanKeyEntryInitialize(&entry[1], 0,
						   Anum_pg_amop_amopclaid,
						   F_OIDEQ,
						   ObjectIdGetDatum(opClassId));

	relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
	scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);

	if (! HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
	{
		elog(ERROR, "DefineIndex: opclass \"%s\" not supported by access method \"%s\"",
			 attribute->class, accessMethodName);
	}

540 541
	oprId = ((Form_pg_amop) GETSTRUCT(tuple))->amopopr;

542 543
	heap_endscan(scan);
	heap_close(relation, AccessShareLock);
544

545 546 547 548 549 550 551 552 553 554 555
	/*
	 * Make sure the operators associated with this opclass actually accept
	 * the column data type.  This prevents possible coredumps caused by
	 * user errors like applying text_ops to an int4 column.  We will accept
	 * an opclass as OK if the operator's input datatype is binary-compatible
	 * with the actual column datatype.  Note we assume that all the operators
	 * associated with an opclass accept the same datatypes, so checking the
	 * first one we happened to find in the table is sufficient.
	 *
	 * If the opclass was the default for the datatype, assume we can skip
	 * this check --- that saves a few cycles in the most common case.
556
	 * If pg_opclass is wrong then we're probably screwed anyway...
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
	 */
	if (doTypeCheck)
	{
		tuple = SearchSysCacheTuple(OPEROID,
									ObjectIdGetDatum(oprId),
									0, 0, 0);
		if (HeapTupleIsValid(tuple))
		{
			Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tuple);
			Oid		opInputType = (optup->oprkind == 'l') ?
				optup->oprright : optup->oprleft;

			if (attrType != opInputType &&
				! IS_BINARY_COMPATIBLE(attrType, opInputType))
				elog(ERROR, "DefineIndex: opclass \"%s\" does not accept datatype \"%s\"",
					 attribute->class, typeidTypeName(attrType));
		}
	}

576
	return opClassId;
577 578
}

579
static char *
M
Marc G. Fournier 已提交
580 581
GetDefaultOpClass(Oid atttypid)
{
582
	HeapTuple	tuple;
M
Marc G. Fournier 已提交
583

584 585 586 587
	tuple = SearchSysCacheTuple(CLADEFTYPE,
								ObjectIdGetDatum(atttypid),
								0, 0, 0);
	if (!HeapTupleIsValid(tuple))
588
		return NULL;
M
Marc G. Fournier 已提交
589

590 591
	return DatumGetCString(DirectFunctionCall1(nameout,
			NameGetDatum(&((Form_pg_opclass) GETSTRUCT(tuple))->opcname)));
M
Marc G. Fournier 已提交
592 593
}

594
/*
B
Bruce Momjian 已提交
595
 * RemoveIndex
596
 *		Deletes an index.
597 598
 *
 * Exceptions:
599 600 601
 *		BadArg if name is invalid.
 *		"WARN" if index nonexistent.
 *		...
602 603 604 605
 */
void
RemoveIndex(char *name)
{
606
	HeapTuple	tuple;
607 608 609 610 611 612

	tuple = SearchSysCacheTuple(RELNAME,
								PointerGetDatum(name),
								0, 0, 0);

	if (!HeapTupleIsValid(tuple))
613
		elog(ERROR, "index \"%s\" does not exist", name);
614 615 616

	if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
	{
617
		elog(ERROR, "relation \"%s\" is of type \"%c\"",
618 619 620 621
			 name,
			 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
	}

622
	index_drop(tuple->t_data->t_oid);
623
}
H
Hiroshi Inoue 已提交
624 625 626 627 628 629 630 631 632 633

/*
 * Reindex
 *		Recreate an index.
 *
 * Exceptions:
 *		"ERROR" if index nonexistent.
 *		...
 */
void
634
ReindexIndex(const char *name, bool force /* currently unused */ )
H
Hiroshi Inoue 已提交
635 636 637
{
	HeapTuple	tuple;

638 639 640 641 642 643 644 645 646
	/* ----------------
	 *	REINDEX within a transaction block is dangerous, because
	 *	if the transaction is later rolled back we have no way to
	 *	undo truncation of the index's physical file.  Disallow it.
	 * ----------------
	 */
	if (IsTransactionBlock())
		elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");

H
Hiroshi Inoue 已提交
647 648 649 650 651
	tuple = SearchSysCacheTuple(RELNAME,
								PointerGetDatum(name),
								0, 0, 0);

	if (!HeapTupleIsValid(tuple))
652
		elog(ERROR, "index \"%s\" does not exist", name);
H
Hiroshi Inoue 已提交
653 654 655 656 657 658 659 660

	if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
	{
		elog(ERROR, "relation \"%s\" is of type \"%c\"",
			 name,
			 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
	}

661
	if (!reindex_index(tuple->t_data->t_oid, force))
662
		elog(NOTICE, "index \"%s\" wasn't reindexed", name);
H
Hiroshi Inoue 已提交
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
}

/*
 * ReindexTable
 *		Recreate indexes of a table.
 *
 * Exceptions:
 *		"ERROR" if table nonexistent.
 *		...
 */
void
ReindexTable(const char *name, bool force)
{
	HeapTuple	tuple;

678 679 680 681 682 683 684 685 686
	/* ----------------
	 *	REINDEX within a transaction block is dangerous, because
	 *	if the transaction is later rolled back we have no way to
	 *	undo truncation of the index's physical file.  Disallow it.
	 * ----------------
	 */
	if (IsTransactionBlock())
		elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");

H
Hiroshi Inoue 已提交
687 688 689 690 691
	tuple = SearchSysCacheTuple(RELNAME,
								PointerGetDatum(name),
								0, 0, 0);

	if (!HeapTupleIsValid(tuple))
692
		elog(ERROR, "table \"%s\" does not exist", name);
H
Hiroshi Inoue 已提交
693 694 695 696 697 698 699 700

	if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION)
	{
		elog(ERROR, "relation \"%s\" is of type \"%c\"",
			 name,
			 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
	}

701
	if (!reindex_relation(tuple->t_data->t_oid, force))
702
		elog(NOTICE, "table \"%s\" wasn't reindexed", name);
H
Hiroshi Inoue 已提交
703 704 705 706 707 708 709 710 711 712 713 714 715
}

/*
 * ReindexDatabase
 *		Recreate indexes of a database.
 *
 * Exceptions:
 *		"ERROR" if table nonexistent.
 *		...
 */
void
ReindexDatabase(const char *dbname, bool force, bool all)
{
716 717
	Relation	relation,
				relationRelation;
718
	HeapTuple	dbtuple,
719 720
				tuple;
	HeapScanDesc scan;
721
	int4		db_owner;
722 723
	Oid			db_id;
	ScanKeyData scankey;
724
	MemoryContext private_context;
725 726 727 728 729 730
	MemoryContext old;
	int			relcnt,
				relalc,
				i,
				oncealc = 200;
	Oid		   *relids = (Oid *) NULL;
H
Hiroshi Inoue 已提交
731 732 733 734 735

	AssertArg(dbname);

	relation = heap_openr(DatabaseRelationName, AccessShareLock);
	ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname,
736
						   F_NAMEEQ, NameGetDatum(dbname));
H
Hiroshi Inoue 已提交
737 738 739
	scan = heap_beginscan(relation, 0, SnapshotNow, 1, &scankey);
	dbtuple = heap_getnext(scan, 0);
	if (!HeapTupleIsValid(dbtuple))
740
		elog(ERROR, "Database \"%s\" does not exist", dbname);
H
Hiroshi Inoue 已提交
741 742 743
	db_id = dbtuple->t_data->t_oid;
	db_owner = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba;
	heap_endscan(scan);
744 745
	heap_close(relation, NoLock);

746
	if (GetUserId() != db_owner && !superuser())
H
Hiroshi Inoue 已提交
747 748 749 750 751
		elog(ERROR, "REINDEX DATABASE: Permission denied.");

	if (db_id != MyDatabaseId)
		elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database.");

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
	/*
	 * We cannot run 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, "REINDEX DATABASE cannot run inside a BEGIN/END block");

	/*
	 * Create a memory context that will survive forced transaction commits
	 * we do below.  Since it is a child of QueryContext, it will go away
	 * eventually even if we suffer an error; there's no need for special
	 * abort cleanup logic.
	 */
	private_context = AllocSetContextCreate(QueryContext,
											"ReindexDatabase",
											ALLOCSET_DEFAULT_MINSIZE,
											ALLOCSET_DEFAULT_INITSIZE,
											ALLOCSET_DEFAULT_MAXSIZE);
H
Hiroshi Inoue 已提交
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785

	relationRelation = heap_openr(RelationRelationName, AccessShareLock);
	scan = heap_beginscan(relationRelation, false, SnapshotNow, 0, NULL);
	relcnt = relalc = 0;
	while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
	{
		if (!all)
		{
			if (!IsSystemRelationName(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)))
				continue;
			if (((Form_pg_class) GETSTRUCT(tuple))->relhasrules)
				continue;
		}
		if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
		{
786
			old = MemoryContextSwitchTo(private_context);
H
Hiroshi Inoue 已提交
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
			if (relcnt == 0)
			{
				relalc = oncealc;
				relids = palloc(sizeof(Oid) * relalc);
			}
			else if (relcnt >= relalc)
			{
				relalc *= 2;
				relids = repalloc(relids, sizeof(Oid) * relalc);
			}
			MemoryContextSwitchTo(old);
			relids[relcnt] = tuple->t_data->t_oid;
			relcnt++;
		}
	}
	heap_endscan(scan);
	heap_close(relationRelation, AccessShareLock);

	CommitTransactionCommand();
	for (i = 0; i < relcnt; i++)
	{
		StartTransactionCommand();
809
		if (reindex_relation(relids[i], force))
810
			elog(NOTICE, "relation %u was reindexed", relids[i]);
H
Hiroshi Inoue 已提交
811 812 813
		CommitTransactionCommand();
	}
	StartTransactionCommand();
814 815

	MemoryContextDelete(private_context);
H
Hiroshi Inoue 已提交
816
}