plancat.c 16.8 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * plancat.c--
4
 *	   routines for accessing the system catalogs
5 6 7 8 9 10
 *
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.24 1999/02/03 21:16:52 momjian Exp $
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include "postgres.h"

#include "access/heapam.h"
#include "access/genam.h"
#include "access/htup.h"
#include "access/itup.h"

#include "catalog/catname.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_index.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_version.h"

29
#include "parser/parsetree.h"	/* for getrelid() */
30 31 32 33 34 35
#include "fmgr.h"

#include "optimizer/internal.h"
#include "optimizer/plancat.h"

#include "utils/syscache.h"
M
Marc G. Fournier 已提交
36
#ifndef HAVE_MEMMOVE
37
#include <regex/utils.h>
M
Marc G. Fournier 已提交
38
#else
39
#include <string.h>
M
Marc G. Fournier 已提交
40
#endif
41 42


43 44 45
static void IndexSelectivity(Oid indexrelid, Oid indrelid, int32 nIndexKeys,
				 Oid *AccessMethodOperatorClasses, Oid *operatorObjectIds,
	   int32 *varAttributeNumbers, char **constValues, int32 *constFlags,
46
				 float *idxPages, float *idxSelec);
47 48 49 50


/*
 * relation-info -
51 52 53 54 55
 *	  Retrieves catalog information for a given relation. Given the oid of
 *	  the relation, return the following information:
 *				whether the relation has secondary indices
 *				number of pages
 *				number of tuples
56 57
 */
void
58 59
relation_info(Query *root, Index relid,
			  bool *hasindex, int *pages, int *tuples)
60
{
61 62 63
	HeapTuple	relationTuple;
	Form_pg_class relation;
	Oid			relationObjectId;
64 65 66

	relationObjectId = getrelid(relid, root->rtable);
	relationTuple = SearchSysCacheTuple(RELOID,
67
									  ObjectIdGetDatum(relationObjectId),
68 69 70 71 72 73 74 75 76 77 78
										0, 0, 0);
	if (HeapTupleIsValid(relationTuple))
	{
		relation = (Form_pg_class) GETSTRUCT(relationTuple);

		*hasindex = (relation->relhasindex) ? TRUE : FALSE;
		*pages = relation->relpages;
		*tuples = relation->reltuples;
	}
	else
	{
79
		elog(ERROR, "RelationCatalogInformation: Relation %d not found",
80 81 82 83
			 relationObjectId);
	}

	return;
84 85 86
}


87
/*
88
 * index-info--
89 90 91 92 93 94 95 96 97
 *	  Retrieves catalog information on an index on a given relation.
 *
 *	  The index relation is opened on the first invocation. The current
 *	  retrieves the next index relation within the catalog that has not
 *	  already been retrieved by a previous call.  The index catalog
 *	  is closed when no more indices for 'relid' can be found.
 *
 * 'first' is 1 if this is the first call
 *
98 99
 * Returns true if successful and false otherwise. Index info is returned
 * via the transient data structure 'info'.
100
 *
101 102
 */
