selfuncs.c 26.2 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * selfuncs.c
4
 *	  Selectivity functions for system catalogs and builtin types
5
 *
6 7
 *	  These routines are registered in the operator catalog in the
 *	  "oprrest" and "oprjoin" attributes.
8 9 10 11 12
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
13
 *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.47 2000/01/15 02:59:38 petere Exp $
14 15 16 17
 *
 *-------------------------------------------------------------------------
 */

18 19
#include "postgres.h"

B
Bruce Momjian 已提交
20
#include "access/heapam.h"
21
#include "catalog/catname.h"
22
#include "catalog/pg_operator.h"
23
#include "catalog/pg_proc.h"
24
#include "catalog/pg_statistic.h"
25
#include "catalog/pg_type.h"
26
#include "parser/parse_func.h"
27
#include "parser/parse_oper.h"
B
Bruce Momjian 已提交
28
#include "utils/builtins.h"
29
#include "utils/int8.h"
B
Bruce Momjian 已提交
30 31
#include "utils/lsyscache.h"
#include "utils/syscache.h"
32 33

/* N is not a valid var/constant or relation id */
34
#define NONVALUE(N)		((N) == 0)
35

36 37
/* are we looking at a functional index selectivity request? */
#define FunctionalSelectivity(nIndKeys,attNum) ((attNum)==InvalidAttrNumber)
38

39 40 41
/* default selectivity estimate for equalities such as "A = b" */
#define DEFAULT_EQ_SEL  0.01

42 43 44
/* default selectivity estimate for inequalities such as "A < b" */
#define DEFAULT_INEQ_SEL  (1.0 / 3.0)

45 46
static bool convert_to_scale(Datum value, Oid typid,
							 double *scaleval);
47 48 49 50 51 52
static void getattproperties(Oid relid, AttrNumber attnum,
							 Oid *typid,
							 int *typlen,
							 bool *typbyval,
							 int32 *typmod);
static bool getattstatistics(Oid relid, AttrNumber attnum,
53
							 Oid opid, Oid typid, int32 typmod,
54 55 56 57 58
							 double *nullfrac,
							 double *commonfrac,
							 Datum *commonval,
							 Datum *loval,
							 Datum *hival);
59 60 61


/*
62
 *		eqsel			- Selectivity of "=" for any data types.
63 64 65 66 67 68
 *
 * Note: this routine is also used to estimate selectivity for some
 * operators that are not "=" but have comparable selectivity behavior,
 * such as "~~" (text LIKE).  Even for "=" we must keep in mind that
 * the left and right datatypes may differ, so the type of the given
 * constant "value" may be different from the type of the attribute.
69 70 71
 */
float64
eqsel(Oid opid,
72 73
	  Oid relid,
	  AttrNumber attno,
74
	  Datum value,
75
	  int32 flag)
