indxpath.c 36.2 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * indxpath.c--
4 5
 *	  Routines to determine which indices are usable for scanning a
 *	  given relation
6 7 8 9 10
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.19 1998/07/31 15:10:40 vadim Exp $
12 13 14 15
 *
 *-------------------------------------------------------------------------
 */
#include <math.h>
Bruce Momjian's avatar
Bruce Momjian committed
16

17
#include "postgres.h"
Bruce Momjian's avatar
Bruce Momjian committed
18

19 20 21
#include "access/attnum.h"
#include "access/heapam.h"
#include "access/nbtree.h"
Bruce Momjian's avatar
Bruce Momjian committed
22 23 24 25
#include "catalog/catname.h"
#include "catalog/pg_amop.h"
#include "executor/executor.h"
#include "fmgr.h"
26 27
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
Bruce Momjian's avatar
Bruce Momjian committed
28 29
#include "nodes/pg_list.h"
#include "nodes/relation.h"
30 31 32
#include "optimizer/clauses.h"
#include "optimizer/clauseinfo.h"
#include "optimizer/cost.h"
Bruce Momjian's avatar
Bruce Momjian committed
33 34 35 36 37
#include "optimizer/internal.h"
#include "optimizer/keys.h"
#include "optimizer/ordering.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
38 39
#include "optimizer/pathnode.h"
#include "optimizer/xfunc.h"
40
#include "parser/parsetree.h"	/* for getrelid() */
Bruce Momjian's avatar
Bruce Momjian committed
41
#include "utils/lsyscache.h"
42 43 44


static void
Bruce Momjian's avatar
Bruce Momjian committed
45
match_index_orclauses(RelOptInfo *rel, RelOptInfo *index, int indexkey,
46
					  int xclass, List *clauseinfo_list);
47
static bool
48
match_index_to_operand(int indexkey, Expr *operand,
Bruce Momjian's avatar
Bruce Momjian committed
49
					   RelOptInfo *rel, RelOptInfo *index);
50
static List *
Bruce Momjian's avatar
Bruce Momjian committed
51
match_index_orclause(RelOptInfo *rel, RelOptInfo *index, int indexkey,
52
			 int xclass, List *or_clauses, List *other_matching_indices);
53
static List *
Bruce Momjian's avatar
Bruce Momjian committed
54
group_clauses_by_indexkey(RelOptInfo *rel, RelOptInfo *index,
55
					int *indexkeys, Oid *classes, List *clauseinfo_list);
56
static List *
Bruce Momjian's avatar
Bruce Momjian committed
57
group_clauses_by_ikey_for_joins(RelOptInfo *rel, RelOptInfo *index,
58
								int *indexkeys, Oid *classes, List *join_cinfo_list, List *restr_cinfo_list);
59
static CInfo *
Bruce Momjian's avatar
Bruce Momjian committed
60
match_clause_to_indexkey(RelOptInfo *rel, RelOptInfo *index, int indexkey,
61
						 int xclass, CInfo *clauseInfo, bool join);
62
static bool
63 64 65 66 67 68
pred_test(List *predicate_list, List *clauseinfo_list,
		  List *joininfo_list);
static bool one_pred_test(Expr *predicate, List *clauseinfo_list);
static bool one_pred_clause_expr_test(Expr *predicate, Node *clause);
static bool one_pred_clause_test(Expr *predicate, Node *clause);
static bool clause_pred_clause_test(Expr *predicate, Node *clause);
69
static List *
Bruce Momjian's avatar
Bruce Momjian committed
70
indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
71
					  List *joininfo_list, List *clauseinfo_list);
72
static List *
Bruce Momjian's avatar
Bruce Momjian committed
73 74
index_innerjoin(Query *root, RelOptInfo *rel,
				List *clausegroup_list, RelOptInfo *index);
75
static List *
Bruce Momjian's avatar
Bruce Momjian committed
76
create_index_paths(Query *root, RelOptInfo *rel, RelOptInfo *index,
77 78
				   List *clausegroup_list, bool join);
static List *add_index_paths(List *indexpaths, List *new_indexpaths);
Bruce Momjian's avatar
Bruce Momjian committed
79 80
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
static bool SingleAttributeIndex(RelOptInfo *index);
81 82 83 84

/* If Spyros can use a constant PRS2_BOOL_TYPEID, I can use this */
#define BOOL_TYPEID ((Oid) 16)

85
/*
86
 * find-index-paths--
87 88 89 90 91 92 93 94 95 96 97 98 99
 *	  Finds all possible index paths by determining which indices in the
 *	  list 'indices' are usable.
 *
 *	  To be usable, an index must match against either a set of
 *	  restriction clauses or join clauses.
 *
 *	  Note that the current implementation requires that there exist
 *	  matching clauses for every key in the index (i.e., no partial
 *	  matches are allowed).
 *
 *	  If an index can't be used with restriction clauses, but its keys
 *	  match those of the result sort order (according to information stored
 *	  within 'sortkeys'), then the index is also considered.
100 101 102 103 104 105
 *
 * 'rel' is the relation entry to which these index paths correspond
 * 'indices' is a list of possible index paths
 * 'clauseinfo-list' is a list of restriction clauseinfo nodes for 'rel'
 * 'joininfo-list' is a list of joininfo nodes for 'rel'
 * 'sortkeys' is a node describing the result sort order (from
106 107
 *		(find_sortkeys))
 *
108
 * Returns a list of index nodes.
109
 *
110
 */
111
List *
112
find_index_paths(Query *root,
Bruce Momjian's avatar
Bruce Momjian committed
113
				 RelOptInfo *rel,
114 115 116
				 List *indices,
				 List *clauseinfo_list,
				 List *joininfo_list)
