Repository: ignite
Updated Branches:
  refs/heads/master 4318be6d5 -> 506060514


IGNITE-3578: CPP: Added properties to support distributed joins in CPP. This 
closes #894.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/33d35b3e
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/33d35b3e
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/33d35b3e

Branch: refs/heads/master
Commit: 33d35b3ed87f986918f87838d4c9f12d23ec8dda
Parents: a7cf124
Author: isapego <isap...@gridgain.com>
Authored: Thu Jul 28 08:59:26 2016 +0300
Committer: vozerov-gridgain <voze...@gridgain.com>
Committed: Thu Jul 28 08:59:26 2016 +0300

----------------------------------------------------------------------
 .../cpp/core-test/config/cache-query.xml        |  39 +-
 .../cpp/core-test/src/cache_query_test.cpp      | 443 ++++++++++++++++---
 .../core/include/ignite/cache/query/query_sql.h |  61 ++-
 .../ignite/cache/query/query_sql_fields.h       | 112 ++++-
 4 files changed, 569 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/modules/platforms/cpp/core-test/config/cache-query.xml
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core-test/config/cache-query.xml 
b/modules/platforms/cpp/core-test/config/cache-query.xml
index 232b6da..06bc7f5 100644
--- a/modules/platforms/cpp/core-test/config/cache-query.xml
+++ b/modules/platforms/cpp/core-test/config/cache-query.xml
@@ -35,7 +35,7 @@
         <property name="cacheConfiguration">
             <list>
                 <bean 
class="org.apache.ignite.configuration.CacheConfiguration">
-                    <property name="name" value="cache"/>
+                    <property name="name" value="QueryPerson"/>
                     <property name="cacheMode" value="PARTITIONED"/>
                     <property name="atomicityMode" value="TRANSACTIONAL"/>
                     <property name="writeSynchronizationMode" 
value="FULL_SYNC"/>
@@ -69,6 +69,43 @@
                                     </list>
                                 </property>
                             </bean>
+                                                       
+                            <bean 
class="org.apache.ignite.cache.CacheTypeMetadata">
+                                <property name="valueType" 
value="QueryRelation"/>
+                                <property name="queryFields">
+                                    <map>
+                                        <entry key="personId" 
value="java.lang.Integer"/>
+                                        <entry key="someVal" 
value="java.lang.Integer"/>
+                                    </map>
+                                </property>
+                            </bean>
+                        </list>
+                    </property>
+                </bean>
+                               
+                <bean 
class="org.apache.ignite.configuration.CacheConfiguration">
+                    <property name="name" value="QueryRelation"/>
+                    <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>
+                    
+                    <property name="typeMetadata">
+                        <list>
+                            <bean 
class="org.apache.ignite.cache.CacheTypeMetadata">
+                                <property name="valueType" 
value="QueryRelation"/>
+                                <property name="queryFields">
+                                    <map>
+                                        <entry key="personId" 
value="java.lang.Integer"/>
+                                        <entry key="someVal" 
value="java.lang.Integer"/>
+                                    </map>
+                                </property>
+                            </bean>
                         </list>
                     </property>
                 </bean>

http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/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 168f3f9..e3fba02 100644
--- a/modules/platforms/cpp/core-test/src/cache_query_test.cpp
+++ b/modules/platforms/cpp/core-test/src/cache_query_test.cpp
@@ -175,12 +175,71 @@ private:
     Timestamp recordCreated;
 };
 
+/**
+ * Relation class for query tests.
+ */
+class IGNITE_IMPORT_EXPORT QueryRelation
+{
+public:
+    /**
+     * Constructor.
+     */
+    QueryRelation() :
+        personId(),
+        someVal()
+    {
+        // No-op.
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param name Name.
+     * @param age Age.
+     */
+    QueryRelation(int32_t personId, int32_t someVal) :
+        personId(personId),
+        someVal(someVal)
+    {
+        // No-op.
+    }
+
+    /**
+     * Get person ID.
+     * 
+     * @return Person ID.
+     */
+    int32_t GetPersonId() const
+    {
+        return personId;
+    }
+
+    /**
+     * Get hobby ID.
+     * 
+     * @return Some test value.
+     */
+    int32_t GetHobbyId() const
+    {
+        return someVal;
+    }
+
+private:
+    /** Person ID. */
+    int32_t personId;
+
+    /** Some test value. */
+    int32_t someVal;
+};
+
+
+
 namespace ignite
 {
     namespace binary
     {
         /**
-         * Binary type definition.
+         * Binary type definition for QueryPerson.
          */
         IGNITE_BINARY_TYPE_START(QueryPerson)
             IGNITE_BINARY_GET_TYPE_ID_AS_HASH(QueryPerson)
@@ -207,31 +266,45 @@ namespace ignite
             
                 return QueryPerson(name, age, birthday, recordCreated);
             }
-
         IGNITE_BINARY_TYPE_END
-    }
-}
 