76
{
77
	float64		result;
78 79 80

	result = (float64) palloc(sizeof(float64data));
	if (NONVALUE(attno) || NONVALUE(relid))
81
		*result = DEFAULT_EQ_SEL;
82
	else
83 84 85 86 87 88 89 90 91 92 93 94 95 96
	{
		Oid			typid;
		int			typlen;
		bool		typbyval;
		int32		typmod;
		double		nullfrac;
		double		commonfrac;
		Datum		commonval;
		double		selec;

		/* get info about the attribute */
		getattproperties(relid, attno,
						 &typid, &typlen, &typbyval, &typmod);

97
		/* get stats for the attribute, if available */
98
		if (getattstatistics(relid, attno, opid, typid, typmod,
99 100 101 102 103
							 &nullfrac, &commonfrac, &commonval,
							 NULL, NULL))
		{
			if (flag & SEL_CONSTANT)
			{
104 105 106 107 108 109
				/* Is the constant "=" to the column's most common value?
				 * (Although the operator may not really be "=",
				 * we will assume that seeing whether it returns TRUE
				 * for the most common value is useful information.
				 * If you don't like it, maybe you shouldn't be using
				 * eqsel for your operator...)
110
				 */
111 112
				RegProcedure	eqproc = get_opcode(opid);
				bool			mostcommon;
113

114 115 116 117 118 119 120 121 122 123 124
				if (eqproc == (RegProcedure) NULL)
					elog(ERROR, "eqsel: no procedure for operator %u",
						 opid);

				/* be careful to apply operator right way 'round */
				if (flag & SEL_RIGHT)
					mostcommon = (bool)
						DatumGetUInt8(fmgr(eqproc, commonval, value));
				else
					mostcommon = (bool)
						DatumGetUInt8(fmgr(eqproc, value, commonval));
125 126 127

				if (mostcommon)
				{
128
					/* Constant is "=" to the most common value.  We know
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
					 * selectivity exactly (or as exactly as VACUUM could
					 * calculate it, anyway).
					 */
					selec = commonfrac;
				}
				else
				{
					/* Comparison is against a constant that is neither the
					 * most common value nor null.  Its selectivity cannot
					 * be more than this:
					 */
					selec = 1.0 - commonfrac - nullfrac;
					if (selec > commonfrac)
						selec = commonfrac;
					/* and in fact it's probably less, so apply a fudge
					 * factor.
					 */
					selec *= 0.5;
				}
			}
			else
			{
				/* Search is for a value that we do not know a priori,
				 * but we will assume it is not NULL.  Selectivity
				 * cannot be more than this:
				 */
				selec = 1.0 - nullfrac;
				if (selec > commonfrac)
					selec = commonfrac;
				/* and in fact it's probably less, so apply a fudge
				 * factor.
				 */
				selec *= 0.5;
			}

			/* result should be in range, but make sure... */
			if (selec < 0.0)
				selec = 0.0;
			else if (selec > 1.0)
				selec = 1.0;

			if (! typbyval)
				pfree(DatumGetPointer(commonval));
		}
		else
		{
			/* No VACUUM ANALYZE stats available, so make a guess using
176 177 178
			 * the disbursion stat (if we have that, which is unlikely
			 * for a normal attribute; but for a system attribute we may
			 * be able to estimate it).
179
			 */
180
			selec = get_attdisbursion(relid, attno, 0.01);
181 182 183 184
		}

		*result = (float64data) selec;
	}
185
	return result;
186 187 188
}

/*
189
 *		neqsel			- Selectivity of "!=" for any data types.
190 191 192 193
 *
 * This routine is also used for some operators that are not "!="
 * but have comparable selectivity behavior.  See above comments
 * for eqsel().
194 195 196
 */
float64
neqsel(Oid opid,
197 198
	   Oid relid,
	   AttrNumber attno,
199
	   Datum value,
200
	   int32 flag)
201
{
202
	float64		result;
203 204 205

	result = eqsel(opid, relid, attno, value, flag);
	*result = 1.0 - *result;
206
	return result;
207 208 209
}

/*
210
 *		intltsel		- Selectivity of "<" (also "<=") for integers.
211 212 213 214 215
 *
 * Actually, this works and is used for all numeric types, so it should
 * be renamed.  In fact, it is also currently called for all manner of
 * non-numeric types, for which it is NOT very helpful.  That needs
 * to be fixed.
216 217 218
 */
float64
intltsel(Oid opid,
219 220
		 Oid relid,
		 AttrNumber attno,
221
		 Datum value,
222
		 int32 flag)
