leekeiabstraction commented on code in PR #611:
URL: https://github.com/apache/fluss-rust/pull/611#discussion_r3406350985


##########
bindings/cpp/test/test_log_table.cpp:
##########


Review Comment:
   Do we need MapWriterOverflowDetection test case as well? 



##########
bindings/cpp/test/test_kv_table.cpp:
##########
@@ -212,24 +214,776 @@ TEST_F(KvTableTest, LookupWithNestedArrayArrayView) {
     fluss::LookupResult result;
     ASSERT_OK(lookuper.Lookup(key, result));
     ASSERT_TRUE(result.Found());
-    EXPECT_EQ(result.GetArraySize("matrix"), 2u);
-    EXPECT_EQ(result.GetArrayElementType("matrix"), fluss::TypeId::Array);
-
-    auto outer = result.GetArrayView("matrix");
+    auto outer = result.GetValue("matrix");
+    EXPECT_EQ(outer.Type(), fluss::TypeId::Array);
     ASSERT_EQ(outer.Size(), 2u);
-    EXPECT_EQ(outer.ElementType(), fluss::TypeId::Array);
 
-    auto first = outer.GetArray(0);
+    auto first = outer.At(0);
+    EXPECT_EQ(first.Type(), fluss::TypeId::Array);
     ASSERT_EQ(first.Size(), 2u);
-    EXPECT_EQ(first.ElementType(), fluss::TypeId::Int);
-    EXPECT_EQ(first.GetInt32(0), 11);
-    EXPECT_EQ(first.GetInt32(1), 12);
+    EXPECT_EQ(first.At(0).GetInt32(), 11);
+    EXPECT_EQ(first.At(1).GetInt32(), 12);
 
-    auto second = outer.GetArray(1);
+    auto second = outer.At(1);
     ASSERT_EQ(second.Size(), 2u);
-    EXPECT_EQ(second.ElementType(), fluss::TypeId::Int);
-    EXPECT_EQ(second.GetInt32(0), 21);
-    EXPECT_EQ(second.GetInt32(1), 22);
+    EXPECT_EQ(second.At(0).GetInt32(), 21);
+    EXPECT_EQ(second.At(1).GetInt32(), 22);
+
+    ASSERT_OK(adm.DropTable(table_path, false));
+}
+
+TEST_F(KvTableTest, LookupComplexTypesMatrix) {
+    auto& adm = admin();
+    auto& conn = connection();
+
+    fluss::TablePath table_path("fluss", "test_lookup_complex_matrix_cpp");
+
+    auto row_seq_label = arrow::struct_(
+        {arrow::field("seq", arrow::int32()), arrow::field("label", 
arrow::utf8())});
+
+    auto arrow_schema = arrow::schema({
+        arrow::field("id", arrow::int32()),
+        arrow::field("m_str_int", arrow::map(arrow::utf8(), arrow::int32())),
+        arrow::field("m_str_row", arrow::map(arrow::utf8(), row_seq_label)),
+        arrow::field("m_str_map",
+                     arrow::map(arrow::utf8(), arrow::map(arrow::utf8(), 
arrow::int32()))),
+        arrow::field("m_str_arr", arrow::map(arrow::utf8(), 
arrow::list(arrow::int32()))),
+        arrow::field("arr_map", arrow::list(arrow::map(arrow::utf8(), 
arrow::int32()))),

Review Comment:
   Should we have coverage for small and tiny ints?



##########
bindings/cpp/test/test_kv_table.cpp:
##########
@@ -212,24 +214,776 @@ TEST_F(KvTableTest, LookupWithNestedArrayArrayView) {
     fluss::LookupResult result;
     ASSERT_OK(lookuper.Lookup(key, result));
     ASSERT_TRUE(result.Found());
-    EXPECT_EQ(result.GetArraySize("matrix"), 2u);
-    EXPECT_EQ(result.GetArrayElementType("matrix"), fluss::TypeId::Array);
-
-    auto outer = result.GetArrayView("matrix");
+    auto outer = result.GetValue("matrix");
+    EXPECT_EQ(outer.Type(), fluss::TypeId::Array);
     ASSERT_EQ(outer.Size(), 2u);
-    EXPECT_EQ(outer.ElementType(), fluss::TypeId::Array);
 
-    auto first = outer.GetArray(0);
+    auto first = outer.At(0);
+    EXPECT_EQ(first.Type(), fluss::TypeId::Array);
     ASSERT_EQ(first.Size(), 2u);
-    EXPECT_EQ(first.ElementType(), fluss::TypeId::Int);
-    EXPECT_EQ(first.GetInt32(0), 11);
-    EXPECT_EQ(first.GetInt32(1), 12);
+    EXPECT_EQ(first.At(0).GetInt32(), 11);
+    EXPECT_EQ(first.At(1).GetInt32(), 12);
 
-    auto second = outer.GetArray(1);
+    auto second = outer.At(1);
     ASSERT_EQ(second.Size(), 2u);
-    EXPECT_EQ(second.ElementType(), fluss::TypeId::Int);
-    EXPECT_EQ(second.GetInt32(0), 21);
-    EXPECT_EQ(second.GetInt32(1), 22);
+    EXPECT_EQ(second.At(0).GetInt32(), 21);
+    EXPECT_EQ(second.At(1).GetInt32(), 22);
+
+    ASSERT_OK(adm.DropTable(table_path, false));
+}
+
+TEST_F(KvTableTest, LookupComplexTypesMatrix) {
+    auto& adm = admin();
+    auto& conn = connection();
+
+    fluss::TablePath table_path("fluss", "test_lookup_complex_matrix_cpp");
+
+    auto row_seq_label = arrow::struct_(
+        {arrow::field("seq", arrow::int32()), arrow::field("label", 
arrow::utf8())});
+
+    auto arrow_schema = arrow::schema({
+        arrow::field("id", arrow::int32()),
+        arrow::field("m_str_int", arrow::map(arrow::utf8(), arrow::int32())),
+        arrow::field("m_str_row", arrow::map(arrow::utf8(), row_seq_label)),
+        arrow::field("m_str_map",
+                     arrow::map(arrow::utf8(), arrow::map(arrow::utf8(), 
arrow::int32()))),
+        arrow::field("m_str_arr", arrow::map(arrow::utf8(), 
arrow::list(arrow::int32()))),
+        arrow::field("arr_map", arrow::list(arrow::map(arrow::utf8(), 
arrow::int32()))),
+        arrow::field("arr_row", arrow::list(row_seq_label)),
+        arrow::field("r_deep", arrow::struct_({arrow::field(
+                                   "inner", arrow::struct_({arrow::field("n", 
arrow::int32())}))})),
+        arrow::field("r_with_arr",
+                     arrow::struct_({arrow::field("f_int", arrow::int32()),
+                                     arrow::field("f_arr", 
arrow::list(arrow::int32()))})),
+        // row_rich: every scalar type + an array field in one ROW.
+        arrow::field("r_rich",
+                     arrow::struct_({
+                         arrow::field("f_bool", arrow::boolean()),
+                         arrow::field("f_int", arrow::int32()),
+                         arrow::field("f_long", arrow::int64()),
+                         arrow::field("f_float", arrow::float32()),
+                         arrow::field("f_double", arrow::float64()),
+                         arrow::field("f_str", arrow::utf8()),
+                         arrow::field("f_bytes", arrow::binary()),
+                         arrow::field("f_decimal", arrow::decimal128(10, 2)),
+                         arrow::field("f_date", arrow::date32()),
+                         arrow::field("f_time", 
arrow::time32(arrow::TimeUnit::MILLI)),
+                         arrow::field("f_ts_ntz", 
arrow::timestamp(arrow::TimeUnit::MICRO)),
+                         arrow::field("f_ts_ltz", 
arrow::timestamp(arrow::TimeUnit::MICRO, "UTC")),
+                         arrow::field("f_binary", arrow::fixed_size_binary(4)),
+                         arrow::field("f_arr", arrow::list(arrow::int32())),
+                     })),
+    });
+    auto schema = fluss::Schema::FromArrow(arrow_schema, {"id"});
+
+    auto table_descriptor = fluss::TableDescriptor::NewBuilder()
+                                .SetSchema(schema)
+                                .SetProperty("table.replication.factor", "1")
+                                .Build();
+    fluss_test::CreateTable(adm, table_path, table_descriptor);
+
+    fluss::Table table;
+    ASSERT_OK(conn.GetTable(table_path, table));
+    auto upsert = table.NewUpsert();
+    fluss::UpsertWriter writer;
+    ASSERT_OK(upsert.CreateWriter(writer));
+
+    {
+        auto row = table.NewRow();
+        row.Set("id", 1);
+
+        // map<string,int> — second entry has a NULL value.
+        {
+            fluss::MapWriter m(2, fluss::DataType::String(), 
fluss::DataType::Int());
+            m.SetKeyString("a");
+            m.SetValueInt32(1);
+            m.Commit();
+            m.SetKeyString("b");
+            m.SetValueNull();
+            m.Commit();
+            row.Set("m_str_int", std::move(m));
+        }
+        // map<string, row<seq,label>> — value is a ROW, so the Arrow ctor.
+        {
+            fluss::MapWriter m(1, arrow::utf8(), row_seq_label);
+            m.SetKeyString("k");
+            fluss::GenericRow v(2);
+            v.SetInt32(0, 7);
+            v.SetString(1, "seven");
+            m.SetValueRow(std::move(v));
+            m.Commit();
+            row.Set("m_str_row", std::move(m));
+        }
+        // map<string, map<string,int>>
+        {
+            fluss::MapWriter m(1, arrow::utf8(), arrow::map(arrow::utf8(), 
arrow::int32()));
+            m.SetKeyString("k");
+            fluss::MapWriter inner(1, fluss::DataType::String(), 
fluss::DataType::Int());
+            inner.SetKeyString("x");
+            inner.SetValueInt32(9);
+            inner.Commit();
+            m.SetValueMap(std::move(inner));
+            m.Commit();
+            row.Set("m_str_map", std::move(m));
+        }
+        // map<string, array<int>> — value array fits the flat ctor.
+        {
+            fluss::MapWriter m(1, fluss::DataType::String(),
+                               fluss::DataType::Array(fluss::DataType::Int()));
+            m.SetKeyString("k");
+            fluss::ArrayWriter v(2, fluss::DataType::Int());
+            v.SetInt32(0, 10);
+            v.SetInt32(1, 20);
+            m.SetValueArray(std::move(v));
+            m.Commit();
+            row.Set("m_str_arr", std::move(m));
+        }
+        // array<map<string,int>> — element is a MAP, so the Arrow ctor.
+        {
+            fluss::ArrayWriter a(1, arrow::map(arrow::utf8(), arrow::int32()));
+            fluss::MapWriter e(1, fluss::DataType::String(), 
fluss::DataType::Int());
+            e.SetKeyString("p");
+            e.SetValueInt32(5);
+            e.Commit();
+            a.SetMap(0, std::move(e));
+            row.Set("arr_map", std::move(a));
+        }
+        // array<row<seq,label>> — element is a ROW, so the Arrow ctor.
+        {
+            fluss::ArrayWriter a(2, row_seq_label);
+            fluss::GenericRow e0(2);
+            e0.SetInt32(0, 1);
+            e0.SetString(1, "one");
+            fluss::GenericRow e1(2);
+            e1.SetInt32(0, 2);
+            e1.SetString(1, "two");
+            a.SetRow(0, std::move(e0));
+            a.SetRow(1, std::move(e1));
+            row.Set("arr_row", std::move(a));
+        }
+        // row<inner: row<n>>
+        {
+            fluss::GenericRow inner(1);
+            inner.SetInt32(0, 42);
+            fluss::GenericRow outer(1);
+            outer.SetRow(0, std::move(inner));
+            row.Set("r_deep", std::move(outer));
+        }
+        // row<f_int, f_arr: array<int>>
+        {
+            fluss::GenericRow r(2);
+            r.SetInt32(0, 100);
+            fluss::ArrayWriter arr(3, fluss::DataType::Int());
+            arr.SetInt32(0, 1);
+            arr.SetInt32(1, 2);
+            arr.SetInt32(2, 3);
+            r.SetArray(1, std::move(arr));
+            row.Set("r_with_arr", std::move(r));
+        }
+        // row_rich: exercise every Value leaf getter + an array field.
+        {
+            fluss::GenericRow rr(14);
+            rr.SetBool(0, true);
+            rr.SetInt32(1, 100000);
+            rr.SetInt64(2, 9876543210LL);
+            rr.SetFloat32(3, 3.5F);
+            rr.SetFloat64(4, 2.5);
+            rr.SetString(5, "hello world");
+            rr.SetBytes(6, {'b', 'i', 'n'});
+            rr.SetDecimal(7, "123.45");
+            rr.SetDate(8, fluss::Date{20476});
+            rr.SetTime(9, fluss::Time{36827123});
+            rr.SetTimestampNtz(10, fluss::Timestamp{1769163227123LL, 456000});
+            rr.SetTimestampLtz(11, fluss::Timestamp{1769163227456LL, 0});
+            rr.SetBytes(12, {1, 2, 3, 4});
+            fluss::ArrayWriter farr(3, fluss::DataType::Int());
+            farr.SetInt32(0, 7);
+            farr.SetNull(1);
+            farr.SetInt32(2, 11);
+            rr.SetArray(13, std::move(farr));
+            row.Set("r_rich", std::move(rr));
+        }
+
+        ASSERT_OK(writer.Upsert(row));
+        ASSERT_OK(writer.Flush());
+    }
+
+    fluss::Lookuper lookuper;
+    ASSERT_OK(table.NewLookup().CreateLookuper(lookuper));
+    auto key = table.NewRow();
+    key.Set("id", 1);
+    fluss::LookupResult result;
+    ASSERT_OK(lookuper.Lookup(key, result));
+    ASSERT_TRUE(result.Found());
+
+    // map<string,int> — entry 1 has a NULL value.
+    {
+        auto m = result.GetValue("m_str_int");
+        ASSERT_EQ(m.Size(), 2u);
+        EXPECT_EQ(m.KeyAt(0).GetString(), "a");
+        EXPECT_FALSE(m.ValueAt(0).IsNull());
+        EXPECT_EQ(m.ValueAt(0).GetInt32(), 1);
+        EXPECT_EQ(m.KeyAt(1).GetString(), "b");
+        EXPECT_TRUE(m.ValueAt(1).IsNull());
+    }
+    // map<string, row<seq,label>>
+    {
+        auto m = result.GetValue("m_str_row");
+        ASSERT_EQ(m.Size(), 1u);
+        EXPECT_EQ(m.KeyAt(0).GetString(), "k");
+        auto v = m.ValueAt(0);
+        EXPECT_EQ(v.Field(0).GetInt32(), 7);
+        EXPECT_EQ(v.Field(1).GetString(), "seven");
+    }
+    // map<string, map<string,int>>
+    {
+        auto inner = result.GetValue("m_str_map").ValueAt(0);
+        ASSERT_EQ(inner.Size(), 1u);
+        EXPECT_EQ(inner.KeyAt(0).GetString(), "x");
+        EXPECT_EQ(inner.ValueAt(0).GetInt32(), 9);
+    }
+    // map<string, array<int>>
+    {
+        auto av = result.GetValue("m_str_arr").ValueAt(0);
+        ASSERT_EQ(av.Size(), 2u);
+        EXPECT_EQ(av.At(0).GetInt32(), 10);
+        EXPECT_EQ(av.At(1).GetInt32(), 20);
+    }
+    // array<map<string,int>>
+    {
+        auto a = result.GetValue("arr_map");
+        ASSERT_EQ(a.Size(), 1u);
+        auto e = a.At(0);
+        EXPECT_EQ(e.KeyAt(0).GetString(), "p");
+        EXPECT_EQ(e.ValueAt(0).GetInt32(), 5);
+    }
+    // array<row<seq,label>>
+    {
+        auto a = result.GetValue("arr_row");
+        ASSERT_EQ(a.Size(), 2u);
+        EXPECT_EQ(a.At(0).Field(0).GetInt32(), 1);
+        EXPECT_EQ(a.At(0).Field(1).GetString(), "one");
+        EXPECT_EQ(a.At(1).Field(0).GetInt32(), 2);
+        EXPECT_EQ(a.At(1).Field(1).GetString(), "two");
+    }
+    // row<inner: row<n>>
+    {
+        auto inner = result.GetValue("r_deep").Field(0);
+        EXPECT_EQ(inner.Field(0).GetInt32(), 42);
+    }
+    // row<f_int, f_arr>
+    {
+        auto r = result.GetValue("r_with_arr");
+        EXPECT_EQ(r.Field(0).GetInt32(), 100);
+        EXPECT_EQ(r.Field("f_int").GetInt32(), 100);  // ROW field by name
+        auto arr = r.Field(1);
+        ASSERT_EQ(arr.Size(), 3u);
+        EXPECT_EQ(arr.At(2).GetInt32(), 3);
+    }
+    // row_rich — every leaf getter on one Value handle.
+    {
+        auto rr = result.GetValue("r_rich");
+        ASSERT_EQ(rr.FieldCount(), 14u);
+        EXPECT_TRUE(rr.Field(0).GetBool());
+        EXPECT_EQ(rr.Field(1).GetInt32(), 100000);
+        EXPECT_EQ(rr.Field(2).GetInt64(), 9876543210LL);
+        EXPECT_FLOAT_EQ(rr.Field(3).GetFloat32(), 3.5F);
+        EXPECT_DOUBLE_EQ(rr.Field(4).GetFloat64(), 2.5);
+        EXPECT_EQ(rr.Field(5).GetString(), "hello world");
+        auto by = rr.Field(6).GetBytes();
+        ASSERT_EQ(by.size(), 3u);
+        EXPECT_EQ(by[0], 'b');
+        EXPECT_EQ(rr.Field(7).GetDecimalString(), "123.45");
+        EXPECT_EQ(rr.Field(8).GetDate().days_since_epoch, 20476);
+        EXPECT_EQ(rr.Field(9).GetTime().millis_since_midnight, 36827123);
+        EXPECT_EQ(rr.Field(10).GetTimestamp().epoch_millis, 1769163227123LL);
+        EXPECT_EQ(rr.Field(11).GetTimestamp().epoch_millis, 1769163227456LL);
+        auto bin = rr.Field(12).GetBytes();
+        ASSERT_EQ(bin.size(), 4u);
+        EXPECT_EQ(bin[3], 4);
+        auto fa = rr.Field(13);
+        ASSERT_EQ(fa.Size(), 3u);
+        EXPECT_TRUE(fa.At(1).IsNull());
+        EXPECT_EQ(fa.At(2).GetInt32(), 11);
+    }
+
+    // Row 2 (id=2) — every compound column NULL.
+    {
+        auto row = table.NewRow();
+        row.SetInt32(0, 2);
+        for (size_t i = 1; i <= 9; ++i) {
+            row.SetNull(i);
+        }
+        ASSERT_OK(writer.Upsert(row));
+        ASSERT_OK(writer.Flush());
+
+        auto key2 = table.NewRow();
+        key2.SetInt32(0, 2);
+        fluss::LookupResult result2;
+        ASSERT_OK(lookuper.Lookup(key2, result2));
+        ASSERT_TRUE(result2.Found());
+        EXPECT_EQ(result2.GetInt32(0), 2);
+        for (size_t i = 1; i <= 9; ++i) {
+            EXPECT_TRUE(result2.IsNull(i)) << "column " << i << " should be 
null";
+        }
+    }
+
+    ASSERT_OK(adm.DropTable(table_path, false));
+}
+
+TEST_F(KvTableTest, LookupWithValueHandle) {

Review Comment:
   Seems like a lot of the checks in LookupWithValueHandle and 
LookupComplexTypesMatrix are similar. Is it worth refactoring to minimise code 
to maintain?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to