iceberg 0.11.2 → 0.12.0
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 +11 -0
- data/Cargo.lock +799 -270
- data/Cargo.toml +7 -0
- data/NOTICE.txt +2 -2
- data/README.md +32 -12
- data/ext/iceberg/Cargo.toml +16 -13
- 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 +160 -33
- data/ext/iceberg/src/encryption.rs +32 -0
- data/ext/iceberg/src/error.rs +50 -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 +305 -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 +222 -164
- data/lib/iceberg/catalog.rb +54 -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,7 +31,7 @@ catalog.create_namespace("main")
|
|
|
31
31
|
Create a table
|
|
32
32
|
|
|
33
33
|
```ruby
|
|
34
|
-
catalog.create_table("
|
|
34
|
+
catalog.create_table("events") do |t|
|
|
35
35
|
t.bigint "id"
|
|
36
36
|
t.float "value"
|
|
37
37
|
end
|
|
@@ -41,14 +41,14 @@ Or with [Polars](https://github.com/ankane/ruby-polars)
|
|
|
41
41
|
|
|
42
42
|
```ruby
|
|
43
43
|
df = Polars::DataFrame.new({"id" => [1, 2], "value" => [3.0, 4.0]})
|
|
44
|
-
table = catalog.create_table("
|
|
44
|
+
table = catalog.create_table("events", schema: df.schema)
|
|
45
45
|
table.append(df)
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Load a table
|
|
49
49
|
|
|
50
50
|
```ruby
|
|
51
|
-
table = catalog.load_table("
|
|
51
|
+
table = catalog.load_table("events")
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
Query a table
|
|
@@ -67,6 +67,26 @@ Iceberg::RestCatalog.new(
|
|
|
67
67
|
)
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
### S3 Tables
|
|
71
|
+
|
|
72
|
+
[unreleased]
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
Iceberg::S3TablesCatalog.new(
|
|
76
|
+
arn: "arn:aws:s3tables:..."
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Glue
|
|
81
|
+
|
|
82
|
+
[unreleased]
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
Iceberg::GlueCatalog.new(
|
|
86
|
+
warehouse: "s3://my-bucket"
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
70
90
|
### SQL
|
|
71
91
|
|
|
72
92
|
```ruby
|
|
@@ -133,7 +153,7 @@ catalog.list_tables("main")
|
|
|
133
153
|
Create a table
|
|
134
154
|
|
|
135
155
|
```ruby
|
|
136
|
-
catalog.create_table("
|
|
156
|
+
catalog.create_table("events") do |t|
|
|
137
157
|
t.integer "id"
|
|
138
158
|
t.float "value"
|
|
139
159
|
end
|
|
@@ -142,31 +162,31 @@ end
|
|
|
142
162
|
Load a table
|
|
143
163
|
|
|
144
164
|
```ruby
|
|
145
|
-
catalog.load_table("
|
|
165
|
+
catalog.load_table("events")
|
|
146
166
|
```
|
|
147
167
|
|
|
148
168
|
Check if a table exists
|
|
149
169
|
|
|
150
170
|
```ruby
|
|
151
|
-
catalog.table_exists?("
|
|
171
|
+
catalog.table_exists?("events")
|
|
152
172
|
```
|
|
153
173
|
|
|
154
174
|
Rename a table
|
|
155
175
|
|
|
156
176
|
```ruby
|
|
157
|
-
catalog.rename_table("
|
|
177
|
+
catalog.rename_table("events", "events2")
|
|
158
178
|
```
|
|
159
179
|
|
|
160
180
|
Register a table
|
|
161
181
|
|
|
162
182
|
```ruby
|
|
163
|
-
catalog.register_table("
|
|
183
|
+
catalog.register_table("events", "metadata.json")
|
|
164
184
|
```
|
|
165
185
|
|
|
166
186
|
Drop a table
|
|
167
187
|
|
|
168
188
|
```ruby
|
|
169
|
-
catalog.drop_table("
|
|
189
|
+
catalog.drop_table("events")
|
|
170
190
|
```
|
|
171
191
|
|
|
172
192
|
## Static Tables
|
|
@@ -196,7 +216,7 @@ To get started with development:
|
|
|
196
216
|
git clone https://github.com/ankane/iceberg-ruby.git
|
|
197
217
|
cd iceberg-ruby
|
|
198
218
|
bundle install
|
|
199
|
-
bundle exec rake compile
|
|
219
|
+
bundle exec rake compile:dev
|
|
200
220
|
|
|
201
221
|
# memory catalog
|
|
202
222
|
bundle exec rake test:memory
|
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.0"
|
|
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.94"
|
|
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"
|
|
16
|
+
arrow-array = { version = "58", features = ["ffi"] }
|
|
17
|
+
arrow-cast = "58"
|
|
18
|
+
arrow-schema = "58"
|
|
19
|
+
datafusion = { version = "53", 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.
|
|
21
|
+
iceberg = "=0.10.0"
|
|
22
|
+
iceberg-catalog-glue = { version = "=0.10.0", optional = true }
|
|
23
|
+
iceberg-catalog-rest = { version = "=0.10.0", optional = true }
|
|
24
|
+
iceberg-catalog-s3tables = { version = "=0.10.0", optional = true }
|
|
25
|
+
iceberg-catalog-sql = { version = "=0.10.0", optional = true }
|
|
26
|
+
iceberg-datafusion = { version = "=0.10.0", optional = true }
|
|
27
|
+
iceberg-storage-opendal = "=0.10.0"
|
|
25
28
|
magnus = "0.8"
|
|
26
|
-
parquet = "
|
|
29
|
+
parquet = "58"
|
|
27
30
|
rb-sys = "0.9"
|
|
28
31
|
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "sqlite"], 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
|
+
}
|