223
{
224
	float64		result;
225 226

	result = (float64) palloc(sizeof(float64data));
227 228
	if (! (flag & SEL_CONSTANT) || NONVALUE(attno) || NONVALUE(relid))
		*result = DEFAULT_INEQ_SEL;
229 230
	else
	{
231 232 233 234 235 236 237 238 239
		HeapTuple	oprtuple;
		Oid			ltype,
					rtype;
		Oid			typid;
		int			typlen;
		bool		typbyval;
		int32		typmod;
		Datum		hival,
					loval;
240
		double		val,
241 242 243 244 245
					high,
					low,
					numerator,
					denominator;

246 247 248
		/* Get left and right datatypes of the operator so we know
		 * what type the constant is.
		 */
249 250 251 252 253 254
		oprtuple = get_operator_tuple(opid);
		if (! HeapTupleIsValid(oprtuple))
			elog(ERROR, "intltsel: no tuple for operator %u", opid);
		ltype = ((Form_pg_operator) GETSTRUCT(oprtuple))->oprleft;
		rtype = ((Form_pg_operator) GETSTRUCT(oprtuple))->oprright;

255
		/* Convert the constant to a uniform comparison scale. */
256
		if (! convert_to_scale(value,
257 258
							   ((flag & SEL_RIGHT) ? rtype : ltype),
							   &val))
259
		{
260 261 262 263 264 265
			/* Ideally we'd produce an error here, on the grounds that
			 * the given operator shouldn't have intltsel registered as its
			 * selectivity func unless we can deal with its operand types.
			 * But currently, all manner of stuff is invoking intltsel,
			 * so give a default estimate until that can be fixed.
			 */
266
			*result = DEFAULT_INEQ_SEL;
267
			return result;
268
		}
269

270
		/* Now get info and stats about the attribute */
271 272 273
		getattproperties(relid, attno,
						 &typid, &typlen, &typbyval, &typmod);

274
		if (! getattstatistics(relid, attno, opid, typid, typmod,
275 276 277
							   NULL, NULL, NULL,
							   &loval, &hival))
		{
278
			/* no stats available, so default result */
279 280 281
			*result = DEFAULT_INEQ_SEL;
			return result;
		}
282 283 284 285

		/* Convert the attribute's loval/hival to common scale. */
		if (! convert_to_scale(loval, typid, &low) ||
			! convert_to_scale(hival, typid, &high))
286
		{
287 288 289 290 291 292 293 294 295
			/* See above comments... */
			if (! typbyval)
			{
				pfree(DatumGetPointer(hival));
				pfree(DatumGetPointer(loval));
			}

			*result = DEFAULT_INEQ_SEL;
			return result;
296
		}
297 298 299

		/* release temp storage if needed */
		if (! typbyval)
300
		{
301 302 303 304 305 306 307 308 309 310
			pfree(DatumGetPointer(hival));
			pfree(DatumGetPointer(loval));
		}

		if (high <= low)
		{
			/* If we trusted the stats fully, we could return a small or
			 * large selec depending on which side of the single data point
			 * the constant is on.  But it seems better to assume that the
			 * stats are out of date and return a default...
311 312
			 */
			*result = DEFAULT_INEQ_SEL;
313
	}
314 315 316 317 318 319 320 321 322 323
		else if (val <= low || val >= high)
		{
			/* If given value is outside the statistical range, return a
			 * small or large value; but not 0.0/1.0 since there is a chance
			 * the stats are out of date.
			 */
			if (flag & SEL_RIGHT)
				*result = (val <= low) ? 0.01 : 0.99;
			else
				*result = (val <= low) ? 0.99 : 0.01;
324 325 326
		}
		else
		{
327
			denominator = high - low;
328
			if (flag & SEL_RIGHT)
329
				numerator = val - low;
330
			else
331
				numerator = high - val;
332
			*result = numerator / denominator;
333
		}
334
	}
335
	return result;
336 337 338
}

/*
339
 *		intgtsel		- Selectivity of ">" (also ">=") for integers.
340 341
 *
 * See above comments for intltsel.
342 343 344
 */
float64
intgtsel(Oid opid,
345 346
		 Oid relid,
		 AttrNumber attno,
347
		 Datum value,
348
		 int32 flag)
349
{
350
	float64		result;
351

352 353 354 355 356 357
	/* Compute selectivity of "<", then invert --- but only if we
	 * were able to produce a non-default estimate.
	 */
	result = intltsel(opid, relid, attno, value, flag);
	if (*result != DEFAULT_INEQ_SEL)
		*result = 1.0 - *result;
358
	return result;
359 360 361
}

/*
362
 *		eqjoinsel		- Join selectivity of "="
363 364 365
 */
float64
eqjoinsel(Oid opid,
366 367 368 369
		  Oid relid1,
		  AttrNumber attno1,
		  Oid relid2,
		  AttrNumber attno2)
370
{
371
	float64		result;
372
	float64data num1,
373
				num2,
374
				min;
375 376
	bool		unknown1 = NONVALUE(relid1) || NONVALUE(attno1);
	bool		unknown2 = NONVALUE(relid2) || NONVALUE(attno2);
377 378

	result = (float64) palloc(sizeof(float64data));
379 380
	if (unknown1 && unknown2)
		*result = DEFAULT_EQ_SEL;
381
	else
382
	{
383 384
		num1 = unknown1 ? 1.0 : get_attdisbursion(relid1, attno1, 0.01);
		num2 = unknown2 ? 1.0 : get_attdisbursion(relid2, attno2, 0.01);
385 386 387 388 389 390 391 392
		/*
		 * The join selectivity cannot be more than num2, since each
		 * tuple in table 1 could match no more than num2 fraction of
		 * tuples in table 2 (and that's only if the table-1 tuple
		 * matches the most common value in table 2, so probably it's
		 * less).  By the same reasoning it is not more than num1.
		 * The min is therefore an upper bound.
		 *
393 394 395
		 * If we know the disbursion of only one side, use it; the reasoning
		 * above still works.
		 *
396 397 398 399 400 401 402 403 404
		 * XXX can we make a better estimate here?  Using the nullfrac
		 * statistic might be helpful, for example.  Assuming the operator
		 * is strict (does not succeed for null inputs) then the selectivity
		 * couldn't be more than (1-nullfrac1)*(1-nullfrac2), which might
		 * be usefully small if there are many nulls.  How about applying
		 * the operator to the most common values?
		 */
		min = (num1 < num2) ? num1 : num2;
		*result = min;
405
	}
406
	return result;
407 408 409
}

