iceberg 0.11.2 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/Cargo.lock +1180 -495
- data/Cargo.toml +7 -0
- data/NOTICE.txt +2 -2
- data/README.md +60 -15
- data/ext/iceberg/Cargo.toml +18 -15
- data/ext/iceberg/src/arrow.rs +22 -7
- data/ext/iceberg/src/batch.rs +174 -0
- data/ext/iceberg/src/capsule.rs +24 -0
- data/ext/iceberg/src/catalog.rs +157 -33
- data/ext/iceberg/src/encryption.rs +32 -0
- data/ext/iceberg/src/error.rs +63 -23
- data/ext/iceberg/src/lib.rs +264 -41
- data/ext/iceberg/src/partitioning.rs +102 -0
- data/ext/iceberg/src/result.rs +201 -0
- data/ext/iceberg/src/runtime.rs +5 -1
- data/ext/iceberg/src/scan.rs +106 -26
- data/ext/iceberg/src/schema.rs +306 -0
- data/ext/iceberg/src/snapshot.rs +85 -0
- data/ext/iceberg/src/sorting.rs +122 -0
- data/ext/iceberg/src/statistics.rs +71 -0
- data/ext/iceberg/src/table.rs +213 -265
- data/ext/iceberg/src/utils.rs +223 -164
- data/lib/iceberg/catalog.rb +59 -35
- data/lib/iceberg/glue_catalog.rb +5 -2
- data/lib/iceberg/memory_catalog.rb +5 -2
- data/lib/iceberg/rest_catalog.rb +5 -2
- data/lib/iceberg/result.rb +22 -0
- data/lib/iceberg/s3_tables_catalog.rb +5 -2
- data/lib/iceberg/sql_catalog.rb +5 -2
- data/lib/iceberg/table.rb +87 -21
- data/lib/iceberg/table_definition.rb +49 -7
- data/lib/iceberg/table_scan.rb +14 -0
- data/lib/iceberg/transforms.rb +64 -0
- data/lib/iceberg/types.rb +137 -0
- data/lib/iceberg/version.rb +1 -1
- data/lib/iceberg.rb +10 -5
- metadata +14 -3
- data/lib/iceberg/schema.rb +0 -10
data/Cargo.toml
CHANGED
data/NOTICE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
Apache Iceberg
|
|
3
|
-
Copyright 2017-
|
|
3
|
+
Copyright 2017-2026 The Apache Software Foundation
|
|
4
4
|
|
|
5
5
|
This product includes software developed at
|
|
6
6
|
The Apache Software Foundation (http://www.apache.org/).
|
|
@@ -8,7 +8,7 @@ The Apache Software Foundation (http://www.apache.org/).
|
|
|
8
8
|
--------------------------------------------------------------------------------
|
|
9
9
|
|
|
10
10
|
Apache Iceberg Rust
|
|
11
|
-
Copyright 2023-
|
|
11
|
+
Copyright 2023-2026 The Apache Software Foundation
|
|
12
12
|
|
|
13
13
|
This product includes software developed at
|
|
14
14
|
The Apache Software Foundation (http://www.apache.org/).
|
data/README.md
CHANGED
|
@@ -19,10 +19,10 @@ gem "iceberg"
|
|
|
19
19
|
Create a client for an Iceberg catalog
|
|
20
20
|
|
|
21
21
|
```ruby
|
|
22
|
-
catalog = Iceberg::RestCatalog.new(uri: "http://localhost:8181")
|
|
22
|
+
catalog = Iceberg::RestCatalog.new(uri: "http://localhost:8181", default_namespace: "main")
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Create a namespace
|
|
25
|
+
Create a namespace if needed
|
|
26
26
|
|
|
27
27
|
```ruby
|
|
28
28
|
catalog.create_namespace("main")
|
|
@@ -31,30 +31,53 @@ catalog.create_namespace("main")
|
|
|
31
31
|
Create a table
|
|
32
32
|
|
|
33
33
|
```ruby
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
table =
|
|
35
|
+
catalog.create_table("events") do |t|
|
|
36
|
+
t.bigint "id"
|
|
37
|
+
t.float "value"
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Append data
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
table.append([{"id" => 1, "value" => 3.0}, {"id" => 2, "value" => 4.0}])
|
|
38
45
|
```
|
|
39
46
|
|
|
40
47
|
Or with [Polars](https://github.com/ankane/ruby-polars)
|
|
41
48
|
|
|
42
49
|
```ruby
|
|
43
50
|
df = Polars::DataFrame.new({"id" => [1, 2], "value" => [3.0, 4.0]})
|
|
44
|
-
table = catalog.create_table("
|
|
51
|
+
table = catalog.create_table("events", schema: df.schema)
|
|
45
52
|
table.append(df)
|
|
46
53
|
```
|
|
47
54
|
|
|
55
|
+
Or with [Nanoarrow](https://github.com/ankane/nanoarrow-ruby)
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
arr =
|
|
59
|
+
Nanoarrow::Array.new(
|
|
60
|
+
[{"id" => 1, "value" => 3.0}, {"id" => 2, "value" => 4.0}],
|
|
61
|
+
Nanoarrow.struct({"a" => Nanoarrow.int64, "b" => Nanoarrow.string})
|
|
62
|
+
)
|
|
63
|
+
table = catalog.create_table("events", schema: arr.schema)
|
|
64
|
+
table.append(arr)
|
|
65
|
+
```
|
|
66
|
+
|
|
48
67
|
Load a table
|
|
49
68
|
|
|
50
69
|
```ruby
|
|
51
|
-
table = catalog.load_table("
|
|
70
|
+
table = catalog.load_table("events")
|
|
52
71
|
```
|
|
53
72
|
|
|
54
73
|
Query a table
|
|
55
74
|
|
|
56
75
|
```ruby
|
|
76
|
+
table.to_a
|
|
77
|
+
# or
|
|
57
78
|
table.to_polars.collect
|
|
79
|
+
# or
|
|
80
|
+
table.scan.to_arrow
|
|
58
81
|
```
|
|
59
82
|
|
|
60
83
|
## Catalog Types
|
|
@@ -67,6 +90,22 @@ Iceberg::RestCatalog.new(
|
|
|
67
90
|
)
|
|
68
91
|
```
|
|
69
92
|
|
|
93
|
+
### S3 Tables
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
Iceberg::S3TablesCatalog.new(
|
|
97
|
+
arn: "arn:aws:s3tables:..."
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Glue
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
Iceberg::GlueCatalog.new(
|
|
105
|
+
warehouse: "s3://my-bucket"
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
70
109
|
### SQL
|
|
71
110
|
|
|
72
111
|
```ruby
|
|
@@ -133,7 +172,7 @@ catalog.list_tables("main")
|
|
|
133
172
|
Create a table
|
|
134
173
|
|
|
135
174
|
```ruby
|
|
136
|
-
catalog.create_table("
|
|
175
|
+
catalog.create_table("events") do |t|
|
|
137
176
|
t.integer "id"
|
|
138
177
|
t.float "value"
|
|
139
178
|
end
|
|
@@ -142,31 +181,31 @@ end
|
|
|
142
181
|
Load a table
|
|
143
182
|
|
|
144
183
|
```ruby
|
|
145
|
-
catalog.load_table("
|
|
184
|
+
catalog.load_table("events")
|
|
146
185
|
```
|
|
147
186
|
|
|
148
187
|
Check if a table exists
|
|
149
188
|
|
|
150
189
|
```ruby
|
|
151
|
-
catalog.table_exists?("
|
|
190
|
+
catalog.table_exists?("events")
|
|
152
191
|
```
|
|
153
192
|
|
|
154
193
|
Rename a table
|
|
155
194
|
|
|
156
195
|
```ruby
|
|
157
|
-
catalog.rename_table("
|
|
196
|
+
catalog.rename_table("events", "events2")
|
|
158
197
|
```
|
|
159
198
|
|
|
160
199
|
Register a table
|
|
161
200
|
|
|
162
201
|
```ruby
|
|
163
|
-
catalog.register_table("
|
|
202
|
+
catalog.register_table("events", "metadata.json")
|
|
164
203
|
```
|
|
165
204
|
|
|
166
205
|
Drop a table
|
|
167
206
|
|
|
168
207
|
```ruby
|
|
169
|
-
catalog.drop_table("
|
|
208
|
+
catalog.drop_table("events")
|
|
170
209
|
```
|
|
171
210
|
|
|
172
211
|
## Static Tables
|
|
@@ -196,7 +235,7 @@ To get started with development:
|
|
|
196
235
|
git clone https://github.com/ankane/iceberg-ruby.git
|
|
197
236
|
cd iceberg-ruby
|
|
198
237
|
bundle install
|
|
199
|
-
bundle exec rake compile
|
|
238
|
+
bundle exec rake compile:dev
|
|
200
239
|
|
|
201
240
|
# memory catalog
|
|
202
241
|
bundle exec rake test:memory
|
|
@@ -208,4 +247,10 @@ bundle exec rake test:rest
|
|
|
208
247
|
# SQL catalog
|
|
209
248
|
createdb iceberg_ruby_test
|
|
210
249
|
bundle exec rake test:sql
|
|
250
|
+
|
|
251
|
+
# S3 Tables catalog
|
|
252
|
+
bundle exec rake test:s3tables
|
|
253
|
+
|
|
254
|
+
# Glue catalog
|
|
255
|
+
bundle exec rake test:glue
|
|
211
256
|
```
|
data/ext/iceberg/Cargo.toml
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "iceberg-ruby"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.12.1"
|
|
4
4
|
license = "Apache-2.0"
|
|
5
5
|
authors = ["Andrew Kane <andrew@ankane.org>"]
|
|
6
6
|
edition = "2024"
|
|
7
|
-
rust-version = "1.
|
|
7
|
+
rust-version = "1.95"
|
|
8
8
|
publish = false
|
|
9
9
|
|
|
10
10
|
[lib]
|
|
@@ -12,25 +12,28 @@ name = "iceberg"
|
|
|
12
12
|
crate-type = ["cdylib"]
|
|
13
13
|
|
|
14
14
|
[dependencies]
|
|
15
|
-
arrow
|
|
16
|
-
arrow-
|
|
17
|
-
|
|
15
|
+
arrow = "58.4"
|
|
16
|
+
arrow-array = { version = "58.4", features = ["ffi"] }
|
|
17
|
+
arrow-cast = "58.4"
|
|
18
|
+
arrow-schema = "58.4"
|
|
19
|
+
datafusion = { version = "54", optional = true }
|
|
18
20
|
futures = "0.3"
|
|
19
|
-
iceberg = "=0.
|
|
20
|
-
iceberg-catalog-glue = { version = "=0.
|
|
21
|
-
iceberg-catalog-rest = { version = "=0.
|
|
22
|
-
iceberg-catalog-s3tables = { version = "=0.
|
|
23
|
-
iceberg-catalog-sql = { version = "=0.
|
|
24
|
-
iceberg-datafusion = { version = "=0.
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
iceberg = "=0.11.0"
|
|
22
|
+
iceberg-catalog-glue = { version = "=0.11.0", optional = true }
|
|
23
|
+
iceberg-catalog-rest = { version = "=0.11.0", optional = true }
|
|
24
|
+
iceberg-catalog-s3tables = { version = "=0.11.0", optional = true }
|
|
25
|
+
iceberg-catalog-sql = { version = "=0.11.0", optional = true }
|
|
26
|
+
iceberg-datafusion = { version = "=0.11.0", optional = true }
|
|
27
|
+
iceberg-storage-opendal = "=0.11.0"
|
|
28
|
+
magnus = "0.9"
|
|
29
|
+
parquet = "58.4"
|
|
27
30
|
rb-sys = "0.9"
|
|
28
|
-
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "sqlite"], default-features = false, optional = true }
|
|
31
|
+
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "sqlite", "tls-rustls-aws-lc-rs"], default-features = false, optional = true }
|
|
29
32
|
tokio = { version = "1", features = ["rt-multi-thread"] }
|
|
30
33
|
uuid = { version = "1", features = ["v4"] }
|
|
31
34
|
|
|
32
35
|
[features]
|
|
33
|
-
default = ["rest", "sql"]
|
|
36
|
+
default = ["glue", "rest", "s3tables", "sql"]
|
|
34
37
|
datafusion = ["dep:datafusion", "dep:iceberg-datafusion"]
|
|
35
38
|
glue = ["dep:iceberg-catalog-glue"]
|
|
36
39
|
rest = ["dep:iceberg-catalog-rest"]
|
data/ext/iceberg/src/arrow.rs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
use arrow_array::ffi_stream::{ArrowArrayStreamReader, FFI_ArrowArrayStream};
|
|
2
|
+
use arrow_schema::Schema as ArrowSchema;
|
|
3
|
+
use arrow_schema::ffi::FFI_ArrowSchema;
|
|
2
4
|
use magnus::{Error as RbErr, Ruby, TryConvert, Value, prelude::*};
|
|
3
5
|
|
|
4
6
|
use crate::RbResult;
|
|
7
|
+
use crate::utils::Wrap;
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
impl TryConvert for RbArrowType<ArrowArrayStreamReader> {
|
|
9
|
+
impl TryConvert for Wrap<ArrowArrayStreamReader> {
|
|
9
10
|
fn try_convert(val: Value) -> RbResult<Self> {
|
|
10
11
|
let ruby = Ruby::get_with(val);
|
|
11
12
|
let addr: usize = val.funcall("to_i", ())?;
|
|
@@ -14,9 +15,23 @@ impl TryConvert for RbArrowType<ArrowArrayStreamReader> {
|
|
|
14
15
|
let stream_ptr =
|
|
15
16
|
Box::new(unsafe { std::ptr::replace(addr as _, FFI_ArrowArrayStream::empty()) });
|
|
16
17
|
|
|
17
|
-
Ok(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Ok(Wrap(ArrowArrayStreamReader::try_new(*stream_ptr).map_err(
|
|
19
|
+
|e| RbErr::new(ruby.exception_arg_error(), e.to_string()),
|
|
20
|
+
)?))
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl TryConvert for Wrap<ArrowSchema> {
|
|
25
|
+
fn try_convert(val: Value) -> RbResult<Self> {
|
|
26
|
+
let ruby = Ruby::get_with(val);
|
|
27
|
+
let addr: usize = val.funcall("to_i", ())?;
|
|
28
|
+
|
|
29
|
+
// use similar approach as Polars to consume pointer and avoid copy
|
|
30
|
+
let schema_ptr =
|
|
31
|
+
Box::new(unsafe { std::ptr::replace(addr as _, FFI_ArrowSchema::empty()) });
|
|
32
|
+
|
|
33
|
+
Ok(Wrap(ArrowSchema::try_from(&*schema_ptr).map_err(|e| {
|
|
34
|
+
RbErr::new(ruby.exception_arg_error(), e.to_string())
|
|
35
|
+
})?))
|
|
21
36
|
}
|
|
22
37
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
|
|
3
|
+
use arrow::array::{
|
|
4
|
+
Array, BooleanBuilder, Date32Builder, Decimal128Builder, Float32Builder, Float64Builder,
|
|
5
|
+
Int32Builder, Int64Builder, LargeBinaryBuilder, StringBuilder, TimestampMicrosecondBuilder,
|
|
6
|
+
};
|
|
7
|
+
use arrow::datatypes::{DataType as ArrowDataType, Decimal128Type};
|
|
8
|
+
use arrow_array::ffi_stream::FFI_ArrowArrayStream;
|
|
9
|
+
use arrow_array::{RecordBatch, RecordBatchIterator};
|
|
10
|
+
use arrow_cast::parse::parse_decimal;
|
|
11
|
+
use arrow_schema::{Field as ArrowField, Schema as ArrowSchema, TimeUnit};
|
|
12
|
+
use magnus::{RArray, RHash, RString, Ruby, TryConvert, Value, value::ReprValue};
|
|
13
|
+
|
|
14
|
+
use crate::RbResult;
|
|
15
|
+
use crate::capsule::RbCapsule;
|
|
16
|
+
use crate::error::todo_error;
|
|
17
|
+
use crate::utils::{Wrap, date_to_i32};
|
|
18
|
+
|
|
19
|
+
#[magnus::wrap(class = "Iceberg::ArrowRecordBatch")]
|
|
20
|
+
pub struct RbArrowRecordBatch {
|
|
21
|
+
pub(crate) batch: RecordBatch,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
impl RbArrowRecordBatch {
|
|
25
|
+
pub fn new(ruby: &Ruby, data: RArray, schema: Wrap<ArrowSchema>) -> RbResult<Self> {
|
|
26
|
+
let schema = Arc::new(schema.0);
|
|
27
|
+
let mut columns = Vec::new();
|
|
28
|
+
for field in &schema.fields {
|
|
29
|
+
let array = match field.data_type() {
|
|
30
|
+
ArrowDataType::Boolean => new_array_boolean(ruby, data, field)?,
|
|
31
|
+
ArrowDataType::Int32 => new_array_int32(ruby, data, field)?,
|
|
32
|
+
ArrowDataType::Int64 => new_array_int64(ruby, data, field)?,
|
|
33
|
+
ArrowDataType::Float32 => new_array_float32(ruby, data, field)?,
|
|
34
|
+
ArrowDataType::Float64 => new_array_float64(ruby, data, field)?,
|
|
35
|
+
ArrowDataType::Decimal128(precision, scale) => {
|
|
36
|
+
new_array_decimal128(ruby, data, field, *precision, *scale)?
|
|
37
|
+
}
|
|
38
|
+
ArrowDataType::Date32 => new_array_date32(ruby, data, field)?,
|
|
39
|
+
ArrowDataType::Timestamp(TimeUnit::Microsecond, None) => {
|
|
40
|
+
new_array_timestamp_us(ruby, data, field)?
|
|
41
|
+
}
|
|
42
|
+
ArrowDataType::Utf8 => new_array_utf8(ruby, data, field)?,
|
|
43
|
+
ArrowDataType::LargeBinary => new_array_large_binary(ruby, data, field)?,
|
|
44
|
+
_ => return Err(todo_error(field.data_type())),
|
|
45
|
+
};
|
|
46
|
+
columns.push(array);
|
|
47
|
+
}
|
|
48
|
+
let batch = RecordBatch::try_new(schema.clone(), columns).unwrap();
|
|
49
|
+
Ok(Self { batch })
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn arrow_c_stream(&self) -> RbCapsule {
|
|
53
|
+
let schema = self.batch.schema();
|
|
54
|
+
let reader = RecordBatchIterator::new([Ok(self.batch.clone())].into_iter(), schema);
|
|
55
|
+
let stream = FFI_ArrowArrayStream::new(Box::new(reader));
|
|
56
|
+
RbCapsule::new(stream, Some("arrow_array_stream".into()))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
macro_rules! new_array {
|
|
61
|
+
($name:ident, $builder:ty, $type:ty) => {
|
|
62
|
+
fn $name(ruby: &Ruby, data: RArray, field: &Arc<ArrowField>) -> RbResult<Arc<dyn Array>> {
|
|
63
|
+
let name = ruby.str_new(field.name());
|
|
64
|
+
let mut builder = <$builder>::new();
|
|
65
|
+
for row in data.into_iter() {
|
|
66
|
+
let row = RHash::try_convert(row)?;
|
|
67
|
+
let value: Option<$type> = row.aref(name)?;
|
|
68
|
+
match value {
|
|
69
|
+
Some(v) => builder.append_value(v),
|
|
70
|
+
None => builder.append_null(),
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
let array: Arc<dyn Array> = Arc::new(builder.finish());
|
|
74
|
+
Ok(array)
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
new_array!(new_array_boolean, BooleanBuilder, bool);
|
|
80
|
+
new_array!(new_array_int32, Int32Builder, i32);
|
|
81
|
+
new_array!(new_array_int64, Int64Builder, i64);
|
|
82
|
+
new_array!(new_array_float32, Float32Builder, f32);
|
|
83
|
+
new_array!(new_array_float64, Float64Builder, f64);
|
|
84
|
+
new_array!(new_array_utf8, StringBuilder, String);
|
|
85
|
+
|
|
86
|
+
fn new_array_decimal128(
|
|
87
|
+
ruby: &Ruby,
|
|
88
|
+
data: RArray,
|
|
89
|
+
field: &Arc<ArrowField>,
|
|
90
|
+
precision: u8,
|
|
91
|
+
scale: i8,
|
|
92
|
+
) -> RbResult<Arc<dyn Array>> {
|
|
93
|
+
let name = ruby.str_new(field.name());
|
|
94
|
+
let mut builder = Decimal128Builder::new()
|
|
95
|
+
.with_precision_and_scale(precision, scale)
|
|
96
|
+
.unwrap();
|
|
97
|
+
for row in data.into_iter() {
|
|
98
|
+
let row = RHash::try_convert(row)?;
|
|
99
|
+
let value: Option<Value> = row.aref(name)?;
|
|
100
|
+
match value {
|
|
101
|
+
Some(v) => {
|
|
102
|
+
let s = v.to_r_string()?;
|
|
103
|
+
builder.append_value(
|
|
104
|
+
parse_decimal::<Decimal128Type>(unsafe { s.as_str()? }, precision, scale)
|
|
105
|
+
.unwrap(),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
None => builder.append_null(),
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
let array: Arc<dyn Array> = Arc::new(builder.finish());
|
|
112
|
+
Ok(array)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fn new_array_date32(
|
|
116
|
+
ruby: &Ruby,
|
|
117
|
+
data: RArray,
|
|
118
|
+
field: &Arc<ArrowField>,
|
|
119
|
+
) -> RbResult<Arc<dyn Array>> {
|
|
120
|
+
let name = ruby.str_new(field.name());
|
|
121
|
+
let mut builder = Date32Builder::new();
|
|
122
|
+
for row in data.into_iter() {
|
|
123
|
+
let row = RHash::try_convert(row)?;
|
|
124
|
+
let value: Option<Value> = row.aref(name)?;
|
|
125
|
+
match value {
|
|
126
|
+
Some(v) => builder.append_value(date_to_i32(v)?),
|
|
127
|
+
None => builder.append_null(),
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
let array: Arc<dyn Array> = Arc::new(builder.finish());
|
|
131
|
+
Ok(array)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fn new_array_timestamp_us(
|
|
135
|
+
ruby: &Ruby,
|
|
136
|
+
data: RArray,
|
|
137
|
+
field: &Arc<ArrowField>,
|
|
138
|
+
) -> RbResult<Arc<dyn Array>> {
|
|
139
|
+
let name = ruby.str_new(field.name());
|
|
140
|
+
let mut builder = TimestampMicrosecondBuilder::new();
|
|
141
|
+
for row in data.into_iter() {
|
|
142
|
+
let row = RHash::try_convert(row)?;
|
|
143
|
+
let value: Option<Value> = row.aref(name)?;
|
|
144
|
+
match value {
|
|
145
|
+
Some(v) => {
|
|
146
|
+
let sec: i64 = v.funcall("to_i", ())?;
|
|
147
|
+
let usec: i64 = v.funcall("usec", ())?;
|
|
148
|
+
builder.append_value(sec * 1_000_000 + usec);
|
|
149
|
+
}
|
|
150
|
+
None => builder.append_null(),
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
let array: Arc<dyn Array> = Arc::new(builder.finish());
|
|
154
|
+
Ok(array)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn new_array_large_binary(
|
|
158
|
+
ruby: &Ruby,
|
|
159
|
+
data: RArray,
|
|
160
|
+
field: &Arc<ArrowField>,
|
|
161
|
+
) -> RbResult<Arc<dyn Array>> {
|
|
162
|
+
let name = ruby.str_new(field.name());
|
|
163
|
+
let mut builder = LargeBinaryBuilder::new();
|
|
164
|
+
for row in data.into_iter() {
|
|
165
|
+
let row = RHash::try_convert(row)?;
|
|
166
|
+
let value: Option<RString> = row.aref(name)?;
|
|
167
|
+
match value {
|
|
168
|
+
Some(v) => builder.append_value(unsafe { v.as_slice() }),
|
|
169
|
+
None => builder.append_null(),
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
let array: Arc<dyn Array> = Arc::new(builder.finish());
|
|
173
|
+
Ok(array)
|
|
174
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
use std::any::Any;
|
|
2
|
+
|
|
3
|
+
#[magnus::wrap(class = "Iceberg::Capsule")]
|
|
4
|
+
pub struct RbCapsule {
|
|
5
|
+
value: Box<dyn Any + Send>,
|
|
6
|
+
name: Option<String>,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
impl RbCapsule {
|
|
10
|
+
pub fn new<T: 'static + Send>(value: T, name: Option<String>) -> Self {
|
|
11
|
+
Self {
|
|
12
|
+
value: Box::new(value),
|
|
13
|
+
name,
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pub fn to_i(&self) -> usize {
|
|
18
|
+
(&*self.value as *const dyn Any as *const ()) as usize
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pub fn name(&self) -> Option<&str> {
|
|
22
|
+
self.name.as_deref()
|
|
23
|
+
}
|
|
24
|
+
}
|