-/** Node started during the test. */
-Ignite grid = Ignite();
+        /**
+         * Binary type definition for QueryRelation.
+         */
+        IGNITE_BINARY_TYPE_START(QueryRelation)
+            IGNITE_BINARY_GET_TYPE_ID_AS_HASH(QueryRelation)
+            IGNITE_BINARY_GET_TYPE_NAME_AS_IS(QueryRelation)
+            IGNITE_BINARY_GET_FIELD_ID_AS_HASH
+            IGNITE_BINARY_GET_HASH_CODE_ZERO(QueryRelation)
+            IGNITE_BINARY_IS_NULL_FALSE(QueryRelation)
+            IGNITE_BINARY_GET_NULL_DEFAULT_CTOR(QueryRelation)
 
-/** Cache accessor. */
-Cache<int, QueryPerson> GetCache()
-{
-    return grid.GetCache<int, QueryPerson>("cache");
+            void Write(BinaryWriter& writer, QueryRelation obj)
+            {
+                writer.WriteInt32("personId", obj.GetPersonId());
+                writer.WriteInt32("someVal", obj.GetHobbyId());
+            }
+
+            QueryRelation Read(BinaryReader& reader)
+            {
+                int32_t personId = reader.ReadInt32("personId");
+                int32_t someVal = reader.ReadInt32("someVal");
+
+                return QueryRelation(personId, someVal);
+            }
+        IGNITE_BINARY_TYPE_END
+    }
 }
 
 /**
  * Test setup fixture.
  */