/*
410
 *		neqjoinsel		- Join selectivity of "!="
411 412 413
 */
float64
neqjoinsel(Oid opid,
414 415 416 417
		   Oid relid1,
		   AttrNumber attno1,
		   Oid relid2,
		   AttrNumber attno2)
418
{
419
	float64		result;
420 421 422

	result = eqjoinsel(opid, relid1, attno1, relid2, attno2);
	*result = 1.0 - *result;
423
	return result;
424 425 426
}

/*
427
 *		intltjoinsel	- Join selectivity of "<" and "<="
428 429 430
 */
float64
intltjoinsel(Oid opid,
431 432 433 434
			 Oid relid1,
			 AttrNumber attno1,
			 Oid relid2,
			 AttrNumber attno2)
435
{
436
	float64		result;
437 438

	result = (float64) palloc(sizeof(float64data));
439
	*result = DEFAULT_INEQ_SEL;
440
	return result;
441 442 443
}

/*
444
 *		intgtjoinsel	- Join selectivity of ">" and ">="
445 446 447
 */
float64
intgtjoinsel(Oid opid,
448 449 450 451
			 Oid relid1,
			 AttrNumber attno1,
			 Oid relid2,
			 AttrNumber attno2)
452
{
453
	float64		result;
454 455

	result = (float64) palloc(sizeof(float64data));
456
	*result = DEFAULT_INEQ_SEL;
457
	return result;
458 459
}

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
/*
 * convert_to_scale
 *	  Convert a given value of the indicated type to the comparison
 *	  scale needed by intltsel().  Returns "true" if successful.
 *
 * All numeric datatypes are simply converted to their equivalent
 * "double" values.
 * Future extension: convert string-like types to some suitable scale.
 */
static bool
convert_to_scale(Datum value, Oid typid,
				 double *scaleval)
{
	/* Fast-path conversions for some built-in types */
	switch (typid)
	{
		case BOOLOID:
			*scaleval = (double) DatumGetUInt8(value);
			return true;
		case INT2OID:
			*scaleval = (double) DatumGetInt16(value);
			return true;
		case INT4OID:
			*scaleval = (double) DatumGetInt32(value);
			return true;
485 486 487
		case INT8OID:
			*scaleval = (double) (* i8tod((int64 *) DatumGetPointer(value)));
			return true;
488 489 490 491 492 493
		case FLOAT4OID:
			*scaleval = (double) (* DatumGetFloat32(value));
			return true;
		case FLOAT8OID:
			*scaleval = (double) (* DatumGetFloat64(value));
			return true;
494 495 496
		case NUMERICOID:
			*scaleval = (double) (* numeric_float8((Numeric) DatumGetPointer(value)));
			return true;
497 498 499 500 501
		case OIDOID:
		case REGPROCOID:
			/* we can treat OIDs as integers... */
			*scaleval = (double) DatumGetObjectId(value);
			return true;
502 503 504 505 506 507 508 509
		case TEXTOID:
			/*
			 * Eventually this should get handled by somehow scaling as a
			 * string value.  For now, we need to call it out to avoid
			 * falling into the default case, because there is a float8(text)
			 * function declared in pg_proc that will do the wrong thing :-(
			 */
			break;
510 511 512 513 514
		default:
		{
			/* See whether there is a registered type-conversion function,
			 * namely a procedure named "float8" with the right signature.
			 */
515
			Oid			oid_array[FUNC_MAX_ARGS];
516 517
			HeapTuple	ftup;

518
			MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
519
			oid_array[0] = typid;
520
			ftup = SearchSysCacheTuple(PROCNAME,
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
									   PointerGetDatum("float8"),
									   Int32GetDatum(1),
									   PointerGetDatum(oid_array),
									   0);
			if (HeapTupleIsValid(ftup) &&
				((Form_pg_proc) GETSTRUCT(ftup))->prorettype == FLOAT8OID)
			{
				RegProcedure convertproc = (RegProcedure) ftup->t_data->t_oid;
				Datum converted = (Datum) fmgr(convertproc, value);
				*scaleval = (double) (* DatumGetFloat64(converted));
				return true;
			}
			break;
		}
	}
	/* Don't know how to convert */
	return false;
}

