@@ -268,11 +268,15 @@ std::vector<std::string> StructFieldNames(const std::shared_ptr<arrow::StructArr
268268 return arrow::schema (array->type ()->fields ())->field_names ();
269269}
270270
271- Result<SystemTableReadResult> ReadSystemTable (const std::string& system_table_path,
272- const std::map<std::string, std::string>& options,
273- bool streaming_mode = false ) {
271+ Result<SystemTableReadResult> ReadSystemTable (
272+ const std::string& system_table_path, const std::map<std::string, std::string>& options,
273+ bool streaming_mode = false , const std::shared_ptr<Predicate>& predicate = nullptr ,
274+ const std::vector<std::string>& read_field_names = {}) {
274275 ScanContextBuilder scan_context_builder (system_table_path);
275276 scan_context_builder.SetOptions (options).WithStreamingMode (streaming_mode);
277+ if (predicate) {
278+ scan_context_builder.SetPredicate (predicate);
279+ }
276280 PAIMON_ASSIGN_OR_RAISE (std::unique_ptr<ScanContext> scan_context,
277281 scan_context_builder.Finish ());
278282 PAIMON_ASSIGN_OR_RAISE (std::unique_ptr<TableScan> table_scan,
@@ -281,6 +285,12 @@ Result<SystemTableReadResult> ReadSystemTable(const std::string& system_table_pa
281285
282286 ReadContextBuilder read_context_builder (system_table_path);
283287 read_context_builder.SetOptions (options);
288+ if (predicate) {
289+ read_context_builder.SetPredicate (predicate);
290+ }
291+ if (!read_field_names.empty ()) {
292+ read_context_builder.SetReadFieldNames (read_field_names);
293+ }
284294 PAIMON_ASSIGN_OR_RAISE (std::unique_ptr<ReadContext> read_context,
285295 read_context_builder.Finish ());
286296 PAIMON_ASSIGN_OR_RAISE (std::unique_ptr<TableRead> table_read,
@@ -292,8 +302,8 @@ Result<SystemTableReadResult> ReadSystemTable(const std::string& system_table_pa
292302 return SystemTableReadResult (std::move (batch_reader), result);
293303}
294304
295- Status WriteAndFullCompact (TestHelper* helper, std::unique_ptr<RecordBatch>&& batch,
296- int64_t commit_identifier ) {
305+ Status WriteAndFullCompact (std::unique_ptr<RecordBatch>&& batch, int64_t commit_identifier ,
306+ TestHelper* helper ) {
297307 PAIMON_RETURN_NOT_OK (helper->write_ ->Write (std::move (batch)));
298308 PAIMON_RETURN_NOT_OK (
299309 helper->write_ ->Compact (/* partition=*/ {}, /* bucket=*/ 0 , /* full_compaction=*/ true ));
@@ -312,6 +322,18 @@ void AssertStructArrayEqualsJson(const std::shared_ptr<arrow::StructArray>& actu
312322 << " expected: " << expected->ToString () << " \n actual: " << actual->ToString ();
313323}
314324
325+ Result<int64_t > CountDataFiles (const std::vector<std::shared_ptr<Split>>& splits) {
326+ int64_t file_count = 0 ;
327+ for (const auto & split : splits) {
328+ auto data_split = std::dynamic_pointer_cast<DataSplit>(split);
329+ if (!data_split) {
330+ return Status::Invalid (" expected data split" );
331+ }
332+ file_count += data_split->GetFileList ().size ();
333+ }
334+ return file_count;
335+ }
336+
315337} // namespace
316338
317339std::vector<TestParam> PrepareTestParam () {
@@ -819,7 +841,7 @@ TEST(SystemTableReadInteTest, TestReadOptimizedSystemTable) {
819841 ASSERT_OK_AND_ASSIGN (std::unique_ptr<RecordBatch> batch_1,
820842 TestHelper::MakeRecordBatch (row_type, R"( [[1, 10], [2, 20]])" ,
821843 /* partition_map=*/ {}, /* bucket=*/ 0 , {}));
822- ASSERT_OK (WriteAndFullCompact (helper. get (), std::move (batch_1), /* commit_identifier=*/ 0 ));
844+ ASSERT_OK (WriteAndFullCompact (std::move (batch_1), /* commit_identifier=*/ 0 , helper. get () ));
823845
824846 ASSERT_OK_AND_ASSIGN (SystemTableReadResult compacted_result,
825847 ReadSystemTable (table_path + " $ro" , options));
@@ -845,7 +867,7 @@ TEST(SystemTableReadInteTest, TestReadOptimizedSystemTable) {
845867 ASSERT_OK_AND_ASSIGN (std::unique_ptr<RecordBatch> batch_3,
846868 TestHelper::MakeRecordBatch (row_type, R"( [[2, 21], [3, 31]])" ,
847869 /* partition_map=*/ {}, /* bucket=*/ 0 , {}));
848- ASSERT_OK (WriteAndFullCompact (helper. get (), std::move (batch_3), /* commit_identifier=*/ 2 ));
870+ ASSERT_OK (WriteAndFullCompact (std::move (batch_3), /* commit_identifier=*/ 2 , helper. get () ));
849871 ASSERT_OK_AND_ASSIGN (SystemTableReadResult refreshed_result,
850872 ReadSystemTable (table_path + " $ro" , options));
851873 std::shared_ptr<arrow::ChunkedArray> expected_refreshed;
@@ -895,6 +917,74 @@ TEST(SystemTableReadInteTest, TestReadOptimizedAppendOnlySystemTableWithStreamin
895917 ASSERT_TRUE (result.array ->Equals (expected)) << result.array ->ToString ();
896918}
897919
920+ TEST (SystemTableReadInteTest, TestReadOptimizedPrimaryKeyProjectionAndPredicatePushdown) {
921+ arrow::FieldVector fields = {
922+ arrow::field (" k" , arrow::int32 ()),
923+ arrow::field (" v" , arrow::int32 ()),
924+ arrow::field (" extra" , arrow::int32 ()),
925+ };
926+ auto schema = arrow::schema (fields);
927+ std::map<std::string, std::string> options = {
928+ {Options::FILE_SYSTEM , " local" },
929+ {Options::FILE_FORMAT , " parquet" },
930+ {Options::MANIFEST_FORMAT , " avro" },
931+ {Options::BUCKET , " 1" },
932+ {Options::BUCKET_KEY , " k" },
933+ {Options::WRITE_BATCH_SIZE , " 1" },
934+ {" parquet.page.size" , " 1" },
935+ {" parquet.enable-dictionary" , " false" },
936+ {" parquet.write.enable-page-index" , " true" },
937+ {" parquet.write.max-row-group-length" , " 1" },
938+ {" parquet.read.enable-page-index-filter" , " true" }};
939+ auto dir = UniqueTestDirectory::Create ();
940+ ASSERT_TRUE (dir);
941+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<TestHelper> helper,
942+ TestHelper::Create (dir->Str (), schema,
943+ /* partition_keys=*/ {}, /* primary_keys=*/ {" k" }, options,
944+ /* is_streaming_mode=*/ true ));
945+ std::string table_path = PathUtil::JoinPath (dir->Str (), " foo.db/bar" );
946+ auto row_type = arrow::struct_ (fields);
947+
948+ ASSERT_OK_AND_ASSIGN (
949+ std::unique_ptr<RecordBatch> batch_1,
950+ TestHelper::MakeRecordBatch (row_type, R"( [[1, 10, 100], [2, 20, 200], [3, 30, 300]])" ,
951+ /* partition_map=*/ {}, /* bucket=*/ 0 , {}));
952+ ASSERT_OK (WriteAndFullCompact (std::move (batch_1), /* commit_identifier=*/ 0 , helper.get ()));
953+
954+ std::shared_ptr<Predicate> predicate =
955+ PredicateBuilder::Equal (/* field_index=*/ 0 , /* field_name=*/ " k" , FieldType::INT , Literal (2 ));
956+
957+ ScanContextBuilder ro_scan_context_builder (table_path + " $ro" );
958+ ro_scan_context_builder.SetOptions (options).SetPredicate (predicate);
959+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<ScanContext> ro_scan_context,
960+ ro_scan_context_builder.Finish ());
961+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<TableScan> ro_table_scan,
962+ TableScan::Create (std::move (ro_scan_context)));
963+ ASSERT_OK_AND_ASSIGN (std::shared_ptr<Plan> ro_plan, ro_table_scan->CreatePlan ());
964+ ASSERT_OK_AND_ASSIGN (int64_t ro_file_count, CountDataFiles (ro_plan->Splits ()));
965+ ASSERT_EQ (ro_file_count, 1 );
966+
967+ ReadContextBuilder read_context_builder (table_path + " $ro" );
968+ // Do not enable row-level predicate filtering: the result must come from the file reader's
969+ // row-group/page predicate pushdown.
970+ read_context_builder.SetOptions (options).SetPredicate (predicate).SetReadFieldNames ({" k" , " v" });
971+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<ReadContext> read_context, read_context_builder.Finish ());
972+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<TableRead> table_read,
973+ TableRead::Create (std::move (read_context)));
974+ ASSERT_OK_AND_ASSIGN (std::unique_ptr<BatchReader> batch_reader,
975+ table_read->CreateReader (ro_plan->Splits ()));
976+ ASSERT_OK_AND_ASSIGN (std::shared_ptr<arrow::ChunkedArray> result,
977+ ReadResultCollector::CollectResult (batch_reader.get ()));
978+ std::shared_ptr<arrow::DataType> expected_type =
979+ arrow::struct_ ({arrow::field (" _VALUE_KIND" , arrow::int8 ()),
980+ arrow::field (" k" , arrow::int32 ()), arrow::field (" v" , arrow::int32 ())});
981+ std::shared_ptr<arrow::ChunkedArray> expected;
982+ ASSERT_TRUE (arrow::ipc::internal::json::ChunkedArrayFromJSON (expected_type, {R"( [[0, 2, 20]])" },
983+ &expected)
984+ .ok ());
985+ ASSERT_TRUE (result->Equals (expected)) << result->ToString ();
986+ }
987+
898988TEST (SystemTableReadInteTest, TestReadOptimizedSystemTableWithBranch) {
899989 arrow::FieldVector fields = {
900990 arrow::field (" k" , arrow::int32 ()),
@@ -919,8 +1009,7 @@ TEST(SystemTableReadInteTest, TestReadOptimizedSystemTableWithBranch) {
9191009 ASSERT_OK_AND_ASSIGN (std::unique_ptr<RecordBatch> branch_batch,
9201010 TestHelper::MakeRecordBatch (row_type, R"( [[1, 10], [2, 20]])" ,
9211011 /* partition_map=*/ {}, /* bucket=*/ 0 , {}));
922- ASSERT_OK (WriteAndFullCompact (helper.get (), std::move (branch_batch),
923- /* commit_identifier=*/ 0 ));
1012+ ASSERT_OK (WriteAndFullCompact (std::move (branch_batch), /* commit_identifier=*/ 0 , helper.get ()));
9241013
9251014 std::string branch_path = PathUtil::JoinPath (table_path, " branch/branch-rt" );
9261015 std::filesystem::create_directories (branch_path);
@@ -932,8 +1021,7 @@ TEST(SystemTableReadInteTest, TestReadOptimizedSystemTableWithBranch) {
9321021 ASSERT_OK_AND_ASSIGN (std::unique_ptr<RecordBatch> main_batch,
9331022 TestHelper::MakeRecordBatch (row_type, R"( [[1, 11], [3, 30]])" ,
9341023 /* partition_map=*/ {}, /* bucket=*/ 0 , {}));
935- ASSERT_OK (WriteAndFullCompact (helper.get (), std::move (main_batch),
936- /* commit_identifier=*/ 1 ));
1024+ ASSERT_OK (WriteAndFullCompact (std::move (main_batch), /* commit_identifier=*/ 1 , helper.get ()));
9371025
9381026 ASSERT_OK_AND_ASSIGN (SystemTableReadResult result,
9391027 ReadSystemTable (table_path + " $branch_rt$ro" , options));
@@ -945,6 +1033,14 @@ TEST(SystemTableReadInteTest, TestReadOptimizedSystemTableWithBranch) {
9451033 expected_type, {R"( [[0, 1, 10], [0, 2, 20]])" }, &expected)
9461034 .ok ());
9471035 ASSERT_TRUE (result.array ->Equals (expected)) << result.array ->ToString ();
1036+
1037+ ASSERT_OK_AND_ASSIGN (SystemTableReadResult main_result,
1038+ ReadSystemTable (table_path + " $ro" , options));
1039+ std::shared_ptr<arrow::ChunkedArray> expected_main;
1040+ ASSERT_TRUE (arrow::ipc::internal::json::ChunkedArrayFromJSON (
1041+ expected_type, {R"( [[0, 1, 11], [0, 2, 20], [0, 3, 30]])" }, &expected_main)
1042+ .ok ());
1043+ ASSERT_TRUE (main_result.array ->Equals (expected_main)) << main_result.array ->ToString ();
9481044}
9491045
9501046TEST (SystemTableReadInteTest, TestReadOptimizedSystemTableWithFirstRowMergeEngine) {
@@ -973,7 +1069,7 @@ TEST(SystemTableReadInteTest, TestReadOptimizedSystemTableWithFirstRowMergeEngin
9731069 std::unique_ptr<RecordBatch> batch,
9741070 TestHelper::MakeRecordBatch (arrow::struct_ (fields), R"( [[1, 10], [2, 20]])" ,
9751071 /* partition_map=*/ {}, /* bucket=*/ 0 , {}));
976- ASSERT_OK (WriteAndFullCompact (helper. get (), std::move (batch), /* commit_identifier=*/ 0 ));
1072+ ASSERT_OK (WriteAndFullCompact (std::move (batch), /* commit_identifier=*/ 0 , helper. get () ));
9771073
9781074 ASSERT_OK_AND_ASSIGN (SystemTableReadResult result,
9791075 ReadSystemTable (table_path + " $ro" , options));
0 commit comments