diff --git a/timeplus/CMakeLists.txt b/timeplus/CMakeLists.txt index fa5a608..c6ce447 100644 --- a/timeplus/CMakeLists.txt +++ b/timeplus/CMakeLists.txt @@ -21,6 +21,7 @@ SET ( timeplus-cpp-lib-src columns/numeric.cpp columns/map.cpp columns/string.cpp + columns/json.cpp columns/tuple.cpp columns/uuid.cpp @@ -69,6 +70,7 @@ SET ( timeplus-cpp-lib-src columns/nullable.h columns/numeric.h columns/string.h + columns/json.h columns/tuple.h columns/utils.h columns/uuid.h @@ -210,6 +212,7 @@ INSTALL(FILES columns/nullable.h DESTINATION include/timeplus/columns/) INSTALL(FILES columns/numeric.h DESTINATION include/timeplus/columns/) INSTALL(FILES columns/map.h DESTINATION include/timeplus/columns/) INSTALL(FILES columns/string.h DESTINATION include/timeplus/columns/) +INSTALL(FILES columns/json.h DESTINATION include/timeplus/columns/) INSTALL(FILES columns/tuple.h DESTINATION include/timeplus/columns/) INSTALL(FILES columns/utils.h DESTINATION include/timeplus/columns/) INSTALL(FILES columns/uuid.h DESTINATION include/timeplus/columns/) diff --git a/timeplus/columns/factory.cpp b/timeplus/columns/factory.cpp index f0a8f1c..a0f34d9 100644 --- a/timeplus/columns/factory.cpp +++ b/timeplus/columns/factory.cpp @@ -14,6 +14,7 @@ #include "nullable.h" #include "numeric.h" #include "string.h" +#include "json.h" #include "tuple.h" #include "uuid.h" @@ -95,6 +96,9 @@ static ColumnRef CreateTerminalColumn(const TypeAst& ast) { case Type::FixedString: return std::make_shared(GetASTChildElement(ast, 0).value); + case Type::Json: + return std::make_shared(); + case Type::DateTime: if (ast.elements.empty()) { return std::make_shared(); @@ -209,6 +213,8 @@ static ColumnRef CreateColumnFromAst(const TypeAst& ast, CreateColumnByTypeSetti return std::make_shared>(); case Type::FixedString: return std::make_shared>(GetASTChildElement(nested, 0).value); + case Type::Json: + return std::make_shared>(); case Type::Nullable: throw UnimplementedError("LowCardinality(" + nested.name + ") is not supported with LowCardinalityAsWrappedColumn on"); default: @@ -222,6 +228,8 @@ static ColumnRef CreateColumnFromAst(const TypeAst& ast, CreateColumnByTypeSetti return std::make_shared>(); case Type::FixedString: return std::make_shared>(GetASTChildElement(nested, 0).value); + case Type::Json: + return std::make_shared>(); case Type::Nullable: return std::make_shared( std::make_shared( diff --git a/timeplus/columns/itemview.cpp b/timeplus/columns/itemview.cpp index f932dca..4038455 100644 --- a/timeplus/columns/itemview.cpp +++ b/timeplus/columns/itemview.cpp @@ -70,6 +70,7 @@ void ItemView::ValidateData(Type::Code type, DataType data) { case Type::Code::String: case Type::Code::FixedString: + case Type::Code::Json: // value can be of any size return; diff --git a/timeplus/columns/json.cpp b/timeplus/columns/json.cpp new file mode 100644 index 0000000..f96da76 --- /dev/null +++ b/timeplus/columns/json.cpp @@ -0,0 +1,8 @@ +#include "json.h" + +#include "../base/wire_format.h" +#include "../exceptions.h" + +namespace timeplus { + +} \ No newline at end of file diff --git a/timeplus/columns/json.h b/timeplus/columns/json.h new file mode 100644 index 0000000..cd74322 --- /dev/null +++ b/timeplus/columns/json.h @@ -0,0 +1,20 @@ +#pragma once + +#include "column.h" +#include "string.h" + +#include +#include + +namespace timeplus { + +/** + * Represents column of JSON documents. + * Leverages the same underlying wire format as strings until + * native JSON serialization support is available server-side. + */ +class ColumnJson : public ColumnString { + +}; + +} \ No newline at end of file diff --git a/timeplus/columns/lowcardinality.cpp b/timeplus/columns/lowcardinality.cpp index a1389a2..5159fbb 100644 --- a/timeplus/columns/lowcardinality.cpp +++ b/timeplus/columns/lowcardinality.cpp @@ -12,6 +12,8 @@ #include +#include "json.h" + namespace { using namespace timeplus; @@ -143,6 +145,9 @@ inline void AppendToDictionary(Column& dictionary, const ItemView & item) { case Type::String: column_down_cast(dictionary).Append(item.get()); return; + case Type::Json: + column_down_cast(dictionary).Append(item.get()); + return; case Type::Nullable: AppendNullableToDictionary(column_down_cast(dictionary), item); return; diff --git a/timeplus/types/type_parser.cpp b/timeplus/types/type_parser.cpp index c177e89..15133ce 100644 --- a/timeplus/types/type_parser.cpp +++ b/timeplus/types/type_parser.cpp @@ -68,6 +68,7 @@ static const std::unordered_map kTypeCode = { { "ring", Type::Ring }, { "polygon", Type::Polygon }, { "multi_polygon", Type::MultiPolygon }, + { "json", Type::Json }, }; template diff --git a/timeplus/types/types.cpp b/timeplus/types/types.cpp index f3c3b7d..5f62b75 100644 --- a/timeplus/types/types.cpp +++ b/timeplus/types/types.cpp @@ -55,6 +55,7 @@ const char* Type::TypeName(Type::Code code) { case Type::Code::UInt128: return "uint128"; case Type::Code::Int256: return "int256"; case Type::Code::UInt256: return "uint256"; + case Type::Code::Json: return "json"; } return "Unknown type"; @@ -76,6 +77,7 @@ std::string Type::GetName() const { case Float32: case Float64: case String: + case Json: case IPv4: case IPv6: case Date: @@ -138,6 +140,7 @@ uint64_t Type::GetTypeUniqueId() const { case Float32: case Float64: case String: + case Json: case IPv4: case IPv6: case Date: @@ -233,6 +236,9 @@ TypeRef Type::CreateString(size_t n) { return TypeRef(new FixedStringType(n)); } +TypeRef Type::CreateJson() { + return TypeRef(new Type(Type::Json)); +} TypeRef Type::CreateTuple(const std::vector& item_types) { return TypeRef(new TupleType(item_types)); } diff --git a/timeplus/types/types.h b/timeplus/types/types.h index cd5733c..ebe4da2 100644 --- a/timeplus/types/types.h +++ b/timeplus/types/types.h @@ -64,7 +64,8 @@ class Type { UInt128, Int256, UInt256, - Decimal256 + Decimal256, + Json }; using EnumItem = std::pair; @@ -150,6 +151,8 @@ class Type { static TypeRef CreateMultiPolygon(); + static TypeRef CreateJson(); + private: uint64_t GetTypeUniqueId() const; diff --git a/ut/CreateColumnByType_ut.cpp b/ut/CreateColumnByType_ut.cpp index 92ca577..5eff462 100644 --- a/ut/CreateColumnByType_ut.cpp +++ b/ut/CreateColumnByType_ut.cpp @@ -2,9 +2,10 @@ #include #include #include +#include +#include #include - namespace { using namespace timeplus; } @@ -31,6 +32,20 @@ TEST(CreateColumnByType, UnmatchedBrackets) { ASSERT_EQ(nullptr, CreateColumnByType("array(low_cardinality(nullable(fixed_string(10000)))")); } +TEST(CreateColumnByType, JsonType) { + auto col = CreateColumnByType("json"); + ASSERT_NE(nullptr, col); + EXPECT_EQ(Type::Json, col->GetType().GetCode()); + EXPECT_NE(nullptr, col->As()); + + auto nullable_json = CreateColumnByType("nullable(json)"); + ASSERT_NE(nullptr, nullable_json); + ASSERT_EQ(Type::Nullable, nullable_json->GetType().GetCode()); + auto nested = nullable_json->As()->Nested(); + EXPECT_EQ(Type::Json, nested->GetType().GetCode()); + EXPECT_NE(nullptr, nested->As()); +} + TEST(CreateColumnByType, LowCardinalityAsWrappedColumn) { CreateColumnByTypeSettings create_column_settings; create_column_settings.low_cardinality_as_wrapped_column = true; @@ -40,6 +55,9 @@ TEST(CreateColumnByType, LowCardinalityAsWrappedColumn) { ASSERT_EQ(Type::FixedString, CreateColumnByType("low_cardinality(fixed_string(10000))", create_column_settings)->GetType().GetCode()); ASSERT_EQ(Type::FixedString, CreateColumnByType("low_cardinality(fixed_string(10000))", create_column_settings)->As()->GetType().GetCode()); + + ASSERT_EQ(Type::Json, CreateColumnByType("low_cardinality(json)", create_column_settings)->GetType().GetCode()); + ASSERT_EQ(Type::Json, CreateColumnByType("low_cardinality(json)", create_column_settings)->As()->GetType().GetCode()); } TEST(CreateColumnByType, DateTime) { @@ -75,7 +93,7 @@ TEST_P(CreateColumnByTypeWithName, CreateColumnByType) INSTANTIATE_TEST_SUITE_P(Basic, CreateColumnByTypeWithName, ::testing::Values( "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", - "string", "date", "datetime", + "string", "json", "date", "datetime", "uuid", "int128" )); @@ -92,5 +110,6 @@ INSTANTIATE_TEST_SUITE_P(Nested, CreateColumnByTypeWithName, ::testing::Values( "nullable(fixed_string(10000))", "nullable(low_cardinality(fixed_string(10000)))", "array(nullable(low_cardinality(fixed_string(10000))))", - "array(enum8('ONE' = 1, 'TWO' = 2))" + "array(enum8('ONE' = 1, 'TWO' = 2))", + "nullable(json)" )); diff --git a/ut/type_parser_ut.cpp b/ut/type_parser_ut.cpp index f8267b3..c6cc207 100644 --- a/ut/type_parser_ut.cpp +++ b/ut/type_parser_ut.cpp @@ -14,6 +14,15 @@ TEST(TypeParserCase, ParseTerminals) { ASSERT_EQ(ast.code, Type::UInt8); } +TEST(TypeParserCase, ParseJson) { + TypeAst ast; + ASSERT_TRUE(TypeParser("json").Parse(&ast)); + + ASSERT_EQ(ast.meta, TypeAst::Terminal); + ASSERT_EQ(ast.name, "json"); + ASSERT_EQ(ast.code, Type::Json); +} + TEST(TypeParserCase, ParseFixedString) { TypeAst ast; TypeParser("fixed_string(24)").Parse(&ast); @@ -198,6 +207,17 @@ TEST(TypeParserCase, LowCardinality_FixedString) { ASSERT_EQ(ast.elements[0].elements[0], param); } +TEST(TypeParserCase, LowCardinality_Json) { + TypeAst ast; + ASSERT_TRUE(TypeParser("low_cardinality(json)").Parse(&ast)); + ASSERT_EQ(ast.meta, TypeAst::LowCardinality); + ASSERT_EQ(ast.code, Type::LowCardinality); + ASSERT_EQ(ast.elements.size(), 1u); + ASSERT_EQ(ast.elements[0].meta, TypeAst::Terminal); + ASSERT_EQ(ast.elements[0].code, Type::Json); + ASSERT_EQ(ast.elements[0].name, "json"); +} + TEST(TypeParserCase, SimpleAggregateFunction_UInt64) { TypeAst ast; TypeParser("simple_aggregate_function(func, uint64)").Parse(&ast);