-struct CacheQueryTestSuiteFixture {
-    /**
-     * Constructor.
-     */
-    CacheQueryTestSuiteFixture()
+struct CacheQueryTestSuiteFixture
+{
+    Ignite StartNode(const char* name)
     {
         IgniteConfiguration cfg;
-        
+
         cfg.jvmOpts.push_back("-Xdebug");
         cfg.jvmOpts.push_back("-Xnoagent");
         cfg.jvmOpts.push_back("-Djava.compiler=NONE");
@@ -246,18 +319,26 @@ struct CacheQueryTestSuiteFixture {
         cfg.jvmMaxMem = 4096;
 #endif
 
-        char* cfgPath = getenv("IGNITE_NATIVE_TEST_CPP_CONFIG_PATH");
-
-        cfg.springCfgPath = 
std::string(cfgPath).append("/").append("cache-query.xml");
+        
cfg.springCfgPath.assign(getenv("IGNITE_NATIVE_TEST_CPP_CONFIG_PATH")).append("/cache-query.xml");
 
         IgniteError err;
 
-        Ignite grid0 = Ignition::Start(cfg, &err);
+        Ignite grid0 = Ignition::Start(cfg, name, &err);
 
         if (err.GetCode() != IgniteError::IGNITE_SUCCESS)
             BOOST_ERROR(err.GetText());
 
-        grid = grid0;
+        return grid0;
+    }
+
+
+    /**
+     * Constructor.
+     */
+    CacheQueryTestSuiteFixture() : 
+        grid(StartNode("Node1"))
+    {
+        // No-op.
     }
 
     /**
@@ -265,11 +346,44 @@ struct CacheQueryTestSuiteFixture {
      */
     ~CacheQueryTestSuiteFixture()
     {
-        Ignition::Stop(grid.GetName(), true);
+        Ignition::StopAll(true);
     }
+
+    /** Person cache accessor. */
+    Cache<int, QueryPerson> GetPersonCache()
+    {
+        return grid.GetCache<int, QueryPerson>("QueryPerson");
+    }
+
+    /** Relation cache accessor. */
+    Cache<int, QueryRelation> GetRelationCache()
+    {
+        return grid.GetCache<int, QueryRelation>("QueryRelation");
+    }
+
+    /** Node started during the test. */
+    Ignite grid;
 };
 
 /**
+ * Count number of records returned by cursor.
+ *
+ * @param cur Cursor.
+ */
+template<typename Cursor>
+int CountRecords(Cursor& cur)
+{
+    int number = 0;
+    while (cur.HasNext())
+    {
+        ++number;
+        cur.GetNext();
+    }
+
+    return number;
+}
+
+/**
  * Ensure that HasNext() fails.
  *
  * @param cur Cursor.
@@ -314,8 +428,7 @@ void CheckGetNextFail(Cursor& cur)
  *
  * @param cur Cursor.
  */
-template<typename Cursor>
-void CheckGetAllFail(Cursor& cur)
+void CheckGetAllFail(QueryCursor<int, QueryPerson>& cur)
 {
     try 
     {
@@ -361,8 +474,7 @@ void CheckEmpty(QueryFieldsCursor& cur)
  *
  * @param cur Cursor.
  */
-template<typename Cursor>
-void CheckEmptyGetAll(Cursor& cur)
+void CheckEmptyGetAll(QueryCursor<int, QueryPerson>& cur)
 {
     std::vector<CacheEntry<int, QueryPerson>> res;
 
@@ -382,8 +494,7 @@ void CheckEmptyGetAll(Cursor& cur)
  * @param name1 Name.
  * @param age1 Age.
  */
-template<typename Cursor>
-void CheckSingle(Cursor& cur, int key, const std::string& name, int age)
+void CheckSingle(QueryCursor<int, QueryPerson>& cur, int key, const 
std::string& name, int age)
 {
     BOOST_REQUIRE(cur.HasNext());
 
@@ -404,6 +515,30 @@ void CheckSingle(Cursor& cur, int key, const std::string& 
name, int age)
 }
 
 /**
+ * Check single result through iteration.
+ *
+ * @param cur Cursor.
+ * @param key Key.
+ * @param name Name.
+ * @param age Age.
+ */
+void CheckSingle(QueryFieldsCursor& cur, int key, const std::string& name, int 
age)
+{
+    BOOST_REQUIRE(cur.HasNext());
+
+    QueryFieldsRow row = cur.GetNext();
+
+    BOOST_REQUIRE_EQUAL(row.GetNext<int32_t>(), key);
+    BOOST_REQUIRE_EQUAL(row.GetNext<std::string>(), name);
+    BOOST_REQUIRE_EQUAL(row.GetNext<int32_t>(), age);
+
+    BOOST_REQUIRE(!row.HasNext());
+    BOOST_REQUIRE(!cur.HasNext());
+
+    CheckGetNextFail(cur);
+}
+
+/**
  * Check single result through GetAll().
  *
  * @param cur Cursor.
@@ -411,8 +546,7 @@ void CheckSingle(Cursor& cur, int key, const std::string& 
name, int age)
  * @param name1 Name.
  * @param age1 Age.
  */
-template<typename Cursor>
-void CheckSingleGetAll(Cursor& cur, int key, const std::string& name, int age)
+void CheckSingleGetAll(QueryCursor<int, QueryPerson>& cur, int key, const 
std::string& name, int age)
 {
     std::vector<CacheEntry<int, QueryPerson>> res;
 
@@ -444,8 +578,7 @@ void CheckSingleGetAll(Cursor& cur, int key, const 
std::string& name, int age)
  * @param name2 Name 2.
  * @param age2 Age 2.
  */
-template<typename Cursor>
-void CheckMultiple(Cursor& cur, int key1, const std::string& name1, 
+void CheckMultiple(QueryCursor<int, QueryPerson>& cur, int key1, const 
std::string& name1,
     int age1, int key2, const std::string& name2, int age2)
 {
     for (int i = 0; i < 2; i++)
@@ -489,8 +622,7 @@ void CheckMultiple(Cursor& cur, int key1, const 
std::string& name1,
  * @param name2 Name 2.
  * @param age2 Age 2.
  */
-template<typename Cursor>
-void CheckMultipleGetAll(Cursor& cur, int key1, const std::string& name1,
+void CheckMultipleGetAll(QueryCursor<int, QueryPerson>& cur, int key1, const 
std::string& name1,
     int age1, int key2, const std::string& name2, int age2)
 {
     std::vector<CacheEntry<int, QueryPerson>> res;
@@ -529,7 +661,7 @@ BOOST_FIXTURE_TEST_SUITE(CacheQueryTestSuite, 
CacheQueryTestSuiteFixture)
  */
 BOOST_AUTO_TEST_CASE(TestSqlQuery)
 {    
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with no results.
     SqlQuery qry("QueryPerson", "age < 20");
@@ -541,8 +673,11 @@ BOOST_AUTO_TEST_CASE(TestSqlQuery)
     CheckEmptyGetAll(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
     
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -550,8 +685,23 @@ BOOST_AUTO_TEST_CASE(TestSqlQuery)
     cursor = cache.Query(qry);
     CheckSingleGetAll(cursor, 1, "A1", 10);
 
+    // Test simple distributed joins query.
+    BOOST_CHECK(!qry.IsDistributedJoins());
+    qry.SetDistributedJoins(true);
+    BOOST_CHECK(qry.IsDistributedJoins());
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+
+    cursor = cache.Query(qry);
+    CheckSingleGetAll(cursor, 1, "A1", 10);
+
+    qry.SetDistributedJoins(false);
+
     // Test simple local query.
+    BOOST_CHECK(!qry.IsLocal());
     qry.SetLocal(true);
+    BOOST_CHECK(qry.IsLocal());
 
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -581,11 +731,60 @@ BOOST_AUTO_TEST_CASE(TestSqlQuery)
 }
 
 /**
+ * Test SQL query distributed joins.
+ */
+BOOST_AUTO_TEST_CASE(TestSqlQueryDistributedJoins)
+{    
+    Cache<int, QueryPerson> cache1 = GetPersonCache();
+    Cache<int, QueryRelation> cache2 = GetRelationCache();
+
+    // Starting second node.
+    Ignite node2 = StartNode("Node2");
+
+    int entryCnt = 1000;
+
+    // Filling caches
+    for (int i = 0; i < entryCnt; i++)
+    {
+        std::stringstream stream;
+
+        stream << "A" << i;
+
+        cache1.Put(i, QueryPerson(stream.str(), i * 10, 
BinaryUtils::MakeDateLocal(1970 + i),
+            BinaryUtils::MakeTimestampLocal(2016, 1, 1, i / 60, i % 60)));
+
+        cache2.Put(i + 1, QueryRelation(i, i * 10));
+    }
+
+    // Test query with no results.
+    SqlQuery qry("QueryPerson",
+        "from \"QueryPerson\".QueryPerson, \"QueryRelation\".QueryRelation "
+        "where \"QueryPerson\".QueryPerson.age = 
\"QueryRelation\".QueryRelation.someVal");
+
+    QueryCursor<int, QueryPerson> cursor = cache1.Query(qry);
+
+    // Ensure that data is not collocated, so not full result set is returned.
+    int recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_GT(recordsNum, 0);
+    BOOST_CHECK_LT(recordsNum, entryCnt);
+
+    qry.SetDistributedJoins(true);
+
+    cursor = cache1.Query(qry);
+
+    // Check that full result set is returned.
+    recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_EQUAL(recordsNum, entryCnt);
+}
+
+/**
  * Test text query.
  */
 BOOST_AUTO_TEST_CASE(TestTextQuery)
 {
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with no results.
     TextQuery qry("QueryPerson", "A1");
@@ -597,8 +796,11 @@ BOOST_AUTO_TEST_CASE(TestTextQuery)
     CheckEmptyGetAll(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
 
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -631,7 +833,7 @@ BOOST_AUTO_TEST_CASE(TestTextQuery)
 BOOST_AUTO_TEST_CASE(TestScanQuery)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with no results.
     ScanQuery qry;
@@ -643,7 +845,8 @@ BOOST_AUTO_TEST_CASE(TestScanQuery)
     CheckEmptyGetAll(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
 
     cursor = cache.Query(qry);
     CheckSingle(cursor, 1, "A1", 10);
@@ -652,7 +855,8 @@ BOOST_AUTO_TEST_CASE(TestScanQuery)
     CheckSingleGetAll(cursor, 1, "A1", 10);
 
     // Test query returning multiple entries.
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
 
     cursor = cache.Query(qry);
     CheckMultiple(cursor, 1, "A1", 10, 2, "A2", 20);
@@ -667,9 +871,9 @@ BOOST_AUTO_TEST_CASE(TestScanQuery)
 BOOST_AUTO_TEST_CASE(TestScanQueryPartitioned)
 {
     // Populate cache with data.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
-    int32_t partCnt = 256;   // Defined in configuration explicitly.   
+    int32_t partCnt = 256;   // Defined in configuration explicitly.
     int32_t entryCnt = 1000; // Should be greater than partCnt.
     
     for (int i = 0; i < entryCnt; i++) 
@@ -678,7 +882,8 @@ BOOST_AUTO_TEST_CASE(TestScanQueryPartitioned)
 
         stream << "A" << i;
 
-        cache.Put(i, QueryPerson(stream.str(), i * 10, 
BinaryUtils::MakeDateLocal(1970 + i), BinaryUtils::MakeTimestampLocal(2016, 1, 
1, i / 60, i % 60)));
+        cache.Put(i, QueryPerson(stream.str(), i * 10, 
BinaryUtils::MakeDateLocal(1970 + i),
+            BinaryUtils::MakeTimestampLocal(2016, 1, 1, i / 60, i % 60)));
     }
 
     // Iterate over all partitions and collect data.
@@ -711,12 +916,125 @@ BOOST_AUTO_TEST_CASE(TestScanQueryPartitioned)
 }
 
 /**
+ * Basic test for SQL fields query.
+ */
+BOOST_AUTO_TEST_CASE(TestSqlFieldsQueryBasic)
+{
+    Cache<int, QueryPerson> cache = GetPersonCache();
+
+    // Test query with no results.
+    SqlFieldsQuery qry("select _key, name, age from QueryPerson where age < 
20");
+
+    QueryFieldsCursor cursor = cache.Query(qry);
+    CheckEmpty(cursor);
+
+    // Test simple query.
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+
+    // Test simple distributed joins query.
+    BOOST_CHECK(!qry.IsDistributedJoins());
+    BOOST_CHECK(!qry.IsEnforceJoinOrder());
+
+    qry.SetDistributedJoins(true);
+
+    BOOST_CHECK(qry.IsDistributedJoins());
+    BOOST_CHECK(!qry.IsEnforceJoinOrder());
+
+    qry.SetEnforceJoinOrder(true);
+
+    BOOST_CHECK(qry.IsDistributedJoins());
+    BOOST_CHECK(qry.IsEnforceJoinOrder());
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+    
+    qry.SetDistributedJoins(false);
+    qry.SetEnforceJoinOrder(false);
+
+    // Test simple local query.
+    BOOST_CHECK(!qry.IsLocal());
+
+    qry.SetLocal(true);
+
+    BOOST_CHECK(qry.IsLocal());
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+
+    // Test query with arguments.
+    qry.SetSql("select _key, name, age from QueryPerson where age < ? AND name 
= ?");
+    qry.AddArgument<int>(20);
+    qry.AddArgument<std::string>("A1");
+
+    cursor = cache.Query(qry);
+    CheckSingle(cursor, 1, "A1", 10);
+}
+
+/**
+ * Test SQL fields query distributed joins.
+ */
+BOOST_AUTO_TEST_CASE(TestSqlFieldsQueryDistributedJoins)
+{
+    Cache<int, QueryPerson> cache1 = GetPersonCache();
+    Cache<int, QueryRelation> cache2 = GetRelationCache();
+
+    // Starting second node.
+    Ignite node2 = StartNode("Node2");
+
+    int entryCnt = 1000;
+
+    // Filling caches
+    for (int i = 0; i < entryCnt; i++)
+    {
+        std::stringstream stream;
+
+        stream << "A" << i;
+
+        cache1.Put(i, QueryPerson(stream.str(), i * 10, 
BinaryUtils::MakeDateLocal(1970 + i),
+            BinaryUtils::MakeTimestampLocal(2016, 1, 1, i / 60, i % 60)));
+
+        cache2.Put(i + 1, QueryRelation(i, i * 10));
+    }
+
+    // Test query with no results.
+    SqlFieldsQuery qry(
+        "select age, name "
+        "from \"QueryPerson\".QueryPerson "
+        "inner join \"QueryRelation\".QueryRelation "
+        "on \"QueryPerson\".QueryPerson.age = 
\"QueryRelation\".QueryRelation.someVal");
+
+    QueryFieldsCursor cursor = cache1.Query(qry);
+
+    // Ensure that data is not collocated, so not full result set is returned.
+    int recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_GT(recordsNum, 0);
+    BOOST_CHECK_LT(recordsNum, entryCnt);
+
+    qry.SetDistributedJoins(true);
+
+    cursor = cache1.Query(qry);
+
+    // Check that full result set is returned.
+    recordsNum = CountRecords(cursor);
+
+    BOOST_CHECK_EQUAL(recordsNum, entryCnt);
+}
+
+/**
  * Test fields query with single entry.
  */
 BOOST_AUTO_TEST_CASE(TestFieldsQuerySingle)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select age, name from QueryPerson");
@@ -725,7 +1043,8 @@ BOOST_AUTO_TEST_CASE(TestFieldsQuerySingle)
     CheckEmpty(cursor);
     
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
 
     cursor = cache.Query(qry);
 
@@ -761,7 +1080,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQuerySingle)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryExceptions)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select age, name from QueryPerson");
@@ -770,7 +1089,8 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryExceptions)
     CheckEmpty(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
 
     cursor = cache.Query(qry);
 
@@ -806,7 +1126,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryExceptions)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTwo)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select age, name from QueryPerson");
@@ -815,8 +1135,11 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTwo)
     CheckEmpty(cursor);
 
     // Test simple query.
-    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
-    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26), BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
+    cache.Put(1, QueryPerson("A1", 10, BinaryUtils::MakeDateLocal(1990, 03, 
18),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 34, 579304685)));
+
+    cache.Put(2, QueryPerson("A2", 20, BinaryUtils::MakeDateLocal(1989, 10, 
26),
+        BinaryUtils::MakeTimestampLocal(2016, 02, 10, 17, 39, 35, 678403201)));
 
     cursor = cache.Query(qry);
 
@@ -869,7 +1192,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTwo)
 BOOST_AUTO_TEST_CASE(TestFieldsQuerySeveral)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with two fields of different type.
     SqlFieldsQuery qry("select name, age from QueryPerson");
@@ -935,7 +1258,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQuerySeveral)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryDateLess)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Date'.
     SqlFieldsQuery qry("select birthday from QueryPerson where 
birthday<'1990-01-01'");
@@ -996,7 +1319,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryDateLess)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryDateMore)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Date'.
     SqlFieldsQuery qry("select birthday from QueryPerson where 
birthday>'2070-01-01'");
@@ -1057,7 +1380,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryDateMore)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryDateEqual)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Date'.
     SqlFieldsQuery qry("select birthday from QueryPerson where 
birthday='2032-01-01'");
@@ -1109,7 +1432,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryDateEqual)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampLess)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Timestamp'.
     SqlFieldsQuery qry("select recordCreated from QueryPerson where 
recordCreated<'2016-01-01 01:00:00'");
@@ -1170,7 +1493,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampLess)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampMore)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Timestamp'.
     SqlFieldsQuery qry("select recordCreated from QueryPerson where 
recordCreated>'2016-01-01 15:30:00'");
@@ -1233,7 +1556,7 @@ BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampMore)
 BOOST_AUTO_TEST_CASE(TestFieldsQueryTimestampEqual)
 {
     // Test simple query.
-    Cache<int, QueryPerson> cache = GetCache();
+    Cache<int, QueryPerson> cache = GetPersonCache();
 
     // Test query with field of type 'Timestamp'.
     SqlFieldsQuery qry("select recordCreated from QueryPerson where 
recordCreated='2016-01-01 09:18:00'");

http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
----------------------------------------------------------------------
diff --git a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h 
b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
index cb7a739..0ec5d5f 100644
--- a/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
+++ b/modules/platforms/cpp/core/include/ignite/cache/query/query_sql.h
@@ -48,8 +48,13 @@ namespace ignite
                  * @param type Type name.
                  * @param sql SQL string.
                  */
-                SqlQuery(const std::string& type, const std::string& sql)
-                    : type(type), sql(sql), pageSize(1024),  loc(false), args()
+                SqlQuery(const std::string& type, const std::string& sql) :
+                    type(type),
+                    sql(sql),
+                    pageSize(1024),
+                    loc(false),
+                    distributedJoins(false),
+                    args()
                 {
                     // No-op.
                 }
@@ -60,13 +65,18 @@ namespace ignite
                  * @param other Other instance.
                  */
                 SqlQuery(const SqlQuery& other) :
-                    type(other.type), sql(other.sql), pageSize(other.pageSize),
-                    loc(other.loc), args()
+                    type(other.type),
+                    sql(other.sql),
+                    pageSize(other.pageSize),
+                    loc(other.loc),
+                    distributedJoins(other.distributedJoins),
+                    args()
                 {
                     args.reserve(other.args.size());
 
-                    for (std::vector<QueryArgumentBase*>::const_iterator i = 
other.args.begin(); 
-                        i != other.args.end(); ++i)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator 
Iter;
+
+                    for (Iter i = other.args.begin(); i != other.args.end(); 
++i)
                         args.push_back((*i)->Copy());
                 }
 
@@ -81,11 +91,7 @@ namespace ignite
                     {
                         SqlQuery tmp(other);
 
-                        std::swap(type, tmp.type);
-                        std::swap(sql, tmp.sql);
-                        std::swap(pageSize, tmp.pageSize);
-                        std::swap(loc, tmp.loc);
-                        std::swap(args, tmp.args);
+                        Swap(tmp);
                     }
 
                     return *this;
@@ -96,7 +102,9 @@ namespace ignite
                  */
                 ~SqlQuery()
                 {
-                    for (std::vector<QueryArgumentBase*>::iterator it = 
args.begin(); it != args.end(); ++it)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator 
Iter;
+
+                    for (Iter it = args.begin(); it != args.end(); ++it)
                         delete *it;
                 }
 
@@ -113,6 +121,7 @@ namespace ignite
                         std::swap(sql, other.sql);
                         std::swap(pageSize, other.pageSize);
                         std::swap(loc, other.loc);
+                        std::swap(distributedJoins, other.distributedJoins);
                         std::swap(args, other.args);
                     }
                 }
@@ -198,6 +207,29 @@ namespace ignite
                 }
 
                 /**
+                 * Check if distributed joins are enabled for this query.
+                 *
+                 * @return True If distributed joind enabled.
+                 */
+                bool IsDistributedJoins() const
+                {
+                    return distributedJoins;
+                }
+
+                /**
+                 * Specify if distributed joins are enabled for this query.
+                 *
+                 * When disabled, join results will only contain colocated 
data (joins work locally).
+                 * When enabled, joins work as expected, no matter how the 
data is distributed.
+                 *
+                 * @param enabled Distributed joins enabled.
+                 */
+                void SetDistributedJoins(bool enabled)
+                {
+                    distributedJoins = enabled;
+                }
+
+                /**
                  * Add argument.
                  *
                  * Template argument type should be copy-constructable and
@@ -229,7 +261,7 @@ namespace ignite
                     for (std::vector<QueryArgumentBase*>::const_iterator it = 
args.begin(); it != args.end(); ++it)
                         (*it)->Write(writer);
 
-                    writer.WriteBool(false);  // distributed joins
+                    writer.WriteBool(distributedJoins);
                 }
 
             private:
@@ -245,6 +277,9 @@ namespace ignite
                 /** Local flag. */
                 bool loc;
 
+                /** Distributed joins flag. */
+                bool distributedJoins;
+
                 /** Arguments. */
                 std::vector<QueryArgumentBase*> args;
             };

