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/utils.rs
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
|
|
1
3
|
use iceberg::spec::{
|
|
2
|
-
EncryptedKey, Literal,
|
|
3
|
-
|
|
4
|
+
EncryptedKey, Literal, MetadataLog, NestedField, NullOrder, PartitionSpec,
|
|
5
|
+
PartitionStatisticsFile, PrimitiveLiteral, PrimitiveType, Schema, Snapshot, SnapshotLog,
|
|
6
|
+
SortDirection, SortOrder, StatisticsFile, Transform, Type,
|
|
4
7
|
};
|
|
5
8
|
use iceberg::{NamespaceIdent, TableIdent};
|
|
6
9
|
use magnus::{
|
|
7
|
-
Error as RbErr, IntoValue,
|
|
8
|
-
|
|
10
|
+
Error as RbErr, IntoValue, RClass, RModule, RString, Ruby, TryConvert, Value, prelude::*,
|
|
11
|
+
value::Lazy,
|
|
9
12
|
};
|
|
10
13
|
|
|
11
14
|
use crate::RbResult;
|
|
12
|
-
use crate::
|
|
15
|
+
use crate::encryption::RbEncryptedKey;
|
|
16
|
+
use crate::error::{to_rb_err, todo_error};
|
|
17
|
+
use crate::partitioning::RbPartitionSpec;
|
|
18
|
+
use crate::schema::{RbNestedField, RbSchema};
|
|
19
|
+
use crate::snapshot::{RbMetadataLogEntry, RbSnapshot, RbSnapshotLogEntry};
|
|
20
|
+
use crate::sorting::RbSortOrder;
|
|
21
|
+
use crate::statistics::{RbPartitionStatisticsFile, RbStatisticsFile};
|
|
13
22
|
|
|
14
23
|
pub struct Wrap<T>(pub T);
|
|
15
24
|
|
|
@@ -42,88 +51,80 @@ impl TryConvert for Wrap<TableIdent> {
|
|
|
42
51
|
}
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
impl TryConvert for Wrap<
|
|
54
|
+
impl TryConvert for Wrap<Transform> {
|
|
46
55
|
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
47
|
-
let
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"double" => Type::Primitive(PrimitiveType::Double),
|
|
60
|
-
// TODO PrimitiveType::Decimal
|
|
61
|
-
"date" => Type::Primitive(PrimitiveType::Date),
|
|
62
|
-
"time" => Type::Primitive(PrimitiveType::Time),
|
|
63
|
-
"timestamp" => Type::Primitive(PrimitiveType::Timestamp),
|
|
64
|
-
"timestamptz" => Type::Primitive(PrimitiveType::Timestamptz),
|
|
65
|
-
"timestamp_ns" => Type::Primitive(PrimitiveType::TimestampNs),
|
|
66
|
-
"timestamptz_ns" => Type::Primitive(PrimitiveType::TimestamptzNs),
|
|
67
|
-
"string" => Type::Primitive(PrimitiveType::String),
|
|
68
|
-
"uuid" => Type::Primitive(PrimitiveType::Uuid),
|
|
69
|
-
// TODO PrimitiveType::Fixed
|
|
70
|
-
"binary" => Type::Primitive(PrimitiveType::Binary),
|
|
71
|
-
_ => {
|
|
72
|
-
return Err(RbErr::new(
|
|
73
|
-
ruby.exception_arg_error(),
|
|
74
|
-
format!("Type not supported: {}", s),
|
|
75
|
-
));
|
|
76
|
-
}
|
|
56
|
+
let v = if let Ok(s) = RString::try_convert(ob) {
|
|
57
|
+
match unsafe { s.as_str() }? {
|
|
58
|
+
"identity" => Transform::Identity,
|
|
59
|
+
"year" => Transform::Year,
|
|
60
|
+
"month" => Transform::Month,
|
|
61
|
+
"day" => Transform::Day,
|
|
62
|
+
"hour" => Transform::Hour,
|
|
63
|
+
_ => {
|
|
64
|
+
return Err(RbErr::new(
|
|
65
|
+
Ruby::get_with(ob).exception_arg_error(),
|
|
66
|
+
"Unsupported transform",
|
|
67
|
+
));
|
|
77
68
|
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
match &*unsafe { ob.classname() } {
|
|
72
|
+
"Iceberg::IdentityTransform" => Transform::Identity,
|
|
73
|
+
"Iceberg::BucketTransform" => Transform::Bucket(ob.funcall("num_buckets", ())?),
|
|
74
|
+
"Iceberg::TruncateTransform" => Transform::Truncate(ob.funcall("width", ())?),
|
|
75
|
+
"Iceberg::YearTransform" => Transform::Year,
|
|
76
|
+
"Iceberg::MonthTransform" => Transform::Month,
|
|
77
|
+
"Iceberg::DayTransform" => Transform::Day,
|
|
78
|
+
"Iceberg::HourTransform" => Transform::Hour,
|
|
79
|
+
"Iceberg::VoidTransform" => Transform::Void,
|
|
80
|
+
"Iceberg::UnknownTransform" => Transform::Unknown,
|
|
81
|
+
_ => {
|
|
82
|
+
return Err(RbErr::new(
|
|
83
|
+
Ruby::get_with(ob).exception_arg_error(),
|
|
84
|
+
format!("Transform not supported: {}", ob),
|
|
85
|
+
));
|
|
96
86
|
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
Ok(Wrap(v))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
101
92
|
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
impl TryConvert for Wrap<SortDirection> {
|
|
94
|
+
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
95
|
+
let s = RString::try_convert(ob)?;
|
|
96
|
+
let v = match unsafe { s.as_str() }? {
|
|
97
|
+
"asc" => SortDirection::Ascending,
|
|
98
|
+
"desc" => SortDirection::Descending,
|
|
99
|
+
_ => {
|
|
100
|
+
return Err(RbErr::new(
|
|
101
|
+
Ruby::get_with(ob).exception_arg_error(),
|
|
102
|
+
"Unsupported sort direction",
|
|
103
|
+
));
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
Ok(Wrap(v))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
104
109
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.with_fields(fields)
|
|
120
|
-
.build()
|
|
121
|
-
.map_err(to_rb_err)?;
|
|
122
|
-
Ok(Wrap(schema))
|
|
110
|
+
impl TryConvert for Wrap<NullOrder> {
|
|
111
|
+
fn try_convert(ob: Value) -> RbResult<Self> {
|
|
112
|
+
let s = RString::try_convert(ob)?;
|
|
113
|
+
let v = match unsafe { s.as_str() }? {
|
|
114
|
+
"first" => NullOrder::First,
|
|
115
|
+
"last" => NullOrder::Last,
|
|
116
|
+
_ => {
|
|
117
|
+
return Err(RbErr::new(
|
|
118
|
+
Ruby::get_with(ob).exception_arg_error(),
|
|
119
|
+
"Unsupported null order",
|
|
120
|
+
));
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
Ok(Wrap(v))
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
fn default_value(ob: Value, field_type: &Type) -> RbResult<Option<Literal>> {
|
|
127
|
+
pub fn default_value(ob: Value, field_type: &Type) -> RbResult<Option<Literal>> {
|
|
127
128
|
if ob.is_nil() {
|
|
128
129
|
return Ok(None);
|
|
129
130
|
}
|
|
@@ -136,114 +137,115 @@ fn default_value(ob: Value, field_type: &Type) -> RbResult<Option<Literal>> {
|
|
|
136
137
|
PrimitiveType::Long => PrimitiveLiteral::Long(i64::try_convert(ob)?),
|
|
137
138
|
PrimitiveType::Float => PrimitiveLiteral::Float(f32::try_convert(ob)?.into()),
|
|
138
139
|
PrimitiveType::Double => PrimitiveLiteral::Double(f64::try_convert(ob)?.into()),
|
|
140
|
+
PrimitiveType::Decimal {
|
|
141
|
+
precision: _,
|
|
142
|
+
scale: _,
|
|
143
|
+
} => return Err(todo_error(field_type)),
|
|
144
|
+
PrimitiveType::Date => return Err(todo_error(field_type)),
|
|
145
|
+
PrimitiveType::Time => return Err(todo_error(field_type)),
|
|
146
|
+
PrimitiveType::Timestamp => return Err(todo_error(field_type)),
|
|
147
|
+
PrimitiveType::Timestamptz => return Err(todo_error(field_type)),
|
|
148
|
+
PrimitiveType::TimestampNs => return Err(todo_error(field_type)),
|
|
149
|
+
PrimitiveType::TimestamptzNs => return Err(todo_error(field_type)),
|
|
139
150
|
PrimitiveType::String => PrimitiveLiteral::String(String::try_convert(ob)?),
|
|
140
|
-
|
|
151
|
+
PrimitiveType::Uuid => return Err(todo_error(field_type)),
|
|
152
|
+
PrimitiveType::Fixed(_) => return Err(todo_error(field_type)),
|
|
153
|
+
PrimitiveType::Binary => {
|
|
154
|
+
let s = RString::try_convert(ob)?;
|
|
155
|
+
PrimitiveLiteral::Binary(unsafe { s.as_slice() }.to_vec())
|
|
156
|
+
}
|
|
141
157
|
};
|
|
142
158
|
Literal::Primitive(pl)
|
|
143
159
|
}
|
|
144
|
-
_ =>
|
|
160
|
+
Type::Struct(_) => return Err(todo_error(field_type)),
|
|
161
|
+
Type::List(_) => return Err(todo_error(field_type)),
|
|
162
|
+
Type::Map(_) => return Err(todo_error(field_type)),
|
|
145
163
|
};
|
|
146
164
|
Ok(Some(lit))
|
|
147
165
|
}
|
|
148
166
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
let field_type = match &*f.field_type {
|
|
157
|
-
Type::Primitive(ty) => match ty {
|
|
158
|
-
PrimitiveType::Boolean => "boolean",
|
|
159
|
-
PrimitiveType::Int => "int",
|
|
160
|
-
PrimitiveType::Long => "long",
|
|
161
|
-
PrimitiveType::Float => "float",
|
|
162
|
-
PrimitiveType::Double => "double",
|
|
163
|
-
PrimitiveType::Decimal {
|
|
164
|
-
precision: _,
|
|
165
|
-
scale: _,
|
|
166
|
-
} => todo!(),
|
|
167
|
-
PrimitiveType::Date => "date",
|
|
168
|
-
PrimitiveType::Time => "time",
|
|
169
|
-
PrimitiveType::Timestamp => "timestamp",
|
|
170
|
-
PrimitiveType::Timestamptz => "timestamptz",
|
|
171
|
-
PrimitiveType::TimestampNs => "timestamp_ns",
|
|
172
|
-
PrimitiveType::TimestamptzNs => "timestamptz_ns",
|
|
173
|
-
PrimitiveType::String => "string",
|
|
174
|
-
PrimitiveType::Uuid => "uuid",
|
|
175
|
-
PrimitiveType::Binary => "binary",
|
|
176
|
-
PrimitiveType::Fixed(_) => todo!(),
|
|
177
|
-
},
|
|
178
|
-
_ => todo!(),
|
|
179
|
-
};
|
|
180
|
-
field.aset(ruby.to_symbol("type"), field_type)?;
|
|
181
|
-
|
|
182
|
-
field.aset(ruby.to_symbol("required"), f.required)?;
|
|
183
|
-
|
|
184
|
-
let initial_default = f.initial_default.as_ref().map(|v| rb_literal(ruby, v));
|
|
185
|
-
field.aset(ruby.to_symbol("initial_default"), initial_default)?;
|
|
186
|
-
|
|
187
|
-
let write_default = f.write_default.as_ref().map(|v| rb_literal(ruby, v));
|
|
188
|
-
field.aset(ruby.to_symbol("write_default"), write_default)?;
|
|
167
|
+
impl From<&Arc<Schema>> for RbSchema {
|
|
168
|
+
fn from(schema: &Arc<Schema>) -> Self {
|
|
169
|
+
Self {
|
|
170
|
+
schema: (**schema).clone(),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
189
174
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
175
|
+
impl From<&Arc<NestedField>> for RbNestedField {
|
|
176
|
+
fn from(field: &Arc<NestedField>) -> Self {
|
|
177
|
+
Self {
|
|
178
|
+
field: field.clone(),
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
194
182
|
|
|
195
|
-
|
|
183
|
+
impl From<&Arc<Snapshot>> for RbSnapshot {
|
|
184
|
+
fn from(snapshot: &Arc<Snapshot>) -> Self {
|
|
185
|
+
Self {
|
|
186
|
+
snapshot: (**snapshot).clone(),
|
|
187
|
+
}
|
|
196
188
|
}
|
|
197
|
-
|
|
189
|
+
}
|
|
198
190
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
191
|
+
impl From<&SnapshotLog> for RbSnapshotLogEntry {
|
|
192
|
+
fn from(snapshot_log: &SnapshotLog) -> Self {
|
|
193
|
+
Self {
|
|
194
|
+
log: snapshot_log.clone(),
|
|
195
|
+
}
|
|
196
|
+
}
|
|
205
197
|
}
|
|
206
198
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
)?;
|
|
214
|
-
rb_snapshot.aset(
|
|
215
|
-
ruby.to_symbol("sequence_number"),
|
|
216
|
-
snapshot.sequence_number(),
|
|
217
|
-
)?;
|
|
218
|
-
rb_snapshot.aset(ruby.to_symbol("manifest_list"), snapshot.manifest_list())?;
|
|
219
|
-
rb_snapshot.aset(ruby.to_symbol("schema_id"), snapshot.schema_id())?;
|
|
220
|
-
Ok(rb_snapshot.as_value())
|
|
199
|
+
impl From<&MetadataLog> for RbMetadataLogEntry {
|
|
200
|
+
fn from(metadata_log: &MetadataLog) -> Self {
|
|
201
|
+
Self {
|
|
202
|
+
log: metadata_log.clone(),
|
|
203
|
+
}
|
|
204
|
+
}
|
|
221
205
|
}
|
|
222
206
|
|
|
223
|
-
|
|
224
|
-
|
|
207
|
+
impl From<&Arc<PartitionSpec>> for RbPartitionSpec {
|
|
208
|
+
fn from(partition_spec: &Arc<PartitionSpec>) -> Self {
|
|
209
|
+
Self {
|
|
210
|
+
spec: (**partition_spec).clone().into(),
|
|
211
|
+
}
|
|
212
|
+
}
|
|
225
213
|
}
|
|
226
214
|
|
|
227
|
-
|
|
228
|
-
|
|
215
|
+
impl From<&Arc<SortOrder>> for RbSortOrder {
|
|
216
|
+
fn from(sort_order: &Arc<SortOrder>) -> Self {
|
|
217
|
+
Self {
|
|
218
|
+
order: (**sort_order).clone(),
|
|
219
|
+
}
|
|
220
|
+
}
|
|
229
221
|
}
|
|
230
222
|
|
|
231
|
-
|
|
232
|
-
|
|
223
|
+
impl From<&StatisticsFile> for RbStatisticsFile {
|
|
224
|
+
fn from(statistics_file: &StatisticsFile) -> Self {
|
|
225
|
+
Self {
|
|
226
|
+
file: statistics_file.clone(),
|
|
227
|
+
}
|
|
228
|
+
}
|
|
233
229
|
}
|
|
234
230
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
231
|
+
impl From<&PartitionStatisticsFile> for RbPartitionStatisticsFile {
|
|
232
|
+
fn from(partition_statistics_file: &PartitionStatisticsFile) -> Self {
|
|
233
|
+
Self {
|
|
234
|
+
file: partition_statistics_file.clone(),
|
|
235
|
+
}
|
|
236
|
+
}
|
|
239
237
|
}
|
|
240
238
|
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
impl From<&EncryptedKey> for RbEncryptedKey {
|
|
240
|
+
fn from(encrypted_key: &EncryptedKey) -> Self {
|
|
241
|
+
Self {
|
|
242
|
+
key: encrypted_key.clone(),
|
|
243
|
+
}
|
|
244
|
+
}
|
|
243
245
|
}
|
|
244
246
|
|
|
245
|
-
pub fn rb_literal(ruby: &Ruby, literal: &Literal) -> Value {
|
|
246
|
-
match literal {
|
|
247
|
+
pub fn rb_literal(ruby: &Ruby, literal: &Literal) -> RbResult<Value> {
|
|
248
|
+
let v = match literal {
|
|
247
249
|
Literal::Primitive(pl) => match pl {
|
|
248
250
|
PrimitiveLiteral::Boolean(v) => v.into_value_with(ruby),
|
|
249
251
|
PrimitiveLiteral::Int(v) => v.into_value_with(ruby),
|
|
@@ -252,8 +254,64 @@ pub fn rb_literal(ruby: &Ruby, literal: &Literal) -> Value {
|
|
|
252
254
|
PrimitiveLiteral::Double(v) => v.into_value_with(ruby),
|
|
253
255
|
PrimitiveLiteral::String(v) => ruby.str_new(v).as_value(),
|
|
254
256
|
PrimitiveLiteral::Binary(v) => ruby.str_from_slice(v).as_value(),
|
|
255
|
-
|
|
257
|
+
PrimitiveLiteral::Int128(v) => v.into_value_with(ruby),
|
|
258
|
+
PrimitiveLiteral::UInt128(v) => v.into_value_with(ruby),
|
|
259
|
+
PrimitiveLiteral::AboveMax => return Err(todo_error(literal)),
|
|
260
|
+
PrimitiveLiteral::BelowMin => return Err(todo_error(literal)),
|
|
256
261
|
},
|
|
257
|
-
_ =>
|
|
258
|
-
|
|
262
|
+
Literal::Struct(_) => return Err(todo_error(literal)),
|
|
263
|
+
Literal::List(_) => return Err(todo_error(literal)),
|
|
264
|
+
Literal::Map(_) => return Err(todo_error(literal)),
|
|
265
|
+
};
|
|
266
|
+
Ok(v)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
pub fn rb_transform(transform: &Transform) -> RbResult<Value> {
|
|
270
|
+
let ruby = Ruby::get().unwrap();
|
|
271
|
+
let iceberg = ruby.class_object().const_get::<_, RModule>("Iceberg")?;
|
|
272
|
+
let v = match transform {
|
|
273
|
+
Transform::Identity => iceberg
|
|
274
|
+
.const_get::<_, RClass>("IdentityTransform")?
|
|
275
|
+
.new_instance(())?,
|
|
276
|
+
Transform::Bucket(num_buckets) => iceberg
|
|
277
|
+
.const_get::<_, RClass>("BucketTransform")?
|
|
278
|
+
.new_instance((*num_buckets,))?,
|
|
279
|
+
Transform::Truncate(width) => iceberg
|
|
280
|
+
.const_get::<_, RClass>("TruncateTransform")?
|
|
281
|
+
.new_instance((*width,))?,
|
|
282
|
+
Transform::Year => iceberg
|
|
283
|
+
.const_get::<_, RClass>("YearTransform")?
|
|
284
|
+
.new_instance(())?,
|
|
285
|
+
Transform::Month => iceberg
|
|
286
|
+
.const_get::<_, RClass>("MonthTransform")?
|
|
287
|
+
.new_instance(())?,
|
|
288
|
+
Transform::Day => iceberg
|
|
289
|
+
.const_get::<_, RClass>("DayTransform")?
|
|
290
|
+
.new_instance(())?,
|
|
291
|
+
Transform::Hour => iceberg
|
|
292
|
+
.const_get::<_, RClass>("HourTransform")?
|
|
293
|
+
.new_instance(())?,
|
|
294
|
+
Transform::Void => iceberg
|
|
295
|
+
.const_get::<_, RClass>("VoidTransform")?
|
|
296
|
+
.new_instance(())?,
|
|
297
|
+
Transform::Unknown => iceberg
|
|
298
|
+
.const_get::<_, RClass>("UnknownTransform")?
|
|
299
|
+
.new_instance(())?,
|
|
300
|
+
};
|
|
301
|
+
Ok(v)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
pub static EPOCH: Lazy<Value> = Lazy::new(|ruby| {
|
|
305
|
+
ruby.class_object()
|
|
306
|
+
.const_get::<_, RClass>("Date")
|
|
307
|
+
.unwrap()
|
|
308
|
+
.new_instance((1970, 1, 1))
|
|
309
|
+
.unwrap()
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
pub fn date_to_i32(value: Value) -> RbResult<i32> {
|
|
313
|
+
let epoch = Ruby::get_with(value).get_inner(&EPOCH);
|
|
314
|
+
value
|
|
315
|
+
.funcall::<_, _, Value>("-", (epoch,))?
|
|
316
|
+
.funcall("to_i", ())
|
|
259
317
|
}
|
data/lib/iceberg/catalog.rb
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
module Iceberg
|
|
2
2
|
class Catalog
|
|
3
|
+
def _initialize(catalog, default_namespace:)
|
|
4
|
+
@catalog = catalog
|
|
5
|
+
@default_namespace = default_namespace
|
|
6
|
+
end
|
|
7
|
+
|
|
3
8
|
def list_namespaces(parent = nil)
|
|
4
9
|
@catalog.list_namespaces(parent)
|
|
5
10
|
end
|
|
6
11
|
|
|
7
12
|
def create_namespace(namespace, properties: {}, if_not_exists: nil)
|
|
8
13
|
@catalog.create_namespace(namespace, properties)
|
|
9
|
-
rescue
|
|
10
|
-
|
|
11
|
-
if !if_not_exists || (e.message != "Cannot create namespace" && !e.message.include?("already exists"))
|
|
14
|
+
rescue NamespaceAlreadyExistsError => e
|
|
15
|
+
if !if_not_exists
|
|
12
16
|
raise e
|
|
13
17
|
end
|
|
14
18
|
nil
|
|
@@ -28,19 +32,18 @@ module Iceberg
|
|
|
28
32
|
|
|
29
33
|
def drop_namespace(namespace, if_exists: nil)
|
|
30
34
|
@catalog.drop_namespace(namespace)
|
|
31
|
-
rescue
|
|
32
|
-
|
|
33
|
-
if !if_exists || (e.message != "Tried to drop a namespace that does not exist" && !e.message.include?("No such namespace") && !e.message.include?("The specified namespace does not exist") && !e.message.include?("not found"))
|
|
35
|
+
rescue NoSuchNamespaceError => e
|
|
36
|
+
if !if_exists
|
|
34
37
|
raise e
|
|
35
38
|
end
|
|
36
39
|
nil
|
|
37
40
|
end
|
|
38
41
|
|
|
39
|
-
def list_tables(namespace)
|
|
40
|
-
@catalog.list_tables(namespace)
|
|
42
|
+
def list_tables(namespace = nil)
|
|
43
|
+
@catalog.list_tables(namespace || @default_namespace)
|
|
41
44
|
end
|
|
42
45
|
|
|
43
|
-
def create_table(table_name, schema: nil, location: nil)
|
|
46
|
+
def create_table(table_name, schema: nil, location: nil, partition_spec: nil, sort_order: nil, properties: {})
|
|
44
47
|
if !schema.nil? && block_given?
|
|
45
48
|
raise ArgumentError, "Must pass schema or block"
|
|
46
49
|
end
|
|
@@ -48,63 +51,79 @@ module Iceberg
|
|
|
48
51
|
if block_given?
|
|
49
52
|
table_definition = TableDefinition.new
|
|
50
53
|
yield table_definition
|
|
51
|
-
schema = Schema.new(table_definition.fields)
|
|
52
|
-
elsif schema.is_a?(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
schema = Schema.new(fields)
|
|
54
|
+
schema = Schema.new(*table_definition.fields)
|
|
55
|
+
elsif schema.is_a?(Schema)
|
|
56
|
+
# do nothing
|
|
57
|
+
elsif schema.respond_to?(:arrow_c_schema)
|
|
58
|
+
schema = Schema.new(schema)
|
|
59
|
+
elsif schema.is_a?(Hash)
|
|
60
|
+
table_definition = TableDefinition.new
|
|
61
|
+
schema.each do |k, v|
|
|
62
|
+
table_definition.column(k, v)
|
|
63
|
+
end
|
|
64
|
+
schema = Schema.new(*table_definition.fields)
|
|
63
65
|
elsif schema.nil?
|
|
64
|
-
schema = Schema.new
|
|
66
|
+
schema = Schema.new
|
|
65
67
|
end
|
|
66
68
|
|
|
67
|
-
Table.new(@catalog.create_table(table_name, schema, location), @catalog)
|
|
69
|
+
Table.new(@catalog.create_table(with_namespace(table_name), schema, location, partition_spec, sort_order, properties), @catalog)
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
def load_table(table_name)
|
|
71
|
-
Table.new(@catalog.load_table(table_name), @catalog)
|
|
73
|
+
Table.new(@catalog.load_table(with_namespace(table_name)), @catalog)
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
def drop_table(table_name, if_exists: nil)
|
|
75
|
-
@catalog.drop_table(table_name)
|
|
76
|
-
rescue
|
|
77
|
-
|
|
78
|
-
if !if_exists || (e.message != "Tried to drop a table that does not exist" && !e.message.include?("No such table") && !e.message.include?("The specified table does not exist") && !e.message.include?("not found"))
|
|
77
|
+
@catalog.drop_table(with_namespace(table_name))
|
|
78
|
+
rescue NoSuchTableError => e
|
|
79
|
+
if !if_exists
|
|
79
80
|
raise e
|
|
80
81
|
end
|
|
81
82
|
nil
|
|
82
83
|
end
|
|
83
84
|
|
|
85
|
+
def purge_table(table_name)
|
|
86
|
+
@catalog.purge_table(with_namespace(table_name))
|
|
87
|
+
end
|
|
88
|
+
|
|
84
89
|
def table_exists?(table_name)
|
|
85
|
-
@catalog.table_exists?(table_name)
|
|
86
|
-
rescue
|
|
90
|
+
@catalog.table_exists?(with_namespace(table_name))
|
|
91
|
+
rescue NoSuchNamespaceError
|
|
87
92
|
false
|
|
88
93
|
end
|
|
89
94
|
|
|
90
95
|
def rename_table(table_name, new_name)
|
|
91
|
-
@catalog.rename_table(table_name, new_name)
|
|
96
|
+
@catalog.rename_table(with_namespace(table_name), with_namespace(new_name))
|
|
92
97
|
end
|
|
93
98
|
|
|
94
99
|
def register_table(table_name, metadata_location)
|
|
95
|
-
@catalog.register_table(table_name, metadata_location)
|
|
100
|
+
@catalog.register_table(with_namespace(table_name), metadata_location)
|
|
96
101
|
end
|
|
97
102
|
|
|
98
|
-
def sql(sql)
|
|
103
|
+
def sql(sql, params = [])
|
|
99
104
|
# requires datafusion feature
|
|
100
|
-
raise Todo unless @catalog.respond_to?(:
|
|
105
|
+
raise Todo unless @catalog.respond_to?(:session_context)
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
session_context.sql(sql, params)
|
|
103
108
|
end
|
|
104
109
|
|
|
105
110
|
# hide internal state
|
|
106
111
|
def inspect
|
|
107
112
|
to_s
|
|
108
113
|
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def with_namespace(table_name)
|
|
118
|
+
if @default_namespace && table_name.is_a?(String) && !table_name.include?(".")
|
|
119
|
+
[@default_namespace, table_name]
|
|
120
|
+
else
|
|
121
|
+
table_name
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def session_context
|
|
126
|
+
@session_context ||= @catalog.session_context(@default_namespace)
|
|
127
|
+
end
|
|
109
128
|
end
|
|
110
129
|
end
|
data/lib/iceberg/glue_catalog.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
module Iceberg
|
|
2
2
|
class GlueCatalog < Catalog
|
|
3
3
|
# warehouse is URI of S3 storage bucket
|
|
4
|
-
def initialize(warehouse:)
|
|
5
|
-
|
|
4
|
+
def initialize(warehouse:, default_namespace: nil)
|
|
5
|
+
_initialize(
|
|
6
|
+
RbCatalog.new_glue(warehouse),
|
|
7
|
+
default_namespace:
|
|
8
|
+
)
|
|
6
9
|
end
|
|
7
10
|
end
|
|
8
11
|
end
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
module Iceberg
|
|
2
2
|
class MemoryCatalog < Catalog
|
|
3
3
|
# warehouse is default storage location
|
|
4
|
-
def initialize(warehouse: nil)
|
|
5
|
-
|
|
4
|
+
def initialize(warehouse: nil, default_namespace: nil)
|
|
5
|
+
_initialize(
|
|
6
|
+
RbCatalog.new_memory(warehouse),
|
|
7
|
+
default_namespace:
|
|
8
|
+
)
|
|
6
9
|
end
|
|
7
10
|
end
|
|
8
11
|
end
|
data/lib/iceberg/rest_catalog.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
module Iceberg
|
|
2
2
|
class RestCatalog < Catalog
|
|
3
3
|
# warehouse is passed to REST server
|
|
4
|
-
def initialize(uri:, warehouse: nil, properties: {})
|
|
5
|
-
|
|
4
|
+
def initialize(uri:, warehouse: nil, properties: {}, default_namespace: nil)
|
|
5
|
+
_initialize(
|
|
6
|
+
RbCatalog.new_rest(uri, warehouse, properties),
|
|
7
|
+
default_namespace:
|
|
8
|
+
)
|
|
6
9
|
end
|
|
7
10
|
end
|
|
8
11
|
end
|