bool DistinctExecutor::p_execute(const NValueArray ¶ms) { DistinctPlanNode* node = dynamic_cast<DistinctPlanNode*>(m_abstractNode); assert(node); Table* output_table = node->getOutputTable(); assert(output_table); Table* input_table = node->getInputTables()[0]; assert(input_table); TableIterator iterator = input_table->iterator(); TableTuple tuple(input_table->schema()); // substitute params for distinct expression AbstractExpression *distinctExpression = node->getDistinctExpression(); distinctExpression->substitute(params); std::set<NValue, NValue::ltNValue> found_values; while (iterator.next(tuple)) { // // Check whether this value already exists in our list // NValue tuple_value = distinctExpression->eval(&tuple, NULL); if (found_values.find(tuple_value) == found_values.end()) { found_values.insert(tuple_value); if (!output_table->insertTuple(tuple)) { VOLT_ERROR("Failed to insert tuple from input table '%s' into" " output table '%s'", input_table->name().c_str(), output_table->name().c_str()); return false; } } } return true; }
bool operator()(TableTuple ta, TableTuple tb) { for (size_t i = 0; i < m_keyCount; ++i) { AbstractExpression* k = m_keys[i]; SortDirectionType dir = m_dirs[i]; int cmp = k->eval(&ta, NULL).compare(k->eval(&tb, NULL)); if (dir == SORT_DIRECTION_TYPE_ASC) { if (cmp < 0) return true; if (cmp > 0) return false; } else if (dir == SORT_DIRECTION_TYPE_DESC) { if (cmp < 0) return false; if (cmp > 0) return true; } else { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "Attempted to sort using" " SORT_DIRECTION_TYPE_INVALID"); } } return false; // ta == tb on these keys }
TupleSchema* AbstractPlanNode::generateTupleSchema(const std::vector<SchemaColumn*>& outputSchema) { int schema_size = static_cast<int>(outputSchema.size()); vector<voltdb::ValueType> columnTypes; vector<int32_t> columnSizes; vector<bool> columnAllowNull(schema_size, true); vector<bool> columnInBytes; for (int i = 0; i < schema_size; i++) { //TODO: SchemaColumn is a sad little class that holds an expression pointer, // a column name that only really comes in handy in one quirky special case, // (see UpdateExecutor::p_init) and a bunch of other stuff that doesn't get used. // Someone should put that class out of our misery. SchemaColumn* col = outputSchema[i]; AbstractExpression * expr = col->getExpression(); columnTypes.push_back(expr->getValueType()); columnSizes.push_back(expr->getValueSize()); columnInBytes.push_back(expr->getInBytes()); } TupleSchema* schema = TupleSchema::createTupleSchema(columnTypes, columnSizes, columnAllowNull, columnInBytes); return schema; }
void test_interpreter() { ContextInterpreter* pContext = new ContextInterpreter(); AbstractExpression* pTe = new TerminalExpression("hello world"); AbstractExpression* pNte = new NonterminalExpression(pTe, 3); pNte->Interpreter(*pContext); }
int main(int argc,char* argv[]) { Context* c = new Context(); AbstractExpression* te = new TerminalExpression("hello"); AbstractExpression* nte = new NonterminalExpression(te,2); nte->Interpret(*c); delete nte; delete c; return 0; }
AbstractExpression *AbstractExpression::CreateExpressionTree( json_spirit::Object &obj) { AbstractExpression *expr = AbstractExpression::CreateExpressionTreeRecurse(obj); if (expr) expr->InitParamShortCircuits(); return expr; }
bool MaterializedScanExecutor::p_execute(const NValueArray ¶ms) { MaterializedScanPlanNode* node = dynamic_cast<MaterializedScanPlanNode*>(m_abstractNode); assert(node); // output table has one column Table* output_table = node->getOutputTable(); TableTuple& tmptup = output_table->tempTuple(); assert(output_table); assert ((int)output_table->columnCount() == 1); // get the output type const TupleSchema::ColumnInfo *columnInfo = output_table->schema()->getColumnInfo(0); ValueType outputType = columnInfo->getVoltType(); bool outputCantBeNull = !columnInfo->allowNull; AbstractExpression* rowsExpression = node->getTableRowsExpression(); assert(rowsExpression); // get array nvalue NValue arrayNValue = rowsExpression->eval(); SortDirectionType sort_direction = node->getSortDirection(); // make a set to eliminate unique values in O(nlogn) time std::vector<NValue> sortedUniques; // iterate over the array of values and build a sorted/deduped set of // values that don't overflow or violate unique constaints arrayNValue.castAndSortAndDedupArrayForInList(outputType, sortedUniques); // insert all items in the set in order if (sort_direction != SORT_DIRECTION_TYPE_DESC) { std::vector<NValue>::const_iterator iter; for (iter = sortedUniques.begin(); iter != sortedUniques.end(); iter++) { if ((*iter).isNull() && outputCantBeNull) { continue; } tmptup.setNValue(0, *iter); output_table->insertTuple(tmptup); } } else { std::vector<NValue>::reverse_iterator reverse_iter; for (reverse_iter = sortedUniques.rbegin(); reverse_iter != sortedUniques.rend(); reverse_iter++) { if ((*reverse_iter).isNull() && outputCantBeNull) { continue; } tmptup.setNValue(0, *reverse_iter); output_table->insertTuple(tmptup); } } VOLT_TRACE("\n%s\n", output_table->debug().c_str()); VOLT_DEBUG("Finished Materializing a Table"); return true; }
TEST_F(FilterTest, ComplexFilter) { // WHERE val1=1 AND val2=2 AND val3=3 AND val4=4 // shared_ptr<AbstractExpression> equal1 // = ComparisonExpression::getInstance(EXPRESSION_TYPE_COMPARE_EQUAL, TupleValueExpression::getInstance(1), ConstantValueExpression::getInstance(voltdb::Value::newBigIntValue(1))); // shared_ptr<AbstractExpression> equal2 // = ComparisonExpression::getInstance(EXPRESSION_TYPE_COMPARE_EQUAL, TupleValueExpression::getInstance(2), ConstantValueExpression::getInstance(voltdb::Value::newBigIntValue(2))); // shared_ptr<AbstractExpression> equal3 // = ComparisonExpression::getInstance(EXPRESSION_TYPE_COMPARE_EQUAL, TupleValueExpression::getInstance(3), ConstantValueExpression::getInstance(voltdb::Value::newBigIntValue(3))); // shared_ptr<AbstractExpression> equal4 // = ComparisonExpression::getInstance(EXPRESSION_TYPE_COMPARE_EQUAL, TupleValueExpression::getInstance(4), ConstantValueExpression::getInstance(voltdb::Value::newBigIntValue(4))); // // shared_ptr<AbstractExpression> predicate3 // = ConjunctionExpression::getInstance(EXPRESSION_TYPE_CONJUNCTION_AND, equal3, equal4); // shared_ptr<AbstractExpression> predicate2 // = ConjunctionExpression::getInstance(EXPRESSION_TYPE_CONJUNCTION_AND, equal2, predicate3); // // ConjunctionExpression predicate(EXPRESSION_TYPE_CONJUNCTION_AND, equal1, predicate2); AbstractExpression *equal1 = comparisonFactory(EXPRESSION_TYPE_COMPARE_EQUAL, new TupleValueExpression(1, std::string("tablename"), std::string("colname")), constantValueFactory(ValueFactory::getBigIntValue(1))); AbstractExpression *equal2 = comparisonFactory(EXPRESSION_TYPE_COMPARE_EQUAL, new TupleValueExpression(2, std::string("tablename"), std::string("colname")), constantValueFactory(ValueFactory::getBigIntValue(2))); AbstractExpression *equal3 = comparisonFactory(EXPRESSION_TYPE_COMPARE_EQUAL, new TupleValueExpression(3, std::string("tablename"), std::string("colname")), constantValueFactory(ValueFactory::getBigIntValue(3))); AbstractExpression *equal4 = comparisonFactory(EXPRESSION_TYPE_COMPARE_EQUAL, new TupleValueExpression(4, std::string("tablename"), std::string("colname")), constantValueFactory(ValueFactory::getBigIntValue(4))); AbstractExpression *predicate3 = conjunctionFactory(EXPRESSION_TYPE_CONJUNCTION_AND, equal3, equal4); AbstractExpression *predicate2 = conjunctionFactory(EXPRESSION_TYPE_CONJUNCTION_AND, equal2, predicate3); AbstractExpression *predicate = conjunctionFactory(EXPRESSION_TYPE_CONJUNCTION_AND, equal1, predicate2); // ::printf("\nFilter:%s\n", predicate->debug().c_str()); int count = 0; TableIterator iter = table->iterator(); TableTuple match(table->schema()); while (iter.next(match)) { if (predicate->eval(&match, NULL).isTrue()) { //::printf(" match:%s\n", match->debug(table).c_str()); ++count; } } ASSERT_EQ(5, count); delete predicate; }
// ------------------------------------------------------------------ // SERIALIZATION METHODS // ------------------------------------------------------------------ AbstractExpression* AbstractExpression::buildExpressionTree(json_spirit::Object &obj) { AbstractExpression * exp = AbstractExpression::buildExpressionTree_recurse(obj); if (exp) exp->initParamShortCircuits(); return exp; }
// ------------------------------------------------------------------ // SERIALIZATION METHODS // ------------------------------------------------------------------ AbstractExpression* AbstractExpression::buildExpressionTree(PlannerDomValue obj) { AbstractExpression * exp = AbstractExpression::buildExpressionTree_recurse(obj); if (exp) exp->initParamShortCircuits(); return exp; }
int main() { Context* con1 = new Context("Hello world."); AbstractExpression* tinter = new TerminalExpression(); AbstractExpression* nont = new NonTerminalExpression(); tinter->Interpreter(con1); nont->Interpreter(con1); return 0; }
bool AbstractExecutor::TupleComparer::operator()(TableTuple ta, TableTuple tb) const { for (size_t i = 0; i < m_keyCount; ++i) { AbstractExpression* k = m_keys[i]; SortDirectionType dir = m_dirs[i]; int cmp = k->eval(&ta, NULL).compare(k->eval(&tb, NULL)); if (cmp < 0) return (dir == SORT_DIRECTION_TYPE_ASC); if (cmp > 0) return (dir == SORT_DIRECTION_TYPE_DESC); } return false; // ta == tb on these keys }
TEST_F(FilterTest, SubstituteFilter) { // WHERE id <= 20 AND val4=$1 // shared_ptr<AbstractExpression> equal1 // = ComparisonExpression::getInstance(EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO, TupleValueExpression::getInstance(0), ConstantValueExpression::getInstance(voltdb::Value::newBigIntValue(20))); // // shared_ptr<AbstractExpression> equal2 // = ComparisonExpression::getInstance(EXPRESSION_TYPE_COMPARE_EQUAL, TupleValueExpression::getInstance(4), ParameterValueExpression::getInstance(0)); // // ConjunctionExpression predicate(EXPRESSION_TYPE_CONJUNCTION_AND, equal1, equal2); AbstractExpression *tv1 = new TupleValueExpression(0, std::string("tablename"), std::string("colname")); AbstractExpression *cv1 = constantValueFactory(ValueFactory::getBigIntValue(20)); AbstractExpression *equal1 = comparisonFactory(EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO, tv1, cv1); AbstractExpression *tv2 = new TupleValueExpression(4, std::string("tablename"), std::string("colname")); AbstractExpression *pv2 = parameterValueFactory(0); AbstractExpression *equal2 = comparisonFactory(EXPRESSION_TYPE_COMPARE_EQUAL, tv2, pv2); AbstractExpression *predicate = conjunctionFactory(EXPRESSION_TYPE_CONJUNCTION_AND, equal1, equal2); // ::printf("\nFilter:%s\n", predicate->debug().c_str()); for (int64_t implantedValue = 1; implantedValue < 5; ++implantedValue) { NValueArray params(1); params[0] = ValueFactory::getBigIntValue(implantedValue); predicate->substitute(params); // ::printf("\nSubstituted Filter:%s\n", predicate->debug().c_str()); // ::printf("\tLEFT: %s\n", predicate->getLeft()->debug().c_str()); // ::printf("\tRIGHT: %s\n", predicate->getRight()->debug().c_str()); int count = 0; TableIterator iter = table->iterator(); TableTuple match(table->schema()); while (iter.next(match)) { if (predicate->eval(&match, NULL).isTrue()) { ++count; } } ASSERT_EQ(3, count); } delete predicate; }
/* * * Helper method responsible for inserting the results of the * aggregation into a new tuple in the output table as well as passing * through any additional columns from the input table. */ inline void WindowFunctionExecutor::insertOutputTuple() { TableTuple& tempTuple = m_tmpOutputTable->tempTuple(); // We copy the aggregate values into the output tuple, // then the passthrough columns. WindowAggregate** aggs = m_aggregateRow->getAggregates(); for (int ii = 0; ii < getAggregateCount(); ii++) { NValue result = aggs[ii]->finalize(tempTuple.getSchema()->columnType(ii)); tempTuple.setNValue(ii, result); } VOLT_TRACE("Setting passthrough columns"); size_t tupleSize = tempTuple.sizeInValues(); for (int ii = getAggregateCount(); ii < tupleSize; ii += 1) { AbstractExpression *expr = m_outputColumnExpressions[ii]; tempTuple.setNValue(ii, expr->eval(&(m_aggregateRow->getPassThroughTuple()))); } m_tmpOutputTable->insertTempTuple(tempTuple); VOLT_TRACE("output_table:\n%s", m_tmpOutputTable->debug().c_str()); }
int main(int argc, char* argv[]) { //生成表达式 AbstractExpression* expression; //解析器对象 Context context; //两个变量 VariableExp* varX = new VariableExp("keyX"); VariableExp* varY = new VariableExp("keyY"); /*一个复杂表达式 或(与(常量,变量),与(变量,非(变量)))*/ expression = new OrExp( new AndExp(new ConstantExp(true), varX), new AndExp(varY, new NotExp(varX))); //在解析器中建立外部名字和值的关联 context.Assign(varX, false); context.Assign(varY, true); //递归运算表达式求值 bool result = expression->Interpret(context); cout<<"result: "<<result<<endl; printf("Hello World!\n"); return 0; }
Expression(const AbstractExpression& expr) : pointer(expr.Clone()) { }
/** Given an expression type and a valuetype, find the best * templated ctor to invoke. Several helpers, above, aid in this * pursuit. Each instantiated expression must consume any * class-specific serialization from serialize_io. */ AbstractExpression* ExpressionUtil::expressionFactory(PlannerDomValue obj, ExpressionType et, ValueType vt, int vs, AbstractExpression* lc, AbstractExpression* rc, const std::vector<AbstractExpression*>* args) { AbstractExpression *ret = NULL; switch (et) { // Casts case (EXPRESSION_TYPE_OPERATOR_CAST): ret = castFactory(vt, lc); break; // Operators case (EXPRESSION_TYPE_OPERATOR_PLUS): case (EXPRESSION_TYPE_OPERATOR_MINUS): case (EXPRESSION_TYPE_OPERATOR_MULTIPLY): case (EXPRESSION_TYPE_OPERATOR_DIVIDE): case (EXPRESSION_TYPE_OPERATOR_CONCAT): case (EXPRESSION_TYPE_OPERATOR_MOD): case (EXPRESSION_TYPE_OPERATOR_NOT): case (EXPRESSION_TYPE_OPERATOR_IS_NULL): case (EXPRESSION_TYPE_OPERATOR_EXISTS): ret = operatorFactory(et, lc, rc); break; // Comparisons case (EXPRESSION_TYPE_COMPARE_EQUAL): case (EXPRESSION_TYPE_COMPARE_NOTEQUAL): case (EXPRESSION_TYPE_COMPARE_LESSTHAN): case (EXPRESSION_TYPE_COMPARE_GREATERTHAN): case (EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO): case (EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO): case (EXPRESSION_TYPE_COMPARE_LIKE): case (EXPRESSION_TYPE_COMPARE_IN): ret = comparisonFactory(obj, et, lc, rc); break; // Conjunctions case (EXPRESSION_TYPE_CONJUNCTION_AND): case (EXPRESSION_TYPE_CONJUNCTION_OR): ret = conjunctionFactory(et, lc, rc); break; // Functions and pseudo-functions case (EXPRESSION_TYPE_FUNCTION): { // add the function id int functionId = obj.valueForKey("FUNCTION_ID").asInt(); if (args) { ret = functionFactory(functionId, args); } if ( ! ret) { std::string nameString; if (obj.hasNonNullKey("NAME")) { nameString = obj.valueForKey("NAME").asStr(); } else { nameString = "?"; } raiseFunctionFactoryError(nameString, functionId, args); } } break; case (EXPRESSION_TYPE_VALUE_VECTOR): { // Parse whatever is needed out of obj and pass the pieces to inListFactory // to make it easier to unit test independently of the parsing. // The first argument is used as the list element type. // If the ValueType of the list builder expression needs to be "ARRAY" or something else, // a separate element type attribute will have to be serialized and passed in here. ret = vectorFactory(vt, args); } break; // Constant Values, parameters, tuples case (EXPRESSION_TYPE_VALUE_CONSTANT): ret = constantValueFactory(obj, vt, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_PARAMETER): ret = parameterValueFactory(obj, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_TUPLE): ret = tupleValueFactory(obj, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_TUPLE_ADDRESS): ret = new TupleAddressExpression(); break; case (EXPRESSION_TYPE_VALUE_SCALAR): ret = new ScalarValueExpression(lc); break; case (EXPRESSION_TYPE_HASH_RANGE): ret = hashRangeFactory(obj); break; case (EXPRESSION_TYPE_OPERATOR_CASE_WHEN): ret = caseWhenFactory(vt, lc, rc); break; case (EXPRESSION_TYPE_OPERATOR_ALTERNATIVE): ret = new OperatorAlternativeExpression(lc, rc); break; // Subquery case (EXPRESSION_TYPE_ROW_SUBQUERY): case (EXPRESSION_TYPE_SELECT_SUBQUERY): ret = subqueryFactory(et, obj, args); break; // must handle all known expressions in this factory default: char message[256]; snprintf(message,256, "Invalid ExpressionType '%s' (%d) requested from factory", expressionToString(et).c_str(), (int)et); throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, message); } ret->setValueType(vt); ret->setValueSize(vs); // written thusly to ease testing/inspecting return content. VOLT_TRACE("Created expression %p", ret); return ret; }
bool SeqScanExecutor::p_execute(const NValueArray ¶ms) { SeqScanPlanNode* node = dynamic_cast<SeqScanPlanNode*>(m_abstractNode); assert(node); Table* output_table = node->getOutputTable(); assert(output_table); Table* input_table = (node->isSubQuery()) ? node->getChildren()[0]->getOutputTable(): node->getTargetTable(); assert(input_table); //* for debug */std::cout << "SeqScanExecutor: node id " << node->getPlanNodeId() << //* for debug */ " input table " << (void*)input_table << //* for debug */ " has " << input_table->activeTupleCount() << " tuples " << std::endl; VOLT_TRACE("Sequential Scanning table :\n %s", input_table->debug().c_str()); VOLT_DEBUG("Sequential Scanning table : %s which has %d active, %d" " allocated", input_table->name().c_str(), (int)input_table->activeTupleCount(), (int)input_table->allocatedTupleCount()); // // OPTIMIZATION: NESTED PROJECTION // // Since we have the input params, we need to call substitute to // change any nodes in our expression tree to be ready for the // projection operations in execute // int num_of_columns = -1; ProjectionPlanNode* projection_node = dynamic_cast<ProjectionPlanNode*>(node->getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION)); if (projection_node != NULL) { num_of_columns = static_cast<int> (projection_node->getOutputColumnExpressions().size()); } // // OPTIMIZATION: NESTED LIMIT // How nice! We can also cut off our scanning with a nested limit! // LimitPlanNode* limit_node = dynamic_cast<LimitPlanNode*>(node->getInlinePlanNode(PLAN_NODE_TYPE_LIMIT)); // // OPTIMIZATION: // // If there is no predicate and no Projection for this SeqScan, // then we have already set the node's OutputTable to just point // at the TargetTable. Therefore, there is nothing we more we need // to do here // if (node->getPredicate() != NULL || projection_node != NULL || limit_node != NULL || m_aggExec != NULL) { // // Just walk through the table using our iterator and apply // the predicate to each tuple. For each tuple that satisfies // our expression, we'll insert them into the output table. // TableTuple tuple(input_table->schema()); TableIterator iterator = input_table->iteratorDeletingAsWeGo(); AbstractExpression *predicate = node->getPredicate(); if (predicate) { VOLT_TRACE("SCAN PREDICATE A:\n%s\n", predicate->debug(true).c_str()); } int limit = -1; int offset = -1; if (limit_node) { limit_node->getLimitAndOffsetByReference(params, limit, offset); } int tuple_ctr = 0; int tuple_skipped = 0; TempTable* output_temp_table = dynamic_cast<TempTable*>(output_table); ProgressMonitorProxy pmp(m_engine, this, node->isSubQuery() ? NULL : input_table); TableTuple temp_tuple; if (m_aggExec != NULL) { const TupleSchema * inputSchema = input_table->schema(); if (projection_node != NULL) { inputSchema = projection_node->getOutputTable()->schema(); } temp_tuple = m_aggExec->p_execute_init(params, &pmp, inputSchema, output_temp_table); } else { temp_tuple = output_temp_table->tempTuple(); } while ((limit == -1 || tuple_ctr < limit) && iterator.next(tuple)) { VOLT_TRACE("INPUT TUPLE: %s, %d/%d\n", tuple.debug(input_table->name()).c_str(), tuple_ctr, (int)input_table->activeTupleCount()); pmp.countdownProgress(); // // For each tuple we need to evaluate it against our predicate // if (predicate == NULL || predicate->eval(&tuple, NULL).isTrue()) { // Check if we have to skip this tuple because of offset if (tuple_skipped < offset) { tuple_skipped++; continue; } ++tuple_ctr; // // Nested Projection // Project (or replace) values from input tuple // if (projection_node != NULL) { VOLT_TRACE("inline projection..."); for (int ctr = 0; ctr < num_of_columns; ctr++) { NValue value = projection_node->getOutputColumnExpressions()[ctr]->eval(&tuple, NULL); temp_tuple.setNValue(ctr, value); } if (m_aggExec != NULL) { if (m_aggExec->p_execute_tuple(temp_tuple)) { break; } } else { output_temp_table->insertTupleNonVirtual(temp_tuple); } } else { if (m_aggExec != NULL) { if (m_aggExec->p_execute_tuple(tuple)) { break; } } else { // // Insert the tuple into our output table // output_temp_table->insertTupleNonVirtual(tuple); } } pmp.countdownProgress(); } } if (m_aggExec != NULL) { m_aggExec->p_execute_finish(); } } //* for debug */std::cout << "SeqScanExecutor: node id " << node->getPlanNodeId() << //* for debug */ " output table " << (void*)output_table << //* for debug */ " put " << output_table->activeTupleCount() << " tuples " << std::endl; VOLT_TRACE("\n%s\n", output_table->debug().c_str()); VOLT_DEBUG("Finished Seq scanning"); return true; }
/** Given an expression type and a valuetype, find the best * templated ctor to invoke. Several helpers, above, aid in this * pursuit. Each instantiated expression must consume any * class-specific serialization from serialize_io. */ AbstractExpression* ExpressionUtil::expressionFactory(json_spirit::Object &obj, ExpressionType et, ValueType vt, int vs, AbstractExpression* lc, AbstractExpression* rc, const std::vector<AbstractExpression*>* args) { AbstractExpression *ret = NULL; switch (et) { // Operators case (EXPRESSION_TYPE_OPERATOR_PLUS): case (EXPRESSION_TYPE_OPERATOR_MINUS): case (EXPRESSION_TYPE_OPERATOR_MULTIPLY): case (EXPRESSION_TYPE_OPERATOR_DIVIDE): case (EXPRESSION_TYPE_OPERATOR_CONCAT): case (EXPRESSION_TYPE_OPERATOR_MOD): case (EXPRESSION_TYPE_OPERATOR_CAST): case (EXPRESSION_TYPE_OPERATOR_NOT): case (EXPRESSION_TYPE_OPERATOR_IS_NULL): ret = operatorFactory(et, lc, rc); break; // Comparisons case (EXPRESSION_TYPE_COMPARE_EQUAL): case (EXPRESSION_TYPE_COMPARE_NOTEQUAL): case (EXPRESSION_TYPE_COMPARE_LESSTHAN): case (EXPRESSION_TYPE_COMPARE_GREATERTHAN): case (EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO): case (EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO): case (EXPRESSION_TYPE_COMPARE_LIKE): ret = comparisonFactory( et, lc, rc); break; // Conjunctions case (EXPRESSION_TYPE_CONJUNCTION_AND): case (EXPRESSION_TYPE_CONJUNCTION_OR): ret = conjunctionFactory(et, lc, rc); break; // Functions and pseudo-functions case (EXPRESSION_TYPE_FUNCTION): { // add the function id json_spirit::Value functionIdValue = json_spirit::find_value(obj, "FUNCTION_ID"); if (functionIdValue == json_spirit::Value::null) { throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, "ExpressionUtil::" "expressionFactory:" " Couldn't find FUNCTION_ID value"); } int functionId = functionIdValue.get_int(); ret = functionFactory(functionId, args); if ( ! ret) { json_spirit::Value functionNameValue = json_spirit::find_value(obj, "NAME"); std::string nameString; if (functionNameValue == json_spirit::Value::null) { nameString = "?"; } else { nameString = functionNameValue.get_str(); } char aliasBuffer[256]; json_spirit::Value functionAliasValue = json_spirit::find_value(obj, "ALIAS"); if (functionAliasValue == json_spirit::Value::null) { aliasBuffer[0] = '\0'; } else { std::string aliasString = functionAliasValue.get_str(); snprintf(aliasBuffer, sizeof(aliasBuffer), " aliased to '%s'", aliasString.c_str()); } char fn_message[1024]; snprintf(fn_message, sizeof(fn_message), "SQL function '%s'%s with ID (%d) with (%d) parameters is not implemented in VoltDB (or may have been incorrectly parsed)", nameString.c_str(), aliasBuffer, functionId, (int)args->size()); throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, fn_message); } } break; // Constant Values, parameters, tuples case (EXPRESSION_TYPE_VALUE_CONSTANT): ret = constantValueFactory(obj, vt, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_PARAMETER): ret = parameterValueFactory(obj, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_TUPLE): ret = tupleValueFactory(obj, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_TUPLE_ADDRESS): ret = new TupleAddressExpression(); break; // must handle all known expressions in this factory default: char message[256]; snprintf(message,256, "Invalid ExpressionType '%s' (%d) requested from factory", expressionToString(et).c_str(), (int)et); throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, message); } ret->setValueType(vt); ret->setValueSize(vs); // written thusly to ease testing/inspecting return content. VOLT_TRACE("Created expression %p", ret); return ret; }
bool interpret( Context* context) { return (pOperand1_->interpret(context) == pOperand2_->interpret(context)); }
/** Given an expression type and a valuetype, find the best * templated ctor to invoke. Several helpers, above, aid in this * pursuit. Each instantiated expression must consume any * class-specific serialization from serialize_io. */ AbstractExpression* ExpressionUtil::expressionFactory(PlannerDomValue obj, ExpressionType et, ValueType vt, int vs, AbstractExpression* lc, AbstractExpression* rc, const std::vector<AbstractExpression*>* args) { AbstractExpression *ret = NULL; switch (et) { // Casts case (EXPRESSION_TYPE_OPERATOR_CAST): ret = castFactory(vt, lc); break; // Operators case (EXPRESSION_TYPE_OPERATOR_PLUS): case (EXPRESSION_TYPE_OPERATOR_MINUS): case (EXPRESSION_TYPE_OPERATOR_MULTIPLY): case (EXPRESSION_TYPE_OPERATOR_DIVIDE): case (EXPRESSION_TYPE_OPERATOR_CONCAT): case (EXPRESSION_TYPE_OPERATOR_MOD): case (EXPRESSION_TYPE_OPERATOR_NOT): case (EXPRESSION_TYPE_OPERATOR_IS_NULL): ret = operatorFactory(et, lc, rc); break; // Comparisons case (EXPRESSION_TYPE_COMPARE_EQUAL): case (EXPRESSION_TYPE_COMPARE_NOTEQUAL): case (EXPRESSION_TYPE_COMPARE_LESSTHAN): case (EXPRESSION_TYPE_COMPARE_GREATERTHAN): case (EXPRESSION_TYPE_COMPARE_LESSTHANOREQUALTO): case (EXPRESSION_TYPE_COMPARE_GREATERTHANOREQUALTO): case (EXPRESSION_TYPE_COMPARE_LIKE): case (EXPRESSION_TYPE_COMPARE_IN): ret = comparisonFactory( et, lc, rc); break; // Conjunctions case (EXPRESSION_TYPE_CONJUNCTION_AND): case (EXPRESSION_TYPE_CONJUNCTION_OR): ret = conjunctionFactory(et, lc, rc); break; // Functions and pseudo-functions case (EXPRESSION_TYPE_FUNCTION): { // add the function id int functionId = obj.valueForKey("FUNCTION_ID").asInt(); ret = functionFactory(functionId, args); if ( ! ret) { std::string nameString; if (obj.hasNonNullKey("NAME")) { nameString = obj.valueForKey("NAME").asStr(); } else { nameString = "?"; } char aliasBuffer[256]; if (obj.hasNonNullKey("ALIAS")) { std::string aliasString = obj.valueForKey("ALIAS").asStr(); snprintf(aliasBuffer, sizeof(aliasBuffer), " aliased to '%s'", aliasString.c_str()); } char fn_message[1024]; snprintf(fn_message, sizeof(fn_message), "SQL function '%s'%s with ID (%d) with (%d) parameters is not implemented in VoltDB (or may have been incorrectly parsed)", nameString.c_str(), aliasBuffer, functionId, (int)args->size()); throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, fn_message); } } break; case (EXPRESSION_TYPE_INLISTBUILDER): { // Parse whatever is needed out of obj and pass the pieces to inListFactory // to make it easier to unit test independently of the parsing. // The first argument is used as the list element type. // If the ValueType of the list builder expression needs to be "ARRAY" or something else, // a separate element type attribute will have to be serialized and passed in here. ret = inListFactory(vt, args); } break; // Constant Values, parameters, tuples case (EXPRESSION_TYPE_VALUE_CONSTANT): ret = constantValueFactory(obj, vt, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_PARAMETER): ret = parameterValueFactory(obj, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_TUPLE): ret = tupleValueFactory(obj, et, lc, rc); break; case (EXPRESSION_TYPE_VALUE_TUPLE_ADDRESS): ret = new TupleAddressExpression(); break; case (EXPRESSION_TYPE_HASH_RANGE): ret = hashRangeFactory(obj); break; // must handle all known expressions in this factory default: char message[256]; snprintf(message,256, "Invalid ExpressionType '%s' (%d) requested from factory", expressionToString(et).c_str(), (int)et); throw SerializableEEException(VOLT_EE_EXCEPTION_TYPE_EEEXCEPTION, message); } ret->setValueType(vt); ret->setValueSize(vs); // written thusly to ease testing/inspecting return content. VOLT_TRACE("Created expression %p", ret); return ret; }
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; }
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 AggregateExecutorBase::p_init(AbstractPlanNode*, TempTableLimits* limits) { AggregatePlanNode* node = dynamic_cast<AggregatePlanNode*>(m_abstractNode); assert(node); assert(node->getChildren().size() == 1); assert(node->getChildren()[0] != NULL); m_inputExpressions = node->getAggregateInputExpressions(); for (int i = 0; i < m_inputExpressions.size(); i++) { VOLT_DEBUG("\nAGG INPUT EXPRESSION: %s\n", m_inputExpressions[i] ? m_inputExpressions[i]->debug().c_str() : "null"); } /* * Find the difference between the set of aggregate output columns * (output columns resulting from an aggregate) and output columns. * Columns that are not the result of aggregates are being passed * through from the input table. Do this extra work here rather then * serialize yet more data. */ std::vector<bool> outputColumnsResultingFromAggregates(node->getOutputSchema().size(), false); std::vector<int> aggregateOutputColumns = node->getAggregateOutputColumns(); BOOST_FOREACH(int aOC, aggregateOutputColumns) { outputColumnsResultingFromAggregates[aOC] = true; } /* * Now collect the indices in the output table of the pass * through columns. */ for (int ii = 0; ii < outputColumnsResultingFromAggregates.size(); ii++) { if (outputColumnsResultingFromAggregates[ii] == false) { m_passThroughColumns.push_back(ii); } } setTempOutputTable(limits); m_aggTypes = node->getAggregates(); m_distinctAggs = node->getDistinctAggregates(); m_groupByExpressions = node->getGroupByExpressions(); node->collectOutputExpressions(m_outputColumnExpressions); m_aggregateOutputColumns = node->getAggregateOutputColumns(); m_prePredicate = node->getPrePredicate(); m_postPredicate = node->getPostPredicate(); std::vector<ValueType> groupByColumnTypes; std::vector<int32_t> groupByColumnSizes; std::vector<bool> groupByColumnAllowNull; for (int ii = 0; ii < m_groupByExpressions.size(); ii++) { AbstractExpression* expr = m_groupByExpressions[ii]; groupByColumnTypes.push_back(expr->getValueType()); groupByColumnSizes.push_back(expr->getValueSize()); groupByColumnAllowNull.push_back(true); } m_groupByKeySchema = TupleSchema::createTupleSchema(groupByColumnTypes, groupByColumnSizes, groupByColumnAllowNull, true); return true; }
AbstractExpression* AbstractExpression::buildExpressionTree_recurse(PlannerDomValue obj) { // build a tree recursively from the bottom upwards. // when the expression node is instantiated, its type, // value and child types will have been discovered. ExpressionType peek_type = EXPRESSION_TYPE_INVALID; ValueType value_type = VALUE_TYPE_INVALID; bool inBytes = false; AbstractExpression *left_child = NULL; AbstractExpression *right_child = NULL; std::vector<AbstractExpression*>* argsVector = NULL; // read the expression type peek_type = static_cast<ExpressionType>(obj.valueForKey("TYPE").asInt()); assert(peek_type != EXPRESSION_TYPE_INVALID); if (obj.hasNonNullKey("VALUE_TYPE")) { int32_t value_type_int = obj.valueForKey("VALUE_TYPE").asInt(); value_type = static_cast<ValueType>(value_type_int); assert(value_type != VALUE_TYPE_INVALID); if (obj.hasNonNullKey("IN_BYTES")) { inBytes = true; } } // add the value size int valueSize = -1; if (obj.hasNonNullKey("VALUE_SIZE")) { valueSize = obj.valueForKey("VALUE_SIZE").asInt(); } else { // This value size should be consistent with VoltType.java valueSize = NValue::getTupleStorageSize(value_type); } // recurse to children try { if (obj.hasNonNullKey("LEFT")) { PlannerDomValue leftValue = obj.valueForKey("LEFT"); left_child = AbstractExpression::buildExpressionTree_recurse(leftValue); } if (obj.hasNonNullKey("RIGHT")) { PlannerDomValue rightValue = obj.valueForKey("RIGHT"); right_child = AbstractExpression::buildExpressionTree_recurse(rightValue); } // NULL argsVector corresponds to a missing ARGS value // vs. an empty argsVector which corresponds to an empty array ARGS value. // Different expression types could assert either a NULL or non-NULL argsVector initializer. if (obj.hasNonNullKey("ARGS")) { PlannerDomValue argsArray = obj.valueForKey("ARGS"); argsVector = new std::vector<AbstractExpression*>(); for (int i = 0; i < argsArray.arrayLen(); i++) { PlannerDomValue argValue = argsArray.valueAtIndex(i); AbstractExpression* argExpr = AbstractExpression::buildExpressionTree_recurse(argValue); argsVector->push_back(argExpr); } } // invoke the factory. obviously it has to handle null children. // pass it the serialization stream in case a subclass has more // to read. yes, the per-class data really does follow the // child serializations. AbstractExpression* finalExpr = ExpressionUtil::expressionFactory(obj, peek_type, value_type, valueSize, left_child, right_child, argsVector); finalExpr->setInBytes(inBytes); return finalExpr; } catch (const SerializableEEException &ex) { delete left_child; delete right_child; delete argsVector; throw; } }
bool SeqScanExecutor::p_execute(const NValueArray ¶ms) { SeqScanPlanNode* node = dynamic_cast<SeqScanPlanNode*>(m_abstractNode); assert(node); Table* output_table = node->getOutputTable(); assert(output_table); Table* target_table = dynamic_cast<Table*>(node->getTargetTable()); assert(target_table); //cout << "SeqScanExecutor: node id" << node->getPlanNodeId() << endl; VOLT_TRACE("Sequential Scanning table :\n %s", target_table->debug().c_str()); VOLT_DEBUG("Sequential Scanning table : %s which has %d active, %d" " allocated, %d used tuples", target_table->name().c_str(), (int)target_table->activeTupleCount(), (int)target_table->allocatedTupleCount(), (int)target_table->usedTupleCount()); // // OPTIMIZATION: NESTED PROJECTION // // Since we have the input params, we need to call substitute to // change any nodes in our expression tree to be ready for the // projection operations in execute // int num_of_columns = (int)output_table->columnCount(); ProjectionPlanNode* projection_node = dynamic_cast<ProjectionPlanNode*>(node->getInlinePlanNode(PLAN_NODE_TYPE_PROJECTION)); if (projection_node != NULL) { for (int ctr = 0; ctr < num_of_columns; ctr++) { assert(projection_node->getOutputColumnExpressions()[ctr]); projection_node->getOutputColumnExpressions()[ctr]->substitute(params); } } // // OPTIMIZATION: NESTED LIMIT // How nice! We can also cut off our scanning with a nested limit! // int limit = -1; int offset = -1; LimitPlanNode* limit_node = dynamic_cast<LimitPlanNode*>(node->getInlinePlanNode(PLAN_NODE_TYPE_LIMIT)); if (limit_node != NULL) { limit_node->getLimitAndOffsetByReference(params, limit, offset); } // // OPTIMIZATION: // // If there is no predicate and no Projection for this SeqScan, // then we have already set the node's OutputTable to just point // at the TargetTable. Therefore, there is nothing we more we need // to do here // if (node->getPredicate() != NULL || projection_node != NULL || limit_node != NULL) { // // Just walk through the table using our iterator and apply // the predicate to each tuple. For each tuple that satisfies // our expression, we'll insert them into the output table. // TableTuple tuple(target_table->schema()); TableIterator iterator = target_table->iterator(); AbstractExpression *predicate = node->getPredicate(); VOLT_TRACE("SCAN PREDICATE A:\n%s\n", predicate->debug(true).c_str()); if (predicate) { predicate->substitute(params); assert(predicate != NULL); VOLT_DEBUG("SCAN PREDICATE B:\n%s\n", predicate->debug(true).c_str()); } int tuple_ctr = 0; int tuple_skipped = 0; while (iterator.next(tuple)) { VOLT_TRACE("INPUT TUPLE: %s, %d/%d\n", tuple.debug(target_table->name()).c_str(), tuple_ctr, (int)target_table->activeTupleCount()); // // For each tuple we need to evaluate it against our predicate // if (predicate == NULL || predicate->eval(&tuple, NULL).isTrue()) { // Check if we have to skip this tuple because of offset if (tuple_skipped < offset) { tuple_skipped++; continue; } // // Nested Projection // Project (or replace) values from input tuple // if (projection_node != NULL) { TableTuple &temp_tuple = output_table->tempTuple(); for (int ctr = 0; ctr < num_of_columns; ctr++) { NValue value = projection_node-> getOutputColumnExpressions()[ctr]->eval(&tuple, NULL); temp_tuple.setNValue(ctr, value); } if (!output_table->insertTuple(temp_tuple)) { VOLT_ERROR("Failed to insert tuple from table '%s' into" " output table '%s'", target_table->name().c_str(), output_table->name().c_str()); return false; } } else { // // Insert the tuple into our output table // if (!output_table->insertTuple(tuple)) { VOLT_ERROR("Failed to insert tuple from table '%s' into" " output table '%s'", target_table->name().c_str(), output_table->name().c_str()); return false; } } ++tuple_ctr; // Check whether we have gone past our limit if (limit >= 0 && tuple_ctr >= limit) { break; } } } } VOLT_TRACE("\n%s\n", output_table->debug().c_str()); VOLT_DEBUG("Finished Seq scanning"); return true; }
bool IndexScanExecutor::p_execute(const NValueArray ¶ms) { assert(m_node); assert(m_node == dynamic_cast<IndexScanPlanNode*>(m_abstractNode)); assert(m_outputTable); assert(m_outputTable == static_cast<TempTable*>(m_node->getOutputTable())); // update local target table with its most recent reference Table* targetTable = m_node->getTargetTable(); TableIndex *tableIndex = targetTable->index(m_node->getTargetIndexName()); 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 PROJECTION // Set params to expression tree via substitute() assert(m_numOfColumns == m_outputTable->columnCount()); if (m_projectionNode != NULL && m_projectionAllTupleArray == NULL) { for (int ctr = 0; ctr < m_numOfColumns; ctr++) { assert(m_projectionNode->getOutputColumnExpressions()[ctr]); m_projectionExpressions[ctr]->substitute(params); assert(m_projectionExpressions[ctr]); } } // // INLINE LIMIT // LimitPlanNode* limit_node = dynamic_cast<LimitPlanNode*>(m_abstractNode->getInlinePlanNode(PLAN_NODE_TYPE_LIMIT)); // // SEARCH KEY // searchKey.setAllNulls(); VOLT_TRACE("Initial (all null) search key: '%s'", searchKey.debugNoHeader().c_str()); for (int ctr = 0; ctr < activeNumOfSearchKeys; ctr++) { m_searchKeyArray[ctr]->substitute(params); NValue candidateValue = m_searchKeyArray[ctr]->eval(NULL, NULL); try { searchKey.setNValue(ctr, candidateValue); } catch (const SQLException &e) { // This next bit of logic handles underflow and overflow while // setting up the search keys. // e.g. TINYINT > 200 or INT <= 6000000000 // re-throw if not an overflow or underflow // currently, it's expected to always be an overflow or underflow if ((e.getInternalFlags() & (SQLException::TYPE_OVERFLOW | SQLException::TYPE_UNDERFLOW)) == 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 return true; } 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 return true; } else { // don't allow GTE because it breaks null handling localLookupType = INDEX_LOOKUP_TYPE_GT; } } // if here, means all tuples with the previous searchkey // columns need to be scaned. Note, if only one column, // then all tuples will be scanned 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 { return true; } break; } } assert((activeNumOfSearchKeys == 0) || (searchKey.getSchema()->columnCount() > 0)); VOLT_TRACE("Search key after substitutions: '%s'", searchKey.debugNoHeader().c_str()); // // END EXPRESSION // AbstractExpression* end_expression = m_node->getEndExpression(); if (end_expression != NULL) { end_expression->substitute(params); VOLT_DEBUG("End Expression:\n%s", end_expression->debug(true).c_str()); } // // POST EXPRESSION // AbstractExpression* post_expression = m_node->getPredicate(); if (post_expression != NULL) { post_expression->substitute(params); VOLT_DEBUG("Post Expression:\n%s", post_expression->debug(true).c_str()); } // INITIAL EXPRESSION AbstractExpression* initial_expression = m_node->getInitialExpression(); if (initial_expression != NULL) { initial_expression->substitute(params); 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) { skipNullExpr->substitute(params); VOLT_DEBUG("COUNT NULL Expression:\n%s", skipNullExpr->debug(true).c_str()); } ProgressMonitorProxy pmp(m_engine, targetTable); // // 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); } else if (localLookupType == INDEX_LOOKUP_TYPE_GT) { tableIndex->moveToGreaterThanKey(&searchKey); } else if (localLookupType == INDEX_LOOKUP_TYPE_GTE) { tableIndex->moveToKeyOrGreater(&searchKey); } else if (localLookupType == INDEX_LOOKUP_TYPE_LT) { tableIndex->moveToLessThanKey(&searchKey); } 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); if (isEnd) { tableIndex->moveToEnd(false); } else { while (!(tuple = tableIndex->nextValue()).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(); break; } } if (tuple.isNullTuple()) { tableIndex->moveToEnd(false); } } } else { return false; } } else { bool toStartActually = (localSortDirection != SORT_DIRECTION_TYPE_DESC); tableIndex->moveToEnd(toStartActually); } 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()).isNullTuple()) || ((localLookupType != INDEX_LOOKUP_TYPE_EQ || activeNumOfSearchKeys == 0) && !(tuple = tableIndex->nextValue()).isNullTuple()))) { 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_projectionNode != NULL) { TableTuple &temp_tuple = m_outputTable->tempTuple(); if (m_projectionAllTupleArray != NULL) { VOLT_TRACE("sweet, all tuples"); for (int ctr = m_numOfColumns - 1; ctr >= 0; --ctr) { temp_tuple.setNValue(ctr, tuple.getNValue(m_projectionAllTupleArray[ctr])); } } else { for (int ctr = m_numOfColumns - 1; ctr >= 0; --ctr) { temp_tuple.setNValue(ctr, m_projectionExpressions[ctr]->eval(&tuple, NULL)); } } m_outputTable->insertTupleNonVirtual(temp_tuple); } else // // Straight Insert // { // // Try to put the tuple into our output table // m_outputTable->insertTupleNonVirtual(tuple); } pmp.countdownProgress(); } } VOLT_DEBUG ("Index Scanned :\n %s", m_outputTable->debug().c_str()); return true; }
bool IndexCountExecutor::p_execute(const NValueArray ¶ms) { assert(m_node); assert(m_node == dynamic_cast<IndexCountPlanNode*>(m_abstractNode)); assert(m_outputTable); assert(m_outputTable == static_cast<TempTable*>(m_node->getOutputTable())); assert(m_targetTable); assert(m_targetTable == m_node->getTargetTable()); VOLT_DEBUG("IndexCount: %s.%s\n", m_targetTable->name().c_str(), m_index->getName().c_str()); int activeNumOfSearchKeys = m_numOfSearchkeys; IndexLookupType localLookupType = m_lookupType; bool searchKeyUnderflow = false, endKeyOverflow = false; // Overflow cases that can return early without accessing the index need this // default 0 count as their result. TableTuple& tmptup = m_outputTable->tempTuple(); tmptup.setNValue(0, ValueFactory::getBigIntValue( 0 )); // // SEARCH KEY // if (m_numOfSearchkeys != 0) { m_searchKey.setAllNulls(); VOLT_DEBUG("<Index Count>Initial (all null) search key: '%s'", m_searchKey.debugNoHeader().c_str()); for (int ctr = 0; ctr < activeNumOfSearchKeys; ctr++) { m_searchKeyArray[ctr]->substitute(params); NValue candidateValue = m_searchKeyArray[ctr]->eval(NULL, NULL); try { m_searchKey.setNValue(ctr, candidateValue); } catch (const SQLException &e) { // This next bit of logic handles underflow and overflow while // setting up the search keys. // e.g. TINYINT > 200 or INT <= 6000000000 // re-throw if not an overflow or underflow // currently, it's expected to always be an overflow or underflow if ((e.getInternalFlags() & (SQLException::TYPE_OVERFLOW | SQLException::TYPE_UNDERFLOW)) == 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))) { assert (localLookupType == INDEX_LOOKUP_TYPE_GT || localLookupType == INDEX_LOOKUP_TYPE_GTE); if (e.getInternalFlags() & SQLException::TYPE_OVERFLOW) { m_outputTable->insertTuple(tmptup); return true; } else if (e.getInternalFlags() & SQLException::TYPE_UNDERFLOW) { searchKeyUnderflow = true; break; } else { throw e; } } // if a EQ comparision is out of range, then return no tuples else { m_outputTable->insertTuple(tmptup); return true; } break; } } VOLT_TRACE("Search key after substitutions: '%s'", m_searchKey.debugNoHeader().c_str()); } if (m_numOfEndkeys != 0) { // // END KEY // m_endKey.setAllNulls(); VOLT_DEBUG("Initial (all null) end key: '%s'", m_endKey.debugNoHeader().c_str()); for (int ctr = 0; ctr < m_numOfEndkeys; ctr++) { m_endKeyArray[ctr]->substitute(params); NValue endKeyValue = m_endKeyArray[ctr]->eval(NULL, NULL); try { m_endKey.setNValue(ctr, endKeyValue); } catch (const SQLException &e) { // This next bit of logic handles underflow and overflow while // setting up the search keys. // e.g. TINYINT > 200 or INT <= 6000000000 // re-throw if not an overflow or underflow // currently, it's expected to always be an overflow or underflow if ((e.getInternalFlags() & (SQLException::TYPE_OVERFLOW | SQLException::TYPE_UNDERFLOW)) == 0) { throw e; } if (ctr == (m_numOfEndkeys - 1)) { assert (m_endType == INDEX_LOOKUP_TYPE_LT || m_endType == INDEX_LOOKUP_TYPE_LTE); if (e.getInternalFlags() & SQLException::TYPE_UNDERFLOW) { m_outputTable->insertTuple(tmptup); return true; } else if (e.getInternalFlags() & SQLException::TYPE_OVERFLOW) { endKeyOverflow = true; const ValueType type = m_endKey.getSchema()->columnType(ctr); NValue tmpEndKeyValue = ValueFactory::getBigIntValue(getMaxTypeValue(type)); m_endKey.setNValue(ctr, tmpEndKeyValue); VOLT_DEBUG("<Index count> end key out of range, MAX value: %ld...\n", (long)getMaxTypeValue(type)); break; } else { throw e; } } // if a EQ comparision is out of range, then return no tuples else { m_outputTable->insertTuple(tmptup); return true; } break; } } VOLT_TRACE("End key after substitutions: '%s'", m_endKey.debugNoHeader().c_str()); } // // POST EXPRESSION // assert (m_node->getPredicate() == NULL); assert (m_index); assert (m_index == m_targetTable->index(m_node->getTargetIndexName())); assert (m_index->isCountableIndex()); // // COUNT NULL EXPRESSION // AbstractExpression* countNULLExpr = m_node->getSkipNullPredicate(); // For reverse scan edge case NULL values and forward scan underflow case. if (countNULLExpr != NULL) { countNULLExpr->substitute(params); VOLT_DEBUG("COUNT NULL Expression:\n%s", countNULLExpr->debug(true).c_str()); } bool reverseScanNullEdgeCase = false; bool reverseScanMovedIndexToScan = false; if (m_numOfSearchkeys < m_numOfEndkeys && (m_endType == INDEX_LOOKUP_TYPE_LT || m_endType == INDEX_LOOKUP_TYPE_LTE)) { reverseScanNullEdgeCase = true; VOLT_DEBUG("Index count: reverse scan edge null case." ); } // An index count has two cases: unique and non-unique int64_t rkStart = 0, rkEnd = 0, rkRes = 0; int leftIncluded = 0, rightIncluded = 0; if (m_numOfSearchkeys != 0) { // Deal with multi-map VOLT_DEBUG("INDEX_LOOKUP_TYPE(%d) m_numSearchkeys(%d) key:%s", localLookupType, activeNumOfSearchKeys, m_searchKey.debugNoHeader().c_str()); if (searchKeyUnderflow == false) { if (localLookupType == INDEX_LOOKUP_TYPE_GT) { rkStart = m_index->getCounterLET(&m_searchKey, true); } else { // handle start inclusive cases. if (m_index->hasKey(&m_searchKey)) { leftIncluded = 1; rkStart = m_index->getCounterLET(&m_searchKey, false); if (reverseScanNullEdgeCase) { m_index->moveToKeyOrGreater(&m_searchKey); reverseScanMovedIndexToScan = true; } } else { rkStart = m_index->getCounterLET(&m_searchKey, true); } } } else { // Do not count null row or columns m_index->moveToKeyOrGreater(&m_searchKey); assert(countNULLExpr); long numNULLs = countNulls(countNULLExpr); rkStart += numNULLs; VOLT_DEBUG("Index count[underflow case]: find out %ld null rows or columns are not counted in.", numNULLs); } } if (reverseScanNullEdgeCase) { // reverse scan case if (!reverseScanMovedIndexToScan && localLookupType != INDEX_LOOKUP_TYPE_GT) { m_index->moveToEnd(true); } assert(countNULLExpr); long numNULLs = countNulls(countNULLExpr); rkStart += numNULLs; VOLT_DEBUG("Index count[reverse case]: find out %ld null rows or columns are not counted in.", numNULLs); } if (m_numOfEndkeys != 0) { if (endKeyOverflow) { rkEnd = m_index->getCounterGET(&m_endKey, true); } else { IndexLookupType localEndType = m_endType; if (localEndType == INDEX_LOOKUP_TYPE_LT) { rkEnd = m_index->getCounterGET(&m_endKey, false); } else { if (m_index->hasKey(&m_endKey)) { rightIncluded = 1; rkEnd = m_index->getCounterGET(&m_endKey, true); } else { rkEnd = m_index->getCounterGET(&m_endKey, false); } } } } else { rkEnd = m_index->getSize(); rightIncluded = 1; } rkRes = rkEnd - rkStart - 1 + leftIncluded + rightIncluded; VOLT_DEBUG("Index Count ANSWER %ld = %ld - %ld - 1 + %d + %d\n", (long)rkRes, (long)rkEnd, (long)rkStart, leftIncluded, rightIncluded); tmptup.setNValue(0, ValueFactory::getBigIntValue( rkRes )); m_outputTable->insertTuple(tmptup); VOLT_DEBUG ("Index Count :\n %s", m_outputTable->debug().c_str()); 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); }
///一些应用提供了内建(Build-In)的脚本或者宏语言来让用户可以定义他们能够在系统 中进行的操作。Interpreter 模式的目的就是使用一个解释器为用户提供一个一门定义语言的 语法表示的解释器,然后通过这个解释器来解释语言中的句子 ///Interpreter 模式中使用类来表示文法规则,因此可以很容易实现文法的扩展。另外对于 终结符我们可以使用 Flyweight 模式来实现终结符的共享。 void InterpreterTest() { Interpreter_Context* c = new Interpreter_Context(); AbstractExpression* te = new TerminalExpression("hello"); AbstractExpression* nte = new NonterminalExpression(te,2); nte->Interpret(*c); }