http://git-wip-us.apache.org/repos/asf/ignite/blob/33d35b3e/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 1c8570b..10dd6ab 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
@@ -48,7 +48,12 @@ namespace ignite
                  * @param sql SQL string.
                  */
                 SqlFieldsQuery(const std::string& sql) :
-                    sql(sql), pageSize(1024), loc(false), args()
+                    sql(sql),
+                    pageSize(1024),
+                    loc(false),
+                    distributedJoins(false),
+                    enforceJoinOrder(false),
+                    args()
                 {
                     // No-op.
                 }
@@ -60,7 +65,12 @@ namespace ignite
                  * @param loc Whether query should be executed locally.
                  */
                 SqlFieldsQuery(const std::string& sql, bool loc) :
-                    sql(sql), pageSize(1024), loc(false), args()
+                    sql(sql),
+                    pageSize(1024),
+                    loc(false),
+                    distributedJoins(false),
+                    enforceJoinOrder(false),
+                    args()
                 {
                     // No-op.
                 }
@@ -71,13 +81,18 @@ namespace ignite
                  * @param other Other instance.
                  */
                 SqlFieldsQuery(const SqlFieldsQuery& other) :
-                    sql(other.sql), pageSize(other.pageSize), loc(other.loc),
+                    sql(other.sql),
+                    pageSize(other.pageSize),
+                    loc(other.loc),
+                    distributedJoins(other.distributedJoins),
+                    enforceJoinOrder(other.enforceJoinOrder),
                     args()
                 {
                     args.reserve(other.args.size());
 
-                    for (std::vector<QueryArgumentBase*>::const_iterator i = 
other.args.begin(); 
-                        i != other.args.end(); ++i)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator 
Iter;
+
+                    for (Iter i = other.args.begin(); i != other.args.end(); 
++i)
                         args.push_back((*i)->Copy());
                 }
 
