iceberg 0.11.1 → 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 +16 -0
- data/Cargo.lock +838 -455
- data/Cargo.toml +7 -0
- data/NOTICE.txt +2 -2
- data/README.md +32 -12
- data/ext/iceberg/Cargo.toml +17 -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 +223 -49
- data/ext/iceberg/src/encryption.rs +32 -0
- data/ext/iceberg/src/error.rs +50 -17
- data/ext/iceberg/src/lib.rs +265 -41
- data/ext/iceberg/src/partitioning.rs +102 -0
- data/ext/iceberg/src/result.rs +201 -0
- data/ext/iceberg/src/ruby.rs +51 -0
- data/ext/iceberg/src/runtime.rs +5 -1
- data/ext/iceberg/src/scan.rs +116 -33
- 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 +255 -263
- 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 +15 -3
- data/lib/iceberg/schema.rb +0 -10
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
use std::ffi::c_void;
|
|
2
|
+
use std::ptr::null_mut;
|
|
3
|
+
|
|
4
|
+
use magnus::Ruby;
|
|
5
|
+
use rb_sys::rb_thread_call_without_gvl;
|
|
6
|
+
|
|
7
|
+
pub trait GvlExt {
|
|
8
|
+
fn detach<T, F>(&self, func: F) -> T
|
|
9
|
+
where
|
|
10
|
+
F: Send + FnOnce() -> T,
|
|
11
|
+
T: Send;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl GvlExt for Ruby {
|
|
15
|
+
fn detach<T, F>(&self, func: F) -> T
|
|
16
|
+
where
|
|
17
|
+
F: Send + FnOnce() -> T,
|
|
18
|
+
T: Send,
|
|
19
|
+
{
|
|
20
|
+
let mut data = CallbackData {
|
|
21
|
+
func: Some(func),
|
|
22
|
+
result: None,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
unsafe {
|
|
26
|
+
rb_thread_call_without_gvl(
|
|
27
|
+
Some(call_without_gvl::<F, T>),
|
|
28
|
+
&mut data as *mut _ as *mut c_void,
|
|
29
|
+
None,
|
|
30
|
+
null_mut(),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
data.result.unwrap()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
struct CallbackData<F, T> {
|
|
39
|
+
func: Option<F>,
|
|
40
|
+
result: Option<T>,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
extern "C" fn call_without_gvl<F, T>(data: *mut c_void) -> *mut c_void
|
|
44
|
+
where
|
|
45
|
+
F: FnOnce() -> T,
|
|
46
|
+
{
|
|
47
|
+
let data = unsafe { &mut *(data as *mut CallbackData<F, T>) };
|
|
48
|
+
let func = data.func.take().unwrap();
|
|
49
|
+
data.result = Some(func());
|
|
50
|
+
null_mut()
|
|
51
|
+
}
|
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,55 +1,138 @@
|
|
|
1
|
+
use std::sync::RwLock;
|
|
2
|
+
|
|
3
|
+
use arrow_array::RecordBatchIterator;
|
|
4
|
+
use arrow_array::ffi_stream::FFI_ArrowArrayStream;
|
|
1
5
|
use futures::TryStreamExt;
|
|
2
|
-
use iceberg::scan::TableScan;
|
|
3
|
-
use magnus::{RArray, Ruby, Value};
|
|
4
|
-
use std::cell::RefCell;
|
|
6
|
+
use iceberg::scan::{FileScanTask, TableScan};
|
|
7
|
+
use magnus::{IntoValue, RArray, Ruby, Value, value::ReprValue};
|
|
5
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;
|
|
13
|
+
use crate::ruby::GvlExt;
|
|
8
14
|
use crate::runtime::runtime;
|
|
9
|
-
use crate::
|
|
15
|
+
use crate::snapshot::RbSnapshot;
|
|
10
16
|
|
|
11
17
|
#[magnus::wrap(class = "Iceberg::RbTableScan")]
|
|
12
18
|
pub struct RbTableScan {
|
|
13
|
-
pub scan:
|
|
19
|
+
pub scan: RwLock<TableScan>,
|
|
20
|
+
}
|
|
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>>,
|
|
14
33
|
}
|
|
15
34
|
|
|
16
35
|
impl RbTableScan {
|
|
17
36
|
pub fn plan_files(ruby: &Ruby, rb_self: &Self) -> RbResult<RArray> {
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
let plan_files: Vec<_> = ruby
|
|
38
|
+
.detach(|| {
|
|
39
|
+
let scan = rb_self.scan.read().unwrap();
|
|
40
|
+
let runtime = runtime();
|
|
41
|
+
let plan_files = runtime.block_on(scan.plan_files())?;
|
|
42
|
+
runtime.block_on(plan_files.try_collect())
|
|
43
|
+
})
|
|
24
44
|
.map_err(to_rb_err)?;
|
|
45
|
+
|
|
25
46
|
let files = ruby.ary_new();
|
|
26
47
|
for v in plan_files {
|
|
27
|
-
|
|
28
|
-
file.aset(ruby.to_symbol("start"), v.start)?;
|
|
29
|
-
file.aset(ruby.to_symbol("length"), v.length)?;
|
|
30
|
-
file.aset(ruby.to_symbol("record_count"), v.record_count)?;
|
|
31
|
-
file.aset(ruby.to_symbol("data_file_path"), v.data_file_path)?;
|
|
32
|
-
file.aset(ruby.to_symbol("project_field_ids"), v.project_field_ids)?;
|
|
33
|
-
|
|
34
|
-
let deletes = ruby.ary_new();
|
|
35
|
-
for d in v.deletes {
|
|
36
|
-
let delete = ruby.hash_new();
|
|
37
|
-
delete.aset(ruby.to_symbol("file_path"), d.file_path)?;
|
|
38
|
-
delete.aset(ruby.to_symbol("partition_spec_id"), d.partition_spec_id)?;
|
|
39
|
-
delete.aset(ruby.to_symbol("equality_ids"), d.equality_ids)?;
|
|
40
|
-
deletes.push(delete)?;
|
|
41
|
-
}
|
|
42
|
-
file.aset(ruby.to_symbol("deletes"), deletes)?;
|
|
43
|
-
|
|
44
|
-
files.push(file)?;
|
|
48
|
+
files.push(RbFileScanTask { scan: v })?;
|
|
45
49
|
}
|
|
46
50
|
Ok(files)
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
pub fn snapshot(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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,
|
|
53
89
|
}
|
|
54
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
|
+
}
|
|
55
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
|
+
}
|