head 1.15; access; symbols; locks; strict; comment @ * @; 1.15 date 92.08.26.19.41.12; author mer; state Exp; branches; next 1.14; 1.14 date 92.08.04.17.37.52; author mer; state Exp; branches; next 1.13; 1.13 date 92.07.09.03.54.18; author hong; state Exp; branches; next 1.12; 1.12 date 92.07.01.05.00.34; author mer; state Exp; branches; next 1.11; 1.11 date 92.03.31.23.11.14; author mer; state Exp; branches; next 1.10; 1.10 date 92.03.05.00.39.23; author hong; state Exp; branches; next 1.9; 1.9 date 91.11.17.21.10.46; author mer; state Exp; branches; next 1.8; 1.8 date 91.11.14.11.04.27; author glass; state Exp; branches; next 1.7; 1.7 date 91.11.14.10.52.57; author glass; state Exp; branches; next 1.6; 1.6 date 91.10.18.08.16.37; author mer; state Exp; branches; next 1.5; 1.5 date 91.10.16.23.04.32; author mer; state Exp; branches; next 1.4; 1.4 date 91.08.25.13.29.34; author hong; state Exp; branches; next 1.3; 1.3 date 91.07.17.23.55.57; author hong; state Exp; branches; next 1.2; 1.2 date 91.06.30.23.40.49; author cimarron; state Exp; branches; next 1.1; 1.1 date 91.06.18.23.32.54; author cimarron; state Exp; branches; next ; desc @ reorganized executor to use tuple table properly for nested dot stuff @ 1.15 log @initialize a buffer to invalid @ text @/* ---------------------------------------------------------------- * FILE * indexscan.c * * DESCRIPTION * Routines to support indexes and indexed scans of relations * * INTERFACE ROUTINES * ExecInsertIndexTuples inserts tuples into indices on result relation * * ExecIndexScan scans a relation using indices * ExecIndexNext using index to retrieve next tuple * ExecInitIndexScan creates and initializes state info. * ExecIndexReScan rescans the indexed relation. * ExecEndIndexScan releases all storage. * ExecIndexMarkPos marks scan position. * ExecIndexRestrPos restores scan position. * * NOTES * the code supporting ExecInsertIndexTuples should be * collected and merged with the genam stuff. * * IDENTIFICATION * $Header: /private/mer/pg/src/executor/RCS/n_indexscan.c,v 1.14 1992/08/04 17:37:52 mer Exp mer $ * ---------------------------------------------------------------- */ #include "tcop/slaves.h" #include "executor/executor.h" RcsId("$Header: /private/mer/pg/src/executor/RCS/n_indexscan.c,v 1.14 1992/08/04 17:37:52 mer Exp mer $"); /* ---------------- * Misc stuff to move to executor.h soon -cim 6/5/90 * ---------------- */ #define NO_OP 0 #define LEFT_OP 1 #define RIGHT_OP 2 /* ---------------------------------------------------------------- * ExecInsertIndexTuples support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- * ExecGetIndexKeyInfo * * Extracts the index key attribute numbers from * an index tuple form (i.e. a tuple from the pg_index relation) * into an array of attribute numbers. The array and the * size of the array are returned to the caller via return * parameters. * ---------------------------------------------------------------- */ /**** xxref: * ExecOpenIndices ****/ void ExecGetIndexKeyInfo(indexTuple, numAttsOutP, attsOutP, fInfoP) IndexTupleForm indexTuple; int *numAttsOutP; AttributeNumberPtr *attsOutP; FuncIndexInfoPtr fInfoP; { int i; int numKeys; AttributeNumberPtr attKeys; /* ---------------- * check parameters * ---------------- */ if (numAttsOutP == NULL && attsOutP == NULL) { elog(DEBUG, "ExecGetIndexKeyInfo: %s", "invalid parameters: numAttsOutP and attsOutP must be non-NULL"); } /* ---------------- * set the procid for a possible functional index. * ---------------- */ FIsetProcOid(fInfoP, indexTuple->indproc); /* ---------------- * count the number of keys.. * ---------------- */ numKeys = 0; for (i=0; i<8 && indexTuple->indkey[i] != 0; i++) numKeys++; /* ---------------- * place number keys in callers return area * or the number of arguments for a functional index. * * If we have a functional index then the number of * attributes defined in the index must 1 (the function's * single return value). * ---------------- */ if (FIgetProcOid(fInfoP) != InvalidObjectId) { FIsetnArgs(fInfoP, numKeys); (*numAttsOutP) = 1; } else (*numAttsOutP) = numKeys; if (numKeys < 1) { elog(DEBUG, "ExecGetIndexKeyInfo: %s", "all index key attribute numbers are zero!"); (*attsOutP) = NULL; return; } /* ---------------- * allocate and fill in array of key attribute numbers * ---------------- */ CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext); attKeys = (AttributeNumberPtr) palloc(numKeys * sizeof(AttributeNumber)); for (i=0; iindkey[i]; /* ---------------- * return array to caller. * ---------------- */ (*attsOutP) = attKeys; } /* ---------------------------------------------------------------- * ExecOpenIndices * * Here we scan the pg_index relation to find indices * associated with a given heap relation oid. Since we * don't know in advance how many indices we have, we * form lists containing the information we need from * pg_index and then process these lists. * * Note: much of this code duplicates effort done by * the IndexCatalogInformation function in plancat.c * because IndexCatalogInformation is poorly written. * * It would be much better the functionality provided * by this function and IndexCatalogInformation was * in the form of a small set of orthogonal routines.. * If you are trying to understand this, I suggest you * look at the code to IndexCatalogInformation and * FormIndexTuple.. -cim 9/27/89 * ---------------------------------------------------------------- */ /**** xxref: * InitPlan ****/ void ExecOpenIndices(resultRelationOid, resultRelationInfo) ObjectId resultRelationOid; RelationInfo resultRelationInfo; { Relation indexRd; HeapScanDesc indexSd; ScanKeyData key; HeapTuple tuple; IndexTupleForm indexStruct; ObjectId indexOid; List oidList; List nkeyList; List keyList; List fiList; List indexoid; List numkeys; List indexkeys; List indexfuncs; int len; RelationPtr relationDescs; IndexInfoPtr indexInfoArray; FuncIndexInfoPtr fInfoP; int numKeyAtts; AttributeNumberPtr indexKeyAtts; int i; /* ---------------- * open pg_index * ---------------- */ indexRd = amopenr(IndexRelationName); /* ---------------- * form a scan key * ---------------- */ ScanKeyEntryInitialize(&key.data[0], 0, IndexHeapRelationIdAttributeNumber, ObjectIdEqualRegProcedure, ObjectIdGetDatum(resultRelationOid)); /* ---------------- * scan the index relation, looking for indices for our * result relation.. * ---------------- */ indexSd = ambeginscan(indexRd, /* scan desc */ false, /* scan backward flag */ NowTimeQual, /* time qual */ 1, /* number scan keys */ &key); /* scan keys */ oidList = LispNil; nkeyList = LispNil; keyList = LispNil; fiList = LispNil; while(tuple = amgetnext(indexSd, /* scan desc */ false, /* scan backward flag */ NULL), /* return: buffer */ HeapTupleIsValid(tuple)) { /* ---------------- * For each index relation we find, extract the information * we need and store it in a list.. * * first get the oid of the index relation from the tuple * ---------------- */ indexStruct = (IndexTupleForm) GETSTRUCT(tuple); indexOid = indexStruct->indexrelid; /* ---------------- * allocate space for functional index information. * ---------------- */ fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) ); /* ---------------- * next get the index key information from the tuple * ---------------- */ ExecGetIndexKeyInfo(indexStruct, &numKeyAtts, &indexKeyAtts, fInfoP); /* ---------------- * save the index information into lists * ---------------- */ oidList = lispCons(lispInteger(indexOid), oidList); nkeyList = lispCons(lispInteger(numKeyAtts), nkeyList); keyList = lispCons((LispValue)indexKeyAtts, keyList); fiList = lispCons((LispValue)fInfoP, fiList); } /* ---------------- * we have the info we need so close the pg_index relation.. * ---------------- */ amendscan(indexSd); amclose(indexRd); /* ---------------- * Now that we've collected the index information into three * lists, we open the index relations and store the descriptors * and the key information into arrays. * ---------------- */ len = length(oidList); if (len > 0) { /* ---------------- * allocate space for relation descs * ---------------- */ CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); relationDescs = (RelationPtr) palloc(len * sizeof(Relation)); /* ---------------- * initialize index info array * ---------------- */ CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext); indexInfoArray = (IndexInfoPtr) palloc(len * sizeof(IndexInfo)); for (i=0; it_tid = (*tupleid); /* structure assignment */ result = AMinsert(relationDescs[i], /* index relation */ indexTuple, /* index tuple */ 0, /* scan (not used) */ 0); /* return: offset */ /* XXX should inspect result for locks */ /* ---------------- * keep track of index inserts for debugging * ---------------- */ IncrIndexInserted(); /* ---------------- * free index tuple after insertion * ---------------- */ pfree(indexTuple); } } /* ---------------- * return rule locks from insertion.. (someday) * ---------------- */ return NULL; } /* ---------------------------------------------------------------- * index scan support * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- * IndexNext * * Retrieve a tuple from the IndexScan node's currentRelation * using the indices in the IndexScanState information. * * note: the old code mentions 'Primary indices'. to my knowledge * we only support a single secondary index. -cim 9/11/89 * * old comments: * retrieve a tuple from relation using the indices given. * The indices are used in the order they appear in 'indices'. * The indices may be primary or secondary indices: * * primary index -- scan the relation 'relID' using keys supplied. * * secondary index -- scan the index relation to get the 'tid' for * a tuple in the relation 'relID'. * If the current index(pointed by 'indexPtr') fails to return a * tuple, the next index in the indices is used. * * bug fix so that it should retrieve on a null scan key. * ---------------------------------------------------------------- */ /**** xxref: * UseNextIndex ****/ TupleTableSlot IndexNext(node) IndexScan node; { EState estate; ScanState scanstate; IndexScanState indexstate; ScanDirection direction; int indexPtr; IndexScanDescPtr scanDescs; IndexScanDesc scandesc; Relation heapRelation; GeneralRetrieveIndexResult result; ItemPointer iptr; HeapTuple tuple; TupleTableSlot returnTuple; TupleTableSlot slot; Buffer buffer = InvalidBuffer; /* ---------------- * extract necessary information from index scan node * ---------------- */ estate = (EState) get_state((Plan) node); direction = get_es_direction(estate); scanstate = get_scanstate((Scan) node); indexstate = get_indxstate(node); indexPtr = get_iss_IndexPtr(indexstate); scanDescs = get_iss_ScanDescs(indexstate); scandesc = scanDescs[ indexPtr ]; heapRelation = get_css_currentRelation((CommonScanState) scanstate); slot = (TupleTableSlot) get_css_ScanTupleSlot((CommonScanState) scanstate); /* ---------------- * ok, now that we have what we need, fetch an index tuple. * ---------------- */ for(;;) { result = AMgettuple(scandesc, direction); /* ---------------- * if scanning this index succeeded then return the * appropriate heap tuple.. else return LispNil. * ---------------- */ if (GeneralRetrieveIndexResultIsValid(result)) { iptr = GeneralRetrieveIndexResultGetHeapItemPointer(result); tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer); /* be tidy */ pfree(result); if (tuple == NULL) { /* ---------------- * we found a deleted tuple, so keep on scanning.. * ---------------- */ if (BufferIsValid(buffer)) ReleaseBuffer(buffer); continue; } /* ---------------- * store the scanned tuple in the scan tuple slot of * the scan state. Eventually we will only do this and not * return a tuple. Note: we pass 'false' because tuples * returned by amgetnext are pointers onto disk pages and * were not created with palloc() and so should not be pfree()'d. * ---------------- */ ExecStoreTuple((Pointer) tuple, /* tuple to store */ (Pointer) slot, /* slot to store in */ buffer, /* buffer associated with tuple */ false); /* don't pfree */ return slot; } /* ---------------- * if we get here it means the index scan failed so we * are at the end of the scan.. * ---------------- */ return (TupleTableSlot) ExecClearTuple((Pointer) slot); } } /* ---------------------------------------------------------------- * ExecIndexScan(node) * * old comments: * Scans the relation using primary or secondary indices and returns * the next qualifying tuple in the direction specified. * It calls ExecScan() and passes it the access methods which returns * the next tuple using the indices. * * Conditions: * -- the "cursor" maintained by the AMI is positioned at the tuple * returned previously. * * Initial States: * -- the relation indicated is opened for scanning so that the * "cursor" is positioned before the first qualifying tuple. * -- all index realtions are opened for scanning. * -- indexPtr points to the first index. * -- state variable ruleFlag = nil. * ---------------------------------------------------------------- */ /**** xxref: * ExecProcNode ****/ TupleTableSlot ExecIndexScan(node) IndexScan node; { TupleTableSlot returnTuple; /* ---------------- * use IndexNext as access method * ---------------- */ returnTuple = ExecScan((Scan) node, (VoidFunctionType)IndexNext); return returnTuple; } /* ---------------------------------------------------------------- * ExecIndexReScan(node) * * old comments * Rescans the indexed relation. * ---------------------------------------------------------------- */ /**** xxref: * ExecReScan ****/ List ExecIndexReScan(node) IndexScan node; { EState estate; IndexScanState indexstate; ScanDirection direction; IndexScanDescPtr scanDescs; ScanKeyPtr scanKeys; IndexScanDesc sdesc; ScanKey skey; int numIndices; int i; /* ---------------- * get information from the node.. * ---------------- */ estate = (EState) get_state((Plan) node); direction = get_es_direction(estate); indexstate = get_indxstate(node); numIndices = get_iss_NumIndices(indexstate); scanDescs = get_iss_ScanDescs(indexstate); scanKeys = get_iss_ScanKeys(indexstate); /* ---------------- * rescans all indices * * note: AMrescan assumes only one scan key. This may have * to change if we ever decide to support multiple keys. * (actually I doubt the thing is even used -cim 9/11/89) * * this is actually necessary because the index scan keys are * copied into the scan desc in AMrescan() and when we call * ExecUpdateIndexScanKeys() we create a new set of keys which * have to be copied into the scan desc. * ---------------- */ for (i = 0; i < numIndices; i++) { sdesc = scanDescs[ i ]; skey = scanKeys[ i ]; AMrescan(sdesc, direction, skey); } /* ---------------- * perhaps return something meaningful * ---------------- */ return LispNil; } /* ---------------------------------------------------------------- * ExecEndIndexScan * * old comments * Releases any storage allocated through C routines. * Returns nothing. * ---------------------------------------------------------------- */ /**** xxref: * ExecEndNode ****/ void ExecEndIndexScan(node) IndexScan node; { ScanState scanstate; IndexScanState indexstate; ScanKeyPtr scanKeys; ScanKey skey; int numIndices; int i; AttributeNumberPtr scanAtts; RelationRuleInfo ruleInfo; scanstate = get_scanstate((Scan) node); indexstate = get_indxstate(node); /* ---------------- * extract information from the node * ---------------- */ numIndices = get_iss_NumIndices(indexstate); scanKeys = get_iss_ScanKeys(indexstate); scanAtts = get_cs_ScanAttributes((CommonState) scanstate); /* ---------------- * Restore the relation level rule stubs. * ---------------- */ ruleInfo = get_css_ruleInfo((CommonScanState)scanstate); if (ruleInfo != NULL && ruleInfo->relationStubsHaveChanged) { ObjectId reloid; reloid = RelationGetRelationId( get_css_currentRelation((CommonScanState)scanstate)); prs2ReplaceRelationStub(reloid, ruleInfo->relationStubs); } /* ---------------- * Free the projection info and the scan attribute info * * Note: we don't ExecFreeResultType(scanstate) * because the rule manager depends on the tupType * returned by ExecMain(). So for now, this * is freed at end-transaction time. -cim 6/2/91 * ---------------- */ ExecFreeProjectionInfo((CommonState)scanstate); ExecFreeScanAttributes(scanAtts); /* ---------------- * close the heap and index relations * ---------------- */ ExecCloseR((Plan) node); /* ---------------- * free the scan keys used in scanning the indices * ---------------- */ for (i=0; i 0) { run_keys = (int *) palloc(n_keys * sizeof(int)); } /* ---------------- * for each opclause in the given qual, * convert each qual's opclause into a single scan key * ---------------- */ for (j=0; j < n_keys; j++) { List clause; /* one part of index qual */ Oper op; /* operator used in scan.. */ Node leftop; /* expr on lhs of operator */ Node rightop; /* expr on rhs ... */ int scanvar; /* which var identifies varattno */ int varattno; /* att number used in scan */ ObjectId opid; /* operator id used in scan */ Datum scanvalue; /* value used in scan (if const) */ /* ---------------- * extract clause information from the qualification * ---------------- */ clause = nth(j, qual); op = (Oper) get_op(clause); if (! ExactNodeType(op,Oper)) elog(WARN, "ExecInitIndexScan: op not an Oper!"); opid = get_opid(op); /* ---------------- * Here we figure out the contents of the index qual. * The usual case is (op var const) or (op const var) * which means we form a scan key for the attribute * listed in the var node and use the value of the const. * * If we don't have a const node, then it means that * one of the var nodes refers to the "scan" tuple and * is used to determine which attribute to scan, and the * other expression is used to calculate the value used in * scanning the index. * * This means our index scan's scan key is a function of * information obtained during the execution of the plan * in which case we need to recalculate the index scan key * at run time. * * Hence, we set have_runtime_keys to true and then set * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP. * The corresponding scan keys are recomputed at run time. * ---------------- */ scanvar = NO_OP; /* ---------------- * determine information in leftop * ---------------- */ leftop = (Node) get_leftop(clause); if (ExactNodeType(leftop,Var) && var_is_rel(leftop)) { /* ---------------- * if the leftop is a "rel-var", then it means * that it is a var node which tells us which * attribute to use for our scan key. * ---------------- */ varattno = get_varattno((Var) leftop); scanvar = LEFT_OP; } else if (ExactNodeType(leftop,Const)) { /* ---------------- * if the leftop is a const node then it means * it identifies the value to place in our scan key. * ---------------- */ run_keys[ j ] = NO_OP; scanvalue = get_constvalue((Const) leftop); } else if (consp(leftop) && ExactNodeType(CAR((List)leftop),Func) && var_is_rel(CADR((List)leftop))) { /* ---------------- * if the leftop is a func node then it means * it identifies the value to place in our scan key. * Since functional indices have only one attribute * the attno must always be set to 1. * ---------------- */ varattno = 1; scanvar = LEFT_OP; } else { /* ---------------- * otherwise, the leftop contains information usable * at runtime to figure out the value to place in our * scan key. * ---------------- */ have_runtime_keys = true; run_keys[ j ] = LEFT_OP; scanvalue = get_constvalue(ConstTrue); } /* ---------------- * now determine information in rightop * ---------------- */ rightop = (Node) get_rightop(clause); if (ExactNodeType(rightop,Var) && var_is_rel(rightop)) { /* ---------------- * here we make sure only one op identifies the * scan-attribute... * ---------------- */ if (scanvar == LEFT_OP) elog(WARN, "ExecInitIndexScan: %s", "both left and right op's are rel-vars"); /* ---------------- * if the rightop is a "rel-var", then it means * that it is a var node which tells us which * attribute to use for our scan key. * ---------------- */ varattno = get_varattno((Var) rightop); scanvar = RIGHT_OP; } else if (ExactNodeType(rightop,Const)) { /* ---------------- * if the leftop is a const node then it means * it identifies the value to place in our scan key. * ---------------- */ run_keys[ j ] = NO_OP; scanvalue = get_constvalue((Const) rightop); } else if (consp(rightop) && ExactNodeType(CAR((List)rightop),Func) && var_is_rel(CADR((List)rightop))) { /* ---------------- * if the rightop is a func node then it means * it identifies the value to place in our scan key. * Since functional indices have only one attribute * the attno must always be set to 1. * ---------------- */ if (scanvar == LEFT_OP) elog(WARN, "ExecInitIndexScan: %s", "both left and right ops are rel-vars"); varattno = 1; scanvar = RIGHT_OP; } else { /* ---------------- * otherwise, the leftop contains information usable * at runtime to figure out the value to place in our * scan key. * ---------------- */ have_runtime_keys = true; run_keys[ j ] = RIGHT_OP; scanvalue = get_constvalue(ConstTrue); } /* ---------------- * now check that at least one op tells us the scan * attribute... * ---------------- */ if (scanvar == NO_OP) elog(WARN, "ExecInitIndexScan: %s", "neither leftop nor rightop refer to scan relation"); /* ---------------- * initialize the scan key's fields appropriately * * XXX UGLY, UGLY, UGLY cast here. scan_keys is an array of * pointers to ScanKeyData structs and the macro first * dereferences then takes address of it and passes this * off as a ScanKeyEntry. It works but its not right. * -- mer 16 Nov. 1991 * ---------------- */ ExecSetSkeys(j, /* index into scan_keys array */ /* array in which to plug scan key */ (ScanKeyEntry)scan_keys, /* attribute number to scan */ (AttributeNumber) varattno, (RegProcedure) opid, /* reg proc to use */ (Datum) scanvalue); /* constant */ } /* ---------------- * store the key information into our array. * ---------------- */ numScanKeys[ i ] = n_keys; scanKeys[ i ] = scan_keys; runtimeKeyInfo[ i ] = (Pointer) run_keys; } set_iss_NumIndices(indexstate, numIndices); set_iss_IndexPtr(indexstate, indexPtr); set_iss_ScanKeys(indexstate, scanKeys); set_iss_NumScanKeys(indexstate, numScanKeys); /* ---------------- * If all of our keys have the form (op var const) , then we have no * runtime keys so we store NULL in the runtime key info. * Otherwise runtime key info contains an array of pointers * (one for each index) to arrays of flags (one for each key) * which indicate that the qual needs to be evaluated at runtime. * -cim 10/24/89 * ---------------- */ if (have_runtime_keys) { set_iss_RuntimeKeyInfo(indexstate, (Pointer) runtimeKeyInfo); } else { set_iss_RuntimeKeyInfo(indexstate, NULL); for (i=0; i < numIndices; i++) { List qual; int n_keys; qual = nth(i, indxqual); n_keys = length(qual); if (n_keys > 0) pfree(runtimeKeyInfo[i]); } pfree(runtimeKeyInfo); } /* ---------------- * get the range table and direction information * from the execution state (these are needed to * open the relations). * ---------------- */ rangeTable = get_es_range_table(estate); direction = get_es_direction(estate); /* ---------------- * open the base relation * ---------------- */ relid = get_scanrelid((Scan) node); rtentry = rt_fetch(relid, rangeTable); reloid = CInteger(rt_relid(rtentry)); timeQual = (TimeQual) CInteger(rt_time(rtentry)); ExecOpenScanR(reloid, /* relation */ 0, /* nkeys */ (ScanKey) NULL, /* scan key */ 0, /* is index */ direction, /* scan direction */ timeQual, /* time qual */ ¤tRelation, /* return: rel desc */ (Pointer *) ¤tScanDesc); /* return: scan desc */ set_css_currentRelation((CommonScanState) scanstate, currentRelation); set_css_currentScanDesc((CommonScanState)scanstate, currentScanDesc); /* * intialize scan state rule info */ set_css_ruleInfo((CommonScanState)scanstate, prs2MakeRelationRuleInfo(currentRelation, RETRIEVE)); /* ---------------- * XXX temporary hack to prevent tuple level locks * from exhausting our semaphores. Remove this * when the tuple level lock stuff is fixed. * ---------------- */ RelationSetLockForRead(currentRelation); /* ---------------- * get the scan type from the relation descriptor. * ---------------- */ ExecAssignScanType((CommonScanState)scanstate, TupDescToExecTupDesc(¤tRelation->rd_att, currentRelation->rd_rel->relnatts), ¤tRelation->rd_att); /* bug -- glass */ ExecAssignResultTypeFromTL((Plan) node, (CommonState)scanstate); /* ---------------- * index scans don't have subtrees.. * ---------------- */ set_ss_ProcOuterFlag(scanstate, false); /* ---------------- * open the index relations and initialize * relation and scan descriptors. * ---------------- */ for (i=0; i < numIndices; i++) { List index; ObjectId indexOid; index = nth(i, indxid); indexOid = CInteger(index); if (indexOid != 0) { ExecOpenScanR(indexOid, /* relation */ numScanKeys[ i ], /* nkeys */ scanKeys[ i ], /* scan key */ true, /* is index */ direction, /* scan direction */ timeQual, /* time qual */ &(relationDescs[ i ]), /* return: rel desc */ (Pointer *) &(scanDescs[ i ])); /* return: scan desc */ } } set_iss_RelationDescs(indexstate, relationDescs); if (ParallelExecutorEnabled() && SlaveLocalInfoD.nparallel > 1) partition_indexscan(numIndices, scanDescs, SlaveLocalInfoD.nparallel); set_iss_ScanDescs(indexstate, scanDescs); set_cs_TupFromTlist((CommonState)indexstate, false); /* ---------------- * all done. * ---------------- */ return LispTrue; } int ExecCountSlotsIndexScan(node) Plan node; { return ExecCountSlotsNode(get_outerPlan(node)) + ExecCountSlotsNode(get_innerPlan(node)) + INDEXSCAN_NSLOTS; } /* --------------------------------------------------------- * partition_indexscan * * XXXXXX this function now only works for indexes on * int2 and int4 and does handle skewed data distribution. * more elaborate schemes will be implemented later. * * -------------------------------------------------------- */ #define INT4LT 66 #define INT4LE 149 #define INT4GT 147 #define INT4GE 150 #define INT2LT 64 #define INT2LE 148 #define INT2GT 146 #define INT2GE 151 void partition_indexscan(numIndices, scanDescs, parallel) int numIndices; IndexScanDescPtr scanDescs; int parallel; { int i; IndexScanDesc scanDesc; ScanKeyEntryData lowKeyEntry; ScanKeyEntryData highKeyEntry; int32 lowKey, newlowkey; int32 highKey, newhighkey; int delt; for (i=0; inumberOfKeys < 2) { /* ------------------------------------- * don't know how to partition, just return * ------------------------------------- */ return; } lowKeyEntry = scanDesc->keyData.data[0]; switch (lowKeyEntry.procedure) { case INT4GT: case INT4GE: highKeyEntry = scanDesc->keyData.data[1]; break; case INT4LT: case INT4LE: highKeyEntry = lowKeyEntry; lowKeyEntry = scanDesc->keyData.data[1]; break; defaults: /* ------------------------------------- * don't know how to partition, just return * ------------------------------------- */ return; } lowKey = DatumGetInt32(lowKeyEntry.argument); highKey = DatumGetInt32(highKeyEntry.argument); delt = highKey - lowKey; Assert(delt > 0); newlowkey = lowKey + SlaveInfoP[MyPid].groupPid * delt / parallel; newhighkey = lowKey + (SlaveInfoP[MyPid].groupPid + 1) * delt/parallel; lowKeyEntry.argument = Int32GetDatum(newlowkey); if (SlaveInfoP[MyPid].groupPid > 0) { lowKeyEntry.procedure = INT4GE; fmgr_info(INT4GE, &lowKeyEntry.func, &lowKeyEntry.nargs); } highKeyEntry.argument = Int32GetDatum(newhighkey); if (SlaveInfoP[MyPid].groupPid < parallel - 1) { highKeyEntry.procedure = INT4LT; fmgr_info(INT4LT, &highKeyEntry.func, &highKeyEntry.nargs); } scanDesc->keyData.data[0] = lowKeyEntry; scanDesc->keyData.data[1] = highKeyEntry; } } @ 1.14 log @must recursively count needed slots for plans for table allocation @ text @d24 1 a24 1 * $Header: /users/mer/pg/src/executor/RCS/n_indexscan.c,v 1.13 1992/07/09 03:54:18 hong Exp mer $ d31 1 a31 1 RcsId("$Header: /users/mer/pg/src/executor/RCS/n_indexscan.c,v 1.13 1992/07/09 03:54:18 hong Exp mer $"); d600 1 a600 1 Buffer buffer; @ 1.13 log @changes to allow arbitrary number of tuple arguments in functions @ text @d24 1 a24 1 * $Header: RCS/n_indexscan.c,v 1.12 92/07/01 05:00:34 mer Exp Locker: hong $ d31 1 a31 1 RcsId("$Header: RCS/n_indexscan.c,v 1.12 92/07/01 05:00:34 mer Exp Locker: hong $"); d1120 1 d1541 9 @ 1.12 log @check the target list before getting the next tuple from disk @ text @d24 1 a24 1 * $Header: /private/mer/pg/src/executor/RCS/n_indexscan.c,v 1.11 1992/03/31 23:11:14 mer Exp mer $ d31 1 a31 1 RcsId("$Header: /private/mer/pg/src/executor/RCS/n_indexscan.c,v 1.11 1992/03/31 23:11:14 mer Exp mer $"); d1491 2 @ 1.11 log @change accessor functions into macros @ text @d24 1 a24 1 * $Header: /users/mer/pg/src/executor/RCS/n_indexscan.c,v 1.10 1992/03/05 00:39:23 hong Exp mer $ d31 1 a31 1 RcsId("$Header: /users/mer/pg/src/executor/RCS/n_indexscan.c,v 1.10 1992/03/05 00:39:23 hong Exp mer $"); d973 1 d1022 4 d1027 1 a1027 1 ExecEvalExpr(scanexpr, econtext, &isNull); d1530 2 @ 1.10 log @whacking out buffer leaks @ text @d24 1 a24 1 * $Header: RCS/n_indexscan.c,v 1.9 91/11/17 21:10:46 mer Exp $ d31 1 a31 1 RcsId("$Header: RCS/n_indexscan.c,v 1.9 91/11/17 21:10:46 mer Exp $"); d1089 1 a1089 1 set_state((Plan) node, estate); d1421 1 d1423 1 @ 1.9 log @prototyping @ text @d24 1 a24 1 * $Header: /users/mer/postgres/src/executor/RCS/n_indexscan.c,v 1.8 1991/11/14 11:04:27 glass Exp mer $ d31 1 a31 1 RcsId("$Header: /users/mer/postgres/src/executor/RCS/n_indexscan.c,v 1.8 1991/11/14 11:04:27 glass Exp mer $"); d644 2 @ 1.8 log @fixes due to prototypes @ text @d24 1 a24 1 * $Header: RCS/n_indexscan.c,v 1.6 91/10/18 08:16:37 mer Exp Locker: glass $ d31 1 a31 1 RcsId("$Header: RCS/n_indexscan.c,v 1.6 91/10/18 08:16:37 mer Exp Locker: glass $"); d252 1 a252 1 keyList = lispCons(indexKeyAtts, keyList); d707 1 a707 1 returnTuple = ExecScan((Scan) node, IndexNext); d1378 6 d1387 3 a1389 1 scan_keys, /* array in which to plug scan key */ a1390 1 /* attribute number to scan */ d1392 1 a1392 1 (Datum) scanvalue); /* constant */ @ 1.7 log @fixes due to prototypes @ text @d24 1 a24 1 * $Header: RCS/n_indexscan.c,v 1.4 91/08/25 13:29:34 hong Exp $ d28 2 a29 1 #include "tmp/postgres.h" d31 1 a31 1 RcsId("$Header: RCS/n_indexscan.c,v 1.4 91/08/25 13:29:34 hong Exp $"); a33 37 * FILE INCLUDE ORDER GUIDELINES * * 1) execdebug.h * 2) various support files ("everything else") * 3) node files * 4) catalog/ files * 5) execdefs.h and execmisc.h * 6) externs.h comes last * ---------------- */ #include "executor/execdebug.h" #include "access/ftup.h" #include "parser/parsetree.h" #include "utils/log.h" #include "nodes/pg_lisp.h" #include "nodes/primnodes.h" #include "nodes/primnodes.a.h" #include "nodes/plannodes.h" #include "nodes/plannodes.a.h" #include "nodes/execnodes.h" #include "nodes/execnodes.a.h" #include "catalog/catname.h" #include "catalog/pg_index.h" #include "catalog/pg_proc.h" #include "executor/execdefs.h" #include "executor/execmisc.h" #include "executor/externs.h" #include "parser/parse.h" /* for RETRIEVE */ #include "tcop/slaves.h" /* ---------------- d59 1 a59 1 ExecGetIndexKeyInfo(indexTuple, numAttsOutP, attsOutP) d63 1 d79 1 a79 1 * first count the number of keys.. d82 6 d94 5 d101 7 a107 1 (*numAttsOutP) = numKeys; d133 1 a133 1 d172 1 d176 1 d181 1 d214 1 d232 6 d243 2 a244 1 &indexKeyAtts); d253 1 d288 1 a288 1 indexInfoArray[i] = MakeIndexInfo(); d331 5 d408 1 d416 1 d446 2 a447 1 nulls); /* return: array of char's */ d606 1 a606 1 estate = (EState) get_state(node); d608 1 a608 2 scanstate = get_scanstate(node); d613 1 a613 1 heapRelation = get_css_currentRelation(scanstate); d616 1 a616 1 get_css_ScanTupleSlot(scanstate); d655 2 a656 2 ExecStoreTuple(tuple, /* tuple to store */ slot, /* slot to store in */ d669 1 a669 1 ExecClearTuple(slot); d707 1 a707 1 returnTuple = ExecScan((Plan) node, IndexNext); d741 1 a741 1 estate = (EState) get_state(node); d799 1 a799 1 scanstate = get_scanstate(node); d808 1 a808 1 scanAtts = get_cs_ScanAttributes(scanstate); d814 1 a814 1 ruleInfo = get_css_ruleInfo(scanstate); d817 3 a819 1 reloid = RelationGetRelationId(get_css_currentRelation(scanstate)); d832 1 a832 1 ExecFreeProjectionInfo(scanstate); d854 6 a859 3 ExecClearTuple(get_cs_ResultTupleSlot(scanstate)); ExecClearTuple(get_css_ScanTupleSlot(scanstate)); ExecClearTuple(get_css_RawTupleSlot(scanstate)); a1081 1 void partition_indexscan(); d1087 1 a1087 1 set_state(node, estate); d1095 2 a1096 2 scanstate = MakeScanState(); set_scanstate(node, scanstate); d1105 1 a1105 1 set_base_id(scanstate, baseid); d1111 1 a1111 1 ExecAssignExprContext(estate, scanstate); d1117 3 a1119 3 ExecInitResultTupleSlot(estate, scanstate); ExecInitScanTupleSlot(estate, scanstate); ExecInitRawTupleSlot(estate, scanstate); d1126 1 a1126 1 ExecAssignProjectionInfo(node, scanstate); d1132 1 a1132 1 ExecInitScanAttributes(node); d1140 1 a1140 1 indexstate = MakeIndexScanState(); d1147 1 a1147 1 set_base_id(indexstate, baseid); d1155 1 a1155 1 ExecAssignDebugHooks(node, indexstate); d1269 1 a1269 1 varattno = get_varattno(leftop); d1278 14 a1291 1 scanvalue = get_constvalue(leftop); d1326 1 a1326 1 varattno = get_varattno(rightop); d1336 19 a1354 1 scanvalue = get_constvalue(rightop); d1382 4 a1385 3 varattno, /* attribute number to scan */ opid, /* reg proc to use */ scanvalue); /* constant */ d1412 1 a1412 1 set_iss_RuntimeKeyInfo(indexstate, runtimeKeyInfo); d1439 1 a1439 1 relid = get_scanrelid(node); d1446 1 a1446 1 NULL, /* scan key */ d1451 1 a1451 1 ¤tScanDesc); /* return: scan desc */ d1453 2 a1454 2 set_css_currentRelation(scanstate, currentRelation); set_css_currentScanDesc(scanstate, currentScanDesc); d1458 1 a1458 1 set_css_ruleInfo(scanstate, d1474 3 a1476 2 ExecAssignScanType(scanstate, ¤tRelation->rd_att); ExecAssignResultTypeFromTL(node, scanstate); d1504 2 a1505 1 &(scanDescs[ i ])); /* return: scan desc */ @ 1.6 log @support base reln changes with secondary functional indices defined on them @ text @d24 1 a24 1 * $Header: /users/mer/postgres/src/executor/RCS/n_indexscan.c,v 1.5 1991/10/16 23:04:32 mer Exp mer $ d30 1 a30 1 RcsId("$Header: /users/mer/postgres/src/executor/RCS/n_indexscan.c,v 1.5 1991/10/16 23:04:32 mer Exp mer $"); d95 1 a95 1 ExecGetIndexKeyInfo(indexTuple, numAttsOutP, attsOutP, fInfoP) a98 1 FuncIndexInfoPtr fInfoP; d114 1 a114 1 * set the procid for a possible functional index. a116 6 FIsetProcOid(fInfoP, indexTuple->indproc); /* ---------------- * count the number of keys.. * ---------------- */ a122 5 * or the number of arguments for a functional index. * * If we have a functional index then the number of * attributes defined in the index must 1 (the function's * single return value). d125 1 a125 7 if (FIgetProcOid(fInfoP) != InvalidObjectId) { FIsetnArgs(fInfoP, numKeys); (*numAttsOutP) = 1; } else (*numAttsOutP) = numKeys; a189 1 List fiList; a192 1 List indexfuncs; a196 1 FuncIndexInfoPtr fInfoP; a228 1 fiList = LispNil; a245 6 * allocate space for functional index information. * ---------------- */ fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) ); /* ---------------- d251 1 a251 2 &indexKeyAtts, fInfoP); a259 1 fiList = lispCons((LispValue)fInfoP, fiList); a336 5 i = 0; foreach (indexfuncs, fiList) { FuncIndexInfoPtr fiP = (FuncIndexInfoPtr)CAR(indexfuncs); set_ii_FuncIndexInfo(indexInfoArray[i++], fiP); } a408 1 FuncIndexInfoPtr fInfoP; a415 1 fInfoP = get_ii_FuncIndexInfo(indexInfo); d445 1 a445 2 nulls, /* return: array of char's */ fInfoP); /* functional index information */ a1273 13 } else if (consp(leftop) && ExactNodeType(CAR((List)leftop),Func) && var_is_rel(CADR((List)leftop))) { /* ---------------- * if the leftop is a func node then it means * it identifies the value to place in our scan key. * Since functional indices have only one attribute * the attno must always be set to 1. * ---------------- */ varattno = 1; scanvar = LEFT_OP; a1318 18 } else if (consp(rightop) && ExactNodeType(CAR((List)rightop),Func) && var_is_rel(CADR((List)rightop))) { /* ---------------- * if the rightop is a func node then it means * it identifies the value to place in our scan key. * Since functional indices have only one attribute * the attno must always be set to 1. * ---------------- */ if (scanvar == LEFT_OP) elog(WARN, "ExecInitIndexScan: %s", "both left and right ops are rel-vars"); varattno = 1; scanvar = RIGHT_OP; @ 1.5 log @change to InitIndexScan to support functional indices @ text @d24 1 a24 1 * $Header: /users/mer/postgres/src/executor/RCS/n_indexscan.c,v 1.4 1991/08/25 13:29:34 hong Exp mer $ d30 1 a30 1 RcsId("$Header: /users/mer/postgres/src/executor/RCS/n_indexscan.c,v 1.4 1991/08/25 13:29:34 hong Exp mer $"); d95 1 a95 1 ExecGetIndexKeyInfo(indexTuple, numAttsOutP, attsOutP) d99 1 d115 1 a115 1 * first count the number of keys.. d118 6 d130 5 d137 7 a143 1 (*numAttsOutP) = numKeys; d208 1 d212 1 d217 1 d250 1 d268 6 d279 2 a280 1 &indexKeyAtts); d289 1 d367 5 d444 1 d452 1 d482 2 a483 1 nulls); /* return: array of char's */ @ 1.4 log @added support for dynamic parallelism adjustment @ text @d24 1 a24 1 * $Header: RCS/n_indexscan.c,v 1.3 91/07/17 23:55:57 hong Exp Locker: hong $ d30 1 a30 1 RcsId("$Header: RCS/n_indexscan.c,v 1.3 91/07/17 23:55:57 hong Exp Locker: hong $"); d1274 13 d1332 18 @ 1.3 log @changes to support inter-fragment and inter-query parallelism fixed a bug in partition_indexscan() that forgot to re-initialize the function cache @ text @d24 1 a24 1 * $Header: RCS/n_indexscan.c,v 1.2 91/06/30 23:40:49 cimarron Exp Locker: hong $ d30 1 a30 1 RcsId("$Header: RCS/n_indexscan.c,v 1.2 91/06/30 23:40:49 cimarron Exp Locker: hong $"); d1471 2 a1472 2 if (ParallelExecutorEnabled() && get_parallel(node) > 1) partition_indexscan(numIndices, scanDescs, get_parallel(node)); @ 1.2 log @tuple descriptor now correctly stored in tuple table slot for scan tuple. @ text @d24 1 a24 1 * $Header: RCS/n_indexscan.c,v 1.1 91/06/18 23:32:54 cimarron Exp $ d30 1 a30 1 RcsId("$Header: RCS/n_indexscan.c,v 1.1 91/06/18 23:32:54 cimarron Exp $"); d67 1 a68 2 extern int MyPid; d1553 2 a1554 2 newlowkey = lowKey + MyPid * delt / parallel; newhighkey = lowKey + (MyPid + 1) * delt / parallel; d1558 1 a1558 1 if (MyPid > 0) d1560 2 d1565 1 a1565 1 if (MyPid < parallel - 1) d1567 2 @ 1.1 log @Initial revision @ text @d24 1 a24 1 * $Header: RCS/indexscan.c,v 1.47 91/04/20 03:46:28 kemnitz Exp $ d30 1 a30 1 RcsId("$Header: RCS/indexscan.c,v 1.47 91/04/20 03:46:28 kemnitz Exp $"); d1435 1 a1435 1 * get the return type from the relation descriptor. d1438 3 a1440 2 ExecAssignResultType(scanstate, ¤tRelation->rd_att); @