117
{
118 119
	List	   *scanclausegroups = NIL;
	List	   *scanpaths = NIL;
Bruce Momjian's avatar
Bruce Momjian committed
120
	RelOptInfo		   *index = (RelOptInfo *) NULL;
121 122 123
	List	   *joinclausegroups = NIL;
	List	   *joinpaths = NIL;
	List	   *retval = NIL;
124 125 126 127

	if (indices == NIL)
		return (NULL);

Bruce Momjian's avatar
Bruce Momjian committed
128
	index = (RelOptInfo *) lfirst(indices);
129 130 131 132 133 134

	retval = find_index_paths(root,
							  rel,
							  lnext(indices),
							  clauseinfo_list,
							  joininfo_list);
135

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
	/* If this is a partial index, return if it fails the predicate test */
	if (index->indpred != NIL)
		if (!pred_test(index->indpred, clauseinfo_list, joininfo_list))
			return retval;

	/*
	 * 1. If this index has only one key, try matching it against
	 * subclauses of an 'or' clause.  The fields of the clauseinfo nodes
	 * are marked with lists of the matching indices no path are actually
	 * created.
	 *
	 * XXX NOTE:  Currently btrees dos not support indices with > 1 key, so
	 * the following test will always be true for now but we have decided
	 * not to support index-scans on disjunction . -- lp
	 */
	if (SingleAttributeIndex(index))
152
	{
153
		match_index_orclauses(rel,
154
							  index,
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
							  index->indexkeys[0],
							  index->classlist[0],
							  clauseinfo_list);
	}

	/*
	 * 2. If the keys of this index match any of the available restriction
	 * clauses, then create pathnodes corresponding to each group of
	 * usable clauses.
	 */
	scanclausegroups = group_clauses_by_indexkey(rel,
												 index,
												 index->indexkeys,
												 index->classlist,
												 clauseinfo_list);

	scanpaths = NIL;
	if (scanclausegroups != NIL)
		scanpaths = create_index_paths(root,
									   rel,
									   index,
									   scanclausegroups,
									   false);

	/*
	 * 3. If this index can be used with any join clause, then create
	 * pathnodes for each group of usable clauses.	An index can be used
	 * with a join clause if its ordering is useful for a mergejoin, or if
	 * the index can possibly be used for scanning the inner relation of a
	 * nestloop join.
	 */
	joinclausegroups = indexable_joinclauses(rel, index, joininfo_list, clauseinfo_list);
	joinpaths = NIL;
188

189 190
	if (joinclausegroups != NIL)
	{
191 192
		List	   *new_join_paths = create_index_paths(root, rel,
														index,
193
														joinclausegroups,
194 195
														true);
		List	   *innerjoin_paths = index_innerjoin(root, rel, joinclausegroups, index);
196 197 198

		rel->innerjoin = nconc(rel->innerjoin, innerjoin_paths);
		joinpaths = new_join_paths;
199
	}
200 201 202 203 204 205 206 207 208 209

	/*
	 * Some sanity checks to make sure that the indexpath is valid.
	 */
	if (joinpaths != NULL)
		retval = add_index_paths(joinpaths, retval);
	if (scanpaths != NULL)
		retval = add_index_paths(scanpaths, retval);

	return retval;
210 211 212 213 214

}


/****************************************************************************
215
 *		----  ROUTINES TO MATCH 'OR' CLAUSES  ----
216 217 218
 ****************************************************************************/


219
/*
220
 * match-index-orclauses--
221 222 223 224 225 226 227
 *	  Attempt to match an index against subclauses within 'or' clauses.
 *	  If the index does match, then the clause is marked with information
 *	  about the index.
 *
 *	  Essentially, this adds 'index' to the list of indices in the
 *	  ClauseInfo field of each of the clauses which it matches.
 *
228 229 230 231 232
 * 'rel' is the node of the relation on which the index is defined.
 * 'index' is the index node.
 * 'indexkey' is the (single) key of the index
 * 'class' is the class of the operator corresponding to 'indexkey'.
 * 'clauseinfo-list' is the list of available restriction clauses.
233
 *
234
 * Returns nothing.
235
 *
236 237
 */
static void
Bruce Momjian's avatar
Bruce Momjian committed
238 239
match_index_orclauses(RelOptInfo *rel,
					  RelOptInfo *index,
240 241
					  int indexkey,
					  int xclass,
242
					  List *clauseinfo_list)
243
{
244 245
	CInfo	   *clauseinfo = (CInfo *) NULL;
	List	   *i = NIL;
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263

	foreach(i, clauseinfo_list)
	{
		clauseinfo = (CInfo *) lfirst(i);
		if (valid_or_clause(clauseinfo))
		{

			/*
			 * Mark the 'or' clause with a list of indices which match
			 * each of its subclauses.	The list is generated by adding
			 * 'index' to the existing list where appropriate.
			 */
			clauseinfo->indexids =
				match_index_orclause(rel, index, indexkey,
									 xclass,
									 clauseinfo->clause->args,
									 clauseinfo->indexids);
		}
264 265 266 267 268
	}
}

/*
 * match_index_operand--
269 270 271
 *	  Generalize test for a match between an existing index's key
 *	  and the operand on the rhs of a restriction clause.  Now check
 *	  for functional indices as well.
272
 */
273
static bool
274
match_index_to_operand(int indexkey,
275
					   Expr *operand,
Bruce Momjian's avatar
Bruce Momjian committed
276 277
					   RelOptInfo *rel,
					   RelOptInfo *index)
278
{
279 280 281 282 283 284 285 286 287 288 289

	/*
	 * Normal index.
	 */
	if (index->indproc == InvalidOid)
		return match_indexkey_operand(indexkey, (Var *) operand, rel);

	/*
	 * functional index check
	 */
	return (function_index_operand(operand, rel, index));
290 291
}

292
/*
293
 * match-index-orclause--
294 295 296 297 298 299 300 301
 *	  Attempts to match an index against the subclauses of an 'or' clause.
 *
 *	  A match means that:
 *	  (1) the operator within the subclause can be used with one
 *				of the index's operator classes, and
 *	  (2) there is a usable key that matches the variable within a
 *				sargable clause.
 *
302 303
 * 'or-clauses' are the remaining subclauses within the 'or' clause
 * 'other-matching-indices' is the list of information on other indices
304 305 306 307
 *		that have already been matched to subclauses within this
 *		particular 'or' clause (i.e., a list previously generated by
 *		this routine)
 *
308 309 310 311 312
 * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where
 * a,b,c are nodes of indices that match the first subclause in
 * 'or-clauses', d,e,f match the second subclause, no indices
 * match the third, g,h match the fourth, etc.
 */