bool
103
index_info(Query *root, bool first, int relid, IdxInfoRetval *info)
104
{
105
	int			i;
106 107
	HeapTuple	indexTuple,
				amopTuple;
108
	Form_pg_index index;
109 110 111 112
	Relation	indexRelation;
	uint16		amstrategy;
	Oid			relam;
	Oid			indrelid;
113 114 115 116 117 118 119 120 121

	static Relation relation = (Relation) NULL;
	static HeapScanDesc scan = (HeapScanDesc) NULL;
	static ScanKeyData indexKey;


	/* find the oid of the indexed relation */
	indrelid = getrelid(relid, root->rtable);

B
Bruce Momjian 已提交
122
	MemSet(info, 0, sizeof(IdxInfoRetval));
123

124
	/*
125 126 127
	 * the maximum number of elements in each of the following arrays is
	 * 8. We allocate one more for a terminating 0 to indicate the end of
	 * the array.
128
	 */
129
	info->indexkeys = (int *) palloc(sizeof(int) * 9);
B
Bruce Momjian 已提交
130
	MemSet(info->indexkeys, 0, sizeof(int) * 9);
131
	info->orderOprs = (Oid *) palloc(sizeof(Oid) * 9);
B
Bruce Momjian 已提交
132
	MemSet(info->orderOprs, 0, sizeof(Oid) * 9);
133
	info->classlist = (Oid *) palloc(sizeof(Oid) * 9);
B
Bruce Momjian 已提交
134
	MemSet(info->classlist, 0, sizeof(Oid) * 9);
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

	/* Find an index on the given relation */
	if (first)
	{
		if (RelationIsValid(relation))
			heap_close(relation);
		if (HeapScanIsValid(scan))
			heap_endscan(scan);

		ScanKeyEntryInitialize(&indexKey, 0,
							   Anum_pg_index_indrelid,
							   F_OIDEQ,
							   ObjectIdGetDatum(indrelid));

		relation = heap_openr(IndexRelationName);
150
		scan = heap_beginscan(relation, 0, SnapshotNow,
151 152 153
							  1, &indexKey);
	}
	if (!HeapScanIsValid(scan))
154
		elog(ERROR, "index_info: scan not started");
155
	indexTuple = heap_getnext(scan, 0);
156 157 158 159 160 161
	if (!HeapTupleIsValid(indexTuple))
	{
		heap_endscan(scan);
		heap_close(relation);
		scan = (HeapScanDesc) NULL;
		relation = (Relation) NULL;
162
		return 0;
163 164 165
	}

	/* Extract info from the index tuple */
166
	index = (Form_pg_index) GETSTRUCT(indexTuple);
167
	info->relid = index->indexrelid;	/* index relation */
B
Bruce Momjian 已提交
168
	for (i = 0; i < INDEX_MAX_KEYS; i++)
169
		info->indexkeys[i] = index->indkey[i];
B
Bruce Momjian 已提交
170
	for (i = 0; i < INDEX_MAX_KEYS; i++)
171
		info->classlist[i] = index->indclass[i];
172

173
	info->indproc = index->indproc;		/* functional index ?? */
174

175 176 177 178 179 180 181 182 183 184
	/* partial index ?? */
	if (VARSIZE(&index->indpred) != 0)
	{

		/*
		 * The memory allocated here for the predicate (in lispReadString)
		 * only needs to stay around until it's used in find_index_paths,
		 * which is all within a command, so the automatic pfree at end of
		 * transaction should be ok.
		 */
185
		char	   *predString;
186 187 188 189 190 191 192 193

		predString = fmgr(F_TEXTOUT, &index->indpred);
		info->indpred = (Node *) stringToNode(predString);
		pfree(predString);
	}

	/* Extract info from the relation descriptor for the index */
	indexRelation = index_open(index->indexrelid);
194
#ifdef notdef
195 196
	/* XXX should iterate through strategies -- but how?  use #1 for now */
	amstrategy = indexRelation->rd_am->amstrategies;
197
#endif	 /* notdef */
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
	amstrategy = 1;
	relam = indexRelation->rd_rel->relam;
	info->relam = relam;
	info->pages = indexRelation->rd_rel->relpages;
	info->tuples = indexRelation->rd_rel->reltuples;
	heap_close(indexRelation);

	/*
	 * Find the index ordering keys
	 *
	 * Must use indclass to know when to stop looking since with functional
	 * indices there could be several keys (args) for one opclass. -mer 27
	 * Sept 1991
	 */
	for (i = 0; i < 8 && index->indclass[i]; ++i)
	{
		amopTuple = SearchSysCacheTuple(AMOPSTRATEGY,
										ObjectIdGetDatum(relam),
									ObjectIdGetDatum(index->indclass[i]),
										UInt16GetDatum(amstrategy),
										0);
		if (!HeapTupleIsValid(amopTuple))
220
			elog(ERROR, "index_info: no amop %d %d %d",
221
				 relam, index->indclass[i], amstrategy);
222
		info->orderOprs[i] = ((Form_pg_amop) GETSTRUCT(amopTuple))->amopopr;
223
	}
224
	return TRUE;
225 226
}

227
/*
228
 * index-selectivity--
229 230 231
 *
 *	  Call util/plancat.c:IndexSelectivity with the indicated arguments.
 *
232 233 234 235 236 237 238 239
 * 'indid' is the index OID
 * 'classes' is a list of index key classes
 * 'opnos' is a list of index key operator OIDs
 * 'relid' is the OID of the relation indexed
 * 'attnos' is a list of the relation attnos which the index keys over
 * 'values' is a list of the values of the clause's constants
 * 'flags' is a list of fixnums which describe the constants
 * 'nkeys' is the number of index keys
240
 *
241
 * Returns two floats: index pages and index selectivity in 'idxPages' and
242 243
 *		'idxSelec'.
 *
244 245 246
 */
void
index_selectivity(Oid indid,
247 248
				  Oid *classes,
				  List *opnos,
249
				  Oid relid,
250 251 252
				  List *attnos,
				  List *values,
				  List *flags,
253 254 255
				  int32 nkeys,
				  float *idxPages,
				  float *idxSelec)
