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/ext/iceberg/src/runtime.rs
CHANGED
|
@@ -26,8 +26,12 @@ pub fn runtime() -> Handle {
|
|
|
26
26
|
match Handle::try_current() {
|
|
27
27
|
Ok(h) => h.clone(),
|
|
28
28
|
_ => {
|
|
29
|
-
let rt =
|
|
29
|
+
let rt = tokio_runtime();
|
|
30
30
|
rt.handle().clone()
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
pub fn tokio_runtime() -> &'static Runtime {
|
|
36
|
+
RUNTIME.get_or_init(|| Runtime::new().unwrap())
|
|
37
|
+
}
|
data/ext/iceberg/src/scan.rs
CHANGED
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
use futures::TryStreamExt;
|
|
2
|
-
use iceberg::scan::TableScan;
|
|
3
|
-
use magnus::{RArray, Ruby, Value};
|
|
4
1
|
use std::sync::RwLock;
|
|
5
2
|
|
|
3
|
+
use arrow_array::RecordBatchIterator;
|
|
4
|
+
use arrow_array::ffi_stream::FFI_ArrowArrayStream;
|
|
5
|
+
use futures::TryStreamExt;
|
|
6
|
+
use iceberg::scan::{FileScanTask, TableScan};
|
|
7
|
+
use magnus::{IntoValue, RArray, Ruby, Value, value::ReprValue};
|
|
8
|
+
|
|
6
9
|
use crate::RbResult;
|
|
10
|
+
use crate::capsule::RbCapsule;
|
|
7
11
|
use crate::error::to_rb_err;
|
|
12
|
+
use crate::result::collect_batches;
|
|
8
13
|
use crate::ruby::GvlExt;
|
|
9
14
|
use crate::runtime::runtime;
|
|
10
|
-
use crate::
|
|
15
|
+
use crate::snapshot::RbSnapshot;
|
|
11
16
|
|
|
12
17
|
#[magnus::wrap(class = "Iceberg::RbTableScan")]
|
|
13
18
|
pub struct RbTableScan {
|
|
14
19
|
pub scan: RwLock<TableScan>,
|
|
15
20
|
}
|
|
16
21
|
|
|
22
|
+
#[magnus::wrap(class = "Iceberg::FileScanTask")]
|
|
23
|
+
pub struct RbFileScanTask {
|
|
24
|
+
pub scan: FileScanTask,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#[magnus::wrap(class = "Iceberg::DataFile")]
|
|
28
|
+
pub struct RbDataFile {
|
|
29
|
+
pub file_path: String,
|
|
30
|
+
pub record_count: Option<u64>,
|
|
31
|
+
pub file_size_in_bytes: u64,
|
|
32
|
+
pub equality_ids: Option<Vec<i32>>,
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
impl RbTableScan {
|
|
18
36
|
pub fn plan_files(ruby: &Ruby, rb_self: &Self) -> RbResult<RArray> {
|
|
19
37
|
let plan_files: Vec<_> = ruby
|
|
@@ -27,32 +45,94 @@ impl RbTableScan {
|
|
|
27
45
|
|
|
28
46
|
let files = ruby.ary_new();
|
|
29
47
|
for v in plan_files {
|
|
30
|
-
|
|
31
|
-
file.aset(ruby.to_symbol("start"), v.start)?;
|
|
32
|
-
file.aset(ruby.to_symbol("length"), v.length)?;
|
|
33
|
-
file.aset(ruby.to_symbol("record_count"), v.record_count)?;
|
|
34
|
-
file.aset(ruby.to_symbol("data_file_path"), v.data_file_path)?;
|
|
35
|
-
file.aset(ruby.to_symbol("project_field_ids"), v.project_field_ids)?;
|
|
36
|
-
|
|
37
|
-
let deletes = ruby.ary_new();
|
|
38
|
-
for d in v.deletes {
|
|
39
|
-
let delete = ruby.hash_new();
|
|
40
|
-
delete.aset(ruby.to_symbol("file_path"), d.file_path)?;
|
|
41
|
-
delete.aset(ruby.to_symbol("partition_spec_id"), d.partition_spec_id)?;
|
|
42
|
-
delete.aset(ruby.to_symbol("equality_ids"), d.equality_ids)?;
|
|
43
|
-
deletes.push(delete)?;
|
|
44
|
-
}
|
|
45
|
-
file.aset(ruby.to_symbol("deletes"), deletes)?;
|
|
46
|
-
|
|
47
|
-
files.push(file)?;
|
|
48
|
+
files.push(RbFileScanTask { scan: v })?;
|
|
48
49
|
}
|
|
49
50
|
Ok(files)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
pub fn snapshot(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
pub fn snapshot(&self) -> Option<RbSnapshot> {
|
|
54
|
+
self.scan.read().unwrap().snapshot().map(|v| v.into())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pub fn collect(ruby: &Ruby, rb_self: &Self) -> RbResult<Value> {
|
|
58
|
+
let runtime = runtime();
|
|
59
|
+
let scan = rb_self.scan.read().unwrap();
|
|
60
|
+
let stream = runtime.block_on(scan.to_arrow()).map_err(to_rb_err)?;
|
|
61
|
+
let batches: Vec<_> = runtime.block_on(stream.try_collect()).map_err(to_rb_err)?;
|
|
62
|
+
collect_batches(ruby, batches)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pub fn arrow_c_stream(&self) -> RbResult<RbCapsule> {
|
|
66
|
+
let runtime = runtime();
|
|
67
|
+
let scan = self.scan.read().unwrap();
|
|
68
|
+
let stream = runtime.block_on(scan.to_arrow()).map_err(to_rb_err)?;
|
|
69
|
+
let batches: Vec<_> = runtime.block_on(stream.try_collect()).map_err(to_rb_err)?;
|
|
70
|
+
let stream = if batches.is_empty() {
|
|
71
|
+
// TODO fix schema
|
|
72
|
+
FFI_ArrowArrayStream::empty()
|
|
73
|
+
} else {
|
|
74
|
+
let schema = batches[0].schema();
|
|
75
|
+
let reader = RecordBatchIterator::new(batches.into_iter().map(Ok), schema);
|
|
76
|
+
FFI_ArrowArrayStream::new(Box::new(reader))
|
|
77
|
+
};
|
|
78
|
+
Ok(RbCapsule::new(stream, Some("arrow_array_stream".into())))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
impl RbFileScanTask {
|
|
83
|
+
pub fn file(&self) -> RbDataFile {
|
|
84
|
+
RbDataFile {
|
|
85
|
+
file_path: self.scan.data_file_path.clone(),
|
|
86
|
+
record_count: self.scan.record_count,
|
|
87
|
+
file_size_in_bytes: self.scan.file_size_in_bytes,
|
|
88
|
+
equality_ids: None,
|
|
56
89
|
}
|
|
57
90
|
}
|
|
91
|
+
|
|
92
|
+
pub fn delete_files(ruby: &Ruby, rb_self: &Self) -> RArray {
|
|
93
|
+
ruby.ary_from_iter(rb_self.scan.deletes.iter().map(|v| RbDataFile {
|
|
94
|
+
file_path: v.file_path.clone(),
|
|
95
|
+
record_count: None,
|
|
96
|
+
file_size_in_bytes: v.file_size_in_bytes,
|
|
97
|
+
equality_ids: v.equality_ids.clone(),
|
|
98
|
+
}))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
102
|
+
format!(
|
|
103
|
+
"#<Iceberg::FileScanTask file={}, delete_files={}>",
|
|
104
|
+
rb_self.file().into_value_with(ruby).inspect(),
|
|
105
|
+
Self::delete_files(ruby, rb_self)
|
|
106
|
+
.into_value_with(ruby)
|
|
107
|
+
.inspect(),
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
impl RbDataFile {
|
|
113
|
+
pub fn file_path(&self) -> &str {
|
|
114
|
+
&self.file_path
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
pub fn record_count(&self) -> Option<u64> {
|
|
118
|
+
self.record_count
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
pub fn file_size_in_bytes(&self) -> u64 {
|
|
122
|
+
self.file_size_in_bytes
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pub fn equality_ids(&self) -> Option<Vec<i32>> {
|
|
126
|
+
self.equality_ids.clone()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
130
|
+
format!(
|
|
131
|
+
"#<Iceberg::DataFile file_path={}, record_count={}, file_size_in_bytes={}, equality_ids={}>",
|
|
132
|
+
rb_self.file_path().into_value_with(ruby).inspect(),
|
|
133
|
+
rb_self.record_count().into_value_with(ruby).inspect(),
|
|
134
|
+
rb_self.file_size_in_bytes().into_value_with(ruby).inspect(),
|
|
135
|
+
rb_self.equality_ids().into_value_with(ruby).inspect(),
|
|
136
|
+
)
|
|
137
|
+
}
|
|
58
138
|
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
|
|
3
|
+
use arrow_schema::Schema as ArrowSchema;
|
|
4
|
+
use arrow_schema::ffi::FFI_ArrowSchema;
|
|
5
|
+
use iceberg::arrow::{arrow_schema_to_schema_auto_assign_ids, schema_to_arrow_schema};
|
|
6
|
+
use iceberg::spec::{ListType, MapType, NestedField, PrimitiveType, Schema, StructType, Type};
|
|
7
|
+
use magnus::{
|
|
8
|
+
Error as RbErr, IntoValue, RArray, RClass, RHash, RModule, Ruby, TryConvert, Value, prelude::*,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
use crate::RbResult;
|
|
12
|
+
use crate::capsule::RbCapsule;
|
|
13
|
+
use crate::error::to_rb_err;
|
|
14
|
+
use crate::utils::{Wrap, default_value, rb_literal};
|
|
15
|
+
|
|
16
|
+
#[magnus::wrap(class = "Iceberg::Schema")]
|
|
17
|
+
pub struct RbSchema {
|
|
18
|
+
pub(crate) schema: Schema,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[magnus::wrap(class = "Iceberg::NestedField")]
|
|
22
|
+
pub struct RbNestedField {
|
|
23
|
+
pub(crate) field: Arc<NestedField>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl RbSchema {
|
|
27
|
+
pub fn new(args: &[Value]) -> RbResult<Self> {
|
|
28
|
+
let schema = if args.len() == 1
|
|
29
|
+
&& let Ok(arrow_schema) =
|
|
30
|
+
args[0].funcall::<_, _, Wrap<ArrowSchema>>("arrow_c_schema", ())
|
|
31
|
+
{
|
|
32
|
+
arrow_schema_to_schema_auto_assign_ids(&arrow_schema.0).map_err(to_rb_err)?
|
|
33
|
+
} else {
|
|
34
|
+
let mut fields = Vec::new();
|
|
35
|
+
for rb_field in args {
|
|
36
|
+
fields.push(<&RbNestedField>::try_convert(*rb_field)?.field.clone());
|
|
37
|
+
}
|
|
38
|
+
Schema::builder()
|
|
39
|
+
.with_fields(fields)
|
|
40
|
+
.build()
|
|
41
|
+
.map_err(to_rb_err)?
|
|
42
|
+
};
|
|
43
|
+
Ok(Self { schema })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn fields(ruby: &Ruby, rb_self: &Self) -> RArray {
|
|
47
|
+
ruby.ary_from_iter(
|
|
48
|
+
rb_self
|
|
49
|
+
.schema
|
|
50
|
+
.as_struct()
|
|
51
|
+
.fields()
|
|
52
|
+
.iter()
|
|
53
|
+
.map(RbNestedField::from),
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pub fn schema_id(&self) -> i32 {
|
|
58
|
+
self.schema.schema_id()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pub fn identifier_field_ids(&self) -> Vec<i32> {
|
|
62
|
+
self.schema.identifier_field_ids().collect()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
pub fn highest_field_id(&self) -> i32 {
|
|
66
|
+
self.schema.highest_field_id()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pub fn as_struct(ruby: &Ruby, rb_self: &Self) -> RbResult<Value> {
|
|
70
|
+
let iceberg = ruby.class_object().const_get::<_, RModule>("Iceberg")?;
|
|
71
|
+
let fields = Self::fields(ruby, rb_self);
|
|
72
|
+
iceberg
|
|
73
|
+
.const_get::<_, RClass>("StructType")?
|
|
74
|
+
.new_instance(unsafe { fields.as_slice() })
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
pub fn arrow_c_schema(&self) -> RbResult<RbCapsule> {
|
|
78
|
+
let schema = schema_to_arrow_schema(&self.schema).map_err(to_rb_err)?;
|
|
79
|
+
let schema = FFI_ArrowSchema::try_from(&schema).unwrap();
|
|
80
|
+
Ok(RbCapsule::new(schema, Some("arrow_schema".into())))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub fn eq(&self, other: &Self) -> bool {
|
|
84
|
+
self.schema == other.schema
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
88
|
+
format!(
|
|
89
|
+
"#<Iceberg::Schema fields={}, schema_id={}, identifier_field_ids={}>",
|
|
90
|
+
Self::fields(ruby, rb_self).inspect(),
|
|
91
|
+
rb_self.schema_id().into_value_with(ruby).inspect(),
|
|
92
|
+
rb_self
|
|
93
|
+
.identifier_field_ids()
|
|
94
|
+
.into_value_with(ruby)
|
|
95
|
+
.inspect(),
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
impl RbNestedField {
|
|
101
|
+
pub fn new(ruby: &Ruby, rb_field: RHash) -> RbResult<Self> {
|
|
102
|
+
let rb_type: Value = rb_field.aref(ruby.to_symbol("field_type"))?;
|
|
103
|
+
let field_type = match &*unsafe { rb_type.classname() } {
|
|
104
|
+
"Iceberg::BooleanType" => Type::Primitive(PrimitiveType::Boolean),
|
|
105
|
+
"Iceberg::IntType" => Type::Primitive(PrimitiveType::Int),
|
|
106
|
+
"Iceberg::LongType" => Type::Primitive(PrimitiveType::Long),
|
|
107
|
+
"Iceberg::FloatType" => Type::Primitive(PrimitiveType::Float),
|
|
108
|
+
"Iceberg::DoubleType" => Type::Primitive(PrimitiveType::Double),
|
|
109
|
+
"Iceberg::DecimalType" => {
|
|
110
|
+
let precision: u32 = rb_type.funcall("precision", ())?;
|
|
111
|
+
let scale: u32 = rb_type.funcall("scale", ())?;
|
|
112
|
+
Type::Primitive(PrimitiveType::Decimal { precision, scale })
|
|
113
|
+
}
|
|
114
|
+
"Iceberg::DateType" => Type::Primitive(PrimitiveType::Date),
|
|
115
|
+
"Iceberg::TimeType" => Type::Primitive(PrimitiveType::Time),
|
|
116
|
+
"Iceberg::TimestampType" => Type::Primitive(PrimitiveType::Timestamp),
|
|
117
|
+
"Iceberg::TimestamptzType" => Type::Primitive(PrimitiveType::Timestamptz),
|
|
118
|
+
"Iceberg::TimestampNanoType" => Type::Primitive(PrimitiveType::TimestampNs),
|
|
119
|
+
"Iceberg::TimestamptzNanoType" => Type::Primitive(PrimitiveType::TimestamptzNs),
|
|
120
|
+
"Iceberg::StringType" => Type::Primitive(PrimitiveType::String),
|
|
121
|
+
"Iceberg::UUIDType" => Type::Primitive(PrimitiveType::Uuid),
|
|
122
|
+
"Iceberg::FixedType" => {
|
|
123
|
+
let length: u64 = rb_type.funcall("length", ())?;
|
|
124
|
+
Type::Primitive(PrimitiveType::Fixed(length))
|
|
125
|
+
}
|
|
126
|
+
"Iceberg::BinaryType" => Type::Primitive(PrimitiveType::Binary),
|
|
127
|
+
"Iceberg::StructType" => {
|
|
128
|
+
let fields = rb_type
|
|
129
|
+
.funcall::<_, _, RArray>("fields", ())?
|
|
130
|
+
.typecheck::<&RbNestedField>()?
|
|
131
|
+
.into_iter()
|
|
132
|
+
.map(|v| v.field.clone())
|
|
133
|
+
.collect::<Vec<_>>();
|
|
134
|
+
Type::Struct(StructType::new(fields))
|
|
135
|
+
}
|
|
136
|
+
"Iceberg::ListType" => {
|
|
137
|
+
let element_field = rb_type
|
|
138
|
+
.funcall::<_, _, &RbNestedField>("element_field", ())?
|
|
139
|
+
.field
|
|
140
|
+
.clone();
|
|
141
|
+
Type::List(ListType::new(element_field))
|
|
142
|
+
}
|
|
143
|
+
"Iceberg::MapType" => {
|
|
144
|
+
let key_field = rb_type
|
|
145
|
+
.funcall::<_, _, &RbNestedField>("key_field", ())?
|
|
146
|
+
.field
|
|
147
|
+
.clone();
|
|
148
|
+
let value_field = rb_type
|
|
149
|
+
.funcall::<_, _, &RbNestedField>("value_field", ())?
|
|
150
|
+
.field
|
|
151
|
+
.clone();
|
|
152
|
+
Type::Map(MapType::new(key_field, value_field))
|
|
153
|
+
}
|
|
154
|
+
_ => {
|
|
155
|
+
return Err(RbErr::new(
|
|
156
|
+
ruby.exception_arg_error(),
|
|
157
|
+
format!("Type not supported: {}", rb_type),
|
|
158
|
+
));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
let initial_default = rb_field.aref(ruby.to_symbol("initial_default"))?;
|
|
163
|
+
let write_default = rb_field.aref(ruby.to_symbol("write_default"))?;
|
|
164
|
+
|
|
165
|
+
let initial_default = default_value(initial_default, &field_type)?;
|
|
166
|
+
let write_default = default_value(write_default, &field_type)?;
|
|
167
|
+
|
|
168
|
+
let field = NestedField {
|
|
169
|
+
id: rb_field.aref(ruby.to_symbol("field_id"))?,
|
|
170
|
+
name: rb_field.aref(ruby.to_symbol("name"))?,
|
|
171
|
+
required: rb_field.aref(ruby.to_symbol("required"))?,
|
|
172
|
+
field_type: field_type.into(),
|
|
173
|
+
doc: rb_field.aref(ruby.to_symbol("doc"))?,
|
|
174
|
+
initial_default,
|
|
175
|
+
write_default,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
Ok(Self {
|
|
179
|
+
field: field.into(),
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
pub fn field_id(&self) -> i32 {
|
|
184
|
+
self.field.id
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
pub fn name(&self) -> &str {
|
|
188
|
+
&self.field.name
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
pub fn field_type(ruby: &Ruby, rb_self: &Self) -> RbResult<Value> {
|
|
192
|
+
let iceberg = ruby.class_object().const_get::<_, RModule>("Iceberg")?;
|
|
193
|
+
let field_type = &*rb_self.field.field_type;
|
|
194
|
+
let v = match field_type {
|
|
195
|
+
Type::Primitive(ty) => match ty {
|
|
196
|
+
PrimitiveType::Boolean => iceberg
|
|
197
|
+
.const_get::<_, RClass>("BooleanType")?
|
|
198
|
+
.new_instance(())?,
|
|
199
|
+
PrimitiveType::Int => iceberg
|
|
200
|
+
.const_get::<_, RClass>("IntType")?
|
|
201
|
+
.new_instance(())?,
|
|
202
|
+
PrimitiveType::Long => iceberg
|
|
203
|
+
.const_get::<_, RClass>("LongType")?
|
|
204
|
+
.new_instance(())?,
|
|
205
|
+
PrimitiveType::Float => iceberg
|
|
206
|
+
.const_get::<_, RClass>("FloatType")?
|
|
207
|
+
.new_instance(())?,
|
|
208
|
+
PrimitiveType::Double => iceberg
|
|
209
|
+
.const_get::<_, RClass>("DoubleType")?
|
|
210
|
+
.new_instance(())?,
|
|
211
|
+
PrimitiveType::Decimal { precision, scale } => iceberg
|
|
212
|
+
.const_get::<_, RClass>("DecimalType")?
|
|
213
|
+
.new_instance((*precision, *scale))?,
|
|
214
|
+
PrimitiveType::Date => iceberg
|
|
215
|
+
.const_get::<_, RClass>("DateType")?
|
|
216
|
+
.new_instance(())?,
|
|
217
|
+
PrimitiveType::Time => iceberg
|
|
218
|
+
.const_get::<_, RClass>("TimeType")?
|
|
219
|
+
.new_instance(())?,
|
|
220
|
+
PrimitiveType::Timestamp => iceberg
|
|
221
|
+
.const_get::<_, RClass>("TimestampType")?
|
|
222
|
+
.new_instance(())?,
|
|
223
|
+
PrimitiveType::Timestamptz => iceberg
|
|
224
|
+
.const_get::<_, RClass>("TimestamptzType")?
|
|
225
|
+
.new_instance(())?,
|
|
226
|
+
PrimitiveType::TimestampNs => iceberg
|
|
227
|
+
.const_get::<_, RClass>("TimestampNanoType")?
|
|
228
|
+
.new_instance(())?,
|
|
229
|
+
PrimitiveType::TimestamptzNs => iceberg
|
|
230
|
+
.const_get::<_, RClass>("TimestamptzNanoType")?
|
|
231
|
+
.new_instance(())?,
|
|
232
|
+
PrimitiveType::String => iceberg
|
|
233
|
+
.const_get::<_, RClass>("StringType")?
|
|
234
|
+
.new_instance(())?,
|
|
235
|
+
PrimitiveType::Uuid => iceberg
|
|
236
|
+
.const_get::<_, RClass>("UUIDType")?
|
|
237
|
+
.new_instance(())?,
|
|
238
|
+
PrimitiveType::Fixed(length) => iceberg
|
|
239
|
+
.const_get::<_, RClass>("FixedType")?
|
|
240
|
+
.new_instance((*length,))?,
|
|
241
|
+
PrimitiveType::Binary => iceberg
|
|
242
|
+
.const_get::<_, RClass>("BinaryType")?
|
|
243
|
+
.new_instance(())?,
|
|
244
|
+
},
|
|
245
|
+
Type::Struct(ty) => iceberg
|
|
246
|
+
.const_get::<_, RClass>("StructType")?
|
|
247
|
+
.new_instance((ruby.ary_from_iter(ty.fields().iter().map(RbNestedField::from)),))?,
|
|
248
|
+
Type::List(ty) => iceberg
|
|
249
|
+
.const_get::<_, RClass>("ListType")?
|
|
250
|
+
.new_instance((RbNestedField::from(&ty.element_field),))?,
|
|
251
|
+
Type::Map(ty) => iceberg.const_get::<_, RClass>("MapType")?.new_instance((
|
|
252
|
+
RbNestedField::from(&ty.key_field),
|
|
253
|
+
RbNestedField::from(&ty.value_field),
|
|
254
|
+
))?,
|
|
255
|
+
};
|
|
256
|
+
Ok(v)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
pub fn required(&self) -> bool {
|
|
260
|
+
self.field.required
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
pub fn doc(&self) -> Option<&str> {
|
|
264
|
+
self.field.doc.as_deref()
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
pub fn initial_default(ruby: &Ruby, rb_self: &Self) -> RbResult<Option<Value>> {
|
|
268
|
+
rb_self
|
|
269
|
+
.field
|
|
270
|
+
.initial_default
|
|
271
|
+
.as_ref()
|
|
272
|
+
.map(|v| rb_literal(ruby, v))
|
|
273
|
+
.transpose()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
pub fn write_default(ruby: &Ruby, rb_self: &Self) -> RbResult<Option<Value>> {
|
|
277
|
+
rb_self
|
|
278
|
+
.field
|
|
279
|
+
.write_default
|
|
280
|
+
.as_ref()
|
|
281
|
+
.map(|v| rb_literal(ruby, v))
|
|
282
|
+
.transpose()
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
pub fn eq(&self, other: &Self) -> bool {
|
|
286
|
+
self.field == other.field
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> RbResult<String> {
|
|
290
|
+
Ok(format!(
|
|
291
|
+
"#<Iceberg::NestedField field_id={}, name={}, field_type={}, required={}, doc={}, initial_default={}, write_default={}>",
|
|
292
|
+
rb_self.field_id().into_value_with(ruby).inspect(),
|
|
293
|
+
rb_self.name().into_value_with(ruby).inspect(),
|
|
294
|
+
Self::field_type(ruby, rb_self)?.inspect(),
|
|
295
|
+
rb_self.required().into_value_with(ruby).inspect(),
|
|
296
|
+
rb_self.doc().into_value_with(ruby).inspect(),
|
|
297
|
+
Self::initial_default(ruby, rb_self)?
|
|
298
|
+
.into_value_with(ruby)
|
|
299
|
+
.inspect(),
|
|
300
|
+
Self::write_default(ruby, rb_self)?
|
|
301
|
+
.into_value_with(ruby)
|
|
302
|
+
.inspect(),
|
|
303
|
+
))
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
use iceberg::spec::{MetadataLog, Snapshot, SnapshotLog};
|
|
2
|
+
use magnus::{IntoValue, Ruby, value::ReprValue};
|
|
3
|
+
|
|
4
|
+
#[magnus::wrap(class = "Iceberg::Snapshot")]
|
|
5
|
+
pub struct RbSnapshot {
|
|
6
|
+
pub(crate) snapshot: Snapshot,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[magnus::wrap(class = "Iceberg::SnapshotLogEntry")]
|
|
10
|
+
pub struct RbSnapshotLogEntry {
|
|
11
|
+
pub(crate) log: SnapshotLog,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[magnus::wrap(class = "Iceberg::MetadataLogEntry")]
|
|
15
|
+
pub struct RbMetadataLogEntry {
|
|
16
|
+
pub(crate) log: MetadataLog,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
impl RbSnapshot {
|
|
20
|
+
pub fn snapshot_id(&self) -> i64 {
|
|
21
|
+
self.snapshot.snapshot_id()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn parent_snapshot_id(&self) -> Option<i64> {
|
|
25
|
+
self.snapshot.parent_snapshot_id()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn sequence_number(&self) -> i64 {
|
|
29
|
+
self.snapshot.sequence_number()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn manifest_list(&self) -> &str {
|
|
33
|
+
self.snapshot.manifest_list()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn schema_id(&self) -> Option<i32> {
|
|
37
|
+
self.snapshot.schema_id()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
41
|
+
format!(
|
|
42
|
+
"#<Iceberg::Snapshot snapshot_id={}, parent_snapshot_id={}, sequence_number={}, schema_id={}>",
|
|
43
|
+
rb_self.snapshot_id().into_value_with(ruby).inspect(),
|
|
44
|
+
rb_self.parent_snapshot_id().into_value_with(ruby).inspect(),
|
|
45
|
+
rb_self.sequence_number().into_value_with(ruby).inspect(),
|
|
46
|
+
rb_self.schema_id().into_value_with(ruby).inspect(),
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
impl RbSnapshotLogEntry {
|
|
52
|
+
pub fn snapshot_id(&self) -> i64 {
|
|
53
|
+
self.log.snapshot_id
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pub fn timestamp_ms(&self) -> i64 {
|
|
57
|
+
self.log.timestamp_ms
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
61
|
+
format!(
|
|
62
|
+
"#<Iceberg::SnapshotLogEntry snapshot_id={}, timestamp_ms={}>",
|
|
63
|
+
rb_self.snapshot_id().into_value_with(ruby).inspect(),
|
|
64
|
+
rb_self.timestamp_ms().into_value_with(ruby).inspect(),
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
impl RbMetadataLogEntry {
|
|
70
|
+
pub fn metadata_file(&self) -> &str {
|
|
71
|
+
&self.log.metadata_file
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
pub fn timestamp_ms(&self) -> i64 {
|
|
75
|
+
self.log.timestamp_ms
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
79
|
+
format!(
|
|
80
|
+
"#<Iceberg::MetadataLogEntry metadata_file={}, timestamp_ms={}>",
|
|
81
|
+
rb_self.metadata_file().into_value_with(ruby).inspect(),
|
|
82
|
+
rb_self.timestamp_ms().into_value_with(ruby).inspect(),
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
use iceberg::spec::{NullOrder, SortDirection, SortField, SortOrder, Transform};
|
|
2
|
+
use magnus::{IntoValue, RArray, RHash, Ruby, TryConvert, Value, value::ReprValue};
|
|
3
|
+
|
|
4
|
+
use crate::RbResult;
|
|
5
|
+
use crate::error::to_rb_err;
|
|
6
|
+
use crate::utils::{Wrap, rb_transform};
|
|
7
|
+
|
|
8
|
+
#[magnus::wrap(class = "Iceberg::SortOrder")]
|
|
9
|
+
pub struct RbSortOrder {
|
|
10
|
+
pub(crate) order: SortOrder,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#[magnus::wrap(class = "Iceberg::SortField")]
|
|
14
|
+
pub struct RbSortField {
|
|
15
|
+
pub(crate) field: SortField,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl RbSortOrder {
|
|
19
|
+
pub fn new(args: &[Value]) -> RbResult<Self> {
|
|
20
|
+
let mut fields = Vec::new();
|
|
21
|
+
for v in args {
|
|
22
|
+
fields.push(<&RbSortField>::try_convert(*v)?.field.clone());
|
|
23
|
+
}
|
|
24
|
+
let order = SortOrder::builder()
|
|
25
|
+
.with_fields(fields)
|
|
26
|
+
.build_unbound()
|
|
27
|
+
.map_err(to_rb_err)?;
|
|
28
|
+
Ok(Self { order })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
pub fn order_id(&self) -> i64 {
|
|
32
|
+
self.order.order_id
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pub fn fields(ruby: &Ruby, rb_self: &Self) -> RArray {
|
|
36
|
+
ruby.ary_from_iter(
|
|
37
|
+
rb_self
|
|
38
|
+
.order
|
|
39
|
+
.fields
|
|
40
|
+
.iter()
|
|
41
|
+
.map(|v| RbSortField { field: v.clone() }),
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pub fn eq(&self, other: &Self) -> bool {
|
|
46
|
+
self.order == other.order
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
50
|
+
format!(
|
|
51
|
+
"#<Iceberg::SortOrder order_id={}, fields={}>",
|
|
52
|
+
rb_self.order_id().into_value_with(ruby).inspect(),
|
|
53
|
+
Self::fields(ruby, rb_self).inspect(),
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
impl RbSortField {
|
|
59
|
+
pub fn new(ruby: &Ruby, ob: RHash) -> RbResult<Self> {
|
|
60
|
+
let transform = ob
|
|
61
|
+
.aref::<_, Wrap<Transform>>(ruby.to_symbol("transform"))?
|
|
62
|
+
.0;
|
|
63
|
+
|
|
64
|
+
let direction = ob
|
|
65
|
+
.aref::<_, Option<Wrap<SortDirection>>>(ruby.to_symbol("direction"))?
|
|
66
|
+
.map(|v| v.0)
|
|
67
|
+
.unwrap_or(SortDirection::Ascending);
|
|
68
|
+
|
|
69
|
+
let null_order = ob
|
|
70
|
+
.aref::<_, Option<Wrap<NullOrder>>>(ruby.to_symbol("null_order"))?
|
|
71
|
+
.map(|v| v.0)
|
|
72
|
+
.unwrap_or(if direction == SortDirection::Ascending {
|
|
73
|
+
NullOrder::First
|
|
74
|
+
} else {
|
|
75
|
+
NullOrder::Last
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
let field = SortField::builder()
|
|
79
|
+
.source_id(ob.aref(ruby.to_symbol("source_id"))?)
|
|
80
|
+
.transform(transform)
|
|
81
|
+
.direction(direction)
|
|
82
|
+
.null_order(null_order)
|
|
83
|
+
.build();
|
|
84
|
+
Ok(Self { field })
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pub fn source_id(&self) -> i32 {
|
|
88
|
+
self.field.source_id
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub fn transform(&self) -> RbResult<Value> {
|
|
92
|
+
rb_transform(&self.field.transform)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pub fn direction(&self) -> &str {
|
|
96
|
+
match self.field.direction {
|
|
97
|
+
SortDirection::Ascending => "asc",
|
|
98
|
+
SortDirection::Descending => "desc",
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
pub fn null_order(&self) -> &str {
|
|
103
|
+
match self.field.null_order {
|
|
104
|
+
NullOrder::First => "first",
|
|
105
|
+
NullOrder::Last => "last",
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pub fn eq(&self, other: &Self) -> bool {
|
|
110
|
+
self.field == other.field
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> RbResult<String> {
|
|
114
|
+
Ok(format!(
|
|
115
|
+
"#<Iceberg::SortField source_id={}, transform={}, direction={}, null_order={}>",
|
|
116
|
+
rb_self.source_id().into_value_with(ruby).inspect(),
|
|
117
|
+
rb_self.transform()?.into_value_with(ruby).inspect(),
|
|
118
|
+
rb_self.direction().into_value_with(ruby).inspect(),
|
|
119
|
+
rb_self.null_order().into_value_with(ruby).inspect(),
|
|
120
|
+
))
|
|
121
|
+
}
|
|
122
|
+
}
|