313
static List *
Bruce Momjian's avatar
Bruce Momjian committed
314 315
match_index_orclause(RelOptInfo *rel,
					 RelOptInfo *index,
316 317
					 int indexkey,
					 int xclass,
318 319
					 List *or_clauses,
					 List *other_matching_indices)
320
{
321 322 323 324 325
	Node	   *clause = NULL;
	List	   *matched_indices = other_matching_indices;
	List	   *index_list = NIL;
	List	   *clist;
	List	   *ind;
326 327 328 329 330 331 332

	if (!matched_indices)
		matched_indices = lcons(NIL, NIL);

	for (clist = or_clauses, ind = matched_indices;
		 clist;
		 clist = lnext(clist), ind = lnext(ind))
333
	{
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
		clause = lfirst(clist);
		if (is_opclause(clause) &&
			op_class(((Oper *) ((Expr *) clause)->oper)->opno,
					 xclass, index->relam) &&
			match_index_to_operand(indexkey,
								   (Expr *) get_leftop((Expr *) clause),
								   rel,
								   index) &&
			IsA(get_rightop((Expr *) clause), Const))
		{

			matched_indices = lcons(index, matched_indices);
			index_list = lappend(index_list,
								 matched_indices);
		}
349
	}
350 351
	return (index_list);

352 353 354
}

/****************************************************************************
355
 *				----  ROUTINES TO CHECK RESTRICTIONS  ----
356 357 358 359 360 361 362 363 364 365 366 367 368
 ****************************************************************************/


/*
 * DoneMatchingIndexKeys() - MACRO
 *
 * Determine whether we should continue matching index keys in a clause.
 * Depends on if there are more to match or if this is a functional index.
 * In the latter case we stop after the first match since the there can
 * be only key (i.e. the function's return value) and the attributes in
 * keys list represent the arguments to the function.  -mer 3 Oct. 1991
 */
#define DoneMatchingIndexKeys(indexkeys, index) \
369 370
		(indexkeys[0] == 0 || \
		 (index->indproc != InvalidOid))
371

372
/*
373
 * group-clauses-by-indexkey--
374 375 376
 *	  Determines whether there are clauses which will match each and every
 *	  one of the remaining keys of an index.
 *
377 378 379 380
 * 'rel' is the node of the relation corresponding to the index.
 * 'indexkeys' are the remaining index keys to be matched.
 * 'classes' are the classes of the index operators on those keys.
 * 'clauses' is either:
381 382 383 384 385 386 387
 *		(1) the list of available restriction clauses on a single
 *				relation, or
 *		(2) a list of join clauses between 'rel' and a fixed set of
 *				relations,
 *		depending on the value of 'join'.
 *
 *		NOTE: it works now for restriction clauses only. - vadim 03/18/97
388
 *
389 390
 * Returns all possible groups of clauses that will match (given that
 * one or more clauses can match any of the remaining keys).
391
 * E.g., if you have clauses A, B, and C, ((A B) (A C)) might be
392
 * returned for an index with 2 keys.
393
 *
394
 */
395
static List *
Bruce Momjian's avatar
Bruce Momjian committed
396 397
group_clauses_by_indexkey(RelOptInfo *rel,
						  RelOptInfo *index,
398
						  int *indexkeys,
399 400
						  Oid *classes,
						  List *clauseinfo_list)
401
{
402 403 404 405 406
	List	   *curCinfo = NIL;
	CInfo	   *matched_clause = (CInfo *) NULL;
	List	   *clausegroup = NIL;
	int			curIndxKey;
	Oid			curClass;
407

408
	if (clauseinfo_list == NIL || indexkeys[0] == 0)
409
		return NIL;
410

411
	do
412
	{
413
		List	   *tempgroup = NIL;
414 415 416 417 418 419

		curIndxKey = indexkeys[0];
		curClass = classes[0];

		foreach(curCinfo, clauseinfo_list)
		{
420
			CInfo	   *temp = (CInfo *) lfirst(curCinfo);
421 422 423 424 425 426 427 428 429

			matched_clause = match_clause_to_indexkey(rel,
													  index,
													  curIndxKey,
													  curClass,
													  temp,
													  false);
			if (!matched_clause)
				continue;
430

431 432 433 434
			tempgroup = lappend(tempgroup, matched_clause);
		}
		if (tempgroup == NIL)
			break;
435

436 437 438 439 440
		clausegroup = nconc(clausegroup, tempgroup);

		indexkeys++;
		classes++;

441
	} while (!DoneMatchingIndexKeys(indexkeys, index));
442

443 444 445 446 447
	/* clausegroup holds all matched clauses ordered by indexkeys */

	if (clausegroup != NIL)
		return (lcons(clausegroup, NIL));
	return NIL;
448 449
}

450
/*
451
 * group-clauses-by-ikey-for-joins--
452 453 454 455
 *	  special edition of group-clauses-by-indexkey - will
 *	  match join & restriction clauses. See comment in indexable_joinclauses.
 *		- vadim 03/18/97
 *
456
 */
457
static List *
Bruce Momjian's avatar
Bruce Momjian committed
458 459
group_clauses_by_ikey_for_joins(RelOptInfo *rel,
								RelOptInfo *index,
460
								int *indexkeys,
461 462 463
								Oid *classes,
								List *join_cinfo_list,
								List *restr_cinfo_list)
