/** * Clean up after consuming indexed tuples. */ void ElasticIndexReadContext::deleteStreamedTuples() { // Delete the indexed tuples that were streamed. // Undo token release will cause the index to delete the corresponding items // via notifications. m_iter->reset(); TableTuple tuple; while (m_iter->next(tuple)) { if (!tuple.isPendingDelete()) { m_surgeon.deleteTuple(tuple); } } }
/** * Clean up after consuming indexed tuples. */ void ElasticIndexReadContext::deleteStreamedTuples() { // Delete the indexed tuples that were streamed. // Undo token release will cause the index to delete the corresponding items // via notifications. DRTupleStreamDisableGuard guard(ExecutorContext::getExecutorContext()->drStream(), ExecutorContext::getExecutorContext()->drReplicatedStream()); m_iter->reset(); TableTuple tuple; while (m_iter->next(tuple)) { if (!tuple.isPendingDelete()) { m_surgeon.deleteTuple(tuple); } } }
/** * Clean up after consuming indexed tuples. */ void ElasticIndexReadContext::deleteStreamedTuples() { // Delete the indexed tuples that were streamed. // Undo token release will cause the index to delete the corresponding items // via notifications. ExecutorContext::getExecutorContext()->drStream()->m_enabled = false; //Not unused, but GCC doesn't think a destructor counts as use class DisableDRGuard { public: ~DisableDRGuard() { ExecutorContext::getExecutorContext()->drStream()->m_enabled = true; } } __attribute__((unused)) guard; m_iter->reset(); TableTuple tuple; while (m_iter->next(tuple)) { if (!tuple.isPendingDelete()) { m_surgeon.deleteTuple(tuple); } } }
bool IndexScanExecutor::p_execute(const NValueArray ¶ms) { assert(m_node); assert(m_node == dynamic_cast<IndexScanPlanNode*>(m_abstractNode)); // update local target table with its most recent reference Table* targetTable = m_node->getTargetTable(); TableIndex *tableIndex = targetTable->index(m_node->getTargetIndexName()); IndexCursor indexCursor(tableIndex->getTupleSchema()); TableTuple searchKey(tableIndex->getKeySchema()); searchKey.moveNoHeader(m_searchKeyBackingStore); assert(m_lookupType != INDEX_LOOKUP_TYPE_EQ || searchKey.getSchema()->columnCount() == m_numOfSearchkeys); int activeNumOfSearchKeys = m_numOfSearchkeys; IndexLookupType localLookupType = m_lookupType; SortDirectionType localSortDirection = m_sortDirection; // // INLINE LIMIT // LimitPlanNode* limit_node = dynamic_cast<LimitPlanNode*>(m_abstractNode->getInlinePlanNode(PLAN_NODE_TYPE_LIMIT)); TableTuple temp_tuple; ProgressMonitorProxy pmp(m_engine, this); if (m_aggExec != NULL) { const TupleSchema * inputSchema = tableIndex->getTupleSchema(); if (m_projectionNode != NULL) { inputSchema = m_projectionNode->getOutputTable()->schema(); } temp_tuple = m_aggExec->p_execute_init(params, &pmp, inputSchema, m_outputTable); } else { temp_tuple = m_outputTable->tempTuple(); } // Short-circuit an empty scan if (m_node->isEmptyScan()) { VOLT_DEBUG ("Empty Index Scan :\n %s", m_outputTable->debug().c_str()); if (m_aggExec != NULL) { m_aggExec->p_execute_finish(); } return true; } // // SEARCH KEY // bool earlyReturnForSearchKeyOutOfRange = false; searchKey.setAllNulls(); VOLT_TRACE("Initial (all null) search key: '%s'", searchKey.debugNoHeader().c_str()); for (int ctr = 0; ctr < activeNumOfSearchKeys; ctr++) { NValue candidateValue = m_searchKeyArray[ctr]->eval(NULL, NULL); if (candidateValue.isNull()) { // when any part of the search key is NULL, the result is false when it compares to anything. // do early return optimization, our index comparator may not handle null comparison correctly. earlyReturnForSearchKeyOutOfRange = true; break; } try { searchKey.setNValue(ctr, candidateValue); } catch (const SQLException &e) { // This next bit of logic handles underflow, overflow and search key length // exceeding variable length column size (variable lenght mismatch) when // setting up the search keys. // e.g. TINYINT > 200 or INT <= 6000000000 // VarChar(3 bytes) < "abcd" or VarChar(3) > "abbd" // re-throw if not an overflow, underflow or variable length mismatch // currently, it's expected to always be an overflow or underflow if ((e.getInternalFlags() & (SQLException::TYPE_OVERFLOW | SQLException::TYPE_UNDERFLOW | SQLException::TYPE_VAR_LENGTH_MISMATCH)) == 0) { throw e; } // handle the case where this is a comparison, rather than equality match // comparison is the only place where the executor might return matching tuples // e.g. TINYINT < 1000 should return all values if ((localLookupType != INDEX_LOOKUP_TYPE_EQ) && (ctr == (activeNumOfSearchKeys - 1))) { if (e.getInternalFlags() & SQLException::TYPE_OVERFLOW) { if ((localLookupType == INDEX_LOOKUP_TYPE_GT) || (localLookupType == INDEX_LOOKUP_TYPE_GTE)) { // gt or gte when key overflows returns nothing except inline agg earlyReturnForSearchKeyOutOfRange = true; break; } else { // for overflow on reverse scan, we need to // do a forward scan to find the correct start // point, which is exactly what LTE would do. // so, set the lookupType to LTE and the missing // searchkey will be handled by extra post filters localLookupType = INDEX_LOOKUP_TYPE_LTE; } } if (e.getInternalFlags() & SQLException::TYPE_UNDERFLOW) { if ((localLookupType == INDEX_LOOKUP_TYPE_LT) || (localLookupType == INDEX_LOOKUP_TYPE_LTE)) { // lt or lte when key underflows returns nothing except inline agg earlyReturnForSearchKeyOutOfRange = true; break; } else { // don't allow GTE because it breaks null handling localLookupType = INDEX_LOOKUP_TYPE_GT; } } if (e.getInternalFlags() & SQLException::TYPE_VAR_LENGTH_MISMATCH) { // shrink the search key and add the updated key to search key table tuple searchKey.shrinkAndSetNValue(ctr, candidateValue); // search will be performed on shrinked key, so update lookup operation // to account for it switch (localLookupType) { case INDEX_LOOKUP_TYPE_LT: case INDEX_LOOKUP_TYPE_LTE: localLookupType = INDEX_LOOKUP_TYPE_LTE; break; case INDEX_LOOKUP_TYPE_GT: case INDEX_LOOKUP_TYPE_GTE: localLookupType = INDEX_LOOKUP_TYPE_GT; break; default: assert(!"IndexScanExecutor::p_execute - can't index on not equals"); return false; } } // if here, means all tuples with the previous searchkey // columns need to be scanned. Note, if only one column, // then all tuples will be scanned. Only exception to this // case is setting of search key in search tuple was due // to search key length exceeding the search column length // of variable length type if (!(e.getInternalFlags() & SQLException::TYPE_VAR_LENGTH_MISMATCH)) { // for variable length mismatch error, the needed search key to perform the search // has been generated and added to the search tuple. So no need to decrement // activeNumOfSearchKeys activeNumOfSearchKeys--; } if (localSortDirection == SORT_DIRECTION_TYPE_INVALID) { localSortDirection = SORT_DIRECTION_TYPE_ASC; } } // if a EQ comparison is out of range, then return no tuples else { earlyReturnForSearchKeyOutOfRange = true; break; } break; } } if (earlyReturnForSearchKeyOutOfRange) { if (m_aggExec != NULL) { m_aggExec->p_execute_finish(); } return true; } assert((activeNumOfSearchKeys == 0) || (searchKey.getSchema()->columnCount() > 0)); VOLT_TRACE("Search key after substitutions: '%s', # of active search keys: %d", searchKey.debugNoHeader().c_str(), activeNumOfSearchKeys); // // END EXPRESSION // AbstractExpression* end_expression = m_node->getEndExpression(); if (end_expression != NULL) { VOLT_DEBUG("End Expression:\n%s", end_expression->debug(true).c_str()); } // // POST EXPRESSION // AbstractExpression* post_expression = m_node->getPredicate(); if (post_expression != NULL) { VOLT_DEBUG("Post Expression:\n%s", post_expression->debug(true).c_str()); } // INITIAL EXPRESSION AbstractExpression* initial_expression = m_node->getInitialExpression(); if (initial_expression != NULL) { VOLT_DEBUG("Initial Expression:\n%s", initial_expression->debug(true).c_str()); } // // SKIP NULL EXPRESSION // AbstractExpression* skipNullExpr = m_node->getSkipNullPredicate(); // For reverse scan edge case NULL values and forward scan underflow case. if (skipNullExpr != NULL) { VOLT_DEBUG("COUNT NULL Expression:\n%s", skipNullExpr->debug(true).c_str()); } // // An index scan has three parts: // (1) Lookup tuples using the search key // (2) For each tuple that comes back, check whether the // end_expression is false. // If it is, then we stop scanning. Otherwise... // (3) Check whether the tuple satisfies the post expression. // If it does, then add it to the output table // // Use our search key to prime the index iterator // Now loop through each tuple given to us by the iterator // TableTuple tuple; if (activeNumOfSearchKeys > 0) { VOLT_TRACE("INDEX_LOOKUP_TYPE(%d) m_numSearchkeys(%d) key:%s", localLookupType, activeNumOfSearchKeys, searchKey.debugNoHeader().c_str()); if (localLookupType == INDEX_LOOKUP_TYPE_EQ) { tableIndex->moveToKey(&searchKey, indexCursor); } else if (localLookupType == INDEX_LOOKUP_TYPE_GT) { tableIndex->moveToGreaterThanKey(&searchKey, indexCursor); } else if (localLookupType == INDEX_LOOKUP_TYPE_GTE) { tableIndex->moveToKeyOrGreater(&searchKey, indexCursor); } else if (localLookupType == INDEX_LOOKUP_TYPE_LT) { tableIndex->moveToLessThanKey(&searchKey, indexCursor); } else if (localLookupType == INDEX_LOOKUP_TYPE_LTE) { // find the entry whose key is greater than search key, // do a forward scan using initialExpr to find the correct // start point to do reverse scan bool isEnd = tableIndex->moveToGreaterThanKey(&searchKey, indexCursor); if (isEnd) { tableIndex->moveToEnd(false, indexCursor); } else { while (!(tuple = tableIndex->nextValue(indexCursor)).isNullTuple()) { pmp.countdownProgress(); if (initial_expression != NULL && !initial_expression->eval(&tuple, NULL).isTrue()) { // just passed the first failed entry, so move 2 backward tableIndex->moveToBeforePriorEntry(indexCursor); break; } } if (tuple.isNullTuple()) { tableIndex->moveToEnd(false, indexCursor); } } } else { return false; } } else { bool toStartActually = (localSortDirection != SORT_DIRECTION_TYPE_DESC); tableIndex->moveToEnd(toStartActually, indexCursor); } int tuple_ctr = 0; int tuples_skipped = 0; // for offset int limit = -1; int offset = -1; if (limit_node != NULL) { limit_node->getLimitAndOffsetByReference(params, limit, offset); } // // We have to different nextValue() methods for different lookup types // while ((limit == -1 || tuple_ctr < limit) && ((localLookupType == INDEX_LOOKUP_TYPE_EQ && !(tuple = tableIndex->nextValueAtKey(indexCursor)).isNullTuple()) || ((localLookupType != INDEX_LOOKUP_TYPE_EQ || activeNumOfSearchKeys == 0) && !(tuple = tableIndex->nextValue(indexCursor)).isNullTuple()))) { if (tuple.isPendingDelete()) { continue; } VOLT_TRACE("LOOPING in indexscan: tuple: '%s'\n", tuple.debug("tablename").c_str()); pmp.countdownProgress(); // // First check to eliminate the null index rows for UNDERFLOW case only // if (skipNullExpr != NULL) { if (skipNullExpr->eval(&tuple, NULL).isTrue()) { VOLT_DEBUG("Index scan: find out null rows or columns."); continue; } else { skipNullExpr = NULL; } } // // First check whether the end_expression is now false // if (end_expression != NULL && !end_expression->eval(&tuple, NULL).isTrue()) { VOLT_TRACE("End Expression evaluated to false, stopping scan"); break; } // // Then apply our post-predicate to do further filtering // if (post_expression == NULL || post_expression->eval(&tuple, NULL).isTrue()) { // // INLINE OFFSET // if (tuples_skipped < offset) { tuples_skipped++; continue; } tuple_ctr++; if (m_projector.numSteps() > 0) { m_projector.exec(temp_tuple, tuple); if (m_aggExec != NULL) { if (m_aggExec->p_execute_tuple(temp_tuple)) { break; } } else { m_outputTable->insertTupleNonVirtual(temp_tuple); } } else { if (m_aggExec != NULL) { if (m_aggExec->p_execute_tuple(tuple)) { break; } } else { // // Straight Insert // m_outputTable->insertTupleNonVirtual(tuple); } } pmp.countdownProgress(); } } if (m_aggExec != NULL) { m_aggExec->p_execute_finish(); } VOLT_DEBUG ("Index Scanned :\n %s", m_outputTable->debug().c_str()); return true; }