@@ -92,10 +107,7 @@ namespace ignite
                     {
                         SqlFieldsQuery tmp(other);
 
-                        std::swap(sql, tmp.sql);
-                        std::swap(pageSize, tmp.pageSize);
-                        std::swap(loc, tmp.loc);
-                        std::swap(args, tmp.args);
+                        Swap(tmp);
                     }
 
                     return *this;
@@ -106,11 +118,31 @@ namespace ignite
                  */
                 ~SqlFieldsQuery()
                 {
-                    for (std::vector<QueryArgumentBase*>::iterator it = 
args.begin(); it != args.end(); ++it)
+                    typedef std::vector<QueryArgumentBase*>::const_iterator 
Iter;
+
+                    for (Iter it = args.begin(); it != args.end(); ++it)
                         delete *it;
                 }
 
                 /**
+                 * Efficiently swaps contents with another SqlQuery instance.
+                 *
+                 * @param other Other instance.
+                 */
+                void Swap(SqlFieldsQuery& other)
+                {
+                    if (this != &other)
+                    {
+                        std::swap(sql, other.sql);
+                        std::swap(pageSize, other.pageSize);
+                        std::swap(loc, other.loc);
+                        std::swap(distributedJoins, other.distributedJoins);
+                        std::swap(enforceJoinOrder, other.enforceJoinOrder);
+                        std::swap(args, other.args);
+                    }
+                }
+
+                /**
                  * Get SQL string.
                  *
                  * @return SQL string.
@@ -171,6 +203,56 @@ namespace ignite
                 }
 
                 /**
+                 * Checks if join order of tables if enforced.
+                 *
+                 * @return Flag value.
+                 */
+                bool IsEnforceJoinOrder() const
+                {
+                    return enforceJoinOrder;
+                }
+
+                /**
+                 * Sets flag to enforce join order of tables in the query.
+                 * If set to true query optimizer will not reorder tables in
+                 * join. By default is false.
+                 *
+                 * It is not recommended to enable this property unless you are
+                 * sure that your indexes and the query itself are correct and
+                 * tuned as much as possible but query optimizer still produces
+                 * wrong join order.
+                 *
+                 * @param enforce Flag value.
+                 */
+                void SetEnforceJoinOrder(bool enforce)
+                {
+                    enforceJoinOrder = enforce;
+                }
+
+                /**
+                 * Check if distributed joins are enabled for this query.
+                 *
+                 * @return True If distributed joind enabled.
+                 */
+                bool IsDistributedJoins() const
+                {
+                    return distributedJoins;
+                }
+
+                /**
+                 * Specify if distributed joins are enabled for this query.
+                 *
+                 * When disabled, join results will only contain colocated 
data (joins work locally).
+                 * When enabled, joins work as expected, no matter how the 
data is distributed.
+                 *
+                 * @param enabled Distributed joins enabled.
+                 */
+                void SetDistributedJoins(bool enabled)
+                {
+                    distributedJoins = enabled;
+                }
+
+                /**
                  * Add argument.
                  *
                  * Template argument type should be copy-constructable and
@@ -201,8 +283,8 @@ namespace ignite
                     for (std::vector<QueryArgumentBase*>::const_iterator it = 
args.begin(); it != args.end(); ++it)
                         (*it)->Write(writer);
 
-                    writer.WriteBool(false);  // distributed joins
-                    writer.WriteBool(false);  // enforce join order
+                    writer.WriteBool(distributedJoins);
+                    writer.WriteBool(enforceJoinOrder);
                 }
 
             private:
@@ -215,6 +297,12 @@ namespace ignite
                 /** Local flag. */
                 bool loc;
 
+                /** Distributed joins flag. */
+                bool distributedJoins;
+
+                /** Enforce join order flag. */
+                bool enforceJoinOrder;
+
                 /** Arguments. */
                 std::vector<QueryArgumentBase*> args;
             };

Reply via email to