464
{
465 466 467 468 469 470
	List	   *curCinfo = NIL;
	CInfo	   *matched_clause = (CInfo *) NULL;
	List	   *clausegroup = NIL;
	int			curIndxKey;
	Oid			curClass;
	bool		jfound = false;
471

472
	if (join_cinfo_list == NIL || indexkeys[0] == 0)
473 474
		return NIL;

475
	do
476
	{
477
		List	   *tempgroup = NIL;
478 479 480 481 482 483

		curIndxKey = indexkeys[0];
		curClass = classes[0];

		foreach(curCinfo, join_cinfo_list)
		{
484
			CInfo	   *temp = (CInfo *) lfirst(curCinfo);
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499

			matched_clause = match_clause_to_indexkey(rel,
													  index,
													  curIndxKey,
													  curClass,
													  temp,
													  true);
			if (!matched_clause)
				continue;

			tempgroup = lappend(tempgroup, matched_clause);
			jfound = true;
		}
		foreach(curCinfo, restr_cinfo_list)
		{
500
			CInfo	   *temp = (CInfo *) lfirst(curCinfo);
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

			matched_clause = match_clause_to_indexkey(rel,
													  index,
													  curIndxKey,
													  curClass,
													  temp,
													  false);
			if (!matched_clause)
				continue;

			tempgroup = lappend(tempgroup, matched_clause);
		}
		if (tempgroup == NIL)
			break;

		clausegroup = nconc(clausegroup, tempgroup);

		indexkeys++;
		classes++;
520

521
	} while (!DoneMatchingIndexKeys(indexkeys, index));
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537

	/* clausegroup holds all matched clauses ordered by indexkeys */

	if (clausegroup != NIL)
	{

		/*
		 * if no one join clause was matched then there ain't clauses for
		 * joins at all.
		 */
		if (!jfound)
		{
			freeList(clausegroup);
			return NIL;
		}
		return (lcons(clausegroup, NIL));
538
	}
539
	return NIL;
540 541
}

542 543 544 545 546 547 548
/*
 * IndexScanableClause ()  MACRO
 *
 * Generalize condition on which we match a clause with an index.
 * Now we can match with functional indices.
 */
#define IndexScanableOperand(opnd, indkeys, rel, index) \
549 550 551
	((index->indproc == InvalidOid) ? \
		match_indexkey_operand(indkeys, opnd, rel) : \
		function_index_operand((Expr*)opnd,rel,index))
552

553 554
/*
 * There was
555
 *		equal_indexkey_var(indkeys,opnd) : \
556
 * above, and now
557
 *		match_indexkey_operand(indkeys, opnd, rel) : \
558 559 560
 * - vadim 01/22/97
 */

561
/*
562
 * match_clause_to-indexkey--
563 564 565 566 567 568 569 570 571 572 573 574
 *	  Finds the first of a relation's available restriction clauses that
 *	  matches a key of an index.
 *
 *	  To match, the clause must:
 *	  (1) be in the form (op var const) if the clause is a single-
 *				relation clause, and
 *	  (2) contain an operator which is in the same class as the index
 *				operator for this key.
 *
 *	  If the clause being matched is a join clause, then 'join' is t.
 *
 * Returns a single clauseinfo node corresponding to the matching
575 576 577
 * clause.
 *
 * NOTE:  returns nil if clause is an or_clause.
578
 *
579
 */
580
static CInfo *
Bruce Momjian's avatar
Bruce Momjian committed
581 582
match_clause_to_indexkey(RelOptInfo *rel,
						 RelOptInfo *index,
583 584
						 int indexkey,
						 int xclass,
585
						 CInfo *clauseInfo,
586
						 bool join)
587
{
588 589 590 591 592 593
	Expr	   *clause = clauseInfo->clause;
	Var		   *leftop,
			   *rightop;
	Oid			join_op = InvalidOid;
	Oid			restrict_op = InvalidOid;
	bool		isIndexable = false;
594 595 596 597 598 599 600 601

	if (or_clause((Node *) clause) ||
		not_clause((Node *) clause) || single_node((Node *) clause))
		return ((CInfo *) NULL);

	leftop = get_leftop(clause);
	rightop = get_rightop(clause);

602
	/*
603 604
	 * If this is not a join clause, check for clauses of the form:
	 * (operator var/func constant) and (operator constant var/func)
605
	 */
606
	if (!join)
607
	{
608

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
		/*
		 * Check for standard s-argable clause
		 */
		if ((rightop && IsA(rightop, Const)) ||
			(rightop && IsA(rightop, Param)))
		{
			restrict_op = ((Oper *) ((Expr *) clause)->oper)->opno;
			isIndexable =
				(op_class(restrict_op, xclass, index->relam) &&
				 IndexScanableOperand(leftop,
									  indexkey,
									  rel,
									  index));
		}

624
		/*
625
		 * Must try to commute the clause to standard s-arg format.
626
		 */
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
		else if ((leftop && IsA(leftop, Const)) ||
				 (leftop && IsA(leftop, Param)))
		{
			restrict_op =
				get_commutator(((Oper *) ((Expr *) clause)->oper)->opno);

			if ((restrict_op != InvalidOid) &&
				op_class(restrict_op, xclass, index->relam) &&
				IndexScanableOperand(rightop,
									 indexkey, rel, index))
			{
				isIndexable = true;

				/*
				 * In place list modification. (op const var/func) -> (op
				 * var/func const)
				 */
				CommuteClause((Node *) clause);
			}
		}
647
	}
648

649 650 651 652 653
	/*
	 * Check for an indexable scan on one of the join relations. clause is
	 * of the form (operator var/func var/func)
	 */
	else
654
	{
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
		if (rightop
		&& match_index_to_operand(indexkey, (Expr *) rightop, rel, index))
		{

			join_op = get_commutator(((Oper *) ((Expr *) clause)->oper)->opno);

		}
		else if (leftop
				 && match_index_to_operand(indexkey,
										   (Expr *) leftop, rel, index))
			join_op = ((Oper *) ((Expr *) clause)->oper)->opno;

		if (join_op && op_class(join_op, xclass, index->relam) &&
			join_clause_p((Node *) clause))
		{
			isIndexable = true;

			/*
			 * If we're using the operand's commutator we must commute the
			 * clause.
			 */
			if (join_op != ((Oper *) ((Expr *) clause)->oper)->opno)
				CommuteClause((Node *) clause);
		}
679 680
	}

681 682
	if (isIndexable)
		return (clauseInfo);
683

684
	return (NULL);
685 686 687
}

