/*------------------------------------------------------------------------- * * istrat.c-- * index scan strategy manipulation code and index strategy manipulation * operator code. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.18 1998/04/06 17:27:06 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include /* could have been access/itup.h */ #include #include #include #ifdef USE_ASSERT_CHECKING static bool StrategyEvaluationIsValid(StrategyEvaluation evaluation); static bool StrategyExpressionIsValid(StrategyExpression expression, StrategyNumber maxStrategy); static ScanKey StrategyMapGetScanKeyEntry(StrategyMap map, StrategyNumber strategyNumber); static bool StrategyOperatorIsValid(StrategyOperator operator, StrategyNumber maxStrategy); static bool StrategyTermIsValid(StrategyTerm term, StrategyNumber maxStrategy); #endif /* ---------------------------------------------------------------- * misc strategy support routines * ---------------------------------------------------------------- */ /* * StrategyNumberIsValid * StrategyNumberIsInBounds * StrategyMapIsValid * StrategyTransformMapIsValid * IndexStrategyIsValid * * ... are now macros in istrat.h -cim 4/27/91 */ /* * StrategyMapGetScanKeyEntry -- * Returns a scan key entry of a index strategy mapping member. * * Note: * Assumes that the index strategy mapping is valid. * Assumes that the index strategy number is valid. * Bounds checking should be done outside this routine. */ static ScanKey StrategyMapGetScanKeyEntry(StrategyMap map, StrategyNumber strategyNumber) { Assert(StrategyMapIsValid(map)); Assert(StrategyNumberIsValid(strategyNumber)); return (&map->entry[strategyNumber - 1]); } /* * IndexStrategyGetStrategyMap -- * Returns an index strategy mapping of an index strategy. * * Note: * Assumes that the index strategy is valid. * Assumes that the number of index strategies is valid. * Bounds checking should be done outside this routine. */ StrategyMap IndexStrategyGetStrategyMap(IndexStrategy indexStrategy, StrategyNumber maxStrategyNum, AttrNumber attrNum) { Assert(IndexStrategyIsValid(indexStrategy)); Assert(StrategyNumberIsValid(maxStrategyNum)); Assert(AttributeNumberIsValid(attrNum)); maxStrategyNum = AMStrategies(maxStrategyNum); /* XXX */ return &indexStrategy->strategyMapData[maxStrategyNum * (attrNum - 1)]; } /* * AttributeNumberGetIndexStrategySize -- * Computes the size of an index strategy. */ Size AttributeNumberGetIndexStrategySize(AttrNumber maxAttributeNumber, StrategyNumber maxStrategyNumber) { maxStrategyNumber = AMStrategies(maxStrategyNumber); /* XXX */ return maxAttributeNumber * maxStrategyNumber * sizeof(ScanKeyData); } #ifdef USE_ASSERT_CHECKING /* * StrategyTransformMapIsValid is now a macro in istrat.h -cim 4/27/91 */ /* ---------------- * StrategyOperatorIsValid * ---------------- */ static bool StrategyOperatorIsValid(StrategyOperator operator, StrategyNumber maxStrategy) { return (bool) (PointerIsValid(operator) && StrategyNumberIsInBounds(operator->strategy, maxStrategy) && !(operator->flags & ~(SK_NEGATE | SK_COMMUTE))); } /* ---------------- * StrategyTermIsValid * ---------------- */ static bool StrategyTermIsValid(StrategyTerm term, StrategyNumber maxStrategy) { Index index; if (!PointerIsValid(term) || term->degree == 0) return false; for (index = 0; index < term->degree; index += 1) { if (!StrategyOperatorIsValid(&term->operatorData[index], maxStrategy)) { return false; } } return true; } /* ---------------- * StrategyExpressionIsValid * ---------------- */ static bool StrategyExpressionIsValid(StrategyExpression expression, StrategyNumber maxStrategy) { StrategyTerm *termP; if (!PointerIsValid(expression)) return true; if (!StrategyTermIsValid(expression->term[0], maxStrategy)) return false; termP = &expression->term[1]; while (StrategyTermIsValid(*termP, maxStrategy)) termP += 1; return (bool) (!PointerIsValid(*termP)); } /* ---------------- * StrategyEvaluationIsValid * ---------------- */ static bool StrategyEvaluationIsValid(StrategyEvaluation evaluation) { Index index; if (!PointerIsValid(evaluation) || !StrategyNumberIsValid(evaluation->maxStrategy) || !StrategyTransformMapIsValid(evaluation->negateTransform) || !StrategyTransformMapIsValid(evaluation->commuteTransform) || !StrategyTransformMapIsValid(evaluation->negateCommuteTransform)) { return false; } for (index = 0; index < evaluation->maxStrategy; index += 1) { if (!StrategyExpressionIsValid(evaluation->expression[index], evaluation->maxStrategy)) { return false; } } return true; } #endif /* ---------------- * StrategyTermEvaluate * ---------------- */ static bool StrategyTermEvaluate(StrategyTerm term, StrategyMap map, Datum left, Datum right) { Index index; long tmpres = 0; bool result = 0; StrategyOperator operator; ScanKey entry; for (index = 0, operator = &term->operatorData[0]; index < term->degree; index += 1, operator += 1) { entry = &map->entry[operator->strategy - 1]; Assert(RegProcedureIsValid(entry->sk_procedure)); switch (operator->flags ^ entry->sk_flags) { case 0x0: tmpres = (long) FMGR_PTR2(&entry->sk_func, left, right); break; case SK_NEGATE: tmpres = (long) !FMGR_PTR2(&entry->sk_func, left, right); break; case SK_COMMUTE: tmpres = (long) FMGR_PTR2(&entry->sk_func, right, left); break; case SK_NEGATE | SK_COMMUTE: tmpres = (long) !FMGR_PTR2(&entry->sk_func, right, left); break; default: elog(FATAL, "StrategyTermEvaluate: impossible case %d", operator->flags ^ entry->sk_flags); } result = (bool) tmpres; if (!result) return result; } return result; } /* ---------------- * RelationGetStrategy * ---------------- */ StrategyNumber RelationGetStrategy(Relation relation, AttrNumber attributeNumber, StrategyEvaluation evaluation, RegProcedure procedure) { StrategyNumber strategy; StrategyMap strategyMap; ScanKey entry; Index index; int numattrs; Assert(RelationIsValid(relation)); numattrs = RelationGetNumberOfAttributes(relation); Assert(relation->rd_rel->relkind == RELKIND_INDEX); /* XXX use accessor */ Assert(AttributeNumberIsValid(attributeNumber)); Assert((attributeNumber >= 1) && (attributeNumber < 1 + numattrs)); Assert(StrategyEvaluationIsValid(evaluation)); Assert(RegProcedureIsValid(procedure)); strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), evaluation->maxStrategy, attributeNumber); /* get a strategy number for the procedure ignoring flags for now */ for (index = 0; index < evaluation->maxStrategy; index += 1) { if (strategyMap->entry[index].sk_procedure == procedure) { break; } } if (index == evaluation->maxStrategy) return InvalidStrategy; strategy = 1 + index; entry = StrategyMapGetScanKeyEntry(strategyMap, strategy); Assert(!(entry->sk_flags & ~(SK_NEGATE | SK_COMMUTE))); switch (entry->sk_flags & (SK_NEGATE | SK_COMMUTE)) { case 0x0: return strategy; case SK_NEGATE: strategy = evaluation->negateTransform->strategy[strategy - 1]; break; case SK_COMMUTE: strategy = evaluation->commuteTransform->strategy[strategy - 1]; break; case SK_NEGATE | SK_COMMUTE: strategy = evaluation->negateCommuteTransform->strategy[strategy - 1]; break; default: elog(FATAL, "RelationGetStrategy: impossible case %d", entry->sk_flags); } if (!StrategyNumberIsInBounds(strategy, evaluation->maxStrategy)) { if (!StrategyNumberIsValid(strategy)) { elog(ERROR, "RelationGetStrategy: corrupted evaluation"); } } return strategy; } /* ---------------- * RelationInvokeStrategy * ---------------- */ bool /* XXX someday, this may return Datum */ RelationInvokeStrategy(Relation relation, StrategyEvaluation evaluation, AttrNumber attributeNumber, StrategyNumber strategy, Datum left, Datum right) { StrategyNumber newStrategy; StrategyMap strategyMap; ScanKey entry; StrategyTermData termData; int numattrs; Assert(RelationIsValid(relation)); Assert(relation->rd_rel->relkind == RELKIND_INDEX); /* XXX use accessor */ numattrs = RelationGetNumberOfAttributes(relation); Assert(StrategyEvaluationIsValid(evaluation)); Assert(AttributeNumberIsValid(attributeNumber)); Assert((attributeNumber >= 1) && (attributeNumber < 1 + numattrs)); Assert(StrategyNumberIsInBounds(strategy, evaluation->maxStrategy)); termData.degree = 1; strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), evaluation->maxStrategy, attributeNumber); entry = StrategyMapGetScanKeyEntry(strategyMap, strategy); if (RegProcedureIsValid(entry->sk_procedure)) { termData.operatorData[0].strategy = strategy; termData.operatorData[0].flags = 0x0; return StrategyTermEvaluate(&termData, strategyMap, left, right); } newStrategy = evaluation->negateTransform->strategy[strategy - 1]; if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) { entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); if (RegProcedureIsValid(entry->sk_procedure)) { termData.operatorData[0].strategy = newStrategy; termData.operatorData[0].flags = SK_NEGATE; return StrategyTermEvaluate(&termData, strategyMap, left, right); } } newStrategy = evaluation->commuteTransform->strategy[strategy - 1]; if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) { entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); if (RegProcedureIsValid(entry->sk_procedure)) { termData.operatorData[0].strategy = newStrategy; termData.operatorData[0].flags = SK_COMMUTE; return StrategyTermEvaluate(&termData, strategyMap, left, right); } } newStrategy = evaluation->negateCommuteTransform->strategy[strategy - 1]; if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) { entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy); if (RegProcedureIsValid(entry->sk_procedure)) { termData.operatorData[0].strategy = newStrategy; termData.operatorData[0].flags = SK_NEGATE | SK_COMMUTE; return StrategyTermEvaluate(&termData, strategyMap, left, right); } } if (PointerIsValid(evaluation->expression[strategy - 1])) { StrategyTerm *termP; termP = &evaluation->expression[strategy - 1]->term[0]; while (PointerIsValid(*termP)) { Index index; for (index = 0; index < (*termP)->degree; index += 1) { entry = StrategyMapGetScanKeyEntry(strategyMap, (*termP)->operatorData[index].strategy); if (!RegProcedureIsValid(entry->sk_procedure)) { break; } } if (index == (*termP)->degree) { return StrategyTermEvaluate(*termP, strategyMap, left, right); } termP += 1; } } elog(ERROR, "RelationInvokeStrategy: cannot evaluate strategy %d", strategy); /* not reached, just to make compiler happy */ return FALSE; } /* ---------------- * OperatorRelationFillScanKeyEntry * ---------------- */ static void OperatorRelationFillScanKeyEntry(Relation operatorRelation, Oid operatorObjectId, ScanKey entry) { HeapScanDesc scan; ScanKeyData scanKeyData; HeapTuple tuple; ScanKeyEntryInitialize(&scanKeyData, 0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure, ObjectIdGetDatum(operatorObjectId)); scan = heap_beginscan(operatorRelation, false, false, 1, &scanKeyData); tuple = heap_getnext(scan, false, (Buffer *) NULL); if (!HeapTupleIsValid(tuple)) { elog(ERROR, "OperatorObjectIdFillScanKeyEntry: unknown operator %lu", (uint32) operatorObjectId); } entry->sk_flags = 0; entry->sk_procedure = ((OperatorTupleForm) GETSTRUCT(tuple))->oprcode; fmgr_info(entry->sk_procedure, &entry->sk_func); entry->sk_nargs = entry->sk_func.fn_nargs; if (!RegProcedureIsValid(entry->sk_procedure)) { elog(ERROR, "OperatorObjectIdFillScanKeyEntry: no procedure for operator %lu", (uint32) operatorObjectId); } heap_endscan(scan); } /* * IndexSupportInitialize -- * Initializes an index strategy and associated support procedures. */ void IndexSupportInitialize(IndexStrategy indexStrategy, RegProcedure *indexSupport, Oid indexObjectId, Oid accessMethodObjectId, StrategyNumber maxStrategyNumber, StrategyNumber maxSupportNumber, AttrNumber maxAttributeNumber) { Relation relation; Relation operatorRelation; HeapScanDesc scan; HeapTuple tuple; ScanKeyData entry[2]; StrategyMap map; AttrNumber attributeNumber; int attributeIndex; Oid operatorClassObjectId[MaxIndexAttributeNumber]; maxStrategyNumber = AMStrategies(maxStrategyNumber); ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_index_indexrelid, ObjectIdEqualRegProcedure, ObjectIdGetDatum(indexObjectId)); relation = heap_openr(IndexRelationName); scan = heap_beginscan(relation, false, false, 1, entry); tuple = heap_getnext(scan, 0, (Buffer *) NULL); if (!HeapTupleIsValid(tuple)) elog(ERROR, "IndexSupportInitialize: corrupted catalogs"); /* * XXX note that the following assumes the INDEX tuple is well formed * and that the key[] and class[] are 0 terminated. */ for (attributeIndex = 0; attributeIndex < maxAttributeNumber; attributeIndex++) { IndexTupleForm iform; iform = (IndexTupleForm) GETSTRUCT(tuple); if (!OidIsValid(iform->indkey[attributeIndex])) { if (attributeIndex == 0) { elog(ERROR, "IndexSupportInitialize: no pg_index tuple"); } break; } operatorClassObjectId[attributeIndex] = iform->indclass[attributeIndex]; } heap_endscan(scan); heap_close(relation); /* if support routines exist for this access method, load them */ if (maxSupportNumber > 0) { ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_amproc_amid, ObjectIdEqualRegProcedure, ObjectIdGetDatum(accessMethodObjectId)); ScanKeyEntryInitialize(&entry[1], 0, Anum_pg_amproc_amopclaid, ObjectIdEqualRegProcedure, 0); /* relation = heap_openr(Name_pg_amproc); */ relation = heap_openr(AccessMethodProcedureRelationName); for (attributeNumber = maxAttributeNumber; attributeNumber > 0; attributeNumber--) { int16 support; Form_pg_amproc form; RegProcedure *loc; loc = &indexSupport[((attributeNumber - 1) * maxSupportNumber)]; for (support = maxSupportNumber; --support >= 0;) { loc[support] = InvalidOid; } entry[1].sk_argument = ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]); scan = heap_beginscan(relation, false, false, 2, entry); while (tuple = heap_getnext(scan, 0, (Buffer *) NULL), HeapTupleIsValid(tuple)) { form = (Form_pg_amproc) GETSTRUCT(tuple); loc[(form->amprocnum - 1)] = form->amproc; } heap_endscan(scan); } heap_close(relation); } ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_amop_amopid, ObjectIdEqualRegProcedure, ObjectIdGetDatum(accessMethodObjectId)); ScanKeyEntryInitialize(&entry[1], 0, Anum_pg_amop_amopclaid, ObjectIdEqualRegProcedure, 0); relation = heap_openr(AccessMethodOperatorRelationName); operatorRelation = heap_openr(OperatorRelationName); for (attributeNumber = maxAttributeNumber; attributeNumber > 0; attributeNumber--) { StrategyNumber strategy; entry[1].sk_argument = ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]); map = IndexStrategyGetStrategyMap(indexStrategy, maxStrategyNumber, attributeNumber); for (strategy = 1; strategy <= maxStrategyNumber; strategy++) ScanKeyEntrySetIllegal(StrategyMapGetScanKeyEntry(map, strategy)); scan = heap_beginscan(relation, false, false, 2, entry); while (tuple = heap_getnext(scan, 0, (Buffer *) NULL), HeapTupleIsValid(tuple)) { Form_pg_amop form; form = (Form_pg_amop) GETSTRUCT(tuple); OperatorRelationFillScanKeyEntry(operatorRelation, form->amopopr, StrategyMapGetScanKeyEntry(map, form->amopstrategy)); } heap_endscan(scan); } heap_close(operatorRelation); heap_close(relation); } /* ---------------- * IndexStrategyDisplay * ---------------- */ #ifdef ISTRATDEBUG int IndexStrategyDisplay(IndexStrategy indexStrategy, StrategyNumber numberOfStrategies, int numberOfAttributes) { StrategyMap strategyMap; AttrNumber attributeNumber; StrategyNumber strategyNumber; for (attributeNumber = 1; attributeNumber <= numberOfAttributes; attributeNumber += 1) { strategyMap = IndexStrategyGetStrategyMap(indexStrategy, numberOfStrategies, attributeNumber); for (strategyNumber = 1; strategyNumber <= AMStrategies(numberOfStrategies); strategyNumber += 1) { printf(":att %d\t:str %d\t:opr 0x%x(%d)\n", attributeNumber, strategyNumber, strategyMap->entry[strategyNumber - 1].sk_procedure, strategyMap->entry[strategyNumber - 1].sk_procedure); } } } #endif /* defined(ISTRATDEBUG) */