| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you 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 <iostream> | |
| #include <map> | |
| #include <string> | |
| #include <tuple> | |
| #include "arrow/api.h" | |
| #include "arrow/c/bridge.h" | |
| #include "arrow/ipc/api.h" | |
| #include "paimon/api.h" | |
| #include "paimon/catalog/catalog.h" | |
| #include "paimon/catalog/table.h" | |
| arrow::Result<std::shared_ptr<arrow::StructArray>> PrepareData(const arrow::FieldVector& fields) { | |
| arrow::StringBuilder f0_builder; | |
| arrow::Int32Builder f1_builder; | |
| arrow::Int32Builder f2_builder; | |
| arrow::DoubleBuilder f3_builder; | |
| std::vector<std::tuple<std::string, int, int, double>> data = { | |
| {"Alice", 1, 0, 11.0}, {"Bob", 1, 1, 12.1}, {"Cathy", 1, 2, 13.2}}; | |
| for (const auto& row : data) { | |
| ARROW_RETURN_NOT_OK(f0_builder.Append(std::get<0>(row))); | |
| ARROW_RETURN_NOT_OK(f1_builder.Append(std::get<1>(row))); | |
| ARROW_RETURN_NOT_OK(f2_builder.Append(std::get<2>(row))); | |
| ARROW_RETURN_NOT_OK(f3_builder.Append(std::get<3>(row))); | |
| } | |
| std::shared_ptr<arrow::Array> f0_array, f1_array, f2_array, f3_array; | |
| ARROW_RETURN_NOT_OK(f0_builder.Finish(&f0_array)); | |
| ARROW_RETURN_NOT_OK(f1_builder.Finish(&f1_array)); | |
| ARROW_RETURN_NOT_OK(f2_builder.Finish(&f2_array)); | |
| ARROW_RETURN_NOT_OK(f3_builder.Finish(&f3_array)); | |
| std::vector<std::shared_ptr<arrow::Array>> children = {f0_array, f1_array, f2_array, f3_array}; | |
| auto struct_type = arrow::struct_(fields); | |
| return std::make_shared<arrow::StructArray>(struct_type, f0_array->length(), children); | |
| } | |
| paimon::Status Run(const std::string& root_path, const std::string& db_name, | |
| const std::string& table_name) { | |
| std::map<std::string, std::string> options = {{paimon::Options::FILE_FORMAT, "parquet"}, | |
| {paimon::Options::FILE_SYSTEM, "local"}}; | |
| // create table | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::Catalog> catalog, | |
| paimon::Catalog::Create(root_path, options)); | |
| PAIMON_RETURN_NOT_OK(catalog->CreateDatabase(db_name, options, /*ignore_if_exists=*/false)); | |
| arrow::FieldVector fields = { | |
| arrow::field("f0", arrow::utf8()), | |
| arrow::field("f1", arrow::int32()), | |
| arrow::field("f2", arrow::int32()), | |
| arrow::field("f3", arrow::float64()), | |
| }; | |
| std::shared_ptr<arrow::Schema> schema = arrow::schema(fields); | |
| ::ArrowSchema arrow_schema; | |
| arrow::Status arrow_status = arrow::ExportSchema(*schema, &arrow_schema); | |
| if (!arrow_status.ok()) { | |
| return paimon::Status::Invalid(arrow_status.message()); | |
| } | |
| PAIMON_RETURN_NOT_OK(catalog->CreateTable(paimon::Identifier(db_name, table_name), | |
| &arrow_schema, | |
| /*partition_keys=*/{}, | |
| /*primary_keys=*/{}, options, | |
| /*ignore_if_exists=*/false)); | |
| // The table every operator below is created from. Each call hands the table's shared owner | |
| // to the operator it creates. | |
| PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<paimon::Table> table, | |
| catalog->GetTable(paimon::Identifier(db_name, table_name))); | |
| std::string table_path = root_path + "/" + db_name + ".db/" + table_name; | |
| std::string commit_user = "some_commit_user"; | |
| // write | |
| paimon::WriteContextBuilder context_builder(table_path, commit_user); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::WriteContext> write_context, | |
| context_builder.SetOptions(options).Finish()); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::FileStoreWrite> writer, | |
| table->NewWrite(std::move(write_context))); | |
| // prepare data | |
| auto struct_array = PrepareData(fields); | |
| if (!struct_array.ok()) { | |
| return paimon::Status::Invalid(struct_array.status().ToString()); | |
| } | |
| ::ArrowArray arrow_array; | |
| arrow_status = arrow::ExportArray(*struct_array.ValueUnsafe(), &arrow_array); | |
| if (!arrow_status.ok()) { | |
| return paimon::Status::Invalid(arrow_status.message()); | |
| } | |
| paimon::RecordBatchBuilder batch_builder(&arrow_array); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::RecordBatch> record_batch, | |
| batch_builder.Finish()); | |
| PAIMON_RETURN_NOT_OK(writer->Write(std::move(record_batch))); | |
| PAIMON_ASSIGN_OR_RAISE(std::vector<std::shared_ptr<paimon::CommitMessage>> commit_message, | |
| writer->PrepareCommit()); | |
| // commit | |
| paimon::CommitContextBuilder commit_context_builder(table_path, commit_user); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::CommitContext> commit_context, | |
| commit_context_builder.SetOptions(options).Finish()); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::FileStoreCommit> committer, | |
| table->NewCommit(std::move(commit_context))); | |
| PAIMON_RETURN_NOT_OK(committer->Commit(commit_message)); | |
| // scan | |
| paimon::ScanContextBuilder scan_context_builder(table_path); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::ScanContext> scan_context, | |
| scan_context_builder.SetOptions(options).Finish()); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::TableScan> scanner, | |
| table->NewScan(std::move(scan_context))); | |
| PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<paimon::Plan> plan, scanner->CreatePlan()); | |
| // read | |
| paimon::ReadContextBuilder read_context_builder(table_path); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::ReadContext> read_context, | |
| read_context_builder.SetOptions(options).Finish()); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::TableRead> table_read, | |
| table->NewRead(std::move(read_context))); | |
| PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::BatchReader> batch_reader, | |
| table_read->CreateReader(plan->Splits())); | |
| arrow::ArrayVector result_array_vector; | |
| while (true) { | |
| PAIMON_ASSIGN_OR_RAISE(paimon::BatchReader::ReadBatch batch, batch_reader->NextBatch()); | |
| if (paimon::BatchReader::IsEofBatch(batch)) { | |
| break; | |
| } | |
| auto& [c_array, c_schema] = batch; | |
| auto arrow_result = arrow::ImportArray(c_array.get(), c_schema.get()); | |
| if (!arrow_result.ok()) { | |
| return paimon::Status::Invalid(arrow_result.status().ToString()); | |
| } | |
| auto result_array = arrow_result.ValueUnsafe(); | |
| result_array_vector.push_back(result_array); | |
| } | |
| auto chunk_result = arrow::ChunkedArray::Make(result_array_vector); | |
| if (!chunk_result.ok()) { | |
| return paimon::Status::Invalid(chunk_result.status().ToString()); | |
| } | |
| std::cout << chunk_result.ValueUnsafe()->ToString() << std::endl; | |
| return paimon::Status::OK(); | |
| } | |
| int main(int argc, char** argv) { | |
| if (argc != 4) { | |
| std::cout << "Usage: " << argv[0] << " <root_path> <database_name> <table_name>" | |
| << std::endl; | |
| return -1; | |
| } | |
| const std::string root_path = argv[1]; | |
| const std::string db_name = argv[2]; | |
| const std::string table_name = argv[3]; | |
| paimon::Status status = Run(root_path, db_name, table_name); | |
| if (!status.ok()) { | |
| std::cerr << "Failed to run example:" << status.ToString() << std::endl; | |
| return -1; | |
| } | |
| return 0; | |
| } |