/****************************************************************************
688
 *				----  ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS	----
689 690
 ****************************************************************************/

691
/*
692
 * pred_test--
693
 *	  Does the "predicate inclusion test" for partial indexes.
694
 *
695 696
 *	  Recursively checks whether the clauses in clauseinfo_list imply
 *	  that the given predicate is true.
697
 *
698 699 700 701 702 703
 *	  This routine (together with the routines it calls) iterates over
 *	  ANDs in the predicate first, then reduces the qualification
 *	  clauses down to their constituent terms, and iterates over ORs
 *	  in the predicate last.  This order is important to make the test
 *	  succeed whenever possible (assuming the predicate has been
 *	  successfully cnfify()-ed). --Nels, Jan '93
704
 */
705
static bool
706
pred_test(List *predicate_list, List *clauseinfo_list, List *joininfo_list)
707
{
708 709 710
	List	   *pred,
			   *items,
			   *item;
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745

	/*
	 * Note: if Postgres tried to optimize queries by forming equivalence
	 * classes over equi-joined attributes (i.e., if it recognized that a
	 * qualification such as "where a.b=c.d and a.b=5" could make use of
	 * an index on c.d), then we could use that equivalence class info
	 * here with joininfo_list to do more complete tests for the usability
	 * of a partial index.	For now, the test only uses restriction
	 * clauses (those in clauseinfo_list). --Nels, Dec '92
	 */

	if (predicate_list == NULL)
		return true;			/* no predicate: the index is usable */
	if (clauseinfo_list == NULL)
		return false;			/* no restriction clauses: the test must
								 * fail */

	foreach(pred, predicate_list)
	{

		/*
		 * if any clause is not implied, the whole predicate is not
		 * implied
		 */
		if (and_clause(lfirst(pred)))
		{
			items = ((Expr *) lfirst(pred))->args;
			foreach(item, items)
			{
				if (!one_pred_test(lfirst(item), clauseinfo_list))
					return false;
			}
		}
		else if (!one_pred_test(lfirst(pred), clauseinfo_list))
			return false;
746
	}
747
	return true;
748 749 750
}


751
/*
752
 * one_pred_test--
753 754
 *	  Does the "predicate inclusion test" for one conjunct of a predicate
 *	  expression.
755
 */
756
static bool
757
one_pred_test(Expr *predicate, List *clauseinfo_list)
758
{
759 760
	CInfo	   *clauseinfo;
	List	   *item;
761 762 763 764 765 766 767 768 769 770

	Assert(predicate != NULL);
	foreach(item, clauseinfo_list)
	{
		clauseinfo = (CInfo *) lfirst(item);
		/* if any clause implies the predicate, return true */
		if (one_pred_clause_expr_test(predicate, (Node *) clauseinfo->clause))
			return true;
	}
	return false;
771 772 773
}


774
/*
775
 * one_pred_clause_expr_test--
776 777
 *	  Does the "predicate inclusion test" for a general restriction-clause
 *	  expression.
778
 */
779
static bool
780
one_pred_clause_expr_test(Expr *predicate, Node *clause)
781
{
782 783
	List	   *items,
			   *item;
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810

	if (is_opclause(clause))
		return one_pred_clause_test(predicate, clause);
	else if (or_clause(clause))
	{
		items = ((Expr *) clause)->args;
		foreach(item, items)
		{
			/* if any OR item doesn't imply the predicate, clause doesn't */
			if (!one_pred_clause_expr_test(predicate, lfirst(item)))
				return false;
		}
		return true;
	}
	else if (and_clause(clause))
	{
		items = ((Expr *) clause)->args;
		foreach(item, items)
		{

			/*
			 * if any AND item implies the predicate, the whole clause
			 * does
			 */
			if (one_pred_clause_expr_test(predicate, lfirst(item)))
				return true;
		}
811 812
		return false;
	}
813 814 815 816
	else
	{
		/* unknown clause type never implies the predicate */
		return false;
817 818 819 820
	}
}


821
/*
822
 * one_pred_clause_test--
823 824
 *	  Does the "predicate inclusion test" for one conjunct of a predicate
 *	  expression for a simple restriction clause.
825
 */
826
static bool
827
one_pred_clause_test(Expr *predicate, Node *clause)
828
{
829 830
	List	   *items,
			   *item;
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857

	if (is_opclause((Node *) predicate))
		return clause_pred_clause_test(predicate, clause);
	else if (or_clause((Node *) predicate))
	{
		items = predicate->args;
		foreach(item, items)
		{
			/* if any item is implied, the whole predicate is implied */
			if (one_pred_clause_test(lfirst(item), clause))
				return true;
		}
		return false;
	}
	else if (and_clause((Node *) predicate))
	{
		items = predicate->args;
		foreach(item, items)
		{

			/*
			 * if any item is not implied, the whole predicate is not
			 * implied
			 */
			if (!one_pred_clause_test(lfirst(item), clause))
				return false;
		}
858 859
		return true;
	}
860 861 862
	else
	{
		elog(DEBUG, "Unsupported predicate type, index will not be used");
863 864 865 866 867 868 869
		return false;
	}
}


/*
 * Define an "operator implication table" for btree operators ("strategies").
870
 * The "strategy numbers" are:	(1) <	(2) <=	 (3) =	 (4) >=   (5) >
871 872 873
 *
 * The interpretation of:
 *
874
 *		test_op = BT_implic_table[given_op-1][target_op-1]
875 876 877 878
 *
 * where test_op, given_op and target_op are strategy numbers (from 1 to 5)
 * of btree operators, is as follows:
 *
879 880 881 882 883
 *	 If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you
 *	 want to determine whether "ATTR target_op CONST2" must also be true, then
 *	 you can use "CONST1 test_op CONST2" as a test.  If this test returns true,
 *	 then the target expression must be true; if the test returns false, then
 *	 the target expression may be false.
884 885 886 887 888
 *
 * An entry where test_op==0 means the implication cannot be determined, i.e.,
 * this test should always be considered false.
 */