540
/*
541 542 543
 * getattproperties
 *	  Retrieve pg_attribute properties for an attribute,
 *	  including type OID, type len, type byval flag, typmod.
544
 */
545 546 547
static void
getattproperties(Oid relid, AttrNumber attnum,
				 Oid *typid, int *typlen, bool *typbyval, int32 *typmod)
548
{
549
	HeapTuple	atp;
550
	Form_pg_attribute att_tup;
551 552 553 554 555

	atp = SearchSysCacheTuple(ATTNUM,
							  ObjectIdGetDatum(relid),
							  Int16GetDatum(attnum),
							  0, 0);
556 557 558 559 560 561 562 563 564
	if (! HeapTupleIsValid(atp))
		elog(ERROR, "getattproperties: no attribute tuple %u %d",
			 relid, (int) attnum);
	att_tup = (Form_pg_attribute) GETSTRUCT(atp);

	*typid = att_tup->atttypid;
	*typlen = att_tup->attlen;
	*typbyval = att_tup->attbyval;
	*typmod = att_tup->atttypmod;
565 566 567
}

/*
568 569 570
 * getattstatistics
 *	  Retrieve the pg_statistic data for an attribute.
 *	  Returns 'false' if no stats are available.
571
 *
572 573 574 575
 * Inputs:
 * 'relid' and 'attnum' are the relation and attribute number.
 * 'typid' and 'typmod' are the type and typmod of the column,
 * which the caller must already have looked up.
576
 *
577 578 579 580 581 582 583 584 585 586 587
 * Outputs:
 * The available stats are nullfrac, commonfrac, commonval, loval, hival.
 * The caller need not retrieve all five --- pass NULL pointers for the
 * unwanted values.
 *
 * commonval, loval, hival are returned as Datums holding the internal
 * representation of the values.  (Note that these should be pfree'd
 * after use if the data type is not by-value.)
 *
 * XXX currently, this does a linear search of pg_statistic because there
 * is no index nor syscache for pg_statistic.  FIX THIS!
588
 */
589
static bool
590 591
getattstatistics(Oid relid, AttrNumber attnum, Oid opid, Oid typid,
				 int32 typmod,
592 593 594 595 596
				 double *nullfrac,
				 double *commonfrac,
				 Datum *commonval,
				 Datum *loval,
				 Datum *hival)
