IGNITE-5621: Support BINARY and VARBINARY SQL types for C++ (cherry picked from commit 96b43e5)
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/97813a82 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/97813a82 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/97813a82 Branch: refs/heads/ignite-5757 Commit: 97813a8280d54c9b4ce08dca954e05fa62393cbe Parents: 3085b20 Author: Igor Sapego <[email protected]> Authored: Fri Jul 28 19:30:35 2017 +0300 Committer: Igor Sapego <[email protected]> Committed: Fri Jul 28 19:32:43 2017 +0300 ---------------------------------------------------------------------- .../core-test/config/cache-query-default.xml | 29 +++ .../cpp/core-test/src/cache_query_test.cpp | 215 ++++++++++++++++++- .../ignite/cache/query/query_fields_row.h | 28 +++ .../ignite/cache/query/query_sql_fields.h | 13 +- .../ignite/impl/cache/query/query_argument.h | 63 ++++++ .../impl/cache/query/query_fields_row_impl.h | 29 +++ 6 files changed, 373 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/97813a82/modules/platforms/cpp/core-test/config/cache-query-default.xml ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/core-test/config/cache-query-default.xml b/modules/platforms/cpp/core-test/config/cache-query-default.xml index 16f601d..42ac80f 100644 --- a/modules/platforms/cpp/core-test/config/cache-query-default.xml +++ b/modules/platforms/cpp/core-test/config/cache-query-default.xml @@ -160,6 +160,35 @@ </list> </property> </bean> + + <bean class="org.apache.ignite.configuration.CacheConfiguration"> + <property name="name" value="ByteArrayCache"/> + <property name="cacheMode" value="PARTITIONED"/> + <property name="atomicityMode" value="TRANSACTIONAL"/> + <property name="writeSynchronizationMode" value="FULL_SYNC"/> + + <property name="affinity"> + <bean class="org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction"> + <property name="partitions" value="256"/> + </bean> + </property> + + <!-- Configure type metadata to enable queries. --> + <property name="queryEntities"> + <list> + <bean class="org.apache.ignite.cache.QueryEntity"> + <property name="keyType" value="java.lang.Integer"/> + <property name="valueType" value="ByteArrayType"/> + <property name="fields"> + <map> + <entry key="intVal" value="java.lang.Integer"/> + <entry key="arrayVal" value="[B"/> + </map> + </property> + </bean> + </list> + </property> + </bean> </list> </property> http://git-wip-us.apache.org/repos/asf/ignite/blob/97813a82/modules/platforms/cpp/core-test/src/cache_query_test.cpp ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/core-test/src/cache_query_test.cpp b/modules/platforms/cpp/core-test/src/cache_query_test.cpp index e763e08..4993279 100644 --- a/modules/platforms/cpp/core-test/src/cache_query_test.cpp +++ b/modules/platforms/cpp/core-test/src/cache_query_test.cpp @@ -47,7 +47,7 @@ using ignite::impl::binary::BinaryUtils; /** * Person class for query tests. */ -class IGNITE_IMPORT_EXPORT QueryPerson +class QueryPerson { public: /** @@ -203,7 +203,7 @@ private: /** * Relation class for query tests. */ -class IGNITE_IMPORT_EXPORT QueryRelation +class QueryRelation { public: /** @@ -257,6 +257,40 @@ private: int32_t someVal; }; +/** + * Byte array test type. + */ +struct ByteArrayType +{ + /** + * Test constructor. + * + * @param val Init value. + */ + ByteArrayType(int32_t val) : + intVal(val), + arrayVal(val + 1, val + 1) + { + // No-op. + } + + /** + * Default constructor. + */ + ByteArrayType() : + intVal(0), + arrayVal() + { + // No-op. + } + + /** Int field. */ + int32_t intVal; + + /** Array field. */ + std::vector<int8_t> arrayVal; +}; + namespace ignite { namespace binary @@ -300,7 +334,7 @@ namespace ignite IGNITE_BINARY_IS_NULL_FALSE(QueryRelation) IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(QueryRelation) - static void Write(BinaryWriter& writer, QueryRelation obj) + static void Write(BinaryWriter& writer, const QueryRelation& obj) { writer.WriteInt32("personId", obj.GetPersonId()); writer.WriteInt32("someVal", obj.GetHobbyId()); @@ -314,6 +348,32 @@ namespace ignite dst = QueryRelation(personId, someVal); } IGNITE_BINARY_TYPE_END + + /** + * Binary type definition for ByteArrayType. + */ + IGNITE_BINARY_TYPE_START(ByteArrayType) + IGNITE_BINARY_GET_TYPE_ID_AS_HASH(ByteArrayType) + IGNITE_BINARY_GET_TYPE_NAME_AS_IS(ByteArrayType) + IGNITE_BINARY_GET_FIELD_ID_AS_HASH + IGNITE_BINARY_IS_NULL_FALSE(ByteArrayType) + IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(ByteArrayType) + + static void Write(BinaryWriter& writer, const ByteArrayType& obj) + { + writer.WriteInt32("intVal", obj.intVal); + writer.WriteInt8Array("arrayVal", &obj.arrayVal[0], static_cast<int32_t>(obj.arrayVal.size())); + } + + static void Read(BinaryReader& reader, ByteArrayType& dst) + { + dst.intVal = reader.ReadInt32("intVal"); + int32_t arrayValSize = reader.ReadInt8Array("arrayVal", 0, 0); + + dst.arrayVal.resize(static_cast<size_t>(arrayValSize)); + reader.ReadInt8Array("arrayVal", &dst.arrayVal[0], arrayValSize); + } + IGNITE_BINARY_TYPE_END } } @@ -1956,4 +2016,153 @@ BOOST_AUTO_TEST_CASE(TestFieldsQuerySetSchema) CheckEmpty(cursor); } +/** + * Test query for byte arrays. + */ +BOOST_AUTO_TEST_CASE(TestFieldsQueryByteArraySelect) +{ + Cache<int32_t, ByteArrayType> byteArrayCache = grid.GetCache<int32_t, ByteArrayType>("ByteArrayCache"); + + int32_t entryCnt = 100; // Number of entries. + + for (int32_t i = 0; i < entryCnt; i++) + byteArrayCache.Put(i, ByteArrayType(i)); + + SqlFieldsQuery qry("select intVal, arrayVal, intVal + 1 from ByteArrayType where _key=42"); + + QueryFieldsCursor cursor = byteArrayCache.Query(qry); + + BOOST_REQUIRE(cursor.HasNext()); + + QueryFieldsRow row = cursor.GetNext(); + + BOOST_REQUIRE(row.HasNext()); + + int32_t intVal1 = row.GetNext<int32_t>(); + + BOOST_CHECK_EQUAL(intVal1, 42); + + BOOST_REQUIRE(row.HasNext()); + + std::vector<int8_t> arrayVal; + int32_t arrayValSize = row.GetNextInt8Array(0, 0); + + arrayVal.resize(static_cast<size_t>(arrayValSize)); + row.GetNextInt8Array(&arrayVal[0], arrayValSize); + + BOOST_CHECK_EQUAL(arrayValSize, 43); + + for (int32_t i = 0; i < arrayValSize; ++i) + BOOST_CHECK_EQUAL(arrayVal[i], 43); + + BOOST_REQUIRE(row.HasNext()); + + int32_t intVal2 = row.GetNext<int32_t>(); + + BOOST_CHECK_EQUAL(intVal2, 43); + + BOOST_REQUIRE(!row.HasNext()); + + CheckEmpty(cursor); +} + +/** + * Test query for byte arrays. + */ +BOOST_AUTO_TEST_CASE(TestFieldsQueryByteArrayInsert) +{ + Cache<int32_t, ByteArrayType> byteArrayCache = grid.GetCache<int32_t, ByteArrayType>("ByteArrayCache"); + + SqlFieldsQuery qry("insert into ByteArrayType(_key, intVal, arrayVal) values (?, ?, ?)"); + + int32_t entryCnt = 100; // Number of entries. + + for (int32_t i = 0; i < entryCnt; i++) + { + int32_t key = i; + int32_t intVal = i; + std::vector<int8_t> arrayVal(i + 1, i + 1); + + qry.AddArgument(key); + qry.AddArgument(intVal); + qry.AddInt8ArrayArgument(&arrayVal[0], i + 1); + + byteArrayCache.Query(qry); + + qry.ClearArguments(); + } + + ByteArrayType val = byteArrayCache.Get(42); + + BOOST_CHECK_EQUAL(val.intVal, 42); + BOOST_CHECK_EQUAL(val.arrayVal.size(), 43); + + for (int32_t i = 0; i < 43; ++i) + BOOST_CHECK_EQUAL(val.arrayVal[i], 43); +} + +/** + * Test query for byte arrays. + */ +BOOST_AUTO_TEST_CASE(TestFieldsQueryByteArrayInsertSelect) +{ + Cache<int32_t, ByteArrayType> byteArrayCache = grid.GetCache<int32_t, ByteArrayType>("ByteArrayCache"); + + SqlFieldsQuery qry("insert into ByteArrayType(_key, intVal, arrayVal) values (?, ?, ?)"); + + int32_t entryCnt = 100; // Number of entries. + + for (int32_t i = 0; i < entryCnt; i++) + { + int32_t key = i; + int32_t intVal = i; + std::vector<int8_t> arrayVal(i + 1, i + 1); + + qry.AddArgument(key); + qry.AddArgument(intVal); + qry.AddInt8ArrayArgument(&arrayVal[0], i + 1); + + byteArrayCache.Query(qry); + + qry.ClearArguments(); + } + + qry = SqlFieldsQuery("select intVal, arrayVal, intVal + 1 from ByteArrayType where _key=42"); + + QueryFieldsCursor cursor = byteArrayCache.Query(qry); + + BOOST_REQUIRE(cursor.HasNext()); + + QueryFieldsRow row = cursor.GetNext(); + + BOOST_REQUIRE(row.HasNext()); + + int32_t intVal1 = row.GetNext<int32_t>(); + + BOOST_CHECK_EQUAL(intVal1, 42); + + BOOST_REQUIRE(row.HasNext()); + + std::vector<int8_t> arrayVal; + int32_t arrayValSize = row.GetNextInt8Array(0, 0); + + arrayVal.resize(static_cast<size_t>(arrayValSize)); + row.GetNextInt8Array(&arrayVal[0], arrayValSize); + + BOOST_CHECK_EQUAL(arrayValSize, 43); + + for (int32_t i = 0; i < arrayValSize; ++i) + BOOST_CHECK_EQUAL(arrayVal[i], 43); + + BOOST_REQUIRE(row.HasNext()); + + int32_t intVal2 = row.GetNext<int32_t>(); + + BOOST_CHECK_EQUAL(intVal2, 43); + + BOOST_REQUIRE(!row.HasNext()); + + CheckEmpty(cursor); +} + BOOST_AUTO_TEST_SUITE_END() http://git-wip-us.apache.org/repos/asf/ignite/blob/97813a82/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h index d3ac2de..8ed587c 100644 --- a/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h +++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_fields_row.h @@ -172,6 +172,34 @@ namespace ignite } /** + * Get next entry assuming it's an array of 8-byte signed + * integers. Maps to "byte[]" type in Java. + * + * This method should only be used on the valid instance. + * + * @param dst Array to store data to. + * @param len Expected length of array. + * @return Actual amount of elements read. If "len" argument is less than actual + * array size or resulting array is set to null, nothing will be written + * to resulting array and returned value will contain required array length. + * -1 will be returned in case array in stream was null. + * + * @throw IgniteError class instance in case of failure. + */ + int32_t GetNextInt8Array(int8_t* dst, int32_t len) + { + impl::cache::query::QueryFieldsRowImpl* impl0 = impl.Get(); + + if (impl0) + return impl0->GetNextInt8Array(dst, len); + else + { + throw IgniteError(IgniteError::IGNITE_ERR_GENERIC, + "Instance is not usable (did you check for error?)."); + } + } + + /** * Check if the instance is valid. * * Invalid instance can be returned if some of the previous http://git-wip-us.apache.org/repos/asf/ignite/blob/97813a82/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h index bf8d7ac..96d794d 100644 --- a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h +++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql_fields.h @@ -139,7 +139,7 @@ namespace ignite using std::swap; swap(sql, other.sql); - swap(sql, other.schema); + swap(schema, other.schema); swap(pageSize, other.pageSize); swap(loc, other.loc); swap(distributedJoins, other.distributedJoins); @@ -274,6 +274,17 @@ namespace ignite } /** + * Add array of bytes as an argument. + * + * @param src Array pointer. + * @param len Array length in bytes. + */ + void AddInt8ArrayArgument(const int8_t* src, int32_t len) + { + args.push_back(new impl::cache::query::QueryInt8ArrayArgument(src, len)); + } + + /** * Remove all added arguments. */ void ClearArguments() http://git-wip-us.apache.org/repos/asf/ignite/blob/97813a82/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_argument.h ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_argument.h b/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_argument.h index f2f55bc..e9e7e51 100644 --- a/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_argument.h +++ b/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_argument.h @@ -129,6 +129,69 @@ namespace ignite /** Value. */ T val; }; + + /** + * Query bytes array argument class. + */ + class QueryInt8ArrayArgument : public QueryArgumentBase + { + public: + /** + * Constructor. + * + * @param src Array. + * @param len Array length. + */ + QueryInt8ArrayArgument(const int8_t* src, int32_t len) : + val(src, src + len) + { + // No-op. + } + + /** + * Copy constructor. + * + * @param other Other instance. + */ + QueryInt8ArrayArgument(const QueryInt8ArrayArgument& other) : + val(other.val) + { + // No-op. + } + + /** + * Assignment operator. + * + * @param other Other instance. + * @return *this. + */ + QueryInt8ArrayArgument& operator=(const QueryInt8ArrayArgument& other) + { + if (this != &other) + val = other.val; + + return *this; + } + + virtual ~QueryInt8ArrayArgument() + { + // No-op. + } + + virtual QueryArgumentBase* Copy() const + { + return new QueryInt8ArrayArgument(*this); + } + + virtual void Write(ignite::binary::BinaryRawWriter& writer) + { + writer.WriteInt8Array(&val[0], static_cast<int32_t>(val.size())); + } + + private: + /** Value. */ + std::vector<int8_t> val; + }; } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/97813a82/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h b/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h index 82cebd5..63e0523 100644 --- a/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h +++ b/modules/platforms/cpp/core/include/ignite/impl/cache/query/query_fields_row_impl.h @@ -127,6 +127,35 @@ namespace ignite } /** + * Get next entry assuming it's an array of 8-byte signed + * integers. Maps to "byte[]" type in Java. + * + * @param dst Array to store data to. + * @param len Expected length of array. + * @return Actual amount of elements read. If "len" argument is less than actual + * array size or resulting array is set to null, nothing will be written + * to resulting array and returned value will contain required array length. + * -1 will be returned in case array in stream was null. + */ + int32_t GetNextInt8Array(int8_t* dst, int32_t len) + { + if (IsValid()) { + + int32_t actualLen = reader.ReadInt8Array(dst, len); + + if (actualLen == 0 || dst && len >= actualLen) + ++processed; + + return actualLen; + } + else + { + throw IgniteError(IgniteError::IGNITE_ERR_GENERIC, + "Instance is not usable (did you check for error?)."); + } + } + + /** * Check if the instance is valid. * * Invalid instance can be returned if some of the previous