889
StrategyNumber BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
890 891 892 893 894
	{2, 2, 0, 0, 0},
	{1, 2, 0, 0, 0},
	{1, 2, 3, 4, 5},
	{0, 0, 0, 4, 5},
	{0, 0, 0, 4, 4}
895 896 897
};


898
/*
899
 * clause_pred_clause_test--
900 901 902 903 904 905 906
 *	  Use operator class info to check whether clause implies predicate.
 *
 *	  Does the "predicate inclusion test" for a "simple clause" predicate
 *	  for a single "simple clause" restriction.  Currently, this only handles
 *	  (binary boolean) operators that are in some btree operator class.
 *	  Eventually, rtree operators could also be handled by defining an
 *	  appropriate "RT_implic_table" array.
907
 */
908
static bool
909
clause_pred_clause_test(Expr *predicate, Node *clause)
910
{
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
	Var		   *pred_var,
			   *clause_var;
	Const	   *pred_const,
			   *clause_const;
	Oid			pred_op,
				clause_op,
				test_op;
	Oid			opclass_id;
	StrategyNumber pred_strategy,
				clause_strategy,
				test_strategy;
	Oper	   *test_oper;
	Expr	   *test_expr;
	bool		test_result,
				isNull;
	Relation	relation;
	HeapScanDesc scan;
	HeapTuple	tuple;
	ScanKeyData entry[3];
	Form_pg_amop form;
931 932 933 934 935 936 937 938 939 940 941 942 943 944

	pred_var = (Var *) get_leftop(predicate);
	pred_const = (Const *) get_rightop(predicate);
	clause_var = (Var *) get_leftop((Expr *) clause);
	clause_const = (Const *) get_rightop((Expr *) clause);

	/* Check the basic form; for now, only allow the simplest case */
	if (!is_opclause(clause) ||
		!IsA(clause_var, Var) ||
		!IsA(clause_const, Const) ||
		!IsA(predicate->oper, Oper) ||
		!IsA(pred_var, Var) ||
		!IsA(pred_const, Const))
		return false;
945

946 947 948 949 950 951
	/*
	 * The implication can't be determined unless the predicate and the
	 * clause refer to the same attribute.
	 */
	if (clause_var->varattno != pred_var->varattno)
		return false;
952

953 954 955
	/* Get the operators for the two clauses we're comparing */
	pred_op = ((Oper *) ((Expr *) predicate)->oper)->opno;
	clause_op = ((Oper *) ((Expr *) clause)->oper)->opno;
956 957


958 959 960 961 962 963
	/*
	 * 1. Find a "btree" strategy number for the pred_op
	 */
	/* XXX - hardcoded amopid value 403 to find "btree" operator classes */
	ScanKeyEntryInitialize(&entry[0], 0,
						   Anum_pg_amop_amopid,
Bruce Momjian's avatar
Bruce Momjian committed
964
						   F_OIDEQ,
965
						   ObjectIdGetDatum(403));
966

967 968
	ScanKeyEntryInitialize(&entry[1], 0,
						   Anum_pg_amop_amopopr,
Bruce Momjian's avatar
Bruce Momjian committed
969
						   F_OIDEQ,
970
						   ObjectIdGetDatum(pred_op));
971

972
	relation = heap_openr(AccessMethodOperatorRelationName);
973

974 975 976 977 978 979 980 981
	/*
	 * The following assumes that any given operator will only be in a
	 * single btree operator class.  This is true at least for all the
	 * pre-defined operator classes.  If it isn't true, then whichever
	 * operator class happens to be returned first for the given operator
	 * will be used to find the associated strategy numbers for the test.
	 * --Nels, Jan '93
	 */
982
	scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
983 984 985 986 987 988 989
	tuple = heap_getnext(scan, false, (Buffer *) NULL);
	if (!HeapTupleIsValid(tuple))
	{
		elog(DEBUG, "clause_pred_clause_test: unknown pred_op");
		return false;
	}
	form = (Form_pg_amop) GETSTRUCT(tuple);
990

991 992
	/* Get the predicate operator's strategy number (1 to 5) */
	pred_strategy = (StrategyNumber) form->amopstrategy;
993

994 995
	/* Remember which operator class this strategy number came from */
	opclass_id = form->amopclaid;
996

997
	heap_endscan(scan);
998 999


1000 1001 1002 1003 1004
	/*
	 * 2. From the same opclass, find a strategy num for the clause_op
	 */
	ScanKeyEntryInitialize(&entry[1], 0,
						   Anum_pg_amop_amopclaid,
Bruce Momjian's avatar
Bruce Momjian committed
1005
						   F_OIDEQ,
1006 1007 1008 1009
						   ObjectIdGetDatum(opclass_id));

	ScanKeyEntryInitialize(&entry[2], 0,
						   Anum_pg_amop_amopopr,
Bruce Momjian's avatar
Bruce Momjian committed
1010
						   F_OIDEQ,
1011 1012
						   ObjectIdGetDatum(clause_op));

1013
	scan = heap_beginscan(relation, false, SnapshotNow, 3, entry);
1014 1015 1016 1017 1018 1019 1020
	tuple = heap_getnext(scan, false, (Buffer *) NULL);
	if (!HeapTupleIsValid(tuple))
	{
		elog(DEBUG, "clause_pred_clause_test: unknown clause_op");
		return false;
	}
	form = (Form_pg_amop) GETSTRUCT(tuple);
1021

1022 1023 1024
	/* Get the restriction clause operator's strategy number (1 to 5) */
	clause_strategy = (StrategyNumber) form->amopstrategy;
	heap_endscan(scan);
1025 1026


1027 1028 1029
	/*
	 * 3. Look up the "test" strategy number in the implication table
	 */
1030

1031 1032 1033
	test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
	if (test_strategy == 0)
		return false;			/* the implication cannot be determined */
1034 1035


1036 1037 1038
	/*
	 * 4. From the same opclass, find the operator for the test strategy
	 */
1039

1040 1041
	ScanKeyEntryInitialize(&entry[2], 0,
						   Anum_pg_amop_amopstrategy,
Bruce Momjian's avatar
Bruce Momjian committed
1042
						   F_INT2EQ,
1043
						   Int16GetDatum(test_strategy));
1044

1045
	scan = heap_beginscan(relation, false, SnapshotNow, 3, entry);
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
	tuple = heap_getnext(scan, false, (Buffer *) NULL);
	if (!HeapTupleIsValid(tuple))
	{
		elog(DEBUG, "clause_pred_clause_test: unknown test_op");
		return false;
	}
	form = (Form_pg_amop) GETSTRUCT(tuple);

	/* Get the test operator */
	test_op = form->amopopr;
	heap_endscan(scan);
1057

1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071

	/*
	 * 5. Evaluate the test
	 */
	test_oper = makeOper(test_op,		/* opno */
						 InvalidOid,	/* opid */
						 BOOL_TYPEID,	/* opresulttype */
						 0,		/* opsize */
						 NULL); /* op_fcache */
	replace_opid(test_oper);

	test_expr = make_opclause(test_oper,
							  copyObject(clause_const),
							  copyObject(pred_const));
1072 1073

#ifndef OMIT_PARTIAL_INDEX
1074 1075 1076 1077 1078 1079 1080 1081
	test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL);
