TEST_F(ExpressionTests, DistinctFromTest) { // Create a table with id column and value column std::vector<catalog::Column> columns; catalog::Column column1(type::TypeId::INTEGER, type::Type::GetTypeSize(type::TypeId::INTEGER), "id", true); catalog::Column column2(type::TypeId::INTEGER, type::Type::GetTypeSize(type::TypeId::INTEGER), "value", true); columns.push_back(column1); columns.push_back(column2); std::unique_ptr<catalog::Schema> schema(new catalog::Schema(columns)); std::unique_ptr<storage::Tuple> tuple(new storage::Tuple(schema.get(), true)); // Create "id IS DISTINCT FROM value" comparison auto lexpr = new expression::TupleValueExpression(type::TypeId::INTEGER, 0, 0); auto rexpr = new expression::TupleValueExpression(type::TypeId::INTEGER, 1, 1); expression::ComparisonExpression expr( StringToExpressionType("COMPARE_DISTINCT_FROM"), lexpr, rexpr); auto pool = TestingHarness::GetInstance().GetTestingPool(); // id, value not NULL with the same values, should be false tuple->SetValue(0, type::ValueFactory::GetIntegerValue(10), pool); tuple->SetValue(1, type::ValueFactory::GetIntegerValue(10), pool); EXPECT_TRUE(expr.Evaluate(tuple.get(), tuple.get(), nullptr).IsFalse()); // id, value not NULL with different values, should be true tuple->SetValue(1, type::ValueFactory::GetIntegerValue(5), pool); EXPECT_TRUE(expr.Evaluate(tuple.get(), tuple.get(), nullptr).IsTrue()); // id not NULL, value is NULL, should be true tuple->SetValue(1, type::ValueFactory::GetNullValueByType(type::TypeId::INTEGER), pool); EXPECT_TRUE(expr.Evaluate(tuple.get(), tuple.get(), nullptr).IsTrue()); // id is NULL, value not NULL, should be true tuple->SetValue(0, type::ValueFactory::GetNullValueByType(type::TypeId::INTEGER), pool); tuple->SetValue(1, type::ValueFactory::GetIntegerValue(10), pool); EXPECT_TRUE(expr.Evaluate(tuple.get(), tuple.get(), nullptr).IsTrue()); // id is NULL, value is NULL, should be false tuple->SetValue(1, type::ValueFactory::GetNullValueByType(type::TypeId::INTEGER), pool); EXPECT_TRUE(expr.Evaluate(tuple.get(), tuple.get(), nullptr).IsFalse()); }
AbstractExpression *AbstractExpression::CreateExpressionTreeRecurse( json_spirit::Object &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; AbstractExpression *left_child = nullptr; AbstractExpression *right_child = nullptr; // read the expression type json_spirit::Value expression_type_value = json_spirit::find_value(obj, "TYPE"); if (expression_type_value == json_spirit::Value::null) { throw ExpressionException( "AbstractExpression:: buildExpressionTree_recurse:" "Couldn't find TYPE value"); } assert(StringToExpressionType(expression_type_value.get_str()) != EXPRESSION_TYPE_INVALID); peek_type = StringToExpressionType(expression_type_value.get_str()); // and the value type json_spirit::Value valueTypeValue = json_spirit::find_value(obj, "VALUE_TYPE"); if (valueTypeValue == json_spirit::Value::null) { throw ExpressionException( "AbstractExpression:: buildExpressionTree_recurse:" " Couldn't find VALUE_TYPE value"); } std::string value_type_string = valueTypeValue.get_str(); value_type = StringToValueType(value_type_string); // this should be relatively safe, though it ignores overflow. if ((value_type == VALUE_TYPE_TINYINT) || (value_type == VALUE_TYPE_SMALLINT) || (value_type == VALUE_TYPE_INTEGER)) { value_type = VALUE_TYPE_BIGINT; } assert(value_type != VALUE_TYPE_INVALID); // add the value size json_spirit::Value value_size_value = json_spirit::find_value(obj, "VALUE_SIZE"); if (value_size_value == json_spirit::Value::null) { throw ExpressionException( "AbstractExpression:: buildExpressionTree_recurse:" " Couldn't find VALUE_SIZE value"); } int value_size = value_size_value.get_int(); // recurse to children try { json_spirit::Value leftValue = json_spirit::find_value(obj, "LEFT"); if (!(leftValue == json_spirit::Value::null)) { left_child = AbstractExpression::CreateExpressionTreeRecurse(leftValue.get_obj()); } else { left_child = nullptr; } json_spirit::Value rightValue = json_spirit::find_value(obj, "RIGHT"); if (!(rightValue == json_spirit::Value::null)) { right_child = AbstractExpression::CreateExpressionTreeRecurse(rightValue.get_obj()); } else { right_child = nullptr; } // 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. return ExpressionUtil::ExpressionFactory(obj, peek_type, value_type, value_size, left_child, right_child); } catch (ExpressionException &ex) { // clean up children delete left_child; delete right_child; throw; } }