bool NestLoopExecutor::p_init(AbstractPlanNode* abstract_node, TempTableLimits* limits) { VOLT_TRACE("init NestLoop Executor"); NestLoopPlanNode* node = dynamic_cast<NestLoopPlanNode*>(abstract_node); assert(node); // Create output table based on output schema from the plan setTempOutputTable(limits); assert(m_tmpOutputTable); // NULL tuple for outer join if (node->getJoinType() == JOIN_TYPE_LEFT) { Table* inner_table = node->getInputTable(1); assert(inner_table); m_null_tuple.init(inner_table->schema()); } // Inline aggregation can be serial, partial or hash m_aggExec = voltdb::getInlineAggregateExecutor(m_abstractNode); return true; }
bool NestLoopExecutor::p_init(AbstractPlanNode* abstract_node, TempTableLimits* limits) { VOLT_TRACE("init NestLoop Executor"); NestLoopPlanNode* node = dynamic_cast<NestLoopPlanNode*>(abstract_node); assert(node); // Create output table based on output schema from the plan setTempOutputTable(limits); // NULL tuple for outer join if (node->getJoinType() == JOIN_TYPE_LEFT) { Table* inner_table = node->getInputTables()[1]; assert(inner_table); m_null_tuple.init(inner_table->schema()); } return true; }
bool NestLoopExecutor::p_execute(const NValueArray ¶ms) { VOLT_DEBUG("executing NestLoop..."); NestLoopPlanNode* node = dynamic_cast<NestLoopPlanNode*>(m_abstractNode); assert(node); assert(node->getInputTables().size() == 2); Table* output_table_ptr = node->getOutputTable(); assert(output_table_ptr); // output table must be a temp table TempTable* output_table = dynamic_cast<TempTable*>(output_table_ptr); assert(output_table); Table* outer_table = node->getInputTables()[0]; assert(outer_table); Table* inner_table = node->getInputTables()[1]; assert(inner_table); VOLT_TRACE ("input table left:\n %s", outer_table->debug().c_str()); VOLT_TRACE ("input table right:\n %s", inner_table->debug().c_str()); // // Pre Join Expression // AbstractExpression *preJoinPredicate = node->getPreJoinPredicate(); if (preJoinPredicate) { preJoinPredicate->substitute(params); VOLT_TRACE ("Pre Join predicate: %s", preJoinPredicate == NULL ? "NULL" : preJoinPredicate->debug(true).c_str()); } // // Join Expression // AbstractExpression *joinPredicate = node->getJoinPredicate(); if (joinPredicate) { joinPredicate->substitute(params); VOLT_TRACE ("Join predicate: %s", joinPredicate == NULL ? "NULL" : joinPredicate->debug(true).c_str()); } // // Where Expression // AbstractExpression *wherePredicate = node->getWherePredicate(); if (wherePredicate) { wherePredicate->substitute(params); VOLT_TRACE ("Where predicate: %s", wherePredicate == NULL ? "NULL" : wherePredicate->debug(true).c_str()); } // Join type JoinType join_type = node->getJoinType(); assert(join_type == JOIN_TYPE_INNER || join_type == JOIN_TYPE_LEFT); int outer_cols = outer_table->columnCount(); int inner_cols = inner_table->columnCount(); TableTuple outer_tuple(node->getInputTables()[0]->schema()); TableTuple inner_tuple(node->getInputTables()[1]->schema()); TableTuple &joined = output_table->tempTuple(); TableTuple null_tuple = m_null_tuple; TableIterator iterator0 = outer_table->iterator(); while (iterator0.next(outer_tuple)) { // did this loop body find at least one match for this tuple? bool match = false; // For outer joins if outer tuple fails pre-join predicate // (join expression based on the outer table only) // it can't match any of inner tuples if (preJoinPredicate == NULL || preJoinPredicate->eval(&outer_tuple, NULL).isTrue()) { // populate output table's temp tuple with outer table's values // probably have to do this at least once - avoid doing it many // times per outer tuple joined.setNValues(0, outer_tuple, 0, outer_cols); TableIterator iterator1 = inner_table->iterator(); while (iterator1.next(inner_tuple)) { // Apply join filter to produce matches for each outer that has them, // then pad unmatched outers, then filter them all if (joinPredicate == NULL || joinPredicate->eval(&outer_tuple, &inner_tuple).isTrue()) { match = true; // Filter the joined tuple if (wherePredicate == NULL || wherePredicate->eval(&outer_tuple, &inner_tuple).isTrue()) { // Matched! Complete the joined tuple with the inner column values. joined.setNValues(outer_cols, inner_tuple, 0, inner_cols); output_table->insertTupleNonVirtual(joined); } } } } // // Left Outer Join // if (join_type == JOIN_TYPE_LEFT && !match) { // Still needs to pass the filter if (wherePredicate == NULL || wherePredicate->eval(&outer_tuple, &null_tuple).isTrue()) { joined.setNValues(outer_cols, null_tuple, 0, inner_cols); output_table->insertTupleNonVirtual(joined); } } } return (true); }
bool NestLoopExecutor::p_execute(const NValueArray ¶ms, ReadWriteTracker *tracker) { VOLT_DEBUG("executing NestLoop..."); NestLoopPlanNode* node = dynamic_cast<NestLoopPlanNode*>(abstract_node); assert(node); assert(node->getInputTables().size() == 2); Table* output_table_ptr = node->getOutputTable(); assert(output_table_ptr); // output table must be a temp table TempTable* output_table = dynamic_cast<TempTable*>(output_table_ptr); assert(output_table); Table* outer_table = node->getInputTables()[0]; assert(outer_table); Table* inner_table = node->getInputTables()[1]; assert(inner_table); VOLT_TRACE ("input table left:\n %s", outer_table->debug().c_str()); VOLT_TRACE ("input table right:\n %s", inner_table->debug().c_str()); // // Join Expression // AbstractExpression *predicate = node->getPredicate(); if (predicate) { predicate->substitute(params); VOLT_TRACE ("predicate: %s", predicate == NULL ? "NULL" : predicate->debug(true).c_str()); } int outer_cols = outer_table->columnCount(); int inner_cols = inner_table->columnCount(); TableTuple outer_tuple(node->getInputTables()[0]->schema()); TableTuple inner_tuple(node->getInputTables()[1]->schema()); TableTuple &joined = output_table->tempTuple(); TableIterator iterator0(outer_table); while (iterator0.next(outer_tuple)) { // populate output table's temp tuple with outer table's values // probably have to do this at least once - avoid doing it many // times per outer tuple for (int col_ctr = 0; col_ctr < outer_cols; col_ctr++) { joined.setNValue(col_ctr, outer_tuple.getNValue(col_ctr)); } TableIterator iterator1(inner_table); while (iterator1.next(inner_tuple)) { if (predicate == NULL || predicate->eval(&outer_tuple, &inner_tuple).isTrue()) { // Matched! Complete the joined tuple with the inner column values. for (int col_ctr = 0; col_ctr < inner_cols; col_ctr++) { joined.setNValue(col_ctr + outer_cols, inner_tuple.getNValue(col_ctr)); } output_table->insertTupleNonVirtual(joined); } } } return (true); }
bool NestLoopExecutor::p_init(AbstractPlanNode* abstract_node, const catalog::Database* catalog_db, int* tempTableMemoryInBytes) { VOLT_TRACE("init NestLoop Executor"); assert(tempTableMemoryInBytes); NestLoopPlanNode* node = dynamic_cast<NestLoopPlanNode*>(abstract_node); assert(node); // produce the fully joined schema relying on a later projection // to narrow the output later as required. assert(node->getInputTables().size() == 2); const TupleSchema *first = node->getInputTables()[0]->schema(); const TupleSchema *second = node->getInputTables()[1]->schema(); TupleSchema *schema = TupleSchema::createTupleSchema(first, second); int combinedColumnCount = first->columnCount() + second->columnCount(); std::string *columnNames = new std::string[combinedColumnCount]; std::vector<int> outputColumnGuids; int index = 0; for (int ctr = 0; ctr < 2; ctr++) { assert(node->getInputTables()[ctr]); for (int col_ctr = 0, col_cnt = node->getInputTables()[ctr]->columnCount(); col_ctr < col_cnt; col_ctr++, index++) { outputColumnGuids. push_back(node->getChildren()[ctr]->getOutputColumnGuids()[col_ctr]); columnNames[index] = node->getInputTables()[ctr]->columnName(col_ctr); } } // Set the mapping of column names to column indexes in output tables node->setOutputColumnGuids(outputColumnGuids); // create the output table node->setOutputTable( TableFactory::getTempTable( node->getInputTables()[0]->databaseId(), "temp", schema, columnNames, tempTableMemoryInBytes)); // for each tuple value expression in the predicate, determine // which tuple is being represented. Tuple could come from outer // table or inner table. Configure the predicate to use the correct // eval() tuple parameter. By convention, eval's first parameter // will always be the outer table and its second parameter the inner const AbstractExpression *predicate = node->getPredicate(); std::stack<const AbstractExpression*> stack; while (predicate != NULL) { const AbstractExpression *left = predicate->getLeft(); const AbstractExpression *right = predicate->getRight(); if (right != NULL) { if (right->getExpressionType() == EXPRESSION_TYPE_VALUE_TUPLE) { if (!assignTupleValueIndex(const_cast<AbstractExpression*>(right), node->getInputTables()[0]->name(), node->getInputTables()[1]->name())) { delete [] columnNames; return false; } } // remember the right node - must visit its children stack.push(right); } if (left != NULL) { if (left->getExpressionType() == EXPRESSION_TYPE_VALUE_TUPLE) { if (!assignTupleValueIndex(const_cast<AbstractExpression*>(left), node->getInputTables()[0]->name(), node->getInputTables()[1]->name())) { delete [] columnNames; return false; } } } predicate = left; if (!predicate && !stack.empty()) { predicate = stack.top(); stack.pop(); } } delete[] columnNames; return true; }
bool NestLoopExecutor::p_execute(const NValueArray ¶ms) { VOLT_DEBUG("executing NestLoop..."); NestLoopPlanNode* node = dynamic_cast<NestLoopPlanNode*>(m_abstractNode); assert(node); assert(node->getInputTableCount() == 2); // output table must be a temp table assert(m_tmpOutputTable); Table* outer_table = node->getInputTable(); assert(outer_table); Table* inner_table = node->getInputTable(1); assert(inner_table); VOLT_TRACE ("input table left:\n %s", outer_table->debug().c_str()); VOLT_TRACE ("input table right:\n %s", inner_table->debug().c_str()); // // Pre Join Expression // AbstractExpression *preJoinPredicate = node->getPreJoinPredicate(); if (preJoinPredicate) { VOLT_TRACE ("Pre Join predicate: %s", preJoinPredicate == NULL ? "NULL" : preJoinPredicate->debug(true).c_str()); } // // Join Expression // AbstractExpression *joinPredicate = node->getJoinPredicate(); if (joinPredicate) { VOLT_TRACE ("Join predicate: %s", joinPredicate == NULL ? "NULL" : joinPredicate->debug(true).c_str()); } // // Where Expression // AbstractExpression *wherePredicate = node->getWherePredicate(); if (wherePredicate) { VOLT_TRACE ("Where predicate: %s", wherePredicate == NULL ? "NULL" : wherePredicate->debug(true).c_str()); } // Join type JoinType join_type = node->getJoinType(); assert(join_type == JOIN_TYPE_INNER || join_type == JOIN_TYPE_LEFT); LimitPlanNode* limit_node = dynamic_cast<LimitPlanNode*>(node->getInlinePlanNode(PLAN_NODE_TYPE_LIMIT)); int limit = -1; int offset = -1; if (limit_node) { limit_node->getLimitAndOffsetByReference(params, limit, offset); } int outer_cols = outer_table->columnCount(); int inner_cols = inner_table->columnCount(); TableTuple outer_tuple(node->getInputTable(0)->schema()); TableTuple inner_tuple(node->getInputTable(1)->schema()); const TableTuple& null_tuple = m_null_tuple.tuple(); TableIterator iterator0 = outer_table->iteratorDeletingAsWeGo(); int tuple_ctr = 0; int tuple_skipped = 0; ProgressMonitorProxy pmp(m_engine, this, inner_table); TableTuple join_tuple; if (m_aggExec != NULL) { VOLT_TRACE("Init inline aggregate..."); const TupleSchema * aggInputSchema = node->getTupleSchemaPreAgg(); join_tuple = m_aggExec->p_execute_init(params, &pmp, aggInputSchema, m_tmpOutputTable); } else { join_tuple = m_tmpOutputTable->tempTuple(); } bool earlyReturned = false; while ((limit == -1 || tuple_ctr < limit) && iterator0.next(outer_tuple)) { pmp.countdownProgress(); // populate output table's temp tuple with outer table's values // probably have to do this at least once - avoid doing it many // times per outer tuple join_tuple.setNValues(0, outer_tuple, 0, outer_cols); // did this loop body find at least one match for this tuple? bool match = false; // For outer joins if outer tuple fails pre-join predicate // (join expression based on the outer table only) // it can't match any of inner tuples if (preJoinPredicate == NULL || preJoinPredicate->eval(&outer_tuple, NULL).isTrue()) { // By default, the delete as we go flag is false. TableIterator iterator1 = inner_table->iterator(); while ((limit == -1 || tuple_ctr < limit) && iterator1.next(inner_tuple)) { pmp.countdownProgress(); // Apply join filter to produce matches for each outer that has them, // then pad unmatched outers, then filter them all if (joinPredicate == NULL || joinPredicate->eval(&outer_tuple, &inner_tuple).isTrue()) { match = true; // Filter the joined tuple if (wherePredicate == NULL || wherePredicate->eval(&outer_tuple, &inner_tuple).isTrue()) { // Check if we have to skip this tuple because of offset if (tuple_skipped < offset) { tuple_skipped++; continue; } ++tuple_ctr; // Matched! Complete the joined tuple with the inner column values. join_tuple.setNValues(outer_cols, inner_tuple, 0, inner_cols); if (m_aggExec != NULL) { if (m_aggExec->p_execute_tuple(join_tuple)) { // Get enough rows for LIMIT earlyReturned = true; break; } } else { m_tmpOutputTable->insertTempTuple(join_tuple); pmp.countdownProgress(); } } } } // END INNER WHILE LOOP } // END IF PRE JOIN CONDITION // // Left Outer Join // if (join_type == JOIN_TYPE_LEFT && !match && (limit == -1 || tuple_ctr < limit)) { // Still needs to pass the filter if (wherePredicate == NULL || wherePredicate->eval(&outer_tuple, &null_tuple).isTrue()) { // Check if we have to skip this tuple because of offset if (tuple_skipped < offset) { tuple_skipped++; continue; } ++tuple_ctr; join_tuple.setNValues(outer_cols, null_tuple, 0, inner_cols); if (m_aggExec != NULL) { if (m_aggExec->p_execute_tuple(join_tuple)) { earlyReturned = true; } } else { m_tmpOutputTable->insertTempTuple(join_tuple); pmp.countdownProgress(); } } } // END IF LEFT OUTER JOIN if (earlyReturned) { // Get enough rows for LIMIT inlined with aggregation break; } } // END OUTER WHILE LOOP if (m_aggExec != NULL) { m_aggExec->p_execute_finish(); } cleanupInputTempTable(inner_table); cleanupInputTempTable(outer_table); return (true); }