This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/master by this push:
new 6cff6a3 ARROW-13816: [Go][C] Implement Consumer APIs for C Data
Interface in Go
6cff6a3 is described below
commit 6cff6a3f4820f2908285e8691f454fa7195ab4fa
Author: Matthew Topol <[email protected]>
AuthorDate: Fri Sep 17 13:31:46 2021 -0400
ARROW-13816: [Go][C] Implement Consumer APIs for C Data Interface in Go
This supercedes some portions of #10995 as I think it's better to
centralize the cdata interface interactions in a specific module that is part
of the arrow module rather than it being a part of the dataset module. So this
should get reviewed and merged first.
CC @pitrou @emkornfield
Closes #11037 from zeroshade/go-cdata
Lead-authored-by: Matthew Topol <[email protected]>
Co-authored-by: Antoine Pitrou <[email protected]>
Co-authored-by: Matt Topol <[email protected]>
Signed-off-by: Matthew Topol <[email protected]>
---
.github/workflows/go.yml | 33 ++
.../debian-10-go-cgo-python.dockerfile} | 38 +-
ci/docker/debian-10-go.dockerfile | 3 +-
ci/scripts/{go_test.sh => go_cgo_python_test.sh} | 27 +-
ci/scripts/go_test.sh | 4 +
dev/release/rat_exclude_files.txt | 1 +
docker-compose.yml | 21 +-
docs/source/status.rst | 40 +-
go/arrow/cdata/arrow/c/abi.h | 103 ++++
go/arrow/cdata/arrow/c/helpers.h | 117 ++++
go/arrow/cdata/cdata.go | 574 +++++++++++++++++++
go/arrow/cdata/cdata_fulltest.c | 379 +++++++++++++
go/arrow/cdata/cdata_test.go | 621 +++++++++++++++++++++
go/arrow/cdata/cdata_test_framework.go | 248 ++++++++
go/arrow/cdata/interface.go | 161 ++++++
go/arrow/cdata/test/go.mod | 23 +
go/arrow/cdata/test/go.sum | 150 +++++
go/arrow/cdata/test/test_cimport.go | 101 ++++
go/arrow/cdata/test/test_export_to_cgo.py | 95 ++++
go/arrow/cdata/utils.h | 56 ++
go/arrow/datatype_fixedwidth.go | 2 +-
go/arrow/datatype_fixedwidth_test.go | 10 +-
22 files changed, 2741 insertions(+), 66 deletions(-)
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 3c9100c..e696de9 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -75,6 +75,39 @@ jobs:
continue-on-error: true
run: archery docker push debian-go
+ docker_cgo_python:
+ name: AMD64 Debian 10 GO ${{ matrix.go }} - CGO Python
+ runs-on: ubuntu-latest
+ if: ${{ !contains(github.event.pull_request.title, 'WIP') }}
+ timeout-minutes: 15
+ strategy:
+ fail-fast: false
+ matrix:
+ go: [1.15]
+ env:
+ GO: ${{ matrix.go }}
+ steps:
+ - name: Checkout Arrow
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ - name: Fetch Submodules and Tags
+ run: ci/scripts/util_checkout.sh
+ - name: Free Up Disk Space
+ run: ci/scripts/util_cleanup.sh
+ - name: Setup Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.8
+ - name: Setup Archery
+ run: pip install -e dev/archery[docker]
+ - name: Execute Docker Build
+ run: archery docker run debian-go-cgo-python
+ - name: Docker Push
+ if: success() && github.event_name == 'push' && github.repository ==
'apache/arrow'
+ continue-on-error: true
+ run: archery docker push debian-go-cgo-python
+
windows:
name: AMD64 Windows 2019 Go ${{ matrix.go }}
runs-on: windows-latest
diff --git a/ci/scripts/go_test.sh
b/ci/docker/debian-10-go-cgo-python.dockerfile
old mode 100755
new mode 100644
similarity index 60%
copy from ci/scripts/go_test.sh
copy to ci/docker/debian-10-go-cgo-python.dockerfile
index 9b2572e..46455a4
--- a/ci/scripts/go_test.sh
+++ b/ci/docker/debian-10-go-cgo-python.dockerfile
@@ -1,5 +1,3 @@
-#!/usr/bin/env bash
-#
# 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
@@ -17,30 +15,22 @@
# specific language governing permissions and limitations
# under the License.
-set -ex
-
-source_dir=${1}/go
-
-testargs="-race"
-case "$(uname)" in
- MINGW*)
- # -race doesn't work on windows currently
- testargs=""
- ;;
-esac
-
-pushd ${source_dir}/arrow
+ARG base
+FROM ${base}
-for d in $(go list ./... | grep -v vendor); do
- go test $testargs -tags "test" $d
-done
+ENV DEBIAN_FRONTEND noninteractive
-popd
+# Install python3 and pip so we can install pyarrow to test the C data
interface.
+RUN apt-get update -y -q && \
+ apt-get install -y -q --no-install-recommends \
+ python3 \
+ python3-pip && \
+ apt-get clean
-pushd ${source_dir}/parquet
+RUN ln -s /usr/bin/python3 /usr/local/bin/python && \
+ ln -s /usr/bin/pip3 /usr/local/bin/pip
-for d in $(go list ./... | grep -v vendor); do
- go test $testargs $d
-done
+# Need a newer pip than Debian's to install manylinux201x wheels
+RUN pip install -U pip
-popd
+RUN pip install pyarrow cffi --only-binary pyarrow
diff --git a/ci/docker/debian-10-go.dockerfile
b/ci/docker/debian-10-go.dockerfile
index 199f09e..3a24b8a 100644
--- a/ci/docker/debian-10-go.dockerfile
+++ b/ci/docker/debian-10-go.dockerfile
@@ -17,7 +17,8 @@
ARG arch=amd64
ARG go=1.15
-FROM ${arch}/golang:${go}
+FROM ${arch}/golang:${go}-buster
+
# TODO(kszucs):
# 1. add the files required to install the dependencies to .dockerignore
diff --git a/ci/scripts/go_test.sh b/ci/scripts/go_cgo_python_test.sh
similarity index 73%
copy from ci/scripts/go_test.sh
copy to ci/scripts/go_cgo_python_test.sh
index 9b2572e..564b5e3 100755
--- a/ci/scripts/go_test.sh
+++ b/ci/scripts/go_cgo_python_test.sh
@@ -21,26 +21,25 @@ set -ex
source_dir=${1}/go
-testargs="-race"
+pushd ${source_dir}/arrow/cdata/test
+
case "$(uname)" in
+ Linux)
+ testlib="cgotest.so"
+ ;;
+ Darwin)
+ testlib="cgotest.so"
+ ;;
MINGW*)
- # -race doesn't work on windows currently
- testargs=""
+ testlib="cgotest.dll"
;;
esac
-pushd ${source_dir}/arrow
-
-for d in $(go list ./... | grep -v vendor); do
- go test $testargs -tags "test" $d
-done
-
-popd
+go build -tags cdata_test -buildmode=c-shared -o $testlib .
-pushd ${source_dir}/parquet
+python test_export_to_cgo.py
-for d in $(go list ./... | grep -v vendor); do
- go test $testargs $d
-done
+rm $testlib
+rm "${testlib%.*}.h"
popd
diff --git a/ci/scripts/go_test.sh b/ci/scripts/go_test.sh
index 9b2572e..18855ac 100755
--- a/ci/scripts/go_test.sh
+++ b/ci/scripts/go_test.sh
@@ -31,6 +31,10 @@ esac
pushd ${source_dir}/arrow
+# the cgo implementation of the c data interface requires the "test"
+# tag in order to run its tests so that the testing functions implemented
+# in .c files don't get included in non-test builds.
+
for d in $(go list ./... | grep -v vendor); do
go test $testargs -tags "test" $d
done
diff --git a/dev/release/rat_exclude_files.txt
b/dev/release/rat_exclude_files.txt
index e2aa628..b07f962 100644
--- a/dev/release/rat_exclude_files.txt
+++ b/dev/release/rat_exclude_files.txt
@@ -129,6 +129,7 @@ go/arrow/flight/Flight.pb.go
go/arrow/flight/Flight_grpc.pb.go
go/arrow/internal/cpu/*
go/arrow/type_string.go
+go/arrow/cdata/test/go.sum
go/*.tmpldata
go/*.s
go/parquet/go.sum
diff --git a/docker-compose.yml b/docker-compose.yml
index 2c3736d..edf432a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -112,7 +112,8 @@ x-hierarchy:
- debian-c-glib:
- debian-ruby
- debian-python
- - debian-go
+ - debian-go:
+ - debian-go-cgo-python
- debian-java:
- debian-java-jni
- debian-js
@@ -1198,6 +1199,24 @@ services:
/arrow/ci/scripts/go_build.sh /arrow &&
/arrow/ci/scripts/go_test.sh /arrow"
+ debian-go-cgo-python:
+ # Usage:
+ # docker-compose build debian-go-cgo-python
+ # docker-compose run debian-go-cgo-python
+ image: ${REPO}:${ARCH}-debian-10-go-${GO}-cgo-python
+ build:
+ context: .
+ dockerfile: ci/docker/debian-10-go-cgo-python.dockerfile
+ cache_from:
+ - ${REPO}:${ARCH}-debian-10-go-${GO}-cgo-python
+ args:
+ base: ${REPO}:${ARCH}-debian-10-go-${GO}
+ shm_size: *shm-size
+ volumes: *debian-volumes
+ command: &go-cgo-python-command >
+ /bin/bash -c "
+ /arrow/ci/scripts/go_cgo_python_test.sh /arrow"
+
############################# JavaScript ####################################
debian-js:
diff --git a/docs/source/status.rst b/docs/source/status.rst
index 4808418..037e74f 100644
--- a/docs/source/status.rst
+++ b/docs/source/status.rst
@@ -177,18 +177,18 @@ Notes:
C Data Interface
================
-+-----------------------------+-------+--------+-------+-------+
-| Feature | C++ | Python | R | Rust |
-| | | | | |
-+=============================+=======+========+=======+=======+
-| Schema export | ✓ | ✓ | ✓ | ✓ |
-+-----------------------------+-------+--------+-------+-------+
-| Array export | ✓ | ✓ | ✓ | ✓ |
-+-----------------------------+-------+--------+-------+-------+
-| Schema import | ✓ | ✓ | ✓ | ✓ |
-+-----------------------------+-------+--------+-------+-------+
-| Array import | ✓ | ✓ | ✓ | ✓ |
-+-----------------------------+-------+--------+-------+-------+
++-----------------------------+-------+--------+-------+-------+----+
+| Feature | C++ | Python | R | Rust | Go |
+| | | | | | |
++=============================+=======+========+=======+=======+====+
+| Schema export | ✓ | ✓ | ✓ | ✓ | |
++-----------------------------+-------+--------+-------+-------+----+
+| Array export | ✓ | ✓ | ✓ | ✓ | |
++-----------------------------+-------+--------+-------+-------+----+
+| Schema import | ✓ | ✓ | ✓ | ✓ | ✓ |
++-----------------------------+-------+--------+-------+-------+-----+
+| Array import | ✓ | ✓ | ✓ | ✓ | ✓ |
++-----------------------------+-------+--------+-------+-------+-----+
.. seealso::
The :ref:`C Data Interface <c-data-interface>` specification.
@@ -197,14 +197,14 @@ C Data Interface
C Stream Interface (experimental)
=================================
-+-----------------------------+-------+--------+
-| Feature | C++ | Python |
-| | | |
-+=============================+=======+========+
-| Stream export | ✓ | ✓ |
-+-----------------------------+-------+--------+
-| Stream import | ✓ | ✓ |
-+-----------------------------+-------+--------+
++-----------------------------+-------+--------+----+
+| Feature | C++ | Python | Go |
+| | | | |
++=============================+=======+========+====+
+| Stream export | ✓ | ✓ | |
++-----------------------------+-------+--------+----+
+| Stream import | ✓ | ✓ | ✓ |
++-----------------------------+-------+--------+----+
.. seealso::
The :ref:`C Stream Interface <c-stream-interface>` specification.
diff --git a/go/arrow/cdata/arrow/c/abi.h b/go/arrow/cdata/arrow/c/abi.h
new file mode 100644
index 0000000..a78170d
--- /dev/null
+++ b/go/arrow/cdata/arrow/c/abi.h
@@ -0,0 +1,103 @@
+// 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 <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ARROW_FLAG_DICTIONARY_ORDERED 1
+#define ARROW_FLAG_NULLABLE 2
+#define ARROW_FLAG_MAP_KEYS_SORTED 4
+
+struct ArrowSchema {
+ // Array type description
+ const char* format;
+ const char* name;
+ const char* metadata;
+ int64_t flags;
+ int64_t n_children;
+ struct ArrowSchema** children;
+ struct ArrowSchema* dictionary;
+
+ // Release callback
+ void (*release)(struct ArrowSchema*);
+ // Opaque producer-specific data
+ void* private_data;
+};
+
+struct ArrowArray {
+ // Array data description
+ int64_t length;
+ int64_t null_count;
+ int64_t offset;
+ int64_t n_buffers;
+ int64_t n_children;
+ const void** buffers;
+ struct ArrowArray** children;
+ struct ArrowArray* dictionary;
+
+ // Release callback
+ void (*release)(struct ArrowArray*);
+ // Opaque producer-specific data
+ void* private_data;
+};
+
+// EXPERIMENTAL: C stream interface
+
+struct ArrowArrayStream {
+ // Callback to get the stream type
+ // (will be the same for all arrays in the stream).
+ //
+ // Return value: 0 if successful, an `errno`-compatible error code otherwise.
+ //
+ // If successful, the ArrowSchema must be released independently from the
stream.
+ int (*get_schema)(struct ArrowArrayStream*, struct ArrowSchema* out);
+
+ // Callback to get the next array
+ // (if no error and the array is released, the stream has ended)
+ //
+ // Return value: 0 if successful, an `errno`-compatible error code otherwise.
+ //
+ // If successful, the ArrowArray must be released independently from the
stream.
+ int (*get_next)(struct ArrowArrayStream*, struct ArrowArray* out);
+
+ // Callback to get optional detailed error information.
+ // This must only be called if the last stream operation failed
+ // with a non-0 return code.
+ //
+ // Return value: pointer to a null-terminated character array describing
+ // the last error, or NULL if no description is available.
+ //
+ // The returned pointer is only valid until the next operation on this stream
+ // (including release).
+ const char* (*get_last_error)(struct ArrowArrayStream*);
+
+ // Release callback: release the stream's own resources.
+ // Note that arrays returned by `get_next` must be individually released.
+ void (*release)(struct ArrowArrayStream*);
+
+ // Opaque producer-specific data
+ void* private_data;
+};
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/go/arrow/cdata/arrow/c/helpers.h b/go/arrow/cdata/arrow/c/helpers.h
new file mode 100644
index 0000000..a5c1f6f
--- /dev/null
+++ b/go/arrow/cdata/arrow/c/helpers.h
@@ -0,0 +1,117 @@
+// 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 <assert.h>
+#include <string.h>
+
+#include "arrow/c/abi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Query whether the C schema is released
+inline int ArrowSchemaIsReleased(const struct ArrowSchema* schema) {
+ return schema->release == NULL;
+}
+
+/// Mark the C schema released (for use in release callbacks)
+inline void ArrowSchemaMarkReleased(struct ArrowSchema* schema) {
+ schema->release = NULL;
+}
+
+/// Move the C schema from `src` to `dest`
+///
+/// Note `dest` must *not* point to a valid schema already, otherwise there
+/// will be a memory leak.
+inline void ArrowSchemaMove(struct ArrowSchema* src, struct ArrowSchema* dest)
{
+ assert(dest != src);
+ assert(!ArrowSchemaIsReleased(src));
+ memcpy(dest, src, sizeof(struct ArrowSchema));
+ ArrowSchemaMarkReleased(src);
+}
+
+/// Release the C schema, if necessary, by calling its release callback
+inline void ArrowSchemaRelease(struct ArrowSchema* schema) {
+ if (!ArrowSchemaIsReleased(schema)) {
+ schema->release(schema);
+ assert(ArrowSchemaIsReleased(schema));
+ }
+}
+
+/// Query whether the C array is released
+inline int ArrowArrayIsReleased(const struct ArrowArray* array) {
+ return array->release == NULL;
+}
+
+/// Mark the C array released (for use in release callbacks)
+inline void ArrowArrayMarkReleased(struct ArrowArray* array) { array->release
= NULL; }
+
+/// Move the C array from `src` to `dest`
+///
+/// Note `dest` must *not* point to a valid array already, otherwise there
+/// will be a memory leak.
+inline void ArrowArrayMove(struct ArrowArray* src, struct ArrowArray* dest) {
+ assert(dest != src);
+ assert(!ArrowArrayIsReleased(src));
+ memcpy(dest, src, sizeof(struct ArrowArray));
+ ArrowArrayMarkReleased(src);
+}
+
+/// Release the C array, if necessary, by calling its release callback
+inline void ArrowArrayRelease(struct ArrowArray* array) {
+ if (!ArrowArrayIsReleased(array)) {
+ array->release(array);
+ assert(ArrowArrayIsReleased(array));
+ }
+}
+
+/// Query whether the C array stream is released
+inline int ArrowArrayStreamIsReleased(const struct ArrowArrayStream* stream) {
+ return stream->release == NULL;
+}
+
+/// Mark the C array stream released (for use in release callbacks)
+inline void ArrowArrayStreamMarkReleased(struct ArrowArrayStream* stream) {
+ stream->release = NULL;
+}
+
+/// Move the C array stream from `src` to `dest`
+///
+/// Note `dest` must *not* point to a valid stream already, otherwise there
+/// will be a memory leak.
+inline void ArrowArrayStreamMove(struct ArrowArrayStream* src,
+ struct ArrowArrayStream* dest) {
+ assert(dest != src);
+ assert(!ArrowArrayStreamIsReleased(src));
+ memcpy(dest, src, sizeof(struct ArrowArrayStream));
+ ArrowArrayStreamMarkReleased(src);
+}
+
+/// Release the C array stream, if necessary, by calling its release callback
+inline void ArrowArrayStreamRelease(struct ArrowArrayStream* stream) {
+ if (!ArrowArrayStreamIsReleased(stream)) {
+ stream->release(stream);
+ assert(ArrowArrayStreamIsReleased(stream));
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/go/arrow/cdata/cdata.go b/go/arrow/cdata/cdata.go
new file mode 100644
index 0000000..7cda454
--- /dev/null
+++ b/go/arrow/cdata/cdata.go
@@ -0,0 +1,574 @@
+// 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.
+
+// +build cgo
+
+package cdata
+
+// implement handling of the Arrow C Data Interface. At least from a consuming
side.
+
+// #include "arrow/c/abi.h"
+// #include "arrow/c/helpers.h"
+// typedef struct ArrowSchema ArrowSchema;
+// typedef struct ArrowArray ArrowArray;
+// typedef struct ArrowArrayStream ArrowArrayStream;
+//
+// int stream_get_schema(struct ArrowArrayStream* st, struct ArrowSchema* out)
{ return st->get_schema(st, out); }
+// int stream_get_next(struct ArrowArrayStream* st, struct ArrowArray* out) {
return st->get_next(st, out); }
+// const char* stream_get_last_error(struct ArrowArrayStream* st) { return
st->get_last_error(st); }
+// struct ArrowArray get_arr() { struct ArrowArray arr; return arr; }
+// struct ArrowArrayStream get_stream() { struct ArrowArrayStream stream;
return stream; }
+//
+import "C"
+
+import (
+ "io"
+ "reflect"
+ "runtime"
+ "strconv"
+ "strings"
+ "syscall"
+ "unsafe"
+
+ "github.com/apache/arrow/go/arrow"
+ "github.com/apache/arrow/go/arrow/array"
+ "github.com/apache/arrow/go/arrow/bitutil"
+ "github.com/apache/arrow/go/arrow/memory"
+ "golang.org/x/xerrors"
+)
+
+type (
+ // CArrowSchema is the C Data Interface for ArrowSchemas defined in
abi.h
+ CArrowSchema = C.ArrowSchema
+ // CArrowArray is the C Data Interface object for Arrow Arrays as
defined in abi.h
+ CArrowArray = C.ArrowArray
+ // CArrowArrayStream is the Experimental API for handling streams of
record batches
+ // through the C Data interface.
+ CArrowArrayStream = C.ArrowArrayStream
+)
+
+// Map from the defined strings to their corresponding arrow.DataType interface
+// object instances, for types that don't require params.
+var formatToSimpleType = map[string]arrow.DataType{
+ "n": arrow.Null,
+ "b": arrow.FixedWidthTypes.Boolean,
+ "c": arrow.PrimitiveTypes.Int8,
+ "C": arrow.PrimitiveTypes.Uint8,
+ "s": arrow.PrimitiveTypes.Int16,
+ "S": arrow.PrimitiveTypes.Uint16,
+ "i": arrow.PrimitiveTypes.Int32,
+ "I": arrow.PrimitiveTypes.Uint32,
+ "l": arrow.PrimitiveTypes.Int64,
+ "L": arrow.PrimitiveTypes.Uint64,
+ "e": arrow.FixedWidthTypes.Float16,
+ "f": arrow.PrimitiveTypes.Float32,
+ "g": arrow.PrimitiveTypes.Float64,
+ "z": arrow.BinaryTypes.Binary,
+ "u": arrow.BinaryTypes.String,
+ "tdD": arrow.FixedWidthTypes.Date32,
+ "tdm": arrow.FixedWidthTypes.Date64,
+ "tts": arrow.FixedWidthTypes.Time32s,
+ "ttm": arrow.FixedWidthTypes.Time32ms,
+ "ttu": arrow.FixedWidthTypes.Time64us,
+ "ttn": arrow.FixedWidthTypes.Time64ns,
+ "tDs": arrow.FixedWidthTypes.Duration_s,
+ "tDm": arrow.FixedWidthTypes.Duration_ms,
+ "tDu": arrow.FixedWidthTypes.Duration_us,
+ "tDn": arrow.FixedWidthTypes.Duration_ns,
+ "tiM": arrow.FixedWidthTypes.MonthInterval,
+ "tiD": arrow.FixedWidthTypes.DayTimeInterval,
+}
+
+// decode metadata from C which is encoded as
+//
+// [int32] -> number of metadata pairs
+// for 0..n
+// [int32] -> number of bytes in key
+// [n bytes] -> key value
+// [int32] -> number of bytes in value
+// [n bytes] -> value
+func decodeCMetadata(md *C.char) arrow.Metadata {
+ if md == nil {
+ return arrow.Metadata{}
+ }
+
+ // don't copy the bytes, just reference them directly
+ const maxlen = 0x7fffffff
+ data := (*[maxlen]byte)(unsafe.Pointer(md))[:]
+
+ readint32 := func() int32 {
+ v := *(*int32)(unsafe.Pointer(&data[0]))
+ data = data[arrow.Int32SizeBytes:]
+ return v
+ }
+
+ readstr := func() string {
+ l := readint32()
+ s := string(data[:l])
+ data = data[l:]
+ return s
+ }
+
+ npairs := readint32()
+ if npairs == 0 {
+ return arrow.Metadata{}
+ }
+
+ keys := make([]string, npairs)
+ vals := make([]string, npairs)
+
+ for i := int32(0); i < npairs; i++ {
+ keys[i] = readstr()
+ vals[i] = readstr()
+ }
+
+ return arrow.NewMetadata(keys, vals)
+}
+
+// convert a C.ArrowSchema to an arrow.Field to maintain metadata with the
schema
+func importSchema(schema *CArrowSchema) (ret arrow.Field, err error) {
+ // always release, even on error
+ defer C.ArrowSchemaRelease(schema)
+
+ var childFields []arrow.Field
+ if schema.n_children > 0 {
+ // call ourselves recursively if there are children.
+ var schemaChildren []*C.ArrowSchema
+ // set up a slice to reference safely
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&schemaChildren))
+ s.Data = uintptr(unsafe.Pointer(schema.children))
+ s.Len = int(schema.n_children)
+ s.Cap = int(schema.n_children)
+
+ childFields = make([]arrow.Field, schema.n_children)
+ for i, c := range schemaChildren {
+ childFields[i], err = importSchema((*CArrowSchema)(c))
+ if err != nil {
+ return
+ }
+ }
+ }
+
+ // copy the schema name from the c-string
+ ret.Name = C.GoString(schema.name)
+ ret.Nullable = (schema.flags & C.ARROW_FLAG_NULLABLE) != 0
+ ret.Metadata = decodeCMetadata(schema.metadata)
+
+ // copies the c-string here, but it's very small
+ f := C.GoString(schema.format)
+ // handle our non-parameterized simple types.
+ dt, ok := formatToSimpleType[f]
+ if ok {
+ ret.Type = dt
+ return
+ }
+
+ // handle types with params via colon
+ typs := strings.Split(f, ":")
+ defaulttz := "UTC"
+ switch typs[0] {
+ case "tss":
+ tz := typs[1]
+ if len(typs[1]) == 0 {
+ tz = defaulttz
+ }
+ dt = &arrow.TimestampType{Unit: arrow.Second, TimeZone: tz}
+ case "tsm":
+ tz := typs[1]
+ if len(typs[1]) == 0 {
+ tz = defaulttz
+ }
+ dt = &arrow.TimestampType{Unit: arrow.Millisecond, TimeZone: tz}
+ case "tsu":
+ tz := typs[1]
+ if len(typs[1]) == 0 {
+ tz = defaulttz
+ }
+ dt = &arrow.TimestampType{Unit: arrow.Microsecond, TimeZone: tz}
+ case "tsn":
+ tz := typs[1]
+ if len(typs[1]) == 0 {
+ tz = defaulttz
+ }
+ dt = &arrow.TimestampType{Unit: arrow.Nanosecond, TimeZone: tz}
+ case "w": // fixed size binary is "w:##" where ## is the byteWidth
+ byteWidth, err := strconv.Atoi(typs[1])
+ if err != nil {
+ return ret, err
+ }
+ dt = &arrow.FixedSizeBinaryType{ByteWidth: byteWidth}
+ case "d": // decimal types are d:<precision>,<scale>[,<bitsize>] size
is assumed 128 if left out
+ props := typs[1]
+ propList := strings.Split(props, ",")
+ if len(propList) == 3 {
+ err = xerrors.New("only decimal128 is supported")
+ return
+ }
+
+ precision, _ := strconv.Atoi(propList[0])
+ scale, _ := strconv.Atoi(propList[1])
+ dt = &arrow.Decimal128Type{Precision: int32(precision), Scale:
int32(scale)}
+ }
+
+ if f[0] == '+' { // types with children
+ switch f[1] {
+ case 'l': // list
+ dt = arrow.ListOf(childFields[0].Type)
+ case 'w': // fixed size list is w:# where # is the list size.
+ listSize, err := strconv.Atoi(strings.Split(f, ":")[1])
+ if err != nil {
+ return ret, err
+ }
+
+ dt = arrow.FixedSizeListOf(int32(listSize),
childFields[0].Type)
+ case 's': // struct
+ dt = arrow.StructOf(childFields...)
+ case 'm': // map type is basically a list of structs.
+ st := childFields[0].Type.(*arrow.StructType)
+ dt = arrow.MapOf(st.Field(0).Type, st.Field(1).Type)
+ dt.(*arrow.MapType).KeysSorted = (schema.flags &
C.ARROW_FLAG_MAP_KEYS_SORTED) != 0
+ }
+ }
+
+ if dt == nil {
+ // if we didn't find a type, then it's something we haven't
implemented.
+ err = xerrors.New("unimplemented type")
+ } else {
+ ret.Type = dt
+ }
+ return
+}
+
+// importer to keep track when importing C ArrowArray objects.
+type cimporter struct {
+ dt arrow.DataType
+ arr *C.ArrowArray
+ data *array.Data
+ parent *cimporter
+ children []cimporter
+ cbuffers []*C.void
+}
+
+func (imp *cimporter) importChild(parent *cimporter, src *C.ArrowArray) error {
+ imp.parent = parent
+ return imp.doImport(src)
+}
+
+// import any child arrays for lists, structs, and so on.
+func (imp *cimporter) doImportChildren() error {
+ var children []*C.ArrowArray
+ // create a proper slice for our children
+ s := (*reflect.SliceHeader)(unsafe.Pointer(&children))
+ s.Data = uintptr(unsafe.Pointer(imp.arr.children))
+ s.Len = int(imp.arr.n_children)
+ s.Cap = int(imp.arr.n_children)
+
+ if len(children) > 0 {
+ imp.children = make([]cimporter, len(children))
+ }
+
+ // handle the cases
+ switch imp.dt.ID() {
+ case arrow.LIST: // only one child to import
+ imp.children[0].dt = imp.dt.(*arrow.ListType).Elem()
+ if err := imp.children[0].importChild(imp, children[0]); err !=
nil {
+ return err
+ }
+ case arrow.FIXED_SIZE_LIST: // only one child to import
+ imp.children[0].dt = imp.dt.(*arrow.FixedSizeListType).Elem()
+ if err := imp.children[0].importChild(imp, children[0]); err !=
nil {
+ return err
+ }
+ case arrow.STRUCT: // import all the children
+ st := imp.dt.(*arrow.StructType)
+ for i, c := range children {
+ imp.children[i].dt = st.Field(i).Type
+ imp.children[i].importChild(imp, c)
+ }
+ case arrow.MAP: // only one child to import, it's a struct array
+ imp.children[0].dt = imp.dt.(*arrow.MapType).ValueType()
+ if err := imp.children[0].importChild(imp, children[0]); err !=
nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (imp *cimporter) initarr() {
+ arr := C.get_arr()
+ imp.arr = &arr
+}
+
+// import is called recursively as needed for importing an array and its
children
+// in order to generate array.Data objects
+func (imp *cimporter) doImport(src *C.ArrowArray) error {
+ imp.initarr()
+ // move the array from the src object passed in to the one referenced by
+ // this importer. That way we can set up a finalizer on the created
+ // *array.Data object so we clean up our Array's memory when garbage
collected.
+ C.ArrowArrayMove(src, imp.arr)
+ defer func(arr *C.ArrowArray) {
+ if imp.data != nil {
+ runtime.SetFinalizer(imp.data, func(*array.Data) {
+ C.ArrowArrayRelease(arr)
+ if C.ArrowArrayIsReleased(arr) != 1 {
+ panic("did not release C mem")
+ }
+ })
+ }
+ }(imp.arr)
+
+ // import any children
+ if err := imp.doImportChildren(); err != nil {
+ return err
+ }
+
+ // get a view of the buffers, zero-copy. we're just looking at the
pointers
+ const maxlen = 0x7fffffff
+ imp.cbuffers =
(*[maxlen]*C.void)(unsafe.Pointer(imp.arr.buffers))[:imp.arr.n_buffers:imp.arr.n_buffers]
+
+ // handle each of our type cases
+ switch dt := imp.dt.(type) {
+ case *arrow.NullType:
+ if err := imp.checkNoChildren(); err != nil {
+ return err
+ }
+ imp.data = array.NewData(dt, int(imp.arr.length), nil, nil,
int(imp.arr.null_count), int(imp.arr.offset))
+ case arrow.FixedWidthDataType:
+ return imp.importFixedSizePrimitive()
+ case *arrow.StringType:
+ return imp.importStringLike()
+ case *arrow.BinaryType:
+ return imp.importStringLike()
+ case *arrow.ListType:
+ return imp.importListLike()
+ case *arrow.MapType:
+ return imp.importListLike()
+ case *arrow.FixedSizeListType:
+ if err := imp.checkNumChildren(1); err != nil {
+ return err
+ }
+
+ if err := imp.checkNumBuffers(1); err != nil {
+ return err
+ }
+
+ nulls, err := imp.importNullBitmap(0)
+ if err != nil {
+ return err
+ }
+
+ imp.data = array.NewData(dt, int(imp.arr.length),
[]*memory.Buffer{nulls}, []*array.Data{imp.children[0].data},
int(imp.arr.null_count), int(imp.arr.offset))
+ case *arrow.StructType:
+ if err := imp.checkNumBuffers(1); err != nil {
+ return err
+ }
+
+ nulls, err := imp.importNullBitmap(0)
+ if err != nil {
+ return err
+ }
+
+ children := make([]*array.Data, len(imp.children))
+ for i := range imp.children {
+ children[i] = imp.children[i].data
+ }
+
+ imp.data = array.NewData(dt, int(imp.arr.length),
[]*memory.Buffer{nulls}, children, int(imp.arr.null_count), int(imp.arr.offset))
+ default:
+ return xerrors.Errorf("unimplemented type %s", dt)
+ }
+
+ return nil
+}
+
+func (imp *cimporter) importStringLike() error {
+ if err := imp.checkNoChildren(); err != nil {
+ return err
+ }
+
+ if err := imp.checkNumBuffers(3); err != nil {
+ return err
+ }
+
+ nulls, err := imp.importNullBitmap(0)
+ if err != nil {
+ return err
+ }
+
+ offsets := imp.importOffsetsBuffer(1)
+ values := imp.importVariableValuesBuffer(2, 1,
arrow.Int32Traits.CastFromBytes(offsets.Bytes()))
+ imp.data = array.NewData(imp.dt, int(imp.arr.length),
[]*memory.Buffer{nulls, offsets, values}, nil, int(imp.arr.null_count),
int(imp.arr.offset))
+ return nil
+}
+
+func (imp *cimporter) importListLike() error {
+ if err := imp.checkNumChildren(1); err != nil {
+ return err
+ }
+
+ if err := imp.checkNumBuffers(2); err != nil {
+ return err
+ }
+
+ nulls, err := imp.importNullBitmap(0)
+ if err != nil {
+ return err
+ }
+
+ offsets := imp.importOffsetsBuffer(1)
+ imp.data = array.NewData(imp.dt, int(imp.arr.length),
[]*memory.Buffer{nulls, offsets}, []*array.Data{imp.children[0].data},
int(imp.arr.null_count), int(imp.arr.offset))
+ return nil
+}
+
+func (imp *cimporter) importFixedSizePrimitive() error {
+ if err := imp.checkNoChildren(); err != nil {
+ return err
+ }
+
+ if err := imp.checkNumBuffers(2); err != nil {
+ return err
+ }
+
+ nulls, err := imp.importNullBitmap(0)
+ if err != nil {
+ return err
+ }
+
+ var values *memory.Buffer
+
+ fw := imp.dt.(arrow.FixedWidthDataType)
+ if bitutil.IsMultipleOf8(int64(fw.BitWidth())) {
+ values = imp.importFixedSizeBuffer(1,
bitutil.BytesForBits(int64(fw.BitWidth())))
+ } else {
+ if fw.BitWidth() != 1 {
+ return xerrors.New("invalid bitwidth")
+ }
+ values = imp.importBitsBuffer(1)
+ }
+ imp.data = array.NewData(imp.dt, int(imp.arr.length),
[]*memory.Buffer{nulls, values}, nil, int(imp.arr.null_count),
int(imp.arr.offset))
+ return nil
+}
+
+func (imp *cimporter) checkNoChildren() error { return imp.checkNumChildren(0)
}
+
+func (imp *cimporter) checkNumChildren(n int64) error {
+ if int64(imp.arr.n_children) != n {
+ return xerrors.Errorf("expected %d children, for imported type
%s, ArrowArray has %d", n, imp.dt, imp.arr.n_children)
+ }
+ return nil
+}
+
+func (imp *cimporter) checkNumBuffers(n int64) error {
+ if int64(imp.arr.n_buffers) != n {
+ return xerrors.Errorf("expected %d buffers for imported type
%s, ArrowArray has %d", n, imp.dt, imp.arr.n_buffers)
+ }
+ return nil
+}
+
+func (imp *cimporter) importBuffer(bufferID int, sz int64) *memory.Buffer {
+ // this is not a copy, we're just having a slice which points at the
data
+ // it's still owned by the C.ArrowArray object and its backing C++
object.
+ const maxLen = 0x7fffffff
+ data := (*[maxLen]byte)(unsafe.Pointer(imp.cbuffers[bufferID]))[:sz:sz]
+ return memory.NewBufferBytes(data)
+}
+
+func (imp *cimporter) importBitsBuffer(bufferID int) *memory.Buffer {
+ bufsize := bitutil.BytesForBits(int64(imp.arr.length) +
int64(imp.arr.offset))
+ return imp.importBuffer(bufferID, bufsize)
+}
+
+func (imp *cimporter) importNullBitmap(bufferID int) (*memory.Buffer, error) {
+ if imp.arr.null_count > 0 && imp.cbuffers[bufferID] == nil {
+ return nil, xerrors.Errorf("arrowarray struct has null bitmap
buffer, but non-zero null_count %d", imp.arr.null_count)
+ }
+
+ if imp.arr.null_count == 0 && imp.cbuffers[bufferID] == nil {
+ return nil, nil
+ }
+
+ return imp.importBitsBuffer(bufferID), nil
+}
+
+func (imp *cimporter) importFixedSizeBuffer(bufferID int, byteWidth int64)
*memory.Buffer {
+ bufsize := byteWidth * int64(imp.arr.length+imp.arr.offset)
+ return imp.importBuffer(bufferID, bufsize)
+}
+
+func (imp *cimporter) importOffsetsBuffer(bufferID int) *memory.Buffer {
+ const offsetsize = int64(arrow.Int32SizeBytes) // go doesn't implement
int64 offsets yet
+ bufsize := offsetsize * int64((imp.arr.length + imp.arr.offset + 1))
+ return imp.importBuffer(bufferID, bufsize)
+}
+
+func (imp *cimporter) importVariableValuesBuffer(bufferID int, byteWidth int,
offsets []int32) *memory.Buffer {
+ bufsize := byteWidth * int(offsets[imp.arr.length])
+ return imp.importBuffer(bufferID, int64(bufsize))
+}
+
+func importCArrayAsType(arr *C.ArrowArray, dt arrow.DataType) (imp *cimporter,
err error) {
+ imp = &cimporter{dt: dt}
+ err = imp.doImport(arr)
+ return
+}
+
+func initReader(rdr *nativeCRecordBatchReader, stream *C.ArrowArrayStream) {
+ st := C.get_stream()
+ rdr.stream = &st
+ C.ArrowArrayStreamMove(stream, rdr.stream)
+ runtime.SetFinalizer(rdr, func(r *nativeCRecordBatchReader) {
C.ArrowArrayStreamRelease(r.stream) })
+}
+
+// Record Batch reader that conforms to arrio.Reader for the ArrowArrayStream
interface
+type nativeCRecordBatchReader struct {
+ stream *C.ArrowArrayStream
+ schema *arrow.Schema
+}
+
+func (n *nativeCRecordBatchReader) getError(errno int) error {
+ return xerrors.Errorf("%w: %s", syscall.Errno(errno),
C.GoString(C.stream_get_last_error(n.stream)))
+}
+
+func (n *nativeCRecordBatchReader) Read() (array.Record, error) {
+ if n.schema == nil {
+ var sc C.ArrowSchema
+ errno := C.stream_get_schema(n.stream, &sc)
+ if errno != 0 {
+ return nil, n.getError(int(errno))
+ }
+ defer C.ArrowSchemaRelease(&sc)
+ s, err := ImportCArrowSchema((*CArrowSchema)(&sc))
+ if err != nil {
+ return nil, err
+ }
+
+ n.schema = s
+ }
+
+ arr := C.get_arr()
+ errno := C.stream_get_next(n.stream, &arr)
+ if errno != 0 {
+ return nil, n.getError(int(errno))
+ }
+
+ if C.ArrowArrayIsReleased(&arr) == 1 {
+ return nil, io.EOF
+ }
+
+ return ImportCRecordBatchWithSchema(&arr, n.schema)
+}
diff --git a/go/arrow/cdata/cdata_fulltest.c b/go/arrow/cdata/cdata_fulltest.c
new file mode 100644
index 0000000..5c4ca49
--- /dev/null
+++ b/go/arrow/cdata/cdata_fulltest.c
@@ -0,0 +1,379 @@
+// 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.
+
+// +build cgo
+// +build test
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "arrow/c/abi.h"
+#include "arrow/c/helpers.h"
+#include "utils.h"
+
+static const int64_t kDefaultFlags = ARROW_FLAG_NULLABLE;
+
+static void release_int32_type(struct ArrowSchema* schema) {
+ // mark released
+ schema->release = NULL;
+}
+
+void export_int32_type(struct ArrowSchema* schema) {
+ const char* encoded_metadata;
+ if (is_little_endian() == 1) {
+ encoded_metadata = kEncodedMeta1LE;
+ } else {
+ encoded_metadata = kEncodedMeta1BE;
+ }
+ *schema = (struct ArrowSchema) {
+ // Type description
+ .format = "i",
+ .name = "",
+ .metadata = encoded_metadata,
+ .flags = 0,
+ .n_children = 0,
+ .children = NULL,
+ .dictionary = NULL,
+ // bookkeeping
+ .release = &release_int32_type,
+ };
+}
+
+static bool test1_released = false;
+
+int test1_is_released() { return test1_released; }
+
+static void release_int32_array(struct ArrowArray* array) {
+ assert(array->n_buffers == 2);
+ // free the buffers and buffers array
+ free((void *) array->buffers[1]);
+ free(array->buffers);
+ // mark released
+ array->release = NULL;
+ test1_released = true;
+}
+
+void export_int32_array(const int32_t* data, int64_t nitems, struct
ArrowArray* array) {
+ // initialize primitive fields
+ *array = (struct ArrowArray) {
+ .length = nitems,
+ .offset = 0,
+ .null_count = 0,
+ .n_buffers = 2,
+ .n_children = 0,
+ .children = NULL,
+ .dictionary = NULL,
+ // bookkeeping
+ .release = &release_int32_array
+ };
+
+ // allocate list of buffers
+ array->buffers = (const void**)malloc(sizeof(void*) * array->n_buffers);
+ assert(array->buffers != NULL);
+ array->buffers[0] = NULL; // no nulls, null bitmap can be omitted
+ array->buffers[1] = data;
+}
+
+
+static void release_primitive(struct ArrowSchema* schema) {
+ free((void *)schema->format);
+ schema->release = NULL;
+}
+
+static void release_nested_internal(struct ArrowSchema* schema,
+ int is_dynamic) {
+ assert(!ArrowSchemaIsReleased(schema));
+ for (int i = 0; i < schema->n_children; ++i) {
+ ArrowSchemaRelease(schema->children[i]);
+ free(schema->children[i]);
+ }
+ if (is_dynamic) {
+ free((void*)schema->format);
+ free((void*)schema->name);
+ }
+ ArrowSchemaMarkReleased(schema);
+}
+
+static void release_nested_static(struct ArrowSchema* schema) {
+ release_nested_internal(schema, /*is_dynamic=*/0);
+}
+
+static void release_nested_dynamic(struct ArrowSchema* schema) {
+ release_nested_internal(schema, /*is_dynamic=*/1);
+}
+
+static void release_nested_dynamic_toplevel(struct ArrowSchema* schema) {
+ assert(!ArrowSchemaIsReleased(schema));
+ for (int i = 0; i < schema->n_children; ++i) {
+ ArrowSchemaRelease(schema->children[i]);
+ free(schema->children[i]);
+ }
+ free((void*)schema->format);
+ if (strlen(schema->name) > 0) {
+ free((void*)schema->name);
+ }
+ ArrowSchemaMarkReleased(schema);
+}
+
+void test_primitive(struct ArrowSchema* schema, const char* fmt) {
+ *schema = (struct ArrowSchema) {
+ // Type description
+ .format = fmt,
+ .name = "",
+ .metadata = NULL,
+ .flags = 0,
+ .n_children = 0,
+ .children = NULL,
+ .dictionary = NULL,
+ // bookkeeping
+ .release = &release_primitive,
+ };
+}
+
+// Since test_lists et al. allocate an entirely array of ArrowSchema pointers,
+// need to expose a function to free it.
+void free_malloced_schemas(struct ArrowSchema** schemas) {
+ free(schemas);
+}
+
+struct ArrowSchema** test_lists(const char** fmts, const char** names, const
int n) {
+ struct ArrowSchema** schemas = malloc(sizeof(struct ArrowSchema*)*n);
+ for (int i = 0; i < n; ++i) {
+ schemas[i] = malloc(sizeof(struct ArrowSchema));
+ *schemas[i] = (struct ArrowSchema) {
+ .format = fmts[i],
+ .name = names[i],
+ .metadata = NULL,
+ .flags = 0,
+ .children = NULL,
+ .n_children = 0,
+ .dictionary = NULL,
+ .release = &release_nested_dynamic,
+ };
+ if (i != 0) {
+ schemas[i-1]->n_children = 1;
+ schemas[i-1]->children = &schemas[i];
+ }
+ }
+ return schemas;
+}
+
+struct ArrowSchema** fill_structs(const char** fmts, const char** names,
int64_t* flags, const int n) {
+ struct ArrowSchema** schemas = malloc(sizeof(struct ArrowSchema*)*n);
+ for (int i = 0; i < n; ++i) {
+ schemas[i] = malloc(sizeof(struct ArrowSchema));
+ *schemas[i] = (struct ArrowSchema) {
+ .format = fmts[i],
+ .name = names[i],
+ .metadata = NULL,
+ .flags = flags[i],
+ .children = NULL,
+ .n_children = 0,
+ .dictionary = NULL,
+ .release = &release_nested_dynamic,
+ };
+ }
+
+ schemas[0]->children = &schemas[1];
+ schemas[0]->n_children = n-1;
+ return schemas;
+}
+
+struct ArrowSchema** test_struct(const char** fmts, const char** names,
int64_t* flags, const int n) {
+ struct ArrowSchema** schemas = fill_structs(fmts, names, flags, n);
+
+ if (is_little_endian() == 1) {
+ schemas[n-1]->metadata = kEncodedMeta2LE;
+ } else {
+ schemas[n-1]->metadata = kEncodedMeta2BE;
+ }
+
+ return schemas;
+}
+
+struct ArrowSchema** test_schema(const char** fmts, const char** names,
int64_t* flags, const int n) {
+ struct ArrowSchema** schemas = fill_structs(fmts, names, flags, n);
+
+ if (is_little_endian() == 1) {
+ schemas[0]->metadata = kEncodedMeta2LE;
+ schemas[n-1]->metadata = kEncodedMeta1LE;
+ } else {
+ schemas[0]->metadata = kEncodedMeta2BE;
+ schemas[n-1]->metadata = kEncodedMeta1BE;
+ }
+ return schemas;
+}
+
+struct ArrowSchema** test_map(const char** fmts, const char** names, int64_t*
flags, const int n) {
+ struct ArrowSchema** schemas = malloc(sizeof(struct ArrowSchema*)*n);
+ for (int i = 0; i < n; ++i) {
+ schemas[i] = malloc(sizeof(struct ArrowSchema));
+ *schemas[i] = (struct ArrowSchema) {
+ .format = fmts[i],
+ .name = names[i],
+ .metadata = NULL,
+ .flags = flags[i],
+ .children = NULL,
+ .n_children = 0,
+ .dictionary = NULL,
+ .release = &release_nested_dynamic,
+ };
+ }
+
+ schemas[0]->n_children = 1;
+ schemas[0]->children = &schemas[1];
+ schemas[1]->n_children = n-2;
+ schemas[1]->children = &schemas[2];
+
+ return schemas;
+}
+
+struct streamcounter {
+ int n;
+ int max;
+};
+
+static int stream_schema(struct ArrowArrayStream* st, struct ArrowSchema* out)
{
+ out->children = malloc(sizeof(struct ArrowSchema*)*2);
+ out->n_children = 2;
+
+ out->children[0] = malloc(sizeof(struct ArrowSchema));
+ *out->children[0] = (struct ArrowSchema) {
+ .format = "i",
+ .name = "a",
+ .metadata = NULL,
+ .flags = ARROW_FLAG_NULLABLE,
+ .children = NULL,
+ .n_children = 0,
+ .dictionary = NULL,
+ .release = &release_nested_static,
+ };
+
+ out->children[1] = malloc(sizeof(struct ArrowSchema));
+ *out->children[1] = (struct ArrowSchema) {
+ .format = "u",
+ .name = "b",
+ .metadata = NULL,
+ .flags = ARROW_FLAG_NULLABLE,
+ .children = NULL,
+ .n_children = 0,
+ .dictionary = NULL,
+ .release = &release_nested_static,
+ };
+
+ out->format = "+s";
+ out->release = &release_nested_static;
+
+ return 0;
+}
+
+static void release_stream(struct ArrowArrayStream* st) {
+ free(st->private_data);
+ ArrowArrayStreamMarkReleased(st);
+}
+
+static void release_the_array(struct ArrowArray* out) {
+ for (int i = 0; i < out->n_children; ++i) {
+ ArrowArrayRelease(out->children[i]);
+ }
+ free((void*)out->children);
+ free(out->buffers);
+ out->release = NULL;
+}
+
+void export_int32_array(const int32_t*, int64_t, struct ArrowArray*);
+
+static void release_str_array(struct ArrowArray* array) {
+ assert(array->n_buffers == 3);
+ free((void*) array->buffers[1]);
+ free((void*) array->buffers[2]);
+ free(array->buffers);
+ array->release = NULL;
+}
+
+void export_str_array(const char* data, const int32_t* offsets, int64_t
nitems, struct ArrowArray* out) {
+ *out = (struct ArrowArray) {
+ .length = nitems,
+ .offset = 0,
+ .null_count = 0,
+ .n_buffers = 3,
+ .n_children = 0,
+ .children = NULL,
+ .dictionary = NULL,
+ // bookkeeping
+ .release = &release_str_array
+ };
+
+ out->buffers = (const void**)malloc(sizeof(void*) * out->n_buffers);
+ assert(out->buffers != NULL);
+ out->buffers[0] = NULL;
+ out->buffers[1] = offsets;
+ out->buffers[2] = data;
+}
+
+static int next_record(struct ArrowArrayStream* st, struct ArrowArray* out) {
+ struct streamcounter* cnter = (struct streamcounter*)(st->private_data);
+ if (cnter->n == cnter->max) {
+ ArrowArrayMarkReleased(out);
+ return 0;
+ }
+
+ cnter->n++;
+
+ *out = (struct ArrowArray) {
+ .offset = 0,
+ .dictionary = NULL,
+ .length = 3,
+ .null_count = 0,
+ .buffers = (const void**)malloc(sizeof(void*)),
+ .n_children = 2,
+ .n_buffers = 1,
+ .release = &release_the_array
+ };
+
+ out->buffers[0] = NULL;
+ out->children = (struct ArrowArray**)malloc(sizeof(struct ArrowArray*)*2);
+ int32_t* intdata = malloc(sizeof(int32_t)*3);
+ for (int i = 0; i < 3; ++i) {
+ intdata[i] = cnter->n * (i+1);
+ }
+
+ out->children[0] = malloc(sizeof(struct ArrowArray));
+ export_int32_array(intdata, 3, out->children[0]);
+ out->children[1] = malloc(sizeof(struct ArrowArray));
+ char* strdata = strdup("foobarbaz");
+ int32_t* offsets = malloc(sizeof(int32_t)*4);
+ offsets[0] = 0;
+ offsets[1] = 3;
+ offsets[2] = 6;
+ offsets[3] = 9;
+ export_str_array(strdata, offsets, 3, out->children[1]);
+
+ return 0;
+}
+
+void setup_array_stream_test(const int n_batches, struct ArrowArrayStream*
out) {
+ struct streamcounter* cnt = malloc(sizeof(struct streamcounter));
+ cnt->max = n_batches;
+ cnt->n = 0;
+
+ out->get_next = &next_record;
+ out->get_schema = &stream_schema;
+ out->release = &release_stream;
+ out->private_data = cnt;
+}
diff --git a/go/arrow/cdata/cdata_test.go b/go/arrow/cdata/cdata_test.go
new file mode 100644
index 0000000..e5e43de
--- /dev/null
+++ b/go/arrow/cdata/cdata_test.go
@@ -0,0 +1,621 @@
+// 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.
+
+// +build cgo
+// +build test
+
+// use test tag so that we only run these tests when the "test" tag is present
+// so that the .c and other framework infrastructure is only compiled in during
+// testing, and the .c files and symbols are not present in release builds.
+
+package cdata
+
+import (
+ "io"
+ "runtime"
+ "testing"
+ "time"
+ "unsafe"
+
+ "github.com/apache/arrow/go/arrow"
+ "github.com/apache/arrow/go/arrow/array"
+ "github.com/apache/arrow/go/arrow/decimal128"
+ "github.com/apache/arrow/go/arrow/memory"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSchemaExport(t *testing.T) {
+ sc := exportInt32TypeSchema()
+ f, err := importSchema(&sc)
+ assert.NoError(t, err)
+
+ keys, _ := getMetadataKeys()
+ vals, _ := getMetadataValues()
+
+ assert.Equal(t, arrow.PrimitiveTypes.Int32, f.Type)
+ assert.Equal(t, keys, f.Metadata.Keys())
+ assert.Equal(t, vals, f.Metadata.Values())
+
+ // schema was released when importing
+ assert.True(t, schemaIsReleased(&sc))
+}
+
+func TestSimpleArrayExport(t *testing.T) {
+ assert.False(t, test1IsReleased())
+
+ testarr := exportInt32Array()
+ arr, err := ImportCArrayWithType(&testarr, arrow.PrimitiveTypes.Int32)
+ assert.NoError(t, err)
+
+ assert.False(t, test1IsReleased())
+ assert.True(t, isReleased(&testarr))
+
+ arr.Release()
+ runtime.GC()
+ assert.Eventually(t, test1IsReleased, 1*time.Second,
10*time.Millisecond)
+}
+
+func TestSimpleArrayAndSchema(t *testing.T) {
+ sc := exportInt32TypeSchema()
+ testarr := exportInt32Array()
+
+ // grab address of the buffer we stuck into the ArrowArray object
+ buflist := (*[2]unsafe.Pointer)(unsafe.Pointer(testarr.buffers))
+ origvals := (*[10]int32)(unsafe.Pointer(buflist[1]))
+
+ fld, arr, err := ImportCArray(&testarr, &sc)
+ assert.NoError(t, err)
+ assert.Equal(t, arrow.PrimitiveTypes.Int32, fld.Type)
+ assert.EqualValues(t, 10, arr.Len())
+
+ // verify that the address is the same of the first integer for the
+ // slice that is being used by the array.Interface and the original
buffer
+ vals := arr.(*array.Int32).Int32Values()
+ assert.Same(t, &vals[0], &origvals[0])
+
+ // and that the values are correct
+ for i, v := range vals {
+ assert.Equal(t, int32(i+1), v)
+ }
+}
+
+func TestPrimitiveSchemas(t *testing.T) {
+ tests := []struct {
+ typ arrow.DataType
+ fmt string
+ }{
+ {arrow.PrimitiveTypes.Int8, "c"},
+ {arrow.PrimitiveTypes.Int16, "s"},
+ {arrow.PrimitiveTypes.Int32, "i"},
+ {arrow.PrimitiveTypes.Int64, "l"},
+ {arrow.PrimitiveTypes.Uint8, "C"},
+ {arrow.PrimitiveTypes.Uint16, "S"},
+ {arrow.PrimitiveTypes.Uint32, "I"},
+ {arrow.PrimitiveTypes.Uint64, "L"},
+ {arrow.FixedWidthTypes.Boolean, "b"},
+ {arrow.Null, "n"},
+ {arrow.FixedWidthTypes.Float16, "e"},
+ {arrow.PrimitiveTypes.Float32, "f"},
+ {arrow.PrimitiveTypes.Float64, "g"},
+ {&arrow.FixedSizeBinaryType{ByteWidth: 3}, "w:3"},
+ {arrow.BinaryTypes.Binary, "z"},
+ {arrow.BinaryTypes.String, "u"},
+ {&arrow.Decimal128Type{Precision: 16, Scale: 4}, "d:16,4"},
+ {&arrow.Decimal128Type{Precision: 15, Scale: 0}, "d:15,0"},
+ {&arrow.Decimal128Type{Precision: 15, Scale: -4}, "d:15,-4"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.typ.Name(), func(t *testing.T) {
+ sc := testPrimitive(tt.fmt)
+
+ f, err := ImportCArrowField(&sc)
+ assert.NoError(t, err)
+
+ assert.True(t, arrow.TypeEqual(tt.typ, f.Type))
+
+ assert.True(t, schemaIsReleased(&sc))
+ })
+ }
+}
+
+func TestImportTemporalSchema(t *testing.T) {
+ tests := []struct {
+ typ arrow.DataType
+ fmt string
+ }{
+ {arrow.FixedWidthTypes.Date32, "tdD"},
+ {arrow.FixedWidthTypes.Date64, "tdm"},
+ {arrow.FixedWidthTypes.Time32s, "tts"},
+ {arrow.FixedWidthTypes.Time32ms, "ttm"},
+ {arrow.FixedWidthTypes.Time64us, "ttu"},
+ {arrow.FixedWidthTypes.Time64ns, "ttn"},
+ {arrow.FixedWidthTypes.Duration_s, "tDs"},
+ {arrow.FixedWidthTypes.Duration_ms, "tDm"},
+ {arrow.FixedWidthTypes.Duration_us, "tDu"},
+ {arrow.FixedWidthTypes.Duration_ns, "tDn"},
+ {arrow.FixedWidthTypes.MonthInterval, "tiM"},
+ {arrow.FixedWidthTypes.DayTimeInterval, "tiD"},
+ {arrow.FixedWidthTypes.Timestamp_s, "tss:"},
+ {&arrow.TimestampType{Unit: arrow.Second, TimeZone:
"Europe/Paris"}, "tss:Europe/Paris"},
+ {arrow.FixedWidthTypes.Timestamp_ms, "tsm:"},
+ {&arrow.TimestampType{Unit: arrow.Millisecond, TimeZone:
"Europe/Paris"}, "tsm:Europe/Paris"},
+ {arrow.FixedWidthTypes.Timestamp_us, "tsu:"},
+ {&arrow.TimestampType{Unit: arrow.Microsecond, TimeZone:
"Europe/Paris"}, "tsu:Europe/Paris"},
+ {arrow.FixedWidthTypes.Timestamp_ns, "tsn:"},
+ {&arrow.TimestampType{Unit: arrow.Nanosecond, TimeZone:
"Europe/Paris"}, "tsn:Europe/Paris"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.typ.Name(), func(t *testing.T) {
+ sc := testPrimitive(tt.fmt)
+
+ f, err := ImportCArrowField(&sc)
+ assert.NoError(t, err)
+
+ assert.True(t, arrow.TypeEqual(tt.typ, f.Type))
+
+ assert.True(t, schemaIsReleased(&sc))
+ })
+ }
+}
+
+func TestListSchemas(t *testing.T) {
+ tests := []struct {
+ typ arrow.DataType
+ fmts []string
+ names []string
+ }{
+ {arrow.ListOf(arrow.PrimitiveTypes.Int8), []string{"+l", "c"},
[]string{"", "item"}},
+ {arrow.FixedSizeListOf(2, arrow.PrimitiveTypes.Int64),
[]string{"+w:2", "l"}, []string{"", "item"}},
+ {arrow.ListOf(arrow.ListOf(arrow.PrimitiveTypes.Int32)),
[]string{"+l", "+l", "i"}, []string{"", "item", "item"}},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.typ.Name(), func(t *testing.T) {
+ sc := testNested(tt.fmts, tt.names)
+ defer freeMallocedSchemas(sc)
+
+ top := (*[1]*CArrowSchema)(unsafe.Pointer(sc))[0]
+ f, err := ImportCArrowField(top)
+ assert.NoError(t, err)
+
+ assert.True(t, arrow.TypeEqual(tt.typ, f.Type))
+
+ assert.True(t, schemaIsReleased(top))
+ })
+ }
+}
+
+func TestStructSchemas(t *testing.T) {
+ tests := []struct {
+ typ arrow.DataType
+ fmts []string
+ names []string
+ flags []int64
+ }{
+ {arrow.StructOf(
+ arrow.Field{Name: "a", Type: arrow.PrimitiveTypes.Int8,
Nullable: true},
+ arrow.Field{Name: "b", Type: arrow.BinaryTypes.String,
Nullable: true, Metadata: metadata2},
+ ), []string{"+s", "c", "u"}, []string{"", "a", "b"},
[]int64{flagIsNullable, flagIsNullable, flagIsNullable}},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.typ.Name(), func(t *testing.T) {
+ sc := testStruct(tt.fmts, tt.names, tt.flags)
+ defer freeMallocedSchemas(sc)
+
+ top := (*[1]*CArrowSchema)(unsafe.Pointer(sc))[0]
+ f, err := ImportCArrowField(top)
+ assert.NoError(t, err)
+
+ assert.True(t, arrow.TypeEqual(tt.typ, f.Type))
+
+ assert.True(t, schemaIsReleased(top))
+ })
+ }
+}
+
+func TestMapSchemas(t *testing.T) {
+ tests := []struct {
+ typ *arrow.MapType
+ keysSorted bool
+ fmts []string
+ names []string
+ flags []int64
+ }{
+ {arrow.MapOf(arrow.PrimitiveTypes.Int8,
arrow.BinaryTypes.String), false, []string{"+m", "+s", "c", "u"}, []string{"",
"entries", "key", "value"}, []int64{flagIsNullable, 0, 0, flagIsNullable}},
+ {arrow.MapOf(arrow.PrimitiveTypes.Int8,
arrow.BinaryTypes.String), true, []string{"+m", "+s", "c", "u"}, []string{"",
"entries", "key", "value"}, []int64{flagIsNullable | flagMapKeysSorted, 0, 0,
flagIsNullable}},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.typ.Name(), func(t *testing.T) {
+ sc := testMap(tt.fmts, tt.names, tt.flags)
+ defer freeMallocedSchemas(sc)
+
+ top := (*[1]*CArrowSchema)(unsafe.Pointer(sc))[0]
+ f, err := ImportCArrowField(top)
+ assert.NoError(t, err)
+
+ tt.typ.KeysSorted = tt.keysSorted
+ assert.True(t, arrow.TypeEqual(tt.typ, f.Type))
+
+ assert.True(t, schemaIsReleased(top))
+ })
+ }
+}
+
+func TestSchema(t *testing.T) {
+ // schema is exported as an equivalent struct type (+ top-level
metadata)
+ sc := arrow.NewSchema([]arrow.Field{
+ {Name: "nulls", Type: arrow.Null, Nullable: false},
+ {Name: "values", Type: arrow.PrimitiveTypes.Int64, Nullable:
true, Metadata: metadata1},
+ }, &metadata2)
+
+ cst := testSchema([]string{"+s", "n", "l"}, []string{"", "nulls",
"values"}, []int64{0, 0, flagIsNullable})
+ defer freeMallocedSchemas(cst)
+
+ top := (*[1]*CArrowSchema)(unsafe.Pointer(cst))[0]
+ out, err := ImportCArrowSchema(top)
+ assert.NoError(t, err)
+
+ assert.True(t, sc.Equal(out))
+ assert.True(t, sc.Metadata().Equal(out.Metadata()))
+
+ assert.True(t, schemaIsReleased(top))
+}
+
+func createTestInt8Arr() array.Interface {
+ bld := array.NewInt8Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]int8{1, 2, 0, -3}, []bool{true, true, false, true})
+ return bld.NewInt8Array()
+}
+
+func createTestInt16Arr() array.Interface {
+ bld := array.NewInt16Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]int16{1, 2, -3}, []bool{true, true, true})
+ return bld.NewInt16Array()
+}
+
+func createTestInt32Arr() array.Interface {
+ bld := array.NewInt32Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]int32{1, 2, 0, -3}, []bool{true, true, false, true})
+ return bld.NewInt32Array()
+}
+
+func createTestInt64Arr() array.Interface {
+ bld := array.NewInt64Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]int64{1, 2, -3}, []bool{true, true, true})
+ return bld.NewInt64Array()
+}
+
+func createTestUint8Arr() array.Interface {
+ bld := array.NewUint8Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]uint8{1, 2, 0, 3}, []bool{true, true, false, true})
+ return bld.NewUint8Array()
+}
+
+func createTestUint16Arr() array.Interface {
+ bld := array.NewUint16Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]uint16{1, 2, 3}, []bool{true, true, true})
+ return bld.NewUint16Array()
+}
+
+func createTestUint32Arr() array.Interface {
+ bld := array.NewUint32Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]uint32{1, 2, 0, 3}, []bool{true, true, false, true})
+ return bld.NewUint32Array()
+}
+
+func createTestUint64Arr() array.Interface {
+ bld := array.NewUint64Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]uint64{1, 2, 3}, []bool{true, true, true})
+ return bld.NewUint64Array()
+}
+
+func createTestBoolArr() array.Interface {
+ bld := array.NewBooleanBuilder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]bool{true, false, false}, []bool{true, true, false})
+ return bld.NewBooleanArray()
+}
+
+func createTestNullArr() array.Interface {
+ return array.NewNull(2)
+}
+
+func createTestFloat32Arr() array.Interface {
+ bld := array.NewFloat32Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]float32{1.5, 0}, []bool{true, false})
+ return bld.NewFloat32Array()
+}
+
+func createTestFloat64Arr() array.Interface {
+ bld := array.NewFloat64Builder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]float64{1.5, 0}, []bool{true, false})
+ return bld.NewFloat64Array()
+}
+
+func createTestFSBArr() array.Interface {
+ bld := array.NewFixedSizeBinaryBuilder(memory.DefaultAllocator,
&arrow.FixedSizeBinaryType{ByteWidth: 3})
+ defer bld.Release()
+
+ bld.AppendValues([][]byte{[]byte("foo"), []byte("bar"), nil},
[]bool{true, true, false})
+ return bld.NewFixedSizeBinaryArray()
+}
+
+func createTestBinaryArr() array.Interface {
+ bld := array.NewBinaryBuilder(memory.DefaultAllocator,
arrow.BinaryTypes.Binary)
+ defer bld.Release()
+
+ bld.AppendValues([][]byte{[]byte("foo"), []byte("bar"), nil},
[]bool{true, true, false})
+ return bld.NewBinaryArray()
+}
+
+func createTestStrArr() array.Interface {
+ bld := array.NewStringBuilder(memory.DefaultAllocator)
+ defer bld.Release()
+
+ bld.AppendValues([]string{"foo", "bar", ""}, []bool{true, true, false})
+ return bld.NewStringArray()
+}
+
+func createTestDecimalArr() array.Interface {
+ bld := array.NewDecimal128Builder(memory.DefaultAllocator,
&arrow.Decimal128Type{Precision: 16, Scale: 4})
+ defer bld.Release()
+
+ bld.AppendValues([]decimal128.Num{decimal128.FromU64(12345670),
decimal128.FromU64(0)}, []bool{true, false})
+ return bld.NewDecimal128Array()
+}
+
+func TestPrimitiveArrs(t *testing.T) {
+ tests := []struct {
+ name string
+ fn func() array.Interface
+ }{
+ {"int8", createTestInt8Arr},
+ {"uint8", createTestUint8Arr},
+ {"int16", createTestInt16Arr},
+ {"uint16", createTestUint16Arr},
+ {"int32", createTestInt32Arr},
+ {"uint32", createTestUint32Arr},
+ {"int64", createTestInt64Arr},
+ {"uint64", createTestUint64Arr},
+ {"bool", createTestBoolArr},
+ {"null", createTestNullArr},
+ {"float32", createTestFloat32Arr},
+ {"float64", createTestFloat64Arr},
+ {"fixed size binary", createTestFSBArr},
+ {"binary", createTestBinaryArr},
+ {"utf8", createTestStrArr},
+ {"decimal128", createTestDecimalArr},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ arr := tt.fn()
+ defer arr.Release()
+
+ carr := createCArr(arr)
+ defer freeTestArr(carr)
+
+ imported, err := ImportCArrayWithType(carr,
arr.DataType())
+ assert.NoError(t, err)
+ assert.True(t, array.ArrayEqual(arr, imported))
+ assert.True(t, isReleased(carr))
+
+ imported.Release()
+ })
+ }
+}
+
+func TestPrimitiveSliced(t *testing.T) {
+ arr := createTestInt16Arr()
+ defer arr.Release()
+
+ sl := array.NewSlice(arr, 1, 2)
+ defer sl.Release()
+
+ carr := createCArr(sl)
+ defer freeTestArr(carr)
+
+ imported, err := ImportCArrayWithType(carr, arr.DataType())
+ assert.NoError(t, err)
+ assert.True(t, array.ArrayEqual(sl, imported))
+ assert.True(t, array.ArraySliceEqual(arr, 1, 2, imported, 0,
int64(imported.Len())))
+ assert.True(t, isReleased(carr))
+
+ imported.Release()
+}
+
+func createTestListArr() array.Interface {
+ bld := array.NewListBuilder(memory.DefaultAllocator,
arrow.PrimitiveTypes.Int8)
+ defer bld.Release()
+
+ vb := bld.ValueBuilder().(*array.Int8Builder)
+
+ bld.Append(true)
+ vb.AppendValues([]int8{1, 2}, []bool{true, true})
+
+ bld.Append(true)
+ vb.AppendValues([]int8{3, 0}, []bool{true, false})
+
+ bld.AppendNull()
+
+ return bld.NewArray()
+}
+
+func createTestFixedSizeList() array.Interface {
+ bld := array.NewFixedSizeListBuilder(memory.DefaultAllocator, 2,
arrow.PrimitiveTypes.Int64)
+ defer bld.Release()
+
+ vb := bld.ValueBuilder().(*array.Int64Builder)
+
+ bld.Append(true)
+ vb.AppendValues([]int64{1, 2}, []bool{true, true})
+
+ bld.Append(true)
+ vb.AppendValues([]int64{3, 0}, []bool{true, false})
+
+ bld.AppendNull()
+ return bld.NewArray()
+}
+
+func createTestStructArr() array.Interface {
+ bld := array.NewStructBuilder(memory.DefaultAllocator, arrow.StructOf(
+ arrow.Field{Name: "a", Type: arrow.PrimitiveTypes.Int8,
Nullable: true},
+ arrow.Field{Name: "b", Type: arrow.BinaryTypes.String,
Nullable: true},
+ ))
+ defer bld.Release()
+
+ f1bld := bld.FieldBuilder(0).(*array.Int8Builder)
+ f2bld := bld.FieldBuilder(1).(*array.StringBuilder)
+
+ bld.Append(true)
+ f1bld.Append(1)
+ f2bld.Append("foo")
+
+ bld.Append(true)
+ f1bld.Append(2)
+ f2bld.AppendNull()
+
+ return bld.NewArray()
+}
+
+func createTestMapArr() array.Interface {
+ bld := array.NewMapBuilder(memory.DefaultAllocator,
arrow.PrimitiveTypes.Int8, arrow.BinaryTypes.String, false)
+ defer bld.Release()
+
+ kb := bld.KeyBuilder().(*array.Int8Builder)
+ vb := bld.ItemBuilder().(*array.StringBuilder)
+
+ bld.Append(true)
+ kb.Append(1)
+ vb.Append("foo")
+ kb.Append(2)
+ vb.AppendNull()
+
+ bld.Append(true)
+ kb.Append(3)
+ vb.Append("bar")
+
+ return bld.NewArray()
+}
+
+func TestNestedArrays(t *testing.T) {
+ tests := []struct {
+ name string
+ fn func() array.Interface
+ }{
+ {"list", createTestListArr},
+ {"fixed size list", createTestFixedSizeList},
+ {"struct", createTestStructArr},
+ {"map", createTestMapArr},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ arr := tt.fn()
+ defer arr.Release()
+
+ carr := createCArr(arr)
+ defer freeTestArr(carr)
+
+ imported, err := ImportCArrayWithType(carr,
arr.DataType())
+ assert.NoError(t, err)
+ assert.True(t, array.ArrayEqual(arr, imported))
+ assert.True(t, isReleased(carr))
+
+ imported.Release()
+ })
+ }
+}
+
+func TestRecordBatch(t *testing.T) {
+ arr := createTestStructArr()
+ defer arr.Release()
+
+ carr := createCArr(arr)
+ defer freeTestArr(carr)
+
+ sc := testStruct([]string{"+s", "c", "u"}, []string{"", "a", "b"},
[]int64{0, flagIsNullable, flagIsNullable})
+ defer freeMallocedSchemas(sc)
+
+ top := (*[1]*CArrowSchema)(unsafe.Pointer(sc))[0]
+ rb, err := ImportCRecordBatch(carr, top)
+ assert.NoError(t, err)
+ defer rb.Release()
+
+ assert.EqualValues(t, 2, rb.NumCols())
+ rbschema := rb.Schema()
+ assert.Equal(t, "a", rbschema.Field(0).Name)
+ assert.Equal(t, "b", rbschema.Field(1).Name)
+
+ rec := array.NewRecord(rbschema,
[]array.Interface{arr.(*array.Struct).Field(0), arr.(*array.Struct).Field(1)},
-1)
+ defer rec.Release()
+
+ assert.True(t, array.RecordEqual(rb, rec))
+}
+
+func TestRecordReaderStream(t *testing.T) {
+ stream := arrayStreamTest()
+ defer releaseStream(stream)
+
+ rdr := ImportCArrayStream(stream, nil)
+ i := 0
+ for {
+ rec, err := rdr.Read()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ assert.NoError(t, err)
+ }
+ defer rec.Release()
+
+ assert.EqualValues(t, 2, rec.NumCols())
+ assert.Equal(t, "a", rec.ColumnName(0))
+ assert.Equal(t, "b", rec.ColumnName(1))
+ i++
+ for j := 0; j < int(rec.NumRows()); j++ {
+ assert.Equal(t, int32((j+1)*i),
rec.Column(0).(*array.Int32).Value(j))
+ }
+ assert.Equal(t, "foo", rec.Column(1).(*array.String).Value(0))
+ assert.Equal(t, "bar", rec.Column(1).(*array.String).Value(1))
+ assert.Equal(t, "baz", rec.Column(1).(*array.String).Value(2))
+ }
+}
diff --git a/go/arrow/cdata/cdata_test_framework.go
b/go/arrow/cdata/cdata_test_framework.go
new file mode 100644
index 0000000..c9ccd74
--- /dev/null
+++ b/go/arrow/cdata/cdata_test_framework.go
@@ -0,0 +1,248 @@
+// 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.
+
+// +build test
+
+package cdata
+
+// #include <stdlib.h>
+// #include <stdint.h>
+// #include "arrow/c/abi.h"
+// #include "arrow/c/helpers.h"
+//
+// void setup_array_stream_test(const int n_batches, struct ArrowArrayStream*
out);
+// struct ArrowArray* get_test_arr() { return (struct
ArrowArray*)(malloc(sizeof(struct ArrowArray))); }
+// struct ArrowArrayStream* get_test_stream() { return (struct
ArrowArrayStream*)malloc(sizeof(struct ArrowArrayStream)); }
+//
+// void release_test_arr(struct ArrowArray* arr) {
+// for (int i = 0; i < arr->n_buffers; ++i) {
+// free((void*)arr->buffers[i]);
+// }
+// ArrowArrayMarkReleased(arr);
+// }
+//
+// int32_t* get_data() {
+// int32_t* data = malloc(sizeof(int32_t)*10);
+// for (int i = 0; i < 10; ++i) { data[i] = i+1; }
+// return data;
+// }
+// void export_int32_type(struct ArrowSchema* schema);
+// void export_int32_array(const int32_t*, int64_t, struct ArrowArray*);
+// int test1_is_released();
+// void test_primitive(struct ArrowSchema* schema, const char* fmt);
+// void free_malloced_schemas(struct ArrowSchema**);
+// struct ArrowSchema** test_lists(const char** fmts, const char** names,
const int n);
+// struct ArrowSchema** test_struct(const char** fmts, const char** names,
int64_t* flags, const int n);
+// struct ArrowSchema** test_map(const char** fmts, const char** names,
int64_t* flags, const int n);
+// struct ArrowSchema** test_schema(const char** fmts, const char** names,
int64_t* flags, const int n);
+import "C"
+import (
+ "unsafe"
+
+ "github.com/apache/arrow/go/arrow"
+ "github.com/apache/arrow/go/arrow/array"
+)
+
+const (
+ flagIsNullable = C.ARROW_FLAG_NULLABLE
+ flagMapKeysSorted = C.ARROW_FLAG_MAP_KEYS_SORTED
+)
+
+var (
+ metadata1 = arrow.NewMetadata([]string{"key1", "key2"}, []string{"",
"bar"})
+ metadata2 = arrow.NewMetadata([]string{"key"}, []string{"abcde"})
+)
+
+func exportInt32TypeSchema() CArrowSchema {
+ var s CArrowSchema
+ C.export_int32_type(&s)
+ return s
+}
+
+func releaseStream(s *CArrowArrayStream) {
+ C.ArrowArrayStreamRelease(s)
+}
+
+func releaseSchema(s *CArrowSchema) {
+ C.ArrowSchemaRelease(s)
+}
+
+func schemaIsReleased(s *CArrowSchema) bool {
+ return C.ArrowSchemaIsReleased(s) == 1
+}
+
+func getMetadataKeys() ([]string, []string) {
+ return []string{"key1", "key2"}, []string{"key"}
+}
+
+func getMetadataValues() ([]string, []string) {
+ return []string{"", "bar"}, []string{"abcde"}
+}
+
+func exportInt32Array() CArrowArray {
+ var arr CArrowArray
+ C.export_int32_array(C.get_data(), C.int64_t(10), &arr)
+ return arr
+}
+
+func isReleased(arr *CArrowArray) bool {
+ return C.ArrowArrayIsReleased(arr) == 1
+}
+
+func test1IsReleased() bool {
+ return C.test1_is_released() == 1
+}
+
+func testPrimitive(fmtstr string) CArrowSchema {
+ var s CArrowSchema
+ fmt := C.CString(fmtstr)
+ C.test_primitive(&s, fmt)
+ return s
+}
+
+func freeMallocedSchemas(schemas **CArrowSchema) {
+ C.free_malloced_schemas(schemas)
+}
+
+func testNested(fmts, names []string) **CArrowSchema {
+ if len(fmts) != len(names) {
+ panic("testing nested lists must have same size fmts and names")
+ }
+ cfmts := make([]*C.char, len(fmts))
+ cnames := make([]*C.char, len(names))
+
+ for i := range fmts {
+ cfmts[i] = C.CString(fmts[i])
+ cnames[i] = C.CString(names[i])
+ }
+
+ return C.test_lists((**C.char)(unsafe.Pointer(&cfmts[0])),
(**C.char)(unsafe.Pointer(&cnames[0])), C.int(len(fmts)))
+}
+
+func testStruct(fmts, names []string, flags []int64) **CArrowSchema {
+ if len(fmts) != len(names) || len(names) != len(flags) {
+ panic("testing structs must all have the same size slices in
args")
+ }
+
+ cfmts := make([]*C.char, len(fmts))
+ cnames := make([]*C.char, len(names))
+ cflags := make([]C.int64_t, len(flags))
+
+ for i := range fmts {
+ cfmts[i] = C.CString(fmts[i])
+ cnames[i] = C.CString(names[i])
+ cflags[i] = C.int64_t(flags[i])
+ }
+
+ return C.test_struct((**C.char)(unsafe.Pointer(&cfmts[0])),
(**C.char)(unsafe.Pointer(&cnames[0])),
(*C.int64_t)(unsafe.Pointer(&cflags[0])), C.int(len(fmts)))
+}
+
+func testMap(fmts, names []string, flags []int64) **CArrowSchema {
+ if len(fmts) != len(names) || len(names) != len(flags) {
+ panic("testing maps must all have the same size slices in args")
+ }
+
+ cfmts := make([]*C.char, len(fmts))
+ cnames := make([]*C.char, len(names))
+ cflags := make([]C.int64_t, len(flags))
+
+ for i := range fmts {
+ cfmts[i] = C.CString(fmts[i])
+ cnames[i] = C.CString(names[i])
+ cflags[i] = C.int64_t(flags[i])
+ }
+
+ return C.test_map((**C.char)(unsafe.Pointer(&cfmts[0])),
(**C.char)(unsafe.Pointer(&cnames[0])),
(*C.int64_t)(unsafe.Pointer(&cflags[0])), C.int(len(fmts)))
+}
+
+func testSchema(fmts, names []string, flags []int64) **CArrowSchema {
+ if len(fmts) != len(names) || len(names) != len(flags) {
+ panic("testing structs must all have the same size slices in
args")
+ }
+
+ cfmts := make([]*C.char, len(fmts))
+ cnames := make([]*C.char, len(names))
+ cflags := make([]C.int64_t, len(flags))
+
+ for i := range fmts {
+ cfmts[i] = C.CString(fmts[i])
+ cnames[i] = C.CString(names[i])
+ cflags[i] = C.int64_t(flags[i])
+ }
+
+ return C.test_schema((**C.char)(unsafe.Pointer(&cfmts[0])),
(**C.char)(unsafe.Pointer(&cnames[0])),
(*C.int64_t)(unsafe.Pointer(&cflags[0])), C.int(len(fmts)))
+}
+
+func freeTestArr(carr *CArrowArray) {
+ C.free(unsafe.Pointer(carr))
+}
+
+func createCArr(arr array.Interface) *CArrowArray {
+ var (
+ carr = C.get_test_arr()
+ children = (**CArrowArray)(nil)
+ nchildren = C.int64_t(0)
+ )
+
+ switch arr := arr.(type) {
+ case *array.List:
+ clist := []*CArrowArray{createCArr(arr.ListValues())}
+ children = (**CArrowArray)(unsafe.Pointer(&clist[0]))
+ nchildren += 1
+ case *array.FixedSizeList:
+ clist := []*CArrowArray{createCArr(arr.ListValues())}
+ children = (**CArrowArray)(unsafe.Pointer(&clist[0]))
+ nchildren += 1
+ case *array.Struct:
+ clist := []*CArrowArray{}
+ for i := 0; i < arr.NumField(); i++ {
+ clist = append(clist, createCArr(arr.Field(i)))
+ nchildren += 1
+ }
+ children = (**CArrowArray)(unsafe.Pointer(&clist[0]))
+ case *array.Map:
+ clist := []*CArrowArray{createCArr(arr.ListValues())}
+ children = (**CArrowArray)(unsafe.Pointer(&clist[0]))
+ nchildren += 1
+ }
+
+ carr.children = children
+ carr.n_children = nchildren
+ carr.dictionary = nil
+ carr.length = C.int64_t(arr.Len())
+ carr.null_count = C.int64_t(arr.NullN())
+ carr.offset = C.int64_t(arr.Data().Offset())
+ buffers := arr.Data().Buffers()
+ cbuf := []unsafe.Pointer{}
+ for _, b := range buffers {
+ if b != nil {
+ cbuf = append(cbuf, C.CBytes(b.Bytes()))
+ }
+ }
+ carr.n_buffers = C.int64_t(len(cbuf))
+ if len(cbuf) > 0 {
+ carr.buffers = &cbuf[0]
+ }
+ carr.release = (*[0]byte)(C.release_test_arr)
+
+ return carr
+}
+
+func arrayStreamTest() *CArrowArrayStream {
+ st := C.get_test_stream()
+ C.setup_array_stream_test(2, st)
+ return st
+}
diff --git a/go/arrow/cdata/interface.go b/go/arrow/cdata/interface.go
new file mode 100644
index 0000000..d6258a5
--- /dev/null
+++ b/go/arrow/cdata/interface.go
@@ -0,0 +1,161 @@
+// 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.
+
+// +build cgo
+
+package cdata
+
+import (
+ "github.com/apache/arrow/go/arrow"
+ "github.com/apache/arrow/go/arrow/array"
+ "github.com/apache/arrow/go/arrow/arrio"
+ "golang.org/x/xerrors"
+)
+
+// ImportCArrowField takes in an ArrowSchema from the C Data interface, it
+// will copy the metadata and type definitions rather than keep direct
references
+// to them. It is safe to call C.ArrowSchemaRelease after receiving the field
+// from this function.
+func ImportCArrowField(out *CArrowSchema) (arrow.Field, error) {
+ return importSchema(out)
+}
+
+// ImportCArrowSchema takes in the ArrowSchema from the C Data Interface, it
+// will copy the metadata and schema definitions over from the C object rather
+// than keep direct references to them. This function will call
ArrowSchemaRelease
+// on the passed in schema regardless of whether or not there is an error
returned.
+//
+// This version is intended to take in a schema for a record batch, which means
+// that the top level of the schema should be a struct of the schema fields. If
+// importing a single array's schema, then use ImportCArrowField instead.
+func ImportCArrowSchema(out *CArrowSchema) (*arrow.Schema, error) {
+ ret, err := importSchema(out)
+ if err != nil {
+ return nil, err
+ }
+
+ return arrow.NewSchema(ret.Type.(*arrow.StructType).Fields(),
&ret.Metadata), nil
+}
+
+// ImportCArrayWithType takes a pointer to a C Data ArrowArray and interprets
the values
+// as an array with the given datatype. If err is not nil, then
ArrowArrayRelease must still
+// be called on arr to release the memory.
+//
+// The underlying buffers will not be copied, but will instead be referenced
directly
+// by the resulting array interface object. The passed in ArrowArray will have
it's ownership
+// transferred to the resulting array.Interface via ArrowArrayMove. The
underlying array.Data
+// object that is owned by the Array will now be the owner of the memory
pointer and
+// will call ArrowArrayRelease when it is released and garbage collected via
runtime.SetFinalizer.
+//
+// NOTE: The array takes ownership of the underlying memory buffers via
ArrowArrayMove,
+// it does not take ownership of the actual arr object itself.
+func ImportCArrayWithType(arr *CArrowArray, dt arrow.DataType)
(array.Interface, error) {
+ imp, err := importCArrayAsType(arr, dt)
+ if err != nil {
+ return nil, err
+ }
+ defer imp.data.Release()
+ return array.MakeFromData(imp.data), nil
+}
+
+// ImportCArray takes a pointer to both a C Data ArrowArray and C Data
ArrowSchema in order
+// to import them into usable Go Objects. If err is not nil, then
ArrowArrayRelease must still
+// be called on arr to release the memory. The ArrowSchemaRelease will be
called on the passed in
+// schema regardless of whether there is an error or not.
+//
+// The Schema will be copied with the information used to populate the
returned Field, complete
+// with metadata. The array will reference the same memory that is referred to
by the ArrowArray
+// object and take ownership of it as per ImportCArrayWithType. The returned
array.Interface will
+// own the C memory and call ArrowArrayRelease when the array.Data object is
cleaned up.
+//
+// NOTE: The array takes ownership of the underlying memory buffers via
ArrowArrayMove,
+// it does not take ownership of the actual arr object itself.
+func ImportCArray(arr *CArrowArray, schema *CArrowSchema) (arrow.Field,
array.Interface, error) {
+ field, err := importSchema(schema)
+ if err != nil {
+ return field, nil, err
+ }
+
+ ret, err := ImportCArrayWithType(arr, field.Type)
+ return field, ret, err
+}
+
+// ImportCRecordBatchWithSchema is used for importing a Record Batch array
when the schema
+// is already known such as when receiving record batches through a stream.
+//
+// All of the semantics regarding memory ownership are the same as when calling
+// ImportCRecordBatch directly with a schema.
+//
+// NOTE: The array takes ownership of the underlying memory buffers via
ArrowArrayMove,
+// it does not take ownership of the actual arr object itself.
+func ImportCRecordBatchWithSchema(arr *CArrowArray, sc *arrow.Schema)
(array.Record, error) {
+ imp, err := importCArrayAsType(arr, arrow.StructOf(sc.Fields()...))
+ if err != nil {
+ return nil, err
+ }
+
+ st := array.NewStructData(imp.data)
+ defer st.Release()
+
+ // now that we have our fields, we can split them out into the slice of
arrays
+ // and construct a record batch from them to return.
+ cols := make([]array.Interface, st.NumField())
+ for i := 0; i < st.NumField(); i++ {
+ cols[i] = st.Field(i)
+ }
+
+ return array.NewRecord(sc, cols, int64(st.Len())), nil
+}
+
+// ImportCRecordBatch imports an ArrowArray from C as a record batch. If err
is not nil,
+// then ArrowArrayRelease must still be called to release the memory.
+//
+// A record batch is represented in the C Data Interface as a Struct Array
whose fields
+// are the columns of the record batch. Thus after importing the schema passed
in here,
+// if it is not a Struct type, this will return an error. As with
ImportCArray, the
+// columns in the record batch will take ownership of the CArrowArray memory
if successful.
+// Since ArrowArrayMove is used, it's still safe to call ArrowArrayRelease on
the source
+// regardless. But if there is an error, it *MUST* be called to ensure there
is no memory leak.
+//
+// NOTE: The array takes ownership of the underlying memory buffers via
ArrowArrayMove,
+// it does not take ownership of the actual arr object itself.
+func ImportCRecordBatch(arr *CArrowArray, sc *CArrowSchema) (array.Record,
error) {
+ field, err := importSchema(sc)
+ if err != nil {
+ return nil, err
+ }
+
+ if field.Type.ID() != arrow.STRUCT {
+ return nil, xerrors.New("recordbatch array import must be of
struct type")
+ }
+
+ return ImportCRecordBatchWithSchema(arr,
arrow.NewSchema(field.Type.(*arrow.StructType).Fields(), &field.Metadata))
+}
+
+// ImportCArrayStream creates an arrio.Reader from an ArrowArrayStream taking
ownership
+// of the underlying stream object via ArrowArrayStreamMove.
+//
+// The records returned by this reader must be released manually after they
are returned.
+// The reader itself will release the stream via SetFinalizer when it is
garbage collected.
+// It will return (nil, io.EOF) from the Read function when there are no more
records to return.
+//
+// NOTE: The reader takes ownership of the underlying memory buffers via
ArrowArrayStreamMove,
+// it does not take ownership of the actual stream object itself.
+func ImportCArrayStream(stream *CArrowArrayStream, schema *arrow.Schema)
arrio.Reader {
+ out := &nativeCRecordBatchReader{schema: schema}
+ initReader(out, stream)
+ return out
+}
diff --git a/go/arrow/cdata/test/go.mod b/go/arrow/cdata/test/go.mod
new file mode 100644
index 0000000..153b127
--- /dev/null
+++ b/go/arrow/cdata/test/go.mod
@@ -0,0 +1,23 @@
+// 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.
+
+module cdatatest
+
+go 1.15
+
+replace github.com/apache/arrow/go/arrow => ../../
+
+require github.com/apache/arrow/go/arrow v0.0.0-00010101000000-000000000000
diff --git a/go/arrow/cdata/test/go.sum b/go/arrow/cdata/test/go.sum
new file mode 100644
index 0000000..9cbf776
--- /dev/null
+++ b/go/arrow/cdata/test/go.sum
@@ -0,0 +1,150 @@
+cloud.google.com/go v0.26.0/go.mod
h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod
h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod
h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/antihax/optional v1.0.0/go.mod
h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod
h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod
h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod
h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod
h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod
h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/davecgh/go-spew v1.1.0/go.mod
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod
h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane
v0.9.1-0.20191026205805-5f8ba28d4473/go.mod
h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod
h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane
v0.9.9-0.20201210154907-fd9021fe5dad/go.mod
h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane
v0.9.9-0.20210217033140-668b12f5399d/go.mod
h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane
v0.9.9-0.20210512163311-63b5d3c536b0/go.mod
h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod
h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/ghodss/yaml v1.0.0/go.mod
h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod
h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod
h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod
h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod
h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod
h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod
h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod
h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod
h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod
h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod
h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod
h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod
h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod
h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod
h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
+github.com/golang/snappy v0.0.3/go.mod
h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/flatbuffers v2.0.0+incompatible
h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI=
+github.com/google/flatbuffers v2.0.0+incompatible/go.mod
h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/go-cmp v0.2.0/go.mod
h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod
h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/uuid v1.1.2/go.mod
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod
h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/klauspost/compress v1.13.1
h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ=
+github.com/klauspost/compress v1.13.1/go.mod
h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
+github.com/pierrec/lz4/v4 v4.1.8
h1:ieHkV+i2BRzngO4Wd/3HGowuZStgq6QkPsD1eolNAO4=
+github.com/pierrec/lz4/v4 v4.1.8/go.mod
h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pmezard/go-difflib v1.0.0
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod
h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod
h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/stretchr/objx v0.1.0/go.mod
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod
h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0
h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.3.5/go.mod
h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod
h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod
h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod
h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod
h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod
h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod
h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod
h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod
h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod
h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod
h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod
h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod
h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod
h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.4/go.mod
h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod
h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod
h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod
h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod
h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod
h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod
h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210630183607-d20f26d13c79/go.mod
h1:yiaVoXHpRzHGyxV3o4DktVWY4mSUErTKaeEOq6C3t3U=
+google.golang.org/grpc v1.19.0/go.mod
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod
h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod
h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod
h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod
h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0/go.mod
h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0/go.mod
h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod
h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod
h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod
h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod
h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod
h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod
h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod
h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod
h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod
h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+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.27.1/go.mod
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/go/arrow/cdata/test/test_cimport.go
b/go/arrow/cdata/test/test_cimport.go
new file mode 100644
index 0000000..0fc5f86
--- /dev/null
+++ b/go/arrow/cdata/test/test_cimport.go
@@ -0,0 +1,101 @@
+// 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.
+
+// +build cdata_test
+
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "unsafe"
+
+ "github.com/apache/arrow/go/arrow"
+ "github.com/apache/arrow/go/arrow/array"
+ "github.com/apache/arrow/go/arrow/cdata"
+ "github.com/apache/arrow/go/arrow/memory"
+)
+
+// #include <stdint.h>
+import "C"
+
+//export runGC
+func runGC() {
+ runtime.GC()
+}
+
+//export importSchema
+func importSchema(ptr uintptr) {
+ sc := (*cdata.CArrowSchema)(unsafe.Pointer(ptr))
+
+ schema, err := cdata.ImportCArrowSchema(sc)
+ if err != nil {
+ panic(err)
+ }
+
+ expectedMetadata := arrow.NewMetadata([]string{"key1"},
[]string{"value1"})
+ expectedSchema := arrow.NewSchema([]arrow.Field{{Name: "ints", Type:
arrow.ListOf(arrow.PrimitiveTypes.Int32), Nullable: true}}, &expectedMetadata)
+ if !schema.Equal(expectedSchema) {
+ panic(fmt.Sprintf("schema didn't match: expected %s, got %s",
expectedSchema, schema))
+ }
+ if !schema.Metadata().Equal(expectedMetadata) {
+ panic(fmt.Sprintf("metadata didn't match: expected %s, got %s",
expectedMetadata, schema.Metadata()))
+ }
+
+ fmt.Println("schema matches! Huzzah!")
+}
+
+//export importRecordBatch
+func importRecordBatch(scptr, rbptr uintptr) {
+ sc := (*cdata.CArrowSchema)(unsafe.Pointer(scptr))
+ rb := (*cdata.CArrowArray)(unsafe.Pointer(rbptr))
+
+ rec, err := cdata.ImportCRecordBatch(rb, sc)
+ if err != nil {
+ panic(err)
+ }
+ defer rec.Release()
+
+ expectedMetadata := arrow.NewMetadata([]string{"key1"},
[]string{"value1"})
+ expectedSchema := arrow.NewSchema([]arrow.Field{{Name: "ints", Type:
arrow.ListOf(arrow.PrimitiveTypes.Int32), Nullable: true}}, &expectedMetadata)
+
+ bldr := array.NewRecordBuilder(memory.DefaultAllocator, expectedSchema)
+ defer bldr.Release()
+
+ lb := bldr.Field(0).(*array.ListBuilder)
+ vb := lb.ValueBuilder().(*array.Int32Builder)
+
+ // [[[1], [], None [2, 42]]]
+ lb.Append(true)
+ vb.Append(int32(1))
+
+ lb.Append(true)
+ lb.Append(false)
+
+ lb.Append(true)
+ vb.AppendValues([]int32{2, 42}, nil)
+
+ expectedRec := bldr.NewRecord()
+ defer expectedRec.Release()
+
+ if !array.RecordEqual(expectedRec, rec) {
+ panic(fmt.Sprintf("records didn't match: expected %s\n got %s",
expectedRec, rec))
+ }
+
+ fmt.Println("record batch matches huzzah!")
+}
+
+func main() {}
diff --git a/go/arrow/cdata/test/test_export_to_cgo.py
b/go/arrow/cdata/test/test_export_to_cgo.py
new file mode 100644
index 0000000..118aebf
--- /dev/null
+++ b/go/arrow/cdata/test/test_export_to_cgo.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+import contextlib
+import gc
+import os
+import unittest
+
+import pyarrow as pa
+from pyarrow.cffi import ffi
+
+
+def load_cgotest():
+ # XXX what about Darwin?
+ libext = 'so'
+ if os.name == 'nt':
+ libext = 'dll'
+
+ ffi.cdef(
+ """
+ void importSchema(uintptr_t ptr);
+ void importRecordBatch(uintptr_t scptr, uintptr_t rbptr);
+ void runGC();
+ """)
+ return ffi.dlopen(f'./cgotest.{libext}')
+
+
+cgotest = load_cgotest()
+
+
+class TestPythonToGo(unittest.TestCase):
+
+ def setUp(self):
+ self.c_schema = ffi.new("struct ArrowSchema*")
+ self.ptr_schema = int(ffi.cast("uintptr_t", self.c_schema))
+ self.c_array = ffi.new("struct ArrowArray*")
+ self.ptr_array = int(ffi.cast("uintptr_t", self.c_array))
+
+ def make_schema(self):
+ return pa.schema([('ints', pa.list_(pa.int32()))],
+ metadata={b'key1': b'value1'})
+
+ def make_batch(self):
+ return pa.record_batch([[[1], [], None, [2, 42]]],
+ self.make_schema())
+
+ def run_gc(self):
+ # Several Go GC runs can be required to run all finalizers
+ for i in range(5):
+ cgotest.runGC()
+ gc.collect()
+
+ @contextlib.contextmanager
+ def assert_pyarrow_memory_released(self):
+ self.run_gc()
+ old_allocated = pa.total_allocated_bytes()
+ yield
+ self.run_gc()
+ diff = pa.total_allocated_bytes() - old_allocated
+ self.assertEqual(
+ pa.total_allocated_bytes(), old_allocated,
+ f"PyArrow memory was not adequately released: {diff} bytes lost")
+
+ def test_schema(self):
+ with self.assert_pyarrow_memory_released():
+ self.make_schema()._export_to_c(self.ptr_schema)
+ # Will panic if expectations are not met
+ cgotest.importSchema(self.ptr_schema)
+
+ def test_record_batch(self):
+ with self.assert_pyarrow_memory_released():
+ self.make_schema()._export_to_c(self.ptr_schema)
+ self.make_batch()._export_to_c(self.ptr_array)
+ # Will panic if expectations are not met
+ cgotest.importRecordBatch(self.ptr_schema, self.ptr_array)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/go/arrow/cdata/utils.h b/go/arrow/cdata/utils.h
new file mode 100644
index 0000000..f382810
--- /dev/null
+++ b/go/arrow/cdata/utils.h
@@ -0,0 +1,56 @@
+// 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.
+
+// +build cgo
+// +build test
+
+/*
+ Function check_for_endianness() returns 1, if architecture
+ is little endian, 0 in case of big endian.
+*/
+inline int is_little_endian()
+{
+ unsigned int x = 1;
+ char *c = (char*) &x;
+ return (int)*c;
+}
+
+// metadata keys 1: {"key1", "key2"}
+// metadata values 1: {"", "bar"}
+static const char kEncodedMeta1LE[] = {
+ 2, 0, 0, 0,
+ 4, 0, 0, 0, 'k', 'e', 'y', '1', 0, 0, 0, 0,
+ 4, 0, 0, 0, 'k', 'e', 'y', '2', 3, 0, 0, 0, 'b', 'a', 'r'};
+
+static const char kEncodedMeta1BE[] = {
+ 0, 0, 0, 2,
+ 0, 0, 0, 4, 'k', 'e', 'y', '1', 0, 0, 0, 0,
+ 0, 0, 0, 4, 'k', 'e', 'y', '2', 0, 0, 0, 3, 'b', 'a', 'r'};
+
+static const char* kMetadataKeys2[] = {"key"};
+static const char* kMetadataValues2[] = {"abcde"};
+
+// metadata keys 2: {"key"}
+// metadata values 2: {"abcde"}
+static const char kEncodedMeta2LE[] = {
+ 1, 0, 0, 0,
+ 3, 0, 0, 0, 'k', 'e', 'y', 5, 0, 0, 0, 'a', 'b', 'c', 'd', 'e'};
+
+static const char kEncodedMeta2BE[] = {
+ 0, 0, 0, 1,
+ 0, 0, 0, 3, 'k', 'e', 'y', 0, 0, 0, 5, 'a', 'b', 'c', 'd', 'e'};
+
+
diff --git a/go/arrow/datatype_fixedwidth.go b/go/arrow/datatype_fixedwidth.go
index 79bb878..c90ee68 100644
--- a/go/arrow/datatype_fixedwidth.go
+++ b/go/arrow/datatype_fixedwidth.go
@@ -132,7 +132,7 @@ type Decimal128Type struct {
func (*Decimal128Type) ID() Type { return DECIMAL }
func (*Decimal128Type) Name() string { return "decimal" }
-func (*Decimal128Type) BitWidth() int { return 16 }
+func (*Decimal128Type) BitWidth() int { return 128 }
func (t *Decimal128Type) String() string {
return fmt.Sprintf("%s(%d, %d)", t.Name(), t.Precision, t.Scale)
}
diff --git a/go/arrow/datatype_fixedwidth_test.go
b/go/arrow/datatype_fixedwidth_test.go
index 3f5153c..3349703 100644
--- a/go/arrow/datatype_fixedwidth_test.go
+++ b/go/arrow/datatype_fixedwidth_test.go
@@ -53,7 +53,7 @@ func TestDecimal128Type(t *testing.T) {
} {
t.Run(tc.want, func(t *testing.T) {
dt := arrow.Decimal128Type{Precision: tc.precision,
Scale: tc.scale}
- if got, want := dt.BitWidth(), 16; got != want {
+ if got, want := dt.BitWidth(), 128; got != want {
t.Fatalf("invalid bitwidth: got=%d, want=%d",
got, want)
}
@@ -80,7 +80,7 @@ func TestFixedSizeBinaryType(t *testing.T) {
} {
t.Run(tc.want, func(t *testing.T) {
dt := arrow.FixedSizeBinaryType{tc.byteWidth}
- if got, want := dt.BitWidth(), 8 * tc.byteWidth; got !=
want {
+ if got, want := dt.BitWidth(), 8*tc.byteWidth; got !=
want {
t.Fatalf("invalid bitwidth: got=%d, want=%d",
got, want)
}
@@ -101,9 +101,9 @@ func TestFixedSizeBinaryType(t *testing.T) {
func TestTimestampType(t *testing.T) {
for _, tc := range []struct {
- unit arrow.TimeUnit
- timeZone string
- want string
+ unit arrow.TimeUnit
+ timeZone string
+ want string
}{
{arrow.Nanosecond, "CST", "timestamp[ns, tz=CST]"},
{arrow.Microsecond, "EST", "timestamp[us, tz=EST]"},