#endif							/* OMIT_PARTIAL_INDEX */
	if (isNull)
	{
		elog(DEBUG, "clause_pred_clause_test: null test result");
		return false;
	}
	return test_result;
1082 1083 1084 1085
}


/****************************************************************************
1086
 *				----  ROUTINES TO CHECK JOIN CLAUSES  ----
1087 1088
 ****************************************************************************/

1089
/*
1090
 * indexable-joinclauses--
1091 1092 1093 1094 1095 1096
 *	  Finds all groups of join clauses from among 'joininfo-list' that can
 *	  be used in conjunction with 'index'.
 *
 *	  The first clause in the group is marked as having the other relation
 *	  in the join clause as its outer join relation.
 *
1097
 * Returns a list of these clause groups.
1098
 *
1099 1100 1101 1102
 *	  Added: clauseinfo_list - list of restriction CInfos. It's to
 *		support multi-column indices in joins and for cases
 *		when a key is in both join & restriction clauses. - vadim 03/18/97
 *
1103
 */
1104
static List *
Bruce Momjian's avatar
Bruce Momjian committed
1105
indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
1106
					  List *joininfo_list, List *clauseinfo_list)
1107
{
1108 1109 1110 1111
	JInfo	   *joininfo = (JInfo *) NULL;
	List	   *cg_list = NIL;
	List	   *i = NIL;
	List	   *clausegroups = NIL;
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128

	foreach(i, joininfo_list)
	{
		joininfo = (JInfo *) lfirst(i);

		if (joininfo->jinfoclauseinfo == NIL)
			continue;
		clausegroups =
			group_clauses_by_ikey_for_joins(rel,
											index,
											index->indexkeys,
											index->classlist,
											joininfo->jinfoclauseinfo,
											clauseinfo_list);

		if (clausegroups != NIL)
		{
1129
			List	   *clauses = lfirst(clausegroups);
1130 1131 1132 1133 1134

			((CInfo *) lfirst(clauses))->cinfojoinid =
				joininfo->otherrels;
		}
		cg_list = nconc(cg_list, clausegroups);
1135
	}
1136
	return (cg_list);
1137 1138 1139
}

/****************************************************************************
1140
 *				----  PATH CREATION UTILITIES  ----
1141 1142 1143 1144
 ****************************************************************************/

/*
 * extract_restrict_clauses -
1145 1146
 *	  the list of clause info contains join clauses and restriction clauses.
 *	  This routine returns the restriction clauses only.
1147
 */
Bruce Momjian's avatar
Bruce Momjian committed
1148
#ifdef NOT_USED
1149
static List *
1150
extract_restrict_clauses(List *clausegroup)
1151
{
1152 1153
	List	   *restrict_cls = NIL;
	List	   *l;
1154 1155 1156

	foreach(l, clausegroup)
	{
1157
		CInfo	   *cinfo = lfirst(l);
1158 1159 1160

		if (!join_clause_p((Node *) cinfo->clause))
			restrict_cls = lappend(restrict_cls, cinfo);
1161
	}
1162
	return restrict_cls;
1163
}
1164

Bruce Momjian's avatar
Bruce Momjian committed
1165
#endif
1166

1167
/*
1168
 * index-innerjoin--
1169 1170
 *	  Creates index path nodes corresponding to paths to be used as inner
 *	  relations in nestloop joins.
1171 1172 1173
 *
 * 'clausegroup-list' is a list of list of clauseinfo nodes which can use
 * 'index' on their inner relation.
1174
 *
1175
 * Returns a list of index pathnodes.
1176
 *
1177
 */
