zjw1111 commented on code in PR #210:
URL: https://github.com/apache/paimon-cpp/pull/210#discussion_r3811189978


##########
src/paimon/core/io/file_index_options.cpp:
##########
@@ -0,0 +1,102 @@
+/*
+ * 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 "paimon/core/io/file_index_options.h"
+
+#include <set>
+#include <utility>
+
+#include "fmt/format.h"
+#include "paimon/common/utils/string_utils.h"
+#include "paimon/core/core_options.h"
+#include "paimon/defs.h"
+#include "paimon/status.h"
+
+namespace paimon {
+namespace {
+
+constexpr char kFileIndexPrefix[] = "file-index.";
+constexpr char kColumnsSuffix[] = ".columns";
+
+}  // namespace
+
+Result<FileIndexOptions> FileIndexOptions::FromCoreOptions(const CoreOptions& 
options) {
+    FileIndexOptions result;
+    const std::map<std::string, std::string>& raw_options = options.ToMap();
+    result.in_manifest_threshold_ = options.FileIndexInManifestThreshold();
+
+    std::set<std::pair<std::string, std::string>> declared;
+    for (const auto& [key, value] : raw_options) {
+        if (!StringUtils::StartsWith(key, kFileIndexPrefix) ||
+            !StringUtils::EndsWith(key, kColumnsSuffix)) {
+            continue;
+        }
+        const size_t index_type_length =
+            key.size() - std::string(kFileIndexPrefix).size() - 
std::string(kColumnsSuffix).size();
+        const std::string index_type =
+            key.substr(std::string(kFileIndexPrefix).size(), 
index_type_length);
+        if (index_type.empty()) {
+            return Status::Invalid(fmt::format("Invalid file index option {}", 
key));
+        }
+        for (std::string column_name : StringUtils::Split(value, ",", 
/*ignore_empty=*/false)) {
+            StringUtils::Trim(&column_name);

Review Comment:
   We intentionally keep ignore_empty=false so leading or middle empty column 
entries, such as f1,,f2, are rejected instead of silently ignored. Java 
String.split drops trailing empty tokens, so malformed values such as f1,f2,, 
currently behave differently. I reverted the ad-hoc special case and added a 
TODO to align this together with ConfigParser::ParseList, so list option 
parsing can be changed consistently.



##########
src/paimon/common/io/byte_array_output_stream.cpp:
##########
@@ -0,0 +1,79 @@
+/*
+ * 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 "paimon/common/io/byte_array_output_stream.h"
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "paimon/common/memory/memory_segment_utils.h"
+#include "paimon/common/utils/math.h"
+#include "paimon/memory/bytes.h"
+#include "paimon/memory/memory_pool.h"
+
+namespace paimon {
+
+ByteArrayOutputStream::ByteArrayOutputStream(int32_t initial_capacity,
+                                             const 
std::shared_ptr<MemoryPool>& pool)
+    : pool_(pool), output_(initial_capacity, pool_) {}
+
+Result<int64_t> ByteArrayOutputStream::Write(const char* buffer, int64_t size) 
{
+    if (closed_) {
+        return Status::Invalid("Byte array output stream is closed");
+    }
+    PAIMON_RETURN_NOT_OK(ValidateValueNonNegative(size, "write length"));
+    if (buffer == nullptr && size > 0) {
+        return Status::Invalid("Write buffer must not be null when size is 
positive");
+    }
+    int64_t remaining = size;
+    while (remaining > 0) {
+        uint32_t to_write = static_cast<uint32_t>(std::min<int64_t>(
+            remaining, 
static_cast<int64_t>(std::numeric_limits<uint32_t>::max())));
+        output_.Write(buffer, to_write);
+        buffer += to_write;
+        remaining -= to_write;
+    }
+    position_ += size;
+    return size;
+}
+
+Status ByteArrayOutputStream::Close() {
+    closed_ = true;
+    return Status::OK();
+}
+
+Result<std::shared_ptr<Bytes>> ByteArrayOutputStream::Finish() {
+    PAIMON_RETURN_NOT_OK(Close());
+    if (result_) {
+        return result_;
+    }
+    // TODO(jinli.zjw): Support int64_t lengths in 
MemorySegmentUtils::CopyToBytes and remove this
+    // limit.
+    if (position_ > std::numeric_limits<int32_t>::max()) {
+        return Status::Invalid("Byte array output stream size exceeds 
INT32_MAX");
+    }
+    const std::vector<MemorySegment>& segments = output_.Segments();
+    result_ = std::shared_ptr<Bytes>(new Bytes(static_cast<size_t>(position_), 
pool_.get()),
+                                     [pool = pool_](Bytes* bytes) { delete 
bytes; });
+    MemorySegmentUtils::CopyToBytes(segments, /*offset=*/0, result_.get(),

Review Comment:
   Thanks. I removed the custom deleter instead of adding AllocateShared. 
ByteArrayOutputStream::Finish now receives a MemoryPool* and constructs the 
result with std::make_shared<Bytes>. The API documents that the caller must 
keep the pool alive until the returned Bytes is destroyed, and the upper layer 
guarantees that lifetime. With this ownership contract, a helper that captures 
shared_ptr<MemoryPool> is no longer needed.



##########
src/paimon/core/io/data_file_index_writer.h:
##########
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "paimon/core/io/file_index_options.h"
+#include "paimon/result.h"
+
+namespace arrow {
+class Field;
+class Schema;
+class StructArray;
+}  // namespace arrow
+
+namespace paimon {
+
+class Bytes;
+class DataFilePathFactory;
+class FileIndexWriter;
+class FileSystem;
+class MemoryPool;
+
+struct FileIndexWriteResult {
+    std::shared_ptr<Bytes> embedded_index;
+    std::vector<std::optional<std::string>> extra_files;
+};
+
+/// Builds every configured column index for one data file.
+class DataFileIndexWriter {
+ public:
+    static Result<std::unique_ptr<DataFileIndexWriter>> Create(
+        const std::shared_ptr<arrow::Schema>& logical_schema, const 
FileIndexOptions& options,
+        const std::shared_ptr<FileSystem>& file_system,
+        const std::shared_ptr<DataFilePathFactory>& path_factory,
+        const std::shared_ptr<MemoryPool>& pool);
+
+    Status AddBatch(const std::shared_ptr<arrow::StructArray>& logical_batch);
+
+    Result<FileIndexWriteResult> Finish(const std::string& data_file_path);
+
+    void Abort();
+
+    const std::optional<std::string>& ExternalIndexPath() const {
+        return external_index_path_;
+    }
+
+ private:
+    struct IndexWriterEntry {
+        std::string column_name;
+        std::string index_type;
+        int32_t field_index;
+        std::shared_ptr<arrow::Field> field;
+        std::shared_ptr<FileIndexWriter> writer;
+    };
+
+    DataFileIndexWriter(std::vector<IndexWriterEntry>&& writers, int64_t 
in_manifest_threshold,
+                        const std::shared_ptr<FileSystem>& file_system,
+                        const std::shared_ptr<DataFilePathFactory>& 
path_factory,
+                        const std::shared_ptr<MemoryPool>& pool);
+
+    Result<std::shared_ptr<Bytes>> SerializeContainer();
+    Status WriteExternal(const std::string& path, const 
std::shared_ptr<Bytes>& bytes);
+
+    std::vector<IndexWriterEntry> writers_;
+    int64_t in_manifest_threshold_;
+    std::shared_ptr<FileSystem> file_system_;
+    std::shared_ptr<DataFilePathFactory> path_factory_;
+    std::shared_ptr<MemoryPool> pool_;

Review Comment:
   Yes, this is about destruction order. Members are destroyed in reverse 
declaration order. The earlier members do not rely on 
DataFileIndexWriter::pool_ during destruction: each current FileIndexWriter 
keeps its own shared_ptr<MemoryPool>. pool_ here is used directly only while 
serializing the container, so declaring it after writers_ is safe. If a future 
member borrows this pool instead of owning it, pool_ should be declared before 
that member.



-- 
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