597
{
598
	Relation	rel;
599 600
	bool		isnull;
	HeapTuple	tuple;
601 602
	HeapTuple	typeTuple;
	FmgrInfo	inputproc;
603

604
	rel = heap_openr(StatisticRelationName, AccessShareLock);
605

B
Bruce Momjian 已提交
606 607 608
	tuple = SearchSysCacheTuple(STATRELID,
									ObjectIdGetDatum(relid),
									Int16GetDatum((int16) attnum),
609
									opid, 0);
610 611
	if (!HeapTupleIsValid(tuple))
	{
612
		/* no such stats entry */
613
		heap_close(rel, AccessShareLock);
614 615
		return false;
	}
616

617 618 619 620 621 622 623 624 625
	/* We assume that there will only be one entry in pg_statistic
	 * for the given rel/att.  Someday, VACUUM might store more than one...
	 */
	if (nullfrac)
		*nullfrac = ((Form_pg_statistic) GETSTRUCT(tuple))->stanullfrac;
	if (commonfrac)
		*commonfrac = ((Form_pg_statistic) GETSTRUCT(tuple))->stacommonfrac;

	/* Get the type input proc for the column datatype */
626
	typeTuple = SearchSysCacheTuple(TYPEOID,
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
									ObjectIdGetDatum(typid),
									0, 0, 0);
	if (! HeapTupleIsValid(typeTuple))
		elog(ERROR, "getattstatistics: Cache lookup failed for type %u",
			 typid);
	fmgr_info(((Form_pg_type) GETSTRUCT(typeTuple))->typinput, &inputproc);

	/* Values are variable-length fields, so cannot access as struct fields.
	 * Must do it the hard way with heap_getattr.
	 */
	if (commonval)
	{
		text *val = (text *) heap_getattr(tuple,
										  Anum_pg_statistic_stacommonval,
										  RelationGetDescr(rel),
										  &isnull);
		if (isnull)
		{
			elog(DEBUG, "getattstatistics: stacommonval is null");
			*commonval = PointerGetDatum(NULL);
		}
		else
		{
			char *strval = textout(val);
			*commonval = (Datum)
				(*fmgr_faddr(&inputproc)) (strval, typid, typmod);
			pfree(strval);
		}
655
	}
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696

	if (loval)
	{
		text *val = (text *) heap_getattr(tuple,
										  Anum_pg_statistic_staloval,
										  RelationGetDescr(rel),
										  &isnull);
		if (isnull)
		{
			elog(DEBUG, "getattstatistics: staloval is null");
			*loval = PointerGetDatum(NULL);
		}
		else
		{
			char *strval = textout(val);
			*loval = (Datum)
				(*fmgr_faddr(&inputproc)) (strval, typid, typmod);
			pfree(strval);
		}
	}

	if (hival)
	{
		text *val = (text *) heap_getattr(tuple,
										  Anum_pg_statistic_stahival,
										  RelationGetDescr(rel),
										  &isnull);
		if (isnull)
		{
			elog(DEBUG, "getattstatistics: stahival is null");
			*hival = PointerGetDatum(NULL);
		}
		else
		{
			char *strval = textout(val);
			*hival = (Datum)
				(*fmgr_faddr(&inputproc)) (strval, typid, typmod);
			pfree(strval);
		}
	}

697
	heap_close(rel, AccessShareLock);
698 699 700
	return true;
}

701 702
float64
btreesel(Oid operatorObjectId,
703 704 705 706 707 708
		 Oid indrelid,
		 AttrNumber attributeNumber,
		 char *constValue,
		 int32 constFlag,
		 int32 nIndexKeys,
		 Oid indexrelid)
709
{
710
	float64		result;
711 712 713

	if (FunctionalSelectivity(nIndexKeys, attributeNumber))
	{
B
Bruce Momjian 已提交
714

715 716 717 718 719
		/*
		 * Need to call the functions selectivity function here.  For now
		 * simply assume it's 1/3 since functions don't currently have
		 * selectivity functions
		 */
720 721
		result = (float64) palloc(sizeof(float64data));
		*result = 1.0 / 3.0;
722 723 724
	}
	else
	{
725 726 727 728 729 730 731 732 733 734
		RegProcedure oprrest = get_oprrest(operatorObjectId);

		/*
		 * Operators used for indexes should have selectivity estimators.
		 * (An alternative is to default to 0.5, as the optimizer does in
		 * dealing with operators occurring in WHERE clauses, but if you
		 * are going to the trouble of making index support you probably
		 * don't want to miss the benefits of a good selectivity estimate.)
		 */
		if (!oprrest)
735 736 737 738 739 740 741 742 743 744
		{
#if 1
			/*
			 * XXX temporary fix for 6.5: rtree operators are missing their
			 * selectivity estimators, so return a default estimate instead.
			 * Ugh.
			 */
			result = (float64) palloc(sizeof(float64data));
			*result = 0.5;
#else
745
			elog(ERROR,
746
				 "Operator %u must have a restriction selectivity estimator to be used in an index",
747
				 operatorObjectId);
748 749 750 751 752 753 754 755 756 757
#endif
		}
		else
			result = (float64) fmgr(oprrest,
									(char *) operatorObjectId,
									(char *) indrelid,
									(char *) (int) attributeNumber,
									(char *) constValue,
									(char *) constFlag,
									NULL);
758 759 760
	}

	if (!PointerIsValid(result))
761
		elog(ERROR, "Btree Selectivity: bad pointer");
762
	if (*result < 0.0 || *result > 1.0)
763
		elog(ERROR, "Btree Selectivity: bad value %f", *result);
764

765
	return result;
766 767 768 769
}

float64
btreenpage(Oid operatorObjectId,
770 771 772 773 774 775
		   Oid indrelid,
		   AttrNumber attributeNumber,
		   char *constValue,
		   int32 constFlag,
		   int32 nIndexKeys,
		   Oid indexrelid)