1178
static List *
Bruce Momjian's avatar
Bruce Momjian committed
1179
index_innerjoin(Query *root, RelOptInfo *rel, List *clausegroup_list, RelOptInfo *index)
1180
{
1181 1182 1183 1184 1185 1186
	List	   *clausegroup = NIL;
	List	   *cg_list = NIL;
	List	   *i = NIL;
	IndexPath  *pathnode = (IndexPath *) NULL;
	Cost		temp_selec;
	float		temp_pages;
1187 1188 1189

	foreach(i, clausegroup_list)
	{
1190 1191 1192
		List	   *attnos,
				   *values,
				   *flags;
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242

		clausegroup = lfirst(i);
		pathnode = makeNode(IndexPath);

		get_joinvars(lfirsti(rel->relids), clausegroup,
					 &attnos, &values, &flags);
		index_selectivity(lfirsti(index->relids),
						  index->classlist,
						  get_opnos(clausegroup),
						  getrelid(lfirsti(rel->relids),
								   root->rtable),
						  attnos,
						  values,
						  flags,
						  length(clausegroup),
						  &temp_pages,
						  &temp_selec);
		pathnode->path.pathtype = T_IndexScan;
		pathnode->path.parent = rel;
		pathnode->indexid = index->relids;
		pathnode->indexkeys = index->indexkeys;
		pathnode->indexqual = clausegroup;

		pathnode->path.joinid = ((CInfo *) lfirst(clausegroup))->cinfojoinid;

		pathnode->path.path_cost =
			cost_index((Oid) lfirsti(index->relids),
					   (int) temp_pages,
					   temp_selec,
					   rel->pages,
					   rel->tuples,
					   index->pages,
					   index->tuples,
					   true);

		/*
		 * copy clauseinfo list into path for expensive function
		 * processing -- JMH, 7/7/92
		 */
		pathnode->path.locclauseinfo =
			set_difference(copyObject((Node *) rel->clauseinfo),
						   clausegroup);

#if 0							/* fix xfunc */
		/* add in cost for expensive functions!  -- JMH, 7/7/92 */
		if (XfuncMode != XFUNC_OFF)
		{
			((Path *) pathnode)->path_cost +=
				xfunc_get_path_cost((Path *) pathnode);
		}
1243
#endif
1244 1245 1246
		cg_list = lappend(cg_list, pathnode);
	}
	return (cg_list);
1247 1248
}

1249
/*
1250
 * create-index-paths--
1251 1252 1253
 *	  Creates a list of index path nodes for each group of clauses
 *	  (restriction or join) that can be used in conjunction with an index.
 *
1254
 * 'rel' is the relation for which 'index' is defined
1255 1256
 * 'clausegroup-list' is the list of clause groups (lists of clauseinfo
 *				nodes) grouped by mergesortorder
1257
 * 'join' is a flag indicating whether or not the clauses are join
1258 1259
 *				clauses
 *
1260
 * Returns a list of new index path nodes.
1261
 *
1262
 */
1263
static List *
1264
create_index_paths(Query *root,
Bruce Momjian's avatar
Bruce Momjian committed
1265 1266
				   RelOptInfo *rel,
				   RelOptInfo *index,
1267
				   List *clausegroup_list,
1268
				   bool join)
1269
{
1270 1271 1272 1273 1274
	List	   *clausegroup = NIL;
	List	   *ip_list = NIL;
	List	   *i = NIL;
	List	   *j = NIL;
	IndexPath  *temp_path;
1275

1276 1277
	foreach(i, clausegroup_list)
	{
1278 1279 1280
		CInfo	   *clauseinfo;
		List	   *temp_node = NIL;
		bool		temp = true;
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301

		clausegroup = lfirst(i);

		foreach(j, clausegroup)
		{
			clauseinfo = (CInfo *) lfirst(j);
			if (!(join_clause_p((Node *) clauseinfo->clause) &&
				  equal_path_merge_ordering(index->ordering,
											clauseinfo->mergesortorder)))
				temp = false;
		}

		if (!join || temp)
		{						/* restriction, ordering scan */
			temp_path = create_index_path(root, rel, index, clausegroup, join);
			temp_node =
				lcons(temp_path, NIL);
			ip_list = nconc(ip_list, temp_node);
		}
	}
	return (ip_list);
1302 1303
}

1304
static List *
1305
add_index_paths(List *indexpaths, List *new_indexpaths)
1306
{
1307
	return append(indexpaths, new_indexpaths);
1308 1309
}

1310
static bool
Bruce Momjian's avatar
Bruce Momjian committed
1311
function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
1312
{
1313 1314 1315 1316 1317 1318
	Oid			heapRelid = (Oid) lfirsti(rel->relids);
	Func	   *function;
	List	   *funcargs;
	int		   *indexKeys = index->indexkeys;
	List	   *arg;
	int			i;
1319

1320 1321 1322 1323 1324 1325 1326
	/*
	 * sanity check, make sure we know what we're dealing with here.
	 */
	if (funcOpnd == NULL ||
		nodeTag(funcOpnd) != T_Expr || funcOpnd->opType != FUNC_EXPR ||
		funcOpnd->oper == NULL || indexKeys == NULL)
		return false;
1327

1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359
	function = (Func *) funcOpnd->oper;
	funcargs = funcOpnd->args;

	if (function->funcid != index->indproc)
		return false;

	/*
	 * Check that the arguments correspond to the same arguments used to
	 * create the functional index.  To do this we must check that 1.
	 * refer to the right relatiion. 2. the args have the right attr.
	 * numbers in the right order.
	 *
	 *
	 * Check all args refer to the correct relation (i.e. the one with the
	 * functional index defined on it (rel).  To do this we can simply
	 * compare range table entry numbers, they must be the same.
	 */
	foreach(arg, funcargs)
	{
		if (heapRelid != ((Var *) lfirst(arg))->varno)
			return false;
	}

	/*
	 * check attr numbers and order.
	 */
	i = 0;
	foreach(arg, funcargs)
	{

		if (indexKeys[i] == 0)
			return (false);
1360

1361 1362 1363 1364 1365 1366 1367
		if (((Var *) lfirst(arg))->varattno != indexKeys[i])
			return (false);

		i++;
	}

	return true;
1368 1369
}

1370
static bool
Bruce Momjian's avatar
Bruce Momjian committed
1371
SingleAttributeIndex(RelOptInfo *index)
1372
{
1373 1374 1375 1376 1377 1378

	/*
	 * return false for now as I don't know if we support index scans on
	 * disjunction and the code doesn't work
	 */
	return (false);
1379 1380

#if 0
1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392

	/*
	 * Non-functional indices.
	 */
	if (index->indproc == InvalidOid)
		return (index->indexkeys[0] != 0 &&
				index->indexkeys[1] == 0);

	/*
	 * We have a functional index which is a single attr index
	 */
	return true;
1393 1394
#endif
}