From 96d440859c5aad25920af71c3ede601057c3e9d4 Mon Sep 17 00:00:00 2001 From: Ted Xu Date: Mon, 11 Nov 2024 11:58:19 +0800 Subject: [PATCH] feat: integrate to cgo (#153) Signed-off-by: Ted Xu --- .github/workflows/ci.yaml | 25 ++++- cpp/CMakeLists.txt | 13 ++- cpp/Makefile | 5 +- cpp/conanfile.py | 9 +- cpp/include/milvus-storage/packed/reader.h | 6 ++ cpp/include/milvus-storage/packed/reader_c.h | 32 ++++++ cpp/src/milvus-storage.pc.in | 9 ++ cpp/src/packed/reader.cpp | 31 +++++- cpp/src/packed/reader_c.cpp | 47 +++++++++ cpp/test/packed/packed_test_base.h | 5 +- go/Makefile | 19 +++- go/go.mod | 49 ++++----- go/go.sum | 100 +++++++++---------- go/packed/packed.go | 50 ++++++++++ go/packed/packed_test.go | 69 +++++++++++++ go/packed/testdata/0 | Bin 0 -> 92668 bytes 16 files changed, 375 insertions(+), 94 deletions(-) create mode 100644 cpp/include/milvus-storage/packed/reader_c.h create mode 100644 cpp/src/milvus-storage.pc.in create mode 100644 cpp/src/packed/reader_c.cpp create mode 100644 go/packed/packed.go create mode 100644 go/packed/packed_test.go create mode 100644 go/packed/testdata/0 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 37f0f986..42192bdd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v2 with: - go-version: 1.19 + go-version: 1.21 - name: Install dependencies run: cd go && go mod download @@ -35,6 +35,27 @@ jobs: -v /tmp/data:/data \ -v /tmp/config:/root/.minio \ minio/minio server /data + + - name: Install dependencies + uses: aminya/setup-cpp@v1 + with: + conan: 1.61.0 + cmake: true + + - name: setup conan + run: + conan remote add default-conan-local https://milvus01.jfrog.io/artifactory/api/conan/default-conan-local --insert + && conan remote list + + - name: conan package cache + uses: actions/cache@v3 + with: + path: ~/.conan + key: conan-${{ hashFiles('./cpp/conanfile.py') }} + restore-keys: conan- + + - name: Build c++ + run: cd cpp && make - name: Run tests - run: cd go && go test -v ./... + run: cd go && make && make test diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index bbb92f2d..e23f5f1d 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -9,6 +9,9 @@ option(WITH_BENCHMARK "Build with micro benchmark." ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}) +include(GNUInstallDirs) + if (WITH_OPENDAL) add_compile_definitions(MILVUS_OPENDAL) @@ -16,12 +19,12 @@ if (WITH_OPENDAL) include(libopendal) endif() -find_package(Azure REQUIRED) +#find_package(Azure REQUIRED) find_package(Boost REQUIRED) find_package(Arrow REQUIRED) find_package(protobuf REQUIRED) find_package(glog REQUIRED) -find_package(AWSSDK REQUIRED) +#find_package(AWSSDK REQUIRED) file(GLOB_RECURSE SRC_FILES src/*.cpp src/*.cc) add_library(milvus-storage ${SRC_FILES}) @@ -49,4 +52,8 @@ endif() if (WITH_BENCHMARK) add_subdirectory(benchmark) -endif() \ No newline at end of file +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/milvus-storage.pc.in "${CMAKE_CURRENT_BINARY_DIR}/milvus-storage.pc" @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/milvus-storage.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libmilvus-storage.dylib" DESTINATION "${CMAKE_INSTALL_LIBDIR}") \ No newline at end of file diff --git a/cpp/Makefile b/cpp/Makefile index f72c7ccc..da6cc9f6 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -7,8 +7,9 @@ endif build: mkdir -p build && cd build && \ - conan install .. --build=missing --update && \ - conan build .. + conan install .. --build=missing -u && \ + conan build .. && \ + conan install .. --install-folder . -g make debug: mkdir -p build && cd build && \ diff --git a/cpp/conanfile.py b/cpp/conanfile.py index 26c7b7db..e6ca9491 100644 --- a/cpp/conanfile.py +++ b/cpp/conanfile.py @@ -12,12 +12,13 @@ class StorageConan(ConanFile): - name = "storage" + name = "milvus-storage" description = "empty" topics = ("vector", "cloud", "ann") url = "https://github.com/milvus-io/milvus-storage" homepage = "https://github.com/milvus-io/milvus-storage" license = "Apache-2.0" + version = "0.1.0" settings = "os", "arch", "compiler", "build_type" options = { @@ -48,11 +49,12 @@ class StorageConan(ConanFile): "arrow:with_jemalloc": True, "boost:without_test": True, } - exports_sources = ( "src/*", + "include/*", "thirdparty/*", "test/*", + "benchmark/*", "CMakeLists.txt", "*.cmake", "conanfile.py", @@ -156,8 +158,7 @@ def build(self): def package(self): cmake = CMake(self) cmake.install() - files.rmdir(self, os.path.join(self.package_folder, "lib", "cmake")) - files.rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig")) + self.copy("*_c.h") def package_info(self): self.cpp_info.set_property("cmake_file_name", "storage") diff --git a/cpp/include/milvus-storage/packed/reader.h b/cpp/include/milvus-storage/packed/reader.h index 06ed1996..c808af0c 100644 --- a/cpp/include/milvus-storage/packed/reader.h +++ b/cpp/include/milvus-storage/packed/reader.h @@ -40,6 +40,12 @@ using RowOffsetMinHeap = class PackedRecordBatchReader : public arrow::RecordBatchReader { public: + // Test only + PackedRecordBatchReader(arrow::fs::FileSystem& fs, + const std::string& path, + const std::shared_ptr schema, + const int64_t buffer_size = DEFAULT_READ_BUFFER_SIZE); + PackedRecordBatchReader(arrow::fs::FileSystem& fs, const std::vector& paths, const std::shared_ptr schema, diff --git a/cpp/include/milvus-storage/packed/reader_c.h b/cpp/include/milvus-storage/packed/reader_c.h new file mode 100644 index 00000000..c7576fab --- /dev/null +++ b/cpp/include/milvus-storage/packed/reader_c.h @@ -0,0 +1,32 @@ +// Copyright 2023 Zilliz +// +// Licensed 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef void* CReader; +typedef void* CStatus; +typedef void* CRecordBatch; +typedef void* CFileSystem; + +int Open(const char* path, struct ArrowSchema* schema, const int64_t buffer_size, struct ArrowArrayStream* out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/cpp/src/milvus-storage.pc.in b/cpp/src/milvus-storage.pc.in new file mode 100644 index 00000000..cd11676a --- /dev/null +++ b/cpp/src/milvus-storage.pc.in @@ -0,0 +1,9 @@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ + +Name: Milvus Storage +Description: Milvus Storage +Version: @PROJECT_VERSION@ + +Libs: -L${libdir} -lstorage +Cflags: -I${includedir} \ No newline at end of file diff --git a/cpp/src/packed/reader.cpp b/cpp/src/packed/reader.cpp index 3e9b94f7..c6aa969e 100644 --- a/cpp/src/packed/reader.cpp +++ b/cpp/src/packed/reader.cpp @@ -27,6 +27,13 @@ namespace milvus_storage { +PackedRecordBatchReader::PackedRecordBatchReader(arrow::fs::FileSystem& fs, + const std::string& path, + const std::shared_ptr schema, + const int64_t buffer_size) + : PackedRecordBatchReader( + fs, std::vector{path}, schema, std::vector(), std::set(), buffer_size) {} + PackedRecordBatchReader::PackedRecordBatchReader(arrow::fs::FileSystem& fs, const std::vector& paths, const std::shared_ptr schema, @@ -39,14 +46,27 @@ PackedRecordBatchReader::PackedRecordBatchReader(arrow::fs::FileSystem& fs, row_limit_(0), absolute_row_position_(0), read_count_(0) { + auto cols = std::set(needed_columns); + if (cols.empty()) { + for (int i = 0; i < schema->num_fields(); i++) { + cols.emplace(i); + } + } + auto offsets = std::vector(column_offsets); + if (column_offsets.empty()) { + for (int i = 0; i < schema->num_fields(); i++) { + offsets.emplace_back(0, i); + } + } std::set needed_paths; - for (int i : needed_columns) { - needed_column_offsets_.push_back(column_offsets[i]); - needed_paths.emplace(column_offsets[i].path_index); + for (int i : cols) { + needed_column_offsets_.push_back(offsets[i]); + needed_paths.emplace(offsets[i].path_index); } for (auto i : needed_paths) { auto result = MakeArrowFileReader(fs, paths[i]); if (!result.ok()) { + LOG_STORAGE_ERROR_ << "Error making file reader " << i << ":" << result.status().ToString(); throw std::runtime_error(result.status().ToString()); } file_readers_.emplace_back(std::move(result.value())); @@ -54,6 +74,10 @@ PackedRecordBatchReader::PackedRecordBatchReader(arrow::fs::FileSystem& fs, for (int i = 0; i < file_readers_.size(); ++i) { auto metadata = file_readers_[i]->parquet_reader()->metadata()->key_value_metadata()->Get(ROW_GROUP_SIZE_META_KEY); + if (!metadata.ok()) { + LOG_STORAGE_ERROR_ << "metadata not found in file " << i; + throw std::runtime_error(metadata.status().ToString()); + } row_group_sizes_.push_back(PackedMetaSerde::deserialize(metadata.ValueOrDie())); LOG_STORAGE_DEBUG_ << " file " << i << " metadata size: " << file_readers_[i]->parquet_reader()->metadata()->size(); } @@ -149,7 +173,6 @@ arrow::Status PackedRecordBatchReader::advanceBuffer() { } buffer_available_ -= plan_buffer_size; row_limit_ = sorted_offsets.top().second; - return arrow::Status::OK(); } diff --git a/cpp/src/packed/reader_c.cpp b/cpp/src/packed/reader_c.cpp new file mode 100644 index 00000000..a94279e6 --- /dev/null +++ b/cpp/src/packed/reader_c.cpp @@ -0,0 +1,47 @@ +// Copyright 2023 Zilliz +// +// Licensed 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 "packed/reader_c.h" +#include "common/log.h" +#include "packed/reader.h" +#include "filesystem/fs.h" +#include "common/config.h" + +#include +#include +#include +#include +#include + +int Open(const char* path, struct ArrowSchema* schema, const int64_t buffer_size, struct ArrowArrayStream* out) { + auto truePath = std::string(path); + auto factory = std::make_shared(); + auto conf = milvus_storage::StorageConfig(); + conf.uri = "file:///tmp/"; + auto r = factory->BuildFileSystem(conf, &truePath); + if (!r.ok()) { + LOG_STORAGE_ERROR_ << "Error building filesystem: " << path; + return -2; + } + auto trueFs = r.value(); + auto trueSchema = arrow::ImportSchema(schema).ValueOrDie(); + auto reader = std::make_shared(*trueFs, path, trueSchema, buffer_size); + auto status = ExportRecordBatchReader(reader, out); + LOG_STORAGE_ERROR_ << "read export done"; + if (!status.ok()) { + LOG_STORAGE_ERROR_ << "Error exporting record batch reader" << status.ToString(); + return static_cast(status.code()); + } + return 0; +} \ No newline at end of file diff --git a/cpp/test/packed/packed_test_base.h b/cpp/test/packed/packed_test_base.h index cc92371c..506e1e49 100644 --- a/cpp/test/packed/packed_test_base.h +++ b/cpp/test/packed/packed_test_base.h @@ -39,8 +39,6 @@ #include #include -using namespace std; - namespace milvus_storage { class PackedTestBase : public ::testing::Test { @@ -76,6 +74,7 @@ class PackedTestBase : public ::testing::Test { void TearDown() override { if (fs_ != nullptr) { + // FIXME: delete is not working fs_->DeleteDir(file_path_); } } @@ -201,7 +200,7 @@ class PackedTestBase : public ::testing::Test { std::vector int32_values; std::vector int64_values; - std::vector> str_values; + std::vector> str_values; }; } // namespace milvus_storage \ No newline at end of file diff --git a/go/Makefile b/go/Makefile index ef7cf605..c00e3461 100644 --- a/go/Makefile +++ b/go/Makefile @@ -1,6 +1,23 @@ +include ../cpp/build/conanbuildinfo.mak + +MILVUS_STORAGE_ROOT = $(abspath $(CURDIR)/..) +MILVUS_STORAGE_INCLUDE_DIR = $(abspath $(MILVUS_STORAGE_ROOT)/cpp/include) +MILVUS_STORAGE_LD_DIR = $(abspath $(MILVUS_STORAGE_ROOT)/cpp/build/Release) + +CFLAGS += $(CONAN_CFLAGS) +CXXFLAGS += $(CONAN_CXXFLAGS) +INCLUDE_DIRS = $(CONAN_INCLUDE_DIRS_ARROW) $(MILVUS_STORAGE_INCLUDE_DIR) +CPPFLAGS = $(addprefix -I, $(INCLUDE_DIRS)) +LDFLAGS += $(addprefix -L, $(MILVUS_STORAGE_LD_DIR)) + .EXPORT_ALL_VARIABLES: -.PHONY: proto +.PHONY: build + +build: + CGO_CFLAGS="$(CPPFLAGS)" CGO_LDFLAGS="$(LDFLAGS) -lmilvus-storage" go build ./... +test: + CGO_CFLAGS="$(CPPFLAGS)" CGO_LDFLAGS="$(LDFLAGS) -Wl,-rpath,$(MILVUS_STORAGE_LD_DIR) -lmilvus-storage" go test -timeout 30s ./... proto: mkdir -p proto/manifest_proto mkdir -p proto/schema_proto diff --git a/go/go.mod b/go/go.mod index 7bdc7af3..90d76a9b 100644 --- a/go/go.mod +++ b/go/go.mod @@ -2,31 +2,32 @@ module github.com/milvus-io/milvus-storage/go go 1.19 +toolchain go1.23.1 + require ( github.com/apache/arrow/go/v12 v12.0.0-20230223012627-e0e740bd7a24 github.com/bits-and-blooms/bitset v1.5.0 - github.com/google/uuid v1.3.0 + github.com/golang/protobuf v1.5.4 + github.com/google/uuid v1.6.0 github.com/minio/minio-go/v7 v7.0.61 - github.com/stretchr/testify v1.8.4 + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.24.0 - google.golang.org/grpc v1.57.0 - google.golang.org/protobuf v1.31.0 ) require ( github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // indirect - github.com/andybalholm/brotli v1.0.4 // indirect - github.com/apache/thrift v0.18.1 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/apache/thrift v0.19.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/goccy/go-json v0.10.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/flatbuffers v2.0.8+incompatible // indirect + github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/asmfmt v1.3.2 // indirect - github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect @@ -34,8 +35,7 @@ require ( github.com/minio/sha256-simd v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pierrec/lz4/v4 v4.1.18 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/rs/xid v1.5.0 // indirect @@ -44,16 +44,19 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.12.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect - golang.org/x/tools v0.11.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.22.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + gonum.org/v1/gonum v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go/go.sum b/go/go.sum index a451209c..02f5243c 100644 --- a/go/go.sum +++ b/go/go.sum @@ -1,11 +1,11 @@ github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/apache/arrow/go/v12 v12.0.0-20230223012627-e0e740bd7a24 h1:3klg6Gtrm0jGkiXWLYricKhI1pYYFuBFXhGzOT5B1eo= github.com/apache/arrow/go/v12 v12.0.0-20230223012627-e0e740bd7a24/go.mod h1:3JcT3bSZFdc7wLPKSlQXhf3L0GjPz0TOmLlG1YXnBfU= -github.com/apache/thrift v0.18.1 h1:lNhK/1nqjbwbiOPDBPFJVKxgDEGSepKuTh6OLiXW8kg= -github.com/apache/thrift v0.18.1/go.mod h1:rdQn/dCcDKEWjjylUeueum4vQEjG2v8v2PqriUnbr+I= +github.com/apache/thrift v0.19.0 h1:sOqkWPzMj7w6XaYbJQG7m4sGqVolaW/0D28Ln7yPzMk= +github.com/apache/thrift v0.19.0/go.mod h1:SUALL216IiaOw2Oy+5Vs9lboJ/t9g40C+G07Dc0QC1I= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= @@ -16,29 +16,27 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI= +github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -58,8 +56,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -74,11 +72,11 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= @@ -90,36 +88,34 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw= -golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e h1:S83+ibolgyZ0bqz7KEsUOPErxcv4VzlszxY+31OfB/E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= diff --git a/go/packed/packed.go b/go/packed/packed.go new file mode 100644 index 00000000..4c718050 --- /dev/null +++ b/go/packed/packed.go @@ -0,0 +1,50 @@ +// Copyright 2023 Zilliz +// +// Licensed 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. + +package packed + +/* +#include +#include "milvus-storage/packed/reader_c.h" +#include "arrow/c/abi.h" +#include "arrow/c/helpers.h" +*/ +import "C" +import ( + "errors" + "fmt" + "unsafe" + + "github.com/apache/arrow/go/v12/arrow" + "github.com/apache/arrow/go/v12/arrow/arrio" + "github.com/apache/arrow/go/v12/arrow/cdata" +) + +func Open(path string, schema *arrow.Schema, bufferSize int) (arrio.Reader, error) { + // var cSchemaPtr uintptr + // cSchema := cdata.SchemaFromPtr(cSchemaPtr) + var cas cdata.CArrowSchema + cdata.ExportArrowSchema(schema, &cas) + casPtr := (*C.struct_ArrowSchema)(unsafe.Pointer(&cas)) + var cass cdata.CArrowArrayStream + + cPath := C.CString(path) + defer C.free(unsafe.Pointer(cPath)) + status := C.Open(cPath, casPtr, C.int64_t(bufferSize), (*C.struct_ArrowArrayStream)(unsafe.Pointer(&cass))) + if status != 0 { + return nil, errors.New(fmt.Sprintf("failed to open file: %s, status: %d", path, status)) + } + reader := cdata.ImportCArrayStream((*cdata.CArrowArrayStream)(unsafe.Pointer(&cass)), schema) + return reader, nil +} diff --git a/go/packed/packed_test.go b/go/packed/packed_test.go new file mode 100644 index 00000000..d236c4be --- /dev/null +++ b/go/packed/packed_test.go @@ -0,0 +1,69 @@ +// Copyright 2023 Zilliz +// +// Licensed 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. + +package packed + +import ( + "testing" + + "github.com/apache/arrow/go/v12/arrow" + "github.com/apache/arrow/go/v12/arrow/array" + "github.com/apache/arrow/go/v12/arrow/memory" + "github.com/stretchr/testify/assert" +) + +func TestRead(t *testing.T) { + schema := arrow.NewSchema([]arrow.Field{ + {Name: "a", Type: arrow.PrimitiveTypes.Int32}, + {Name: "b", Type: arrow.PrimitiveTypes.Int64}, + {Name: "c", Type: arrow.BinaryTypes.String}, + }, nil) + + b := array.NewRecordBuilder(memory.DefaultAllocator, schema) + defer b.Release() + for idx := range schema.Fields() { + switch idx { + case 0: + b.Field(idx).(*array.Int32Builder).AppendValues( + []int32{int32(1), int32(2), int32(3)}, nil, + ) + case 1: + b.Field(idx).(*array.Int64Builder).AppendValues( + []int64{int64(4), int64(5), int64(6)}, nil, + ) + case 2: + b.Field(idx).(*array.StringBuilder).AppendValues( + []string{"a", "b", "c"}, nil, + ) + } + } + //rec := b.NewRecord() + + path := "testdata/0" + // file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666) + // assert.NoError(t, err) + // writer, err := pqarrow.NewFileWriter(schema, file, parquet.NewWriterProperties(), pqarrow.DefaultWriterProps()) + // assert.NoError(t, err) + // err = writer.Write(rec) + // assert.NoError(t, err) + // err = writer.Close() + // assert.NoError(t, err) + + reader, err := Open(path, schema, 10*1024*1024 /* 10MB */) + assert.NoError(t, err) + rr, err := reader.Read() + assert.NoError(t, err) + defer rr.Release() + assert.Equal(t, int64(300), rr.NumRows()) +} diff --git a/go/packed/testdata/0 b/go/packed/testdata/0 new file mode 100644 index 0000000000000000000000000000000000000000..f5658b395f6f7c8015500e2b73aaf53e6b528482 GIT binary patch literal 92668 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 L8tw-6#YO)wie4Y1 literal 0 HcmV?d00001