776
{
777 778 779 780 781
	float64		temp,
				result;
	float64data tempData;
	HeapTuple	atp;
	int			npage;
782 783 784

	if (FunctionalSelectivity(nIndexKeys, attributeNumber))
	{
B
Bruce Momjian 已提交
785

786 787 788 789 790 791 792 793 794 795
		/*
		 * Need to call the functions selectivity function here.  For now
		 * simply assume it's 1/3 since functions don't currently have
		 * selectivity functions
		 */
		tempData = 1.0 / 3.0;
		temp = &tempData;
	}
	else
	{
796 797 798 799 800 801 802 803 804 805
		RegProcedure oprrest = get_oprrest(operatorObjectId);

		/*
		 * Operators used for indexes should have selectivity estimators.
		 * (An alternative is to default to 0.5, as the optimizer does in
		 * dealing with operators occurring in WHERE clauses, but if you
		 * are going to the trouble of making index support you probably
		 * don't want to miss the benefits of a good selectivity estimate.)
		 */
		if (!oprrest)
806 807 808 809 810 811 812 813 814 815
		{
#if 1
			/*
			 * XXX temporary fix for 6.5: rtree operators are missing their
			 * selectivity estimators, so return a default estimate instead.
			 * Ugh.
			 */
			tempData = 0.5;
			temp = &tempData;
#else
816
			elog(ERROR,
817
				 "Operator %u must have a restriction selectivity estimator to be used in an index",
818
				 operatorObjectId);
819 820 821 822 823 824 825 826 827 828
#endif
		}
		else
			temp = (float64) fmgr(oprrest,
								  (char *) operatorObjectId,
								  (char *) indrelid,
								  (char *) (int) attributeNumber,
								  (char *) constValue,
								  (char *) constFlag,
								  NULL);
829
	}
830

831 832 833 834 835
	atp = SearchSysCacheTuple(RELOID,
							  ObjectIdGetDatum(indexrelid),
							  0, 0, 0);
	if (!HeapTupleIsValid(atp))
	{
836
		elog(ERROR, "btreenpage: no index tuple %u", indexrelid);
837
		return 0;
838 839 840 841 842
	}

	npage = ((Form_pg_class) GETSTRUCT(atp))->relpages;
	result = (float64) palloc(sizeof(float64data));
	*result = *temp * npage;
843
	return result;
844 845 846 847
}

float64
hashsel(Oid operatorObjectId,
848 849 850 851 852 853
		Oid indrelid,
		AttrNumber attributeNumber,
		char *constValue,
		int32 constFlag,
		int32 nIndexKeys,
		Oid indexrelid)
854
{
855

856 857 858 859
	float64		result;
	float64data resultData;
	HeapTuple	atp;
	int			ntuples;
860 861 862 863 864 865 866 867 868 869

	if (FunctionalSelectivity(nIndexKeys, attributeNumber))
	{

		/*
		 * Need to call the functions selectivity function here.  For now
		 * simply use 1/Number of Tuples since functions don't currently
		 * have selectivity functions
		 */

870 871
		atp = SearchSysCacheTuple(RELOID,
								  ObjectIdGetDatum(indexrelid),
872 873 874
								  0, 0, 0);
		if (!HeapTupleIsValid(atp))
		{
875
			elog(ERROR, "hashsel: no index tuple %u", indexrelid);
876
			return 0;
877 878 879 880 881 882 883 884
		}
		ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples;
		if (ntuples > 0)
			resultData = 1.0 / (float64data) ntuples;
		else
			resultData = (float64data) (1.0 / 100.0);
		result = &resultData;

885
	}
886 887
	else
	{
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
		RegProcedure oprrest = get_oprrest(operatorObjectId);

		/*
		 * Operators used for indexes should have selectivity estimators.
		 * (An alternative is to default to 0.5, as the optimizer does in
		 * dealing with operators occurring in WHERE clauses, but if you
		 * are going to the trouble of making index support you probably
		 * don't want to miss the benefits of a good selectivity estimate.)
		 */
		if (!oprrest)
			elog(ERROR,
				 "Operator %u must have a restriction selectivity estimator to be used in a hash index",
				 operatorObjectId);

		result = (float64) fmgr(oprrest,
903 904 905 906 907 908
								(char *) operatorObjectId,
								(char *) indrelid,
								(char *) (int) attributeNumber,
								(char *) constValue,
								(char *) constFlag,
								NULL);
909
	}
910 911

	if (!PointerIsValid(result))
912
		elog(ERROR, "Hash Table Selectivity: bad pointer");
913
	if (*result < 0.0 || *result > 1.0)
914
		elog(ERROR, "Hash Table Selectivity: bad value %f", *result);
915

916
	return result;
917 918


919 920 921 922
}