256
{
257 258 259 260 261 262 263 264 265
	Oid		   *opno_array;
	int		   *attno_array,
			   *flag_array;
	char	  **value_array;
	int			i = 0;
	List	   *xopno,
			   *xattno,
			   *value,
			   *flag;
266 267 268 269 270 271 272 273 274

	if (length(opnos) != nkeys || length(attnos) != nkeys ||
		length(values) != nkeys || length(flags) != nkeys)
	{

		*idxPages = 0.0;
		*idxSelec = 1.0;
		return;
	}
275

276 277 278 279
	opno_array = (Oid *) palloc(nkeys * sizeof(Oid));
	attno_array = (int *) palloc(nkeys * sizeof(int32));
	value_array = (char **) palloc(nkeys * sizeof(char *));
	flag_array = (int *) palloc(nkeys * sizeof(int32));
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
	i = 0;
	foreach(xopno, opnos)
		opno_array[i++] = lfirsti(xopno);

	i = 0;
	foreach(xattno, attnos)
		attno_array[i++] = lfirsti(xattno);

	i = 0;
	foreach(value, values)
		value_array[i++] = (char *) lfirst(value);

	i = 0;
	foreach(flag, flags)
		flag_array[i++] = lfirsti(flag);

	IndexSelectivity(indid,
					 relid,
					 nkeys,
					 classes,	/* not used */
					 opno_array,
					 attno_array,
					 value_array,
					 flag_array,
					 idxPages,
					 idxSelec);
307 308 309 310 311 312
	return;
}

/*
 * restriction_selectivity in lisp system.--
 *
313 314
 *	  NOTE: The routine is now merged with RestrictionClauseSelectivity
 *	  as defined in plancat.c
315 316 317 318 319 320
 *
 * Returns the selectivity of a specified operator.
 * This code executes registered procedures stored in the
 * operator relation, by calling the function manager.
 *
 * XXX The assumption in the selectivity procedures is that if the
321 322
 *		relation OIDs or attribute numbers are -1, then the clause
 *		isn't of the form (op var const).
323 324 325
 */
Cost
restriction_selectivity(Oid functionObjectId,
326 327 328 329 330
						Oid operatorObjectId,
						Oid relationObjectId,
						AttrNumber attributeNumber,
						char *constValue,
						int32 constFlag)
331
{
332
	float64		result;
333 334 335 336 337 338 339 340 341

	result = (float64) fmgr(functionObjectId,
							(char *) operatorObjectId,
							(char *) relationObjectId,
							(char *) (int) attributeNumber,
							(char *) constValue,
							(char *) constFlag,
							NULL);
	if (!PointerIsValid(result))
342
		elog(ERROR, "RestrictionClauseSelectivity: bad pointer");
343 344

	if (*result < 0.0 || *result > 1.0)
345
		elog(ERROR, "RestrictionClauseSelectivity: bad value %lf",
346 347
			 *result);

348
	return (Cost) *result;
349 350 351 352
}

/*
 * join_selectivity--
353 354
 *	  Similarly, this routine is merged with JoinClauseSelectivity in
 *	  plancat.c
355
 *
356 357
 *	  Returns the selectivity of an operator, given the join clause
 *	  information.
358 359
 *
 * XXX The assumption in the selectivity procedures is that if the
360 361
 *		relation OIDs or attribute numbers are -1, then the clause
 *		isn't of the form (op var var).
362 363
 */
Cost
364 365 366 367 368 369
join_selectivity(Oid functionObjectId,
				 Oid operatorObjectId,
				 Oid relationObjectId1,
				 AttrNumber attributeNumber1,
				 Oid relationObjectId2,
				 AttrNumber attributeNumber2)
370
{
371
	float64		result;
372 373 374 375 376 377 378 379 380

	result = (float64) fmgr(functionObjectId,
							(char *) operatorObjectId,
							(char *) relationObjectId1,
							(char *) (int) attributeNumber1,
							(char *) relationObjectId2,
							(char *) (int) attributeNumber2,
							NULL);
	if (!PointerIsValid(result))
381
		elog(ERROR, "JoinClauseSelectivity: bad pointer");
382 383

	if (*result < 0.0 || *result > 1.0)
384
		elog(ERROR, "JoinClauseSelectivity: bad value %lf",
385 386
			 *result);

387
	return (Cost) *result;
388 389 390 391 392 393 394 395
}

