Releases: thanos/ex_arrow
v0.6.0
v0.5.0
Full Changelog: v0.4.0...v0.5.0 ExArrow 0.5.0 — Release Notes
Released: 2026-04-16
ExArrow 0.5.0 adds a production-grade Arrow Flight SQL client, making Elixir
a first-class participant in the Flight SQL ecosystem alongside DuckDB,
DataFusion, Dremio, and InfluxDB v3. The release also delivers lazy streaming
with Enumerable support for all stream types, a Mox-compatible behaviour for
unit testing, and structured error types with gRPC status codes.
All changes are backward compatible; upgrading from 0.4.0 requires only a
version bump.
What is new
Arrow Flight SQL client
Arrow Flight SQL layers SQL query semantics on top of Arrow Flight (gRPC +
Arrow IPC). Queries are dispatched to the server, which executes them and
streams results back as columnar RecordBatch data — the same Arrow format
used everywhere in ExArrow.
Quick start:
{:ok, client} = ExArrow.FlightSQL.Client.connect("localhost:32010")
# Materialised query — all batches collected before returning
{:ok, result} = ExArrow.FlightSQL.Client.query(client, "SELECT id, name FROM users")
result.num_rows #=> 42
result.schema #=> %ExArrow.Schema{...}
# Lazy query — stream one batch at a time
{:ok, stream} = ExArrow.FlightSQL.Client.stream_query(client, "SELECT * FROM big_table")
Enum.each(stream, fn batch -> process(batch) end)
# DML
{:ok, 3} = ExArrow.FlightSQL.Client.execute(client, "DELETE FROM t WHERE id < 4")
{:ok, :unknown} = ExArrow.FlightSQL.Client.execute(client, "CREATE TABLE t (id INT)")TLS connections — plaintext is used automatically for loopback addresses;
remote hosts use the OS trust store; a custom CA certificate can be provided:
# TLS with OS trust store (automatic for remote hosts)
{:ok, client} = ExArrow.FlightSQL.Client.connect("dremio.example.com:32010")
# Custom CA
pem = File.read!("priv/ca.pem")
{:ok, client} = ExArrow.FlightSQL.Client.connect("secure.server:32010",
tls: [ca_cert_pem: pem])Bearer-token authentication:
{:ok, client} = ExArrow.FlightSQL.Client.connect("dremio.example.com:32010",
tls: true,
headers: [{"authorization", "Bearer my-pat-token"}]
)Lazy streaming with Enumerable
ExArrow.Stream now implements the Enumerable protocol, so all Enum.*
and Stream.* functions work directly on any stream handle — IPC, Parquet,
ADBC, and Flight SQL alike.
{:ok, stream} = ExArrow.FlightSQL.Client.stream_query(client, "SELECT * FROM events")
# Collect all batches
batches = Enum.to_list(stream)
# Map, filter, reduce — standard Elixir idioms
row_counts = Enum.map(stream, &ExArrow.RecordBatch.num_rows/1)
# Take only the first N batches — the rest are never fetched
first_two = Enum.take(stream, 2)
# Comprehension syntax
for batch <- stream, do: process_batch(batch)Early termination (e.g. Enum.take/2) is safe — the underlying gRPC channel
is released when the stream variable goes out of scope.
Prepared statements
Server-side prepared statements allow the server to parse and plan a query
once and then execute it one or more times:
{:ok, stmt} = ExArrow.FlightSQL.Client.prepare(client,
"SELECT * FROM events WHERE ts > '2024-01-01'")
# Execute as a streaming query
{:ok, stream} = ExArrow.FlightSQL.Statement.execute(stmt)
batches = Enum.to_list(stream)
# Re-execute the same statement (reuses the server plan)
{:ok, stream2} = ExArrow.FlightSQL.Statement.execute(stmt)
# Or execute as DML
{:ok, dml_stmt} = ExArrow.FlightSQL.Client.prepare(client,
"DELETE FROM logs WHERE ts < '2020-01-01'")
{:ok, 1042} = ExArrow.FlightSQL.Statement.execute_update(dml_stmt)Servers that do not support prepared statements return
{:error, %Error{code: :unimplemented}}.
Metadata discovery
{:ok, stream} = ExArrow.FlightSQL.Client.get_tables(client,
db_schema_filter: "public", table_types: ["TABLE", "VIEW"])
batches = Enum.to_list(stream)
{:ok, stream} = ExArrow.FlightSQL.Client.get_db_schemas(client)
{:ok, stream} = ExArrow.FlightSQL.Client.get_sql_info(client)Explorer and Nx integration
# Query → Explorer DataFrame
{:ok, result} = ExArrow.FlightSQL.Client.query(client, "SELECT * FROM sales")
{:ok, df} = ExArrow.FlightSQL.Result.to_dataframe(result)
# Query → Nx tensor (first batch only)
{:ok, result} = ExArrow.FlightSQL.Client.query(client, "SELECT price FROM quotes")
{:ok, tensor} = ExArrow.FlightSQL.Result.to_tensor(result, "price")
# Lazy stream → Explorer DataFrame (large result sets)
{:ok, stream} = ExArrow.FlightSQL.Client.stream_query(client, "SELECT * FROM big_table")
{:ok, df} = ExArrow.Explorer.from_stream(stream)to_dataframe/1 requires {:explorer, "~> 0.11"}. to_tensor/2 requires
{:nx, "~> 0.9"}. Both return
{:error, %ExArrow.FlightSQL.Error{code: :conversion_error}} when the
optional dependency is absent.
Mox-compatible behaviour for unit testing
Swap the real implementation for a mock without a live server:
# test/test_helper.exs
Mox.defmock(MyApp.FlightSQLMock, for: ExArrow.FlightSQL.ClientBehaviour)
# In your test
Application.put_env(:ex_arrow, :flight_sql_client_impl, MyApp.FlightSQLMock)
Mox.expect(MyApp.FlightSQLMock, :query, fn _client, "SELECT 1", [] ->
{:ok, %ExArrow.FlightSQL.Result{schema: schema, batches: [], num_rows: 0}}
end)Structured errors
All non-bang functions return {:ok, value} or
{:error, %ExArrow.FlightSQL.Error{}}:
case ExArrow.FlightSQL.Client.query(client, sql) do
{:ok, result} -> handle(result)
{:error, %Error{code: :unauthenticated}} -> reauthenticate()
{:error, %Error{code: :not_found, message: msg}} -> Logger.warn(msg)
{:error, err} -> raise err
endError codes: :transport_error, :server_error, :unimplemented,
:unauthenticated, :permission_denied, :not_found, :invalid_argument,
:protocol_error, :multi_endpoint, :invalid_option, :conversion_error.
New public API
| Module | Function | Description |
|---|---|---|
ExArrow.FlightSQL.Client |
connect/1,2 |
Connect to a Flight SQL server |
ExArrow.FlightSQL.Client |
query/2, query!/2 |
Materialised SQL query |
ExArrow.FlightSQL.Client |
stream_query/2 |
Lazy SQL query returning ExArrow.Stream |
ExArrow.FlightSQL.Client |
execute/2 |
DML/DDL with affected-row count |
ExArrow.FlightSQL.Client |
prepare/2 |
Server-side prepared statement |
ExArrow.FlightSQL.Client |
get_tables/1,2 |
List tables visible to the connected user |
ExArrow.FlightSQL.Client |
get_db_schemas/1,2 |
List database schemas |
ExArrow.FlightSQL.Client |
get_sql_info/1 |
Server capability flags |
ExArrow.FlightSQL.Statement |
execute/1 |
Execute a prepared statement as a lazy stream |
ExArrow.FlightSQL.Statement |
execute_update/1 |
Execute a prepared DML statement |
ExArrow.FlightSQL.Result |
from_stream/1 |
Materialise a stream into a Result struct |
ExArrow.FlightSQL.Result |
to_dataframe/1 |
Convert result to Explorer.DataFrame |
ExArrow.FlightSQL.Result |
to_tensor/2 |
Extract a numeric column as Nx.Tensor |
ExArrow.Stream |
— | Now implements Enumerable |
Changed behaviour
ExArrow.Stream is now Enumerable — Enum.to_list/1, Enum.map/2,
Enum.take/2, and all other Enum.* / Stream.* functions work directly on
stream handles. Existing code using Stream.next/1 and Stream.to_list/1
continues to work unchanged.
Bug fixes
Elixir 1.17+ typing warning in Adbc.Result.from_py/1 — the
{:ok, stream_ref, capsule} match was unreachable when Pythonx is not
loaded. Both from_py/1 and from_py!/1 are now guarded with
Code.ensure_loaded?(Pythonx), eliminating the "clause will never match"
compiler warning.
Dependencies
No new required dependencies. Optional dependencies for new features:
# Required only for TLS with a custom CA (Flight SQL connect option)
# Uses OTP :ssl and :public_key — no new Hex packages needed.
# Optional (unchanged from 0.4.0 — enable for ecosystem bridges)
{:explorer, "~> 0.11", optional: true} # Result.to_dataframe/1
{:nx, "~> 0.9", optional: true} # Result.to_tensor/2Upgrade guide
No breaking changes. Update your version pin:
# Before
{:ex_arrow, "~> 0.4.0"}
# After
{:ex_arrow, "~> 0.5.0"}Then run mix deps.get and mix compile.
Full changelog
See CHANGELOG.md for the complete list of changes including
internal fixes and documentation updates.
v0.4.0
v0.3.0
ExArrow 0.3.0 — Release Notes
Released: 2026-03-10
Hex: https://hex.pm/packages/ex_arrow/0.3.0
Docs: https://hexdocs.pm/ex_arrow/0.3.0
Changelog: https://github.com/thanos/ex_arrow/blob/main/CHANGELOG.md#030---2026-03-10
What's new
Arrow compute kernels (ExArrow.Compute)
Three native operations on RecordBatch values — entirely in Rust Arrow
memory, no BEAM-side data copy:
| Function | Description |
|---|---|
filter/2 |
Keep rows where the first column of a boolean batch is true |
project/2 |
Select and reorder columns by name |
sort/3 |
Sort a batch by a named column, ascending or descending |
Results are new ExArrow.RecordBatch handles that compose with IPC writers,
Flight do_put, further compute calls, or the Parquet writer.
{:ok, filtered} = ExArrow.Compute.filter(batch, mask_batch)
{:ok, projected} = ExArrow.Compute.project(filtered, ["id", "score"])
{:ok, sorted} = ExArrow.Compute.sort(projected, "score", direction: :desc)Backed by the arrow-select and arrow-ord Rust crates (both part of the
arrow-rs 56 release family, so no extra dependency resolution).
Parquet support (ExArrow.Parquet.Reader / ExArrow.Parquet.Writer)
Read and write the Apache Parquet format using the parquet Rust crate.
Both file paths and in-memory binaries are supported on both sides.
# Read
{:ok, stream} = ExArrow.Parquet.Reader.from_file("/data/events.parquet")
{:ok, schema} = ExArrow.Stream.schema(stream)
batches = ExArrow.Stream.to_list(stream)
# Write
:ok = ExArrow.Parquet.Writer.to_file("/out/result.parquet", schema, batches)
# In-memory round-trip
{:ok, bytes} = ExArrow.Parquet.Writer.to_binary(schema, batches)
{:ok, stream2} = ExArrow.Parquet.Reader.from_binary(bytes)Parquet streams use the same ExArrow.Stream interface as IPC and ADBC
streams — schema/1, next/1, and to_list/1 work identically across all
three backends. No new consumption patterns to learn.
Explorer bridge (ExArrow.Explorer)
One-call conversion between ExArrow.Stream / ExArrow.RecordBatch and
Explorer.DataFrame:
# ExArrow → Explorer
{:ok, stream} = ExArrow.IPC.Reader.from_file("/data/events.arrow")
{:ok, df} = ExArrow.Explorer.from_stream(stream)
# Explorer → ExArrow (e.g. write to Parquet or send via Flight)
df = Explorer.DataFrame.new(x: [1, 2, 3], y: ["a", "b", "c"])
{:ok, stream} = ExArrow.Explorer.to_stream(df)All four bridge functions: from_stream/1, from_record_batch/1,
to_stream/1, to_record_batches/1.
Opt in by adding {:explorer, "~> 0.8"} to your mix.exs. When Explorer is
absent every function returns {:error, "Explorer is not available…"} rather
than failing to compile.
Nx bridge (ExArrow.Nx)
Zero-copy column extraction to Nx.Tensor by sharing the raw byte buffer:
{:ok, stream} = ExArrow.Parquet.Reader.from_file("/data/features.parquet")
batch = ExArrow.Stream.next(stream)
{:ok, tensor} = ExArrow.Nx.column_to_tensor(batch, "price")
# Or extract all numeric columns at once
{:ok, tensors} = ExArrow.Nx.to_tensors(batch)
# %{"price" => #Nx.Tensor<f64[1000]>, "qty" => #Nx.Tensor<s32[1000]>, …}Reverse path: from_tensor/2 writes an Nx.Tensor into a
single-column RecordBatch without going through an Elixir list.
Supported Arrow types: Int8/16/32/64, UInt8/16/32/64, Float32/64.
Non-numeric columns return {:error, …} from column_to_tensor/2 and are
silently skipped by to_tensors/1.
Opt in by adding {:nx, "~> 0.9"} to your mix.exs.
New public API surface
| Module | New functions |
|---|---|
ExArrow.Compute |
filter/2, project/2, sort/3 |
ExArrow.Parquet.Reader |
from_file/1, from_binary/1 |
ExArrow.Parquet.Writer |
to_file/3, to_binary/2 |
ExArrow.Explorer |
from_stream/1, from_record_batch/1, to_stream/1, to_record_batches/1 |
ExArrow.Nx |
column_to_tensor/2, to_tensors/1, from_tensor/2 |
Optional dependencies
Add to mix.exs |
Unlocks |
|---|---|
{:explorer, "~> 0.8"} |
ExArrow.Explorer bridge |
{:nx, "~> 0.9"} |
ExArrow.Nx bridge |
Both are optional. ExArrow compiles and works without them; the bridge modules
gracefully degrade to {:error, "… is not available…"} at runtime.
Upgrade guide from 0.2.0
# mix.exs — bump the version pin
{:ex_arrow, "~> 0.3.0"}
# Optional: add if you use the new bridges
{:explorer, "~> 0.8", optional: true}
{:nx, "~> 0.9", optional: true}No breaking changes to existing IPC, Flight, or ADBC APIs. All existing calls
continue to work without modification.
Fixes
- Dialyzer
call_without_opaqueerrors inExArrow.ExplorerandExArrow.Nx
— function heads no longer pattern-match on the concrete struct behind
@opaquetypes; resource extraction uses the dedicatedresource_ref/1
helpers instead. - Credo warnings in new test files (alias ordering, pipe chains,
length/1
vs!= []).
What's next (v0.4.0)
v0.4.0 is already released and ships:
- Arrow C Data Interface (
ExArrow.CDI) — zero-copy batch transfer via
raw C-struct pointers; foundation for a future zero-copy Explorer bridge. ExArrow.Nx.from_tensors/1— multi-columnRecordBatchfrom a tensor
map in one call.- Lazy Parquet streaming — row groups decoded on demand via
Stream.next/1; peak memory proportional to the largest row group, not
the full file.
# ExArrow 0.2.0 — Release Notes
Released: 2026-03-09
ExArrow 0.2.0 delivers the four features promised on the v0.2 roadmap: TLS for
Arrow Flight, multi-dataset server routing, an ADBC connection pool, and a
broader integration test matrix. All changes are backward compatible; no
existing code needs to change to upgrade from 0.1.0.
What is new
TLS for Arrow Flight
The built-in Flight server now supports encrypted connections. Pass a :tls
option to Server.start_link/2:
# One-way TLS (server presents a certificate)
cert = File.read!("server.crt")
key = File.read!("server.key")
{:ok, server} = ExArrow.Flight.Server.start_link(9999,
tls: [cert_pem: cert, key_pem: key])
# Mutual TLS (both sides present certificates)
ca = File.read!("ca.crt")
{:ok, server} = ExArrow.Flight.Server.start_link(9999,
tls: [cert_pem: cert, key_pem: key, ca_cert_pem: ca])The client already selected TLS automatically for non-loopback hosts (using the
OS certificate store). For a custom or self-signed CA, pass
tls: [ca_cert_pem: pem] to Client.connect/3.
Plaintext (tls: false, or no :tls option on loopback) continues to work
exactly as before.
Flight server routing — multiple named datasets
The built-in Flight server now stores datasets in a HashMap keyed by ticket,
rather than always overwriting a single "echo" slot.
Upload with a descriptor to store under a named ticket:
:ok = ExArrow.Flight.Client.do_put(client, schema, batches,
descriptor: {:cmd, "sales_2024"})
:ok = ExArrow.Flight.Client.do_put(client, schema, other_batches,
descriptor: {:path, ["metrics", "daily"]})Retrieve by ticket:
{:ok, stream} = ExArrow.Flight.Client.do_get(client, "sales_2024")
{:ok, stream} = ExArrow.Flight.Client.do_get(client, "metrics/daily")List all stored datasets:
{:ok, flights} = ExArrow.Flight.Client.list_flights(client)
{:ok, tickets} = ExArrow.Flight.Client.do_action(client, "list_tickets", <<>>)Calls that do not pass a :descriptor default to the "echo" ticket, so all
existing code continues to work without modification.
ADBC connection pool
ExArrow.ADBC.ConnectionPool is a NimblePool-backed pool that reuses open
Connection handles across callers.
Supervised pool (recommended)
children = [
{ExArrow.ADBC.DatabaseServer,
name: :mydb,
driver_path: "/usr/local/lib/libadbc_driver_duckdb.so"},
{ExArrow.ADBC.ConnectionPool,
name: :mypool, database: :mydb, pool_size: 4}
]
Supervisor.start_link(children, strategy: :one_for_one)
# Anywhere in the application:
{:ok, stream} = ExArrow.ADBC.ConnectionPool.query(:mypool,
"SELECT * FROM events WHERE day = today()")Ad-hoc pool
{:ok, db} = ExArrow.ADBC.Database.open(driver_path: "/path/to/driver.so")
{:ok, pool} = ExArrow.ADBC.ConnectionPool.start_link(database: db, pool_size: 4)
{:ok, stream} = ExArrow.ADBC.ConnectionPool.query(pool, "SELECT 42 AS n")Multi-statement checkout
ExArrow.ADBC.ConnectionPool.with_connection(pool, fn conn ->
{:ok, stmt} = ExArrow.ADBC.Statement.new(conn)
ExArrow.ADBC.Statement.set_sql(stmt, "BEGIN")
ExArrow.ADBC.Statement.execute(stmt)
# ... more statements ...
{result, :ok}
end)Larger integration test matrix
A new .github/workflows/integration.yml workflow runs ADBC integration tests
against:
- PostgreSQL 14, 15, and 16 (via GitHub Actions service containers)
- DuckDB 1.1.3 and 1.2.0 (via downloaded ADBC driver binary)
The tests live in test/ex_arrow/adbc_integration_test.exs and are excluded
from the default test run. To run them locally:
# Start PostgreSQL
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16
PG_HOST=localhost mix test --include adbc_integration \
test/ex_arrow/adbc_integration_test.exs
# DuckDB
DUCKDB_DRIVER=/usr/local/lib/libduckdb_adbc.so \
mix test --include adbc_integration \
test/ex_arrow/adbc_integration_test.exsNew public API additions
| Module | Function | Description |
|---|---|---|
ExArrow.Schema |
field_names/1 |
Returns field names as [String.t()] |
ExArrow.Stream |
to_list/1 |
Collects all batches into a list |
ExArrow.ADBC.Database |
close/1 |
Explicit handle release |
ExArrow.ADBC.Connection |
close/1 |
Explicit handle release |
ExArrow.ADBC.ConnectionPool |
start_link/1, query/3, with_connection/3 |
NimblePool pool |
ExArrow.ADBC.DatabaseServer |
start_link/1, get/1 |
Supervised database handle |
Upgrade guide
No breaking changes. The only API change that requires attention is if you
have a Mox stub for ClientBehaviour.do_put: the callback now takes four
arguments (client, schema, batches, opts). Update your mock expectation:
# Before (0.1.0)
Mox.expect(MyMock, :do_put, fn client, schema, batches -> :ok end)
# After (0.2.0)
Mox.expect(MyMock, :do_put, fn client, schema, batches, _opts -> :ok end)Dependencies
No new required dependencies. The connection pool requires nimble_pool
(already optional in 0.1.0); add it to your mix.exs to use
ExArrow.ADBC.ConnectionPool:
{:nimble_pool, "~> 1.1"}Full changelog
See CHANGELOG.md for the complete list of changes.
v0.1.0-rc.9
Full Changelog: v0.1.0-rc.8...v0.1.0-rc.9
v0.1.0
ExArrow 0.1.0 — Release notes
Release date: 2026-02-26
Package: Hex | Docs: hexdocs.pm/ex_arrow | Source: GitHub
Summary
ExArrow 0.1.0 is the first public release. It brings Apache Arrow support to the BEAM: IPC (stream and file), Arrow Flight (client and server), and ADBC (Arrow Database Connectivity). Data stays in native Rust/Arrow buffers; Elixir uses opaque handles. Precompiled NIFs are provided for Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (x86_64), so no Rust toolchain is required for normal use.
Requirements: Elixir ~> 1.14 (OTP 25 / NIF 2.15 and OTP 26+ / NIF 2.16)
What's included
IPC (Inter-Process Communication)
- Read/write Arrow stream format from binary or file.
- Random-access file format: open by path or binary, read schema, batch count, and any batch by index.
- Same
ExArrow.StreamandExArrow.Schema/ExArrow.RecordBatchhandles as Flight and ADBC.
Arrow Flight
- gRPC client: connect, do_put, do_get, list_flights, get_flight_info, get_schema, list_actions, do_action.
- Built-in echo server for testing or simple services.
- Plaintext HTTP/2 only in this release (TLS planned). Compatible with Dremio, InfluxDB IOx, and custom Flight servers.
ADBC
- Open database by driver path or driver name (e.g. SQLite, PostgreSQL). Execute SQL and get an Arrow result stream.
- Metadata APIs where the driver supports them: get_table_types, get_table_schema, get_objects.
- Statement.bind for parameter binding where supported.
- Optional integration with the
adbcpackage: use it to configure and download drivers (e.g. SQLite, PostgreSQL) on first use; ExArrow then opens the database and returns Arrow streams. ExArrow.ADBC.DriverHelper.ensure_driver_and_open/2: one-step “ensure driver then open”: when theadbcpackage is present it callsAdbc.download_driver/1, then opens the database viaExArrow.ADBC.Database.open/1. Whenadbcis not installed, it skips the download step and opens with the inferred options. Returns{:ok, db}or{:error, reason}(no raises).
Livebook tutorials
- Four notebooks in the repo: quick start (IPC, Flight, ADBC in one place), plus tutorials for IPC (stream vs file, read/write, schema, Explorer interop), Flight (echo server, client, list_flights, get_schema, actions), and ADBC (Database → Connection → Statement → Stream, metadata). Suitable for learning and for an introductory article.
Explorer interop
- Move data between ExArrow and Explorer via Arrow IPC: use
ExArrow.IPC.Writer.to_binary/2(orto_file/3) andExplorer.DataFrame.load_ipc_stream!/1to get a dataframe from ExArrow streams (stream format); useExplorer.DataFrame.dump_ipc_stream!/1(stream format) andExArrow.IPC.Reader.from_binary/1for the reverse. ExArrow handles streaming and protocols; Explorer handles in-memory analysis.
Memory and scheduling
- Arrow data lives in native memory; no BEAM heap copy by default.
- Long-running NIF work uses dirty schedulers so the BEAM is not blocked.
Installation
def deps do
[{:ex_arrow, "~> 0.1.0"}]
endThen mix deps.get and mix compile. The precompiled NIF is downloaded from GitHub releases. To build from source (e.g. unsupported platform), set EX_ARROW_BUILD=1 and have Rust installed.
Changelog
See CHANGELOG.md for the full 0.1.0 entry.
Feedback
Issues and discussions: GitHub Issues.
v0.1.0-rc.8
v0.1.0-rc.7
Full Changelog: v0.1.0-rc.6...v0.1.0-rc.7
v0.1.0-rc.6
What's Changed
- Implement Milestone 1: IPC MVP (streaming only) by @thanos in #12
- Milestone 2/ipc complete by @thanos in #24
- Milestone 3 by @thanos in #34
- closed #37 and closed #36 - In native/ex_arrow_native/src/flight.rs E… by @thanos in #38
- feat: Milestone 4 — complete Arrow Flight RPC surface by @thanos in #44
- Implement Milestone 5: ADBC MVP (Database, Connection, Statement, exe… by @thanos in #55
- Milestone 6/adbc complete by @thanos in #66
- Release/prepare by @thanos in #71
New Contributors
Full Changelog: https://github.com/thanos/ex_arrow/commits/v0.1.0-rc.6