float64
hashnpage(Oid operatorObjectId,
923 924 925 926 927 928
		  Oid indrelid,
		  AttrNumber attributeNumber,
		  char *constValue,
		  int32 constFlag,
		  int32 nIndexKeys,
		  Oid indexrelid)
929
{
930 931 932 933 934 935
	float64		temp,
				result;
	float64data tempData;
	HeapTuple	atp;
	int			npage;
	int			ntuples;
936

937 938
	atp = SearchSysCacheTuple(RELOID,
							  ObjectIdGetDatum(indexrelid),
939 940 941
							  0, 0, 0);
	if (!HeapTupleIsValid(atp))
	{
942
		elog(ERROR, "hashsel: no index tuple %u", indexrelid);
943
		return 0;
944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
	}


	if (FunctionalSelectivity(nIndexKeys, attributeNumber))
	{

		/*
		 * Need to call the functions selectivity function here.  For now,
		 * use 1/Number of Tuples since functions don't currently have
		 * selectivity functions
		 */

		ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples;
		if (ntuples > 0)
			tempData = 1.0 / (float64data) ntuples;
		else
			tempData = (float64data) (1.0 / 100.0);
		temp = &tempData;

963
	}
964 965
	else
	{
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980
		RegProcedure oprrest = get_oprrest(operatorObjectId);

		/*
		 * Operators used for indexes should have selectivity estimators.
		 * (An alternative is to default to 0.5, as the optimizer does in
		 * dealing with operators occurring in WHERE clauses, but if you
		 * are going to the trouble of making index support you probably
		 * don't want to miss the benefits of a good selectivity estimate.)
		 */
		if (!oprrest)
			elog(ERROR,
				 "Operator %u must have a restriction selectivity estimator to be used in a hash index",
				 operatorObjectId);

		temp = (float64) fmgr(oprrest,
981 982 983 984 985 986
							  (char *) operatorObjectId,
							  (char *) indrelid,
							  (char *) (int) attributeNumber,
							  (char *) constValue,
							  (char *) constFlag,
							  NULL);
987
	}
988 989 990 991

	npage = ((Form_pg_class) GETSTRUCT(atp))->relpages;
	result = (float64) palloc(sizeof(float64data));
	*result = *temp * npage;
992
	return result;
993 994 995 996 997
}


float64
rtsel(Oid operatorObjectId,
998 999 1000 1001 1002 1003
	  Oid indrelid,
	  AttrNumber attributeNumber,
	  char *constValue,
	  int32 constFlag,
	  int32 nIndexKeys,
	  Oid indexrelid)
1004
{
1005 1006
	return (btreesel(operatorObjectId, indrelid, attributeNumber,
					 constValue, constFlag, nIndexKeys, indexrelid));
1007 1008 1009 1010
}

float64
rtnpage(Oid operatorObjectId,
1011 1012 1013 1014 1015 1016
		Oid indrelid,
		AttrNumber attributeNumber,
		char *constValue,
		int32 constFlag,
		int32 nIndexKeys,
		Oid indexrelid)
1017
{
1018 1019
	return (btreenpage(operatorObjectId, indrelid, attributeNumber,
					   constValue, constFlag, nIndexKeys, indexrelid));
1020
}
1021 1022 1023

float64
gistsel(Oid operatorObjectId,
1024 1025 1026 1027 1028 1029
		Oid indrelid,
		AttrNumber attributeNumber,
		char *constValue,
		int32 constFlag,
		int32 nIndexKeys,
		Oid indexrelid)
1030
{
1031 1032
	return (btreesel(operatorObjectId, indrelid, attributeNumber,
					 constValue, constFlag, nIndexKeys, indexrelid));
1033 1034 1035 1036
}

float64
gistnpage(Oid operatorObjectId,
1037 1038 1039 1040 1041 1042
		  Oid indrelid,
		  AttrNumber attributeNumber,
		  char *constValue,
		  int32 constFlag,
		  int32 nIndexKeys,
		  Oid indexrelid)
1043
{
1044 1045
	return (btreenpage(operatorObjectId, indrelid, attributeNumber,
					   constValue, constFlag, nIndexKeys, indexrelid));
1046
}