/*
 * find_all_inheritors--
 *
 * Returns a LISP list containing the OIDs of all relations which
 * inherits from the relation with OID 'inhparent'.
 */
396
List *
397 398
find_inheritance_children(Oid inhparent)
{
399 400 401 402
	static ScanKeyData key[1] = {
		{0, Anum_pg_inherits_inhparent, F_OIDEQ}
	};

403 404 405 406 407
	HeapTuple	inheritsTuple;
	Relation	relation;
	HeapScanDesc scan;
	List	   *list = NIL;
	Oid			inhrelid;
408

409 410
	fmgr_info(F_OIDEQ, &key[0].sk_func);
	key[0].sk_nargs = key[0].sk_func.fn_nargs;
411 412 413

	key[0].sk_argument = ObjectIdGetDatum((Oid) inhparent);
	relation = heap_openr(InheritsRelationName);
414
	scan = heap_beginscan(relation, 0, SnapshotNow, 1, key);
415
	while (HeapTupleIsValid(inheritsTuple = heap_getnext(scan, 0)))
416
	{
417
		inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrel;
418 419 420 421
		list = lappendi(list, inhrelid);
	}
	heap_endscan(scan);
	heap_close(relation);
422
	return list;
423 424
}

425
#ifdef NOT_USED
426 427 428 429 430 431
/*
 * VersionGetParents--
 *
 * Returns a LISP list containing the OIDs of all relations which are
 * base relations of the relation with OID 'verrelid'.
 */
432
List *
433 434
VersionGetParents(Oid verrelid)
{
435 436 437 438
	static ScanKeyData key[1] = {
		{0, Anum_pg_version_verrelid, F_OIDEQ}
	};

439 440 441 442 443
	HeapTuple	versionTuple;
	Relation	relation;
	HeapScanDesc scan;
	Oid			verbaseid;
	List	   *list = NIL;
444

445 446
	fmgr_info(F_OIDEQ, &key[0].sk_func);
	key[0].sk_nargs = key[0].sk_func.fn_nargs;
447 448
	relation = heap_openr(VersionRelationName);
	key[0].sk_argument = ObjectIdGetDatum(verrelid);
449
	scan = heap_beginscan(relation, 0, SnapshotNow, 1, key);
450
	while (HeapTupleIsValid(versionTuple = heap_getnext(scan, 0)))
451
	{
452
		verbaseid = ((Form_pg_version)
453 454 455 456 457 458 459 460 461
					 GETSTRUCT(versionTuple))->verbaseid;

		list = lconsi(verbaseid, list);

		key[0].sk_argument = ObjectIdGetDatum(verbaseid);
		heap_rescan(scan, 0, key);
	}
	heap_endscan(scan);
	heap_close(relation);
462
	return list;
463
}
464 465
#endif

466 467 468 469 470 471 472 473

/*****************************************************************************
 *
 *****************************************************************************/

/*
 * IdexSelectivity--
 *
474 475 476 477
 *	  Retrieves the 'amopnpages' and 'amopselect' parameters for each
 *	  AM operator when a given index (specified by 'indexrelid') is used.
 *	  These two parameters are returned by copying them to into an array of
 *	  floats.
478
 *
479 480 481
 *	  Assumption: the attribute numbers and operator ObjectIds are in order
 *	  WRT to each other (otherwise, you have no way of knowing which
 *	  AM operator class or attribute number corresponds to which operator.
482 483 484 485 486 487 488 489 490 491 492 493
 *
 * 'varAttributeNumbers' contains attribute numbers for variables
 * 'constValues' contains the constant values
 * 'constFlags' describes how to treat the constants in each clause
 * 'nIndexKeys' describes how many keys the index actually has
 *
 * Returns 'selectivityInfo' filled with the sum of all pages touched
 * and the product of each clause's selectivity.
 *
 */
static void
IndexSelectivity(Oid indexrelid,
494 495
				 Oid indrelid,
				 int32 nIndexKeys,
496 497 498 499 500
				 Oid *AccessMethodOperatorClasses,		/* XXX not used? */
				 Oid *operatorObjectIds,
				 int32 *varAttributeNumbers,
				 char **constValues,
				 int32 *constFlags,
501 502
				 float *idxPages,
				 float *idxSelec)
