From 0b8ca9961099d63221d22cebce7d83e7dcde41ba Mon Sep 17 00:00:00 2001 From: lganzzzo Date: Mon, 4 Jan 2021 02:02:30 +0200 Subject: [PATCH] mapping::Serializer: 1D - Array Deserialization - General case. --- src/oatpp-postgresql/mapping/Deserializer.cpp | 26 +++- src/oatpp-postgresql/mapping/Deserializer.hpp | 45 +++++++ src/oatpp-postgresql/mapping/PgArray.hpp | 4 + test/CMakeLists.txt | 2 + test/oatpp-postgresql/migration/ArrayTest.sql | 51 ++++++++ test/oatpp-postgresql/tests.cpp | 12 +- test/oatpp-postgresql/types/ArrayTest.cpp | 122 ++++++++++++++++++ test/oatpp-postgresql/types/ArrayTest.hpp | 40 ++++++ 8 files changed, 292 insertions(+), 10 deletions(-) create mode 100644 test/oatpp-postgresql/migration/ArrayTest.sql create mode 100644 test/oatpp-postgresql/types/ArrayTest.cpp create mode 100644 test/oatpp-postgresql/types/ArrayTest.hpp diff --git a/src/oatpp-postgresql/mapping/Deserializer.cpp b/src/oatpp-postgresql/mapping/Deserializer.cpp index 4be87fc..f480169 100644 --- a/src/oatpp-postgresql/mapping/Deserializer.cpp +++ b/src/oatpp-postgresql/mapping/Deserializer.cpp @@ -70,9 +70,9 @@ Deserializer::Deserializer() { setDeserializerMethod(data::mapping::type::__class::AbstractObject::CLASS_ID, nullptr); setDeserializerMethod(data::mapping::type::__class::AbstractEnum::CLASS_ID, &Deserializer::deserializeEnum); - setDeserializerMethod(data::mapping::type::__class::AbstractVector::CLASS_ID, &Deserializer::deserializeArray); - setDeserializerMethod(data::mapping::type::__class::AbstractList::CLASS_ID, nullptr); - setDeserializerMethod(data::mapping::type::__class::AbstractUnorderedSet::CLASS_ID, nullptr); + setDeserializerMethod(data::mapping::type::__class::AbstractVector::CLASS_ID, &Deserializer::deserializeArray2); + setDeserializerMethod(data::mapping::type::__class::AbstractList::CLASS_ID, &Deserializer::deserializeArray2); + setDeserializerMethod(data::mapping::type::__class::AbstractUnorderedSet::CLASS_ID, &Deserializer::deserializeArray2); setDeserializerMethod(data::mapping::type::__class::AbstractPairList::CLASS_ID, nullptr); setDeserializerMethod(data::mapping::type::__class::AbstractUnorderedMap::CLASS_ID, nullptr); @@ -280,10 +280,26 @@ const oatpp::Type* Deserializer::guessAnyType(Oid oid) { case TIMESTAMPOID: return oatpp::UInt64::Class::getType(); - case FLOAT8ARRAYOID: return oatpp::Vector::Class::getType(); - case UUIDOID: return oatpp::postgresql::Uuid::Class::getType(); + // Arrays + + case TEXTARRAYOID: + case VARCHARARRAYOID: return oatpp::Vector::Class::getType(); + + case INT2ARRAYOID: return oatpp::Vector::Class::getType(); + case INT4ARRAYOID: return oatpp::Vector::Class::getType(); + case INT8ARRAYOID: return oatpp::Vector::Class::getType(); + + case FLOAT4ARRAYOID: return oatpp::Vector::Class::getType(); + case FLOAT8ARRAYOID: return oatpp::Vector::Class::getType(); + + case BOOLARRAYOID: return oatpp::Vector::Class::getType(); + + case TIMESTAMPARRAYOID: return oatpp::Vector::Class::getType(); + + case UUIDARRAYOID: return oatpp::Vector::Class::getType(); + } return nullptr; diff --git a/src/oatpp-postgresql/mapping/Deserializer.hpp b/src/oatpp-postgresql/mapping/Deserializer.hpp index 62569b7..48967c6 100644 --- a/src/oatpp-postgresql/mapping/Deserializer.hpp +++ b/src/oatpp-postgresql/mapping/Deserializer.hpp @@ -25,6 +25,8 @@ #ifndef oatpp_postgresql_mapping_Deserializer_hpp #define oatpp_postgresql_mapping_Deserializer_hpp +#include "PgArray.hpp" + #include "oatpp/core/data/mapping/TypeResolver.hpp" #include "oatpp/core/Types.hpp" @@ -40,6 +42,8 @@ public: struct InData { + InData() = default; + InData(PGresult* dbres, int row, int col, const std::shared_ptr& pTypeResolver); std::shared_ptr typeResolver; @@ -98,6 +102,47 @@ private: static oatpp::Void deserializeArray(const Deserializer* _this, const InData& data, const Type* type); + template + static oatpp::Void deserializeArray2(const Deserializer* _this, const InData& data, const Type* type) { + + if(data.isNull) { + return oatpp::Void(nullptr, type); + } + + auto polymorphicDispatcher = static_cast(type->polymorphicDispatcher); + auto itemType = *type->params.begin(); // Get "wanted" type of the list item + auto listWrapper = polymorphicDispatcher->createObject(); // Instantiate list of the "wanted" type + + PgArrayHeader* arr = (PgArrayHeader*) data.data; + arr->size = (v_int32) htonl(arr->size); + arr->oid = (v_int32) htonl(arr->oid); + p_char8 payload = (p_char8) &data.data[sizeof(PgArrayHeader)]; + + for(v_int32 i = 0; i < arr->size; i ++) { + + InData itemData; + itemData.typeResolver = data.typeResolver; + itemData.size = (v_int32)ntohl(*((p_int32) payload)); + itemData.data = (const char*) (payload + sizeof(v_int32)); + itemData.oid = arr->oid; + itemData.isNull = itemData.size < 0; + + if(itemData.size > 0) { + payload += sizeof(v_int32) + itemData.size; + } else { + payload += sizeof(v_int32); + } + + const auto& item = _this->deserialize(itemData, itemType); + + polymorphicDispatcher->addPolymorphicItem(listWrapper, item); + + } + + return oatpp::Void(listWrapper.getPtr(), listWrapper.valueType); + + } + }; }}} diff --git a/src/oatpp-postgresql/mapping/PgArray.hpp b/src/oatpp-postgresql/mapping/PgArray.hpp index aabc396..255642b 100644 --- a/src/oatpp-postgresql/mapping/PgArray.hpp +++ b/src/oatpp-postgresql/mapping/PgArray.hpp @@ -5,6 +5,10 @@ #ifndef oatpp_postgresql_mapping_PgArray_hpp #define oatpp_postgresql_mapping_PgArray_hpp +#include "oatpp/core/Types.hpp" + +#include + // TODO: Assumes 64 bits for each element -- only valid for float64 and int64! struct PgElem { v_int32 size; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4d88cb3..d0a6da7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,6 +14,8 @@ add_definitions ( add_executable(module-tests oatpp-postgresql/ql_template/ParserTest.cpp oatpp-postgresql/ql_template/ParserTest.hpp + oatpp-postgresql/types/ArrayTest.cpp + oatpp-postgresql/types/ArrayTest.hpp oatpp-postgresql/types/FloatTest.cpp oatpp-postgresql/types/FloatTest.hpp oatpp-postgresql/types/InterpretationTest.cpp diff --git a/test/oatpp-postgresql/migration/ArrayTest.sql b/test/oatpp-postgresql/migration/ArrayTest.sql new file mode 100644 index 0000000..609d868 --- /dev/null +++ b/test/oatpp-postgresql/migration/ArrayTest.sql @@ -0,0 +1,51 @@ +DROP TABLE IF EXISTS test_arrays1; +DROP TABLE IF EXISTS test_arrays2; + +CREATE TABLE test_arrays1 ( + f_real real[], + f_double double precision[], + + f_int16 smallint[], + f_int32 integer[], + f_int64 bigint[], + f_bool boolean[], + + f_text text[] +); + +CREATE TABLE test_arrays2 ( + f_real real[][], + f_double double precision[][], + + f_int16 smallint[][], + f_int32 integer[][], + f_int64 bigint[][], + f_bool boolean[][], + + f_text text[][] +); + +INSERT INTO test_arrays1 +(f_real, f_double, f_int16, f_int32, f_int64, f_bool, f_text) +VALUES +(null, null, null, null, null, null, null); + +INSERT INTO test_arrays1 +(f_real, f_double, f_int16, f_int32, f_int64, f_bool, f_text) +VALUES +('{}', '{}', '{}', '{}', '{}', '{}', '{}'); + +INSERT INTO test_arrays1 +(f_real, f_double, f_int16, f_int32, f_int64, f_bool, f_text) +VALUES +('{null, null}', '{null, null}', '{null, null}', '{null, null}', '{null, null}', '{null, null}', '{null, null}'); + +INSERT INTO test_arrays1 +(f_real, f_double, f_int16, f_int32, f_int64, f_bool, f_text) +VALUES +('{0}', '{0}', '{0}', '{0}', '{0}', '{false}', '{"", ""}'); + +INSERT INTO test_arrays1 +(f_real, f_double, f_int16, f_int32, f_int64, f_bool, f_text) +VALUES +('{1}', '{1}', '{1}', '{1}', '{1}', '{true}', '{"hello"}'); \ No newline at end of file diff --git a/test/oatpp-postgresql/tests.cpp b/test/oatpp-postgresql/tests.cpp index b533f34..df8680f 100644 --- a/test/oatpp-postgresql/tests.cpp +++ b/test/oatpp-postgresql/tests.cpp @@ -1,6 +1,7 @@ #include "ql_template/ParserTest.hpp" +#include "types/ArrayTest.hpp" #include "types/IntTest.hpp" #include "types/FloatTest.hpp" #include "types/InterpretationTest.hpp" @@ -11,11 +12,12 @@ namespace { void runTests() { - OATPP_RUN_TEST(oatpp::test::postgresql::ql_template::ParserTest); - - OATPP_RUN_TEST(oatpp::test::postgresql::types::IntTest); - OATPP_RUN_TEST(oatpp::test::postgresql::types::FloatTest); - OATPP_RUN_TEST(oatpp::test::postgresql::types::InterpretationTest); +// OATPP_RUN_TEST(oatpp::test::postgresql::ql_template::ParserTest); +// +// OATPP_RUN_TEST(oatpp::test::postgresql::types::IntTest); +// OATPP_RUN_TEST(oatpp::test::postgresql::types::FloatTest); + OATPP_RUN_TEST(oatpp::test::postgresql::types::ArrayTest); +// OATPP_RUN_TEST(oatpp::test::postgresql::types::InterpretationTest); } diff --git a/test/oatpp-postgresql/types/ArrayTest.cpp b/test/oatpp-postgresql/types/ArrayTest.cpp new file mode 100644 index 0000000..757f547 --- /dev/null +++ b/test/oatpp-postgresql/types/ArrayTest.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#include "ArrayTest.hpp" + +#include "oatpp-postgresql/orm.hpp" +#include "oatpp/parser/json/mapping/ObjectMapper.hpp" + +#include +#include +#include + +namespace oatpp { namespace test { namespace postgresql { namespace types { + +namespace { + +#include OATPP_CODEGEN_BEGIN(DTO) + +class Row : public oatpp::DTO { + + DTO_INIT(Row, DTO); + + DTO_FIELD(Float32, f_real); + DTO_FIELD(Float64, f_double); + +}; + +#include OATPP_CODEGEN_END(DTO) + +#include OATPP_CODEGEN_BEGIN(DbClient) + +class MyClient : public oatpp::orm::DbClient { +public: + + MyClient(const std::shared_ptr& executor) + : oatpp::orm::DbClient(executor) + { + + executeQuery("DROP TABLE IF EXISTS oatpp_schema_version_ArrayTest;", {}); + + oatpp::orm::SchemaMigration migration(executor, "ArrayTest"); + migration.addFile(1, TEST_DB_MIGRATION "ArrayTest.sql"); + migration.migrate(); + + auto version = executor->getSchemaVersion("ArrayTest"); + OATPP_LOGD("DbClient", "Migration - OK. Version=%d.", version); + + } + + QUERY(insertValues, + "INSERT INTO test_floats " + "(f_real, f_double) " + "VALUES " + "(:row.f_real, :row.f_double);", + PARAM(oatpp::Object, row), PREPARE(true)) + + QUERY(deleteValues, + "DELETE FROM test_floats;") + + QUERY(selectValues, "SELECT * FROM test_arrays1;") + +}; + +#include OATPP_CODEGEN_END(DbClient) + +} + +void ArrayTest::onRun() { + + OATPP_LOGI(TAG, "DB-URL='%s'", TEST_DB_URL); + + auto connectionProvider = std::make_shared(TEST_DB_URL); + auto executor = std::make_shared(connectionProvider); + + auto client = MyClient(executor); + + { + auto res = client.selectValues(); + if(res->isSuccess()) { + OATPP_LOGD(TAG, "OK, knownCount=%d, hasMore=%d", res->getKnownCount(), res->hasMoreToFetch()); + } else { + auto message = res->getErrorMessage(); + OATPP_LOGD(TAG, "Error, message=%s", message->c_str()); + } + + auto dataset = res->fetch>>(); + + oatpp::parser::json::mapping::ObjectMapper om; + om.getSerializer()->getConfig()->useBeautifier = true; + om.getSerializer()->getConfig()->enabledInterpretations = {"postgresql"}; + + auto str = om.writeToString(dataset); + + std::cout << "\n" << str->std_str() << std::endl; + + } + + +} + +}}}} diff --git a/test/oatpp-postgresql/types/ArrayTest.hpp b/test/oatpp-postgresql/types/ArrayTest.hpp new file mode 100644 index 0000000..5e8379a --- /dev/null +++ b/test/oatpp-postgresql/types/ArrayTest.hpp @@ -0,0 +1,40 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#ifndef oatpp_test_postgresql_types_ArrayTest_hpp +#define oatpp_test_postgresql_types_ArrayTest_hpp + +#include "oatpp-test/UnitTest.hpp" + +namespace oatpp { namespace test { namespace postgresql { namespace types { + +class ArrayTest : public UnitTest { +public: + ArrayTest() : UnitTest("TEST[postgresql::types::ArrayTest]") {} + void onRun() override; +}; + +}}}} + +#endif // oatpp_test_postgresql_types_ArrayTest_hpp