503
{
504
	int			i,
505 506 507 508
				n;
	HeapTuple	indexTuple,
				amopTuple,
				indRel;
509
	Form_pg_index index;
510 511 512 513 514 515 516 517 518
	Form_pg_amop amop;
	Oid			indclass;
	float64data npages,
				select;
	float64		amopnpages,
				amopselect;
	Oid			relam;
	bool		nphack = false;
	float64data fattr_select = 1.0;
519 520 521 522 523

	indRel = SearchSysCacheTuple(RELOID,
								 ObjectIdGetDatum(indexrelid),
								 0, 0, 0);
	if (!HeapTupleIsValid(indRel))
524
		elog(ERROR, "IndexSelectivity: index %d not found",
525 526 527 528 529 530 531
			 indexrelid);
	relam = ((Form_pg_class) GETSTRUCT(indRel))->relam;

	indexTuple = SearchSysCacheTuple(INDEXRELID,
									 ObjectIdGetDatum(indexrelid),
									 0, 0, 0);
	if (!HeapTupleIsValid(indexTuple))
532
		elog(ERROR, "IndexSelectivity: index %d not found",
533
			 indexrelid);
534
	index = (Form_pg_index) GETSTRUCT(indexTuple);
535 536 537 538

	/*
	 * Hack for non-functional btree npages estimation: npages =
	 * index_pages * selectivity_of_1st_attr_clause(s) - vadim 04/24/97
539
	 */
540 541 542
	if (relam == BTREE_AM_OID &&
		varAttributeNumbers[0] != InvalidAttrNumber)
		nphack = true;
543

544 545 546
	npages = 0.0;
	select = 1.0;
	for (n = 0; n < nIndexKeys; ++n)
547
	{
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586

		/*
		 * Find the AM class for this key.
		 *
		 * If the first attribute number is invalid then we have a functional
		 * index, and AM class is the first one defined since functional
		 * indices have exactly one key.
		 */
		indclass = (varAttributeNumbers[0] == InvalidAttrNumber) ?
			index->indclass[0] : InvalidOid;
		i = 0;
		while ((i < nIndexKeys) && (indclass == InvalidOid))
		{
			if (varAttributeNumbers[n] == index->indkey[i])
			{
				indclass = index->indclass[i];
				break;
			}
			i++;
		}
		if (!OidIsValid(indclass))
		{

			/*
			 * Presumably this means that we are using a functional index
			 * clause and so had no variable to match to the index key ...
			 * if not we are in trouble.
			 */
			elog(NOTICE, "IndexSelectivity: no key %d in index %d",
				 varAttributeNumbers[n], indexrelid);
			continue;
		}

		amopTuple = SearchSysCacheTuple(AMOPOPID,
										ObjectIdGetDatum(indclass),
								  ObjectIdGetDatum(operatorObjectIds[n]),
										ObjectIdGetDatum(relam),
										0);
		if (!HeapTupleIsValid(amopTuple))
587
			elog(ERROR, "IndexSelectivity: no amop %d %d",
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
				 indclass, operatorObjectIds[n]);
		amop = (Form_pg_amop) GETSTRUCT(amopTuple);

		if (!nphack)
		{
			amopnpages = (float64) fmgr(amop->amopnpages,
										(char *) operatorObjectIds[n],
										(char *) indrelid,
										(char *) varAttributeNumbers[n],
										(char *) constValues[n],
										(char *) constFlags[n],
										(char *) nIndexKeys,
										(char *) indexrelid);
#if 0
/*
603 604 605
 * So cool guys! Npages for x > 10 and x < 20 is twice as
 * npages for x > 10!	- vadim 04/09/97
 */
606 607 608
			npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0;
			if ((i = npages) < npages)	/* ceil(npages)? */
				npages += 1.0;
609
#endif
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
			npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0;
		}

		amopselect = (float64) fmgr(amop->amopselect,
									(char *) operatorObjectIds[n],
									(char *) indrelid,
									(char *) varAttributeNumbers[n],
									(char *) constValues[n],
									(char *) constFlags[n],
									(char *) nIndexKeys,
									(char *) indexrelid);

		if (nphack && varAttributeNumbers[n] == index->indkey[0])
			fattr_select *= PointerIsValid(amopselect) ? *amopselect : 1.0;

		select *= PointerIsValid(amopselect) ? *amopselect : 1.0;
626
	}
627

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
	/*
	 * Estimation of npages below is hack of course, but it's better than
	 * it was before.		- vadim 04/09/97
	 */
	if (nphack)
	{
		npages = fattr_select * ((Form_pg_class) GETSTRUCT(indRel))->relpages;
		*idxPages = ceil((double) npages);
	}
	else
	{
		if (nIndexKeys > 1)
			npages = npages / (1.0 + nIndexKeys);
		*idxPages = ceil((double) (npages / nIndexKeys));
	}
	*idxSelec = select;
}