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
data/ext/iceberg/src/catalog.rs
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
use std::sync::{Arc, RwLock};
|
|
3
|
+
|
|
4
|
+
#[cfg(feature = "datafusion")]
|
|
5
|
+
use datafusion::common::ScalarValue;
|
|
1
6
|
#[cfg(feature = "datafusion")]
|
|
2
|
-
use datafusion::execution::context::SessionContext;
|
|
3
|
-
use iceberg::io::LocalFsStorageFactory;
|
|
7
|
+
use datafusion::execution::context::{SessionConfig, SessionContext};
|
|
4
8
|
use iceberg::memory::{MEMORY_CATALOG_WAREHOUSE, MemoryCatalogBuilder};
|
|
5
|
-
use iceberg::spec::
|
|
6
|
-
use iceberg::{
|
|
9
|
+
use iceberg::spec::FormatVersion;
|
|
10
|
+
use iceberg::{
|
|
11
|
+
Catalog, CatalogBuilder, MemoryCatalog, NamespaceIdent, Runtime, TableCreation, TableIdent,
|
|
12
|
+
};
|
|
7
13
|
#[cfg(feature = "glue")]
|
|
8
14
|
use iceberg_catalog_glue::{GLUE_CATALOG_PROP_WAREHOUSE, GlueCatalog, GlueCatalogBuilder};
|
|
9
15
|
#[cfg(feature = "rest")]
|
|
@@ -21,14 +27,23 @@ use iceberg_catalog_sql::{
|
|
|
21
27
|
};
|
|
22
28
|
#[cfg(feature = "datafusion")]
|
|
23
29
|
use iceberg_datafusion::IcebergCatalogProvider;
|
|
24
|
-
use
|
|
25
|
-
use
|
|
26
|
-
|
|
30
|
+
use iceberg_storage_opendal::OpenDalStorageFactory;
|
|
31
|
+
use magnus::{Error as RbErr, Ruby};
|
|
32
|
+
#[cfg(feature = "datafusion")]
|
|
33
|
+
use magnus::{
|
|
34
|
+
Float, Integer, RArray, RString, Value, value::Qfalse, value::Qtrue, value::ReprValue,
|
|
35
|
+
};
|
|
27
36
|
|
|
28
37
|
use crate::error::to_rb_err;
|
|
29
|
-
|
|
38
|
+
#[cfg(feature = "datafusion")]
|
|
39
|
+
use crate::error::{datafusion_error, todo_error};
|
|
40
|
+
#[cfg(feature = "datafusion")]
|
|
41
|
+
use crate::result::collect_batches;
|
|
42
|
+
use crate::runtime::{runtime, tokio_runtime};
|
|
30
43
|
use crate::utils::Wrap;
|
|
31
|
-
|
|
44
|
+
#[cfg(feature = "datafusion")]
|
|
45
|
+
use crate::utils::date_to_i32;
|
|
46
|
+
use crate::{RbPartitionSpec, RbResult, RbSchema, RbSortOrder, RbTable};
|
|
32
47
|
|
|
33
48
|
pub enum RbCatalogType {
|
|
34
49
|
#[cfg(feature = "glue")]
|
|
@@ -75,7 +90,7 @@ impl RbCatalogType {
|
|
|
75
90
|
|
|
76
91
|
#[magnus::wrap(class = "Iceberg::RbCatalog")]
|
|
77
92
|
pub struct RbCatalog {
|
|
78
|
-
pub catalog:
|
|
93
|
+
pub catalog: RwLock<RbCatalogType>,
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
impl RbCatalog {
|
|
@@ -83,7 +98,11 @@ impl RbCatalog {
|
|
|
83
98
|
pub fn new_glue(warehouse: String) -> RbResult<Self> {
|
|
84
99
|
let props = HashMap::from([(GLUE_CATALOG_PROP_WAREHOUSE.to_string(), warehouse)]);
|
|
85
100
|
let catalog = runtime()
|
|
86
|
-
.block_on(
|
|
101
|
+
.block_on(
|
|
102
|
+
GlueCatalogBuilder::default()
|
|
103
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
104
|
+
.load("glue", props),
|
|
105
|
+
)
|
|
87
106
|
.map_err(to_rb_err)?;
|
|
88
107
|
Ok(Self {
|
|
89
108
|
catalog: RbCatalogType::Glue(catalog.into()).into(),
|
|
@@ -92,13 +111,14 @@ impl RbCatalog {
|
|
|
92
111
|
|
|
93
112
|
pub fn new_memory(warehouse: Option<String>) -> RbResult<Self> {
|
|
94
113
|
let mut props = HashMap::new();
|
|
95
|
-
if let Some(v) = warehouse {
|
|
96
|
-
props.insert(MEMORY_CATALOG_WAREHOUSE.to_string(), v);
|
|
114
|
+
if let Some(ref v) = warehouse {
|
|
115
|
+
props.insert(MEMORY_CATALOG_WAREHOUSE.to_string(), v.clone());
|
|
97
116
|
}
|
|
98
117
|
let catalog = runtime()
|
|
99
118
|
.block_on(
|
|
100
119
|
MemoryCatalogBuilder::default()
|
|
101
|
-
.
|
|
120
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
121
|
+
.with_storage_factory(Arc::new(storage_factory(warehouse)))
|
|
102
122
|
.load("memory", props),
|
|
103
123
|
)
|
|
104
124
|
.map_err(to_rb_err)?;
|
|
@@ -115,13 +135,14 @@ impl RbCatalog {
|
|
|
115
135
|
) -> RbResult<Self> {
|
|
116
136
|
let mut props = props;
|
|
117
137
|
props.insert(REST_CATALOG_PROP_URI.to_string(), uri);
|
|
118
|
-
if let Some(v) = warehouse {
|
|
119
|
-
props.insert(REST_CATALOG_PROP_WAREHOUSE.to_string(), v);
|
|
138
|
+
if let Some(ref v) = warehouse {
|
|
139
|
+
props.insert(REST_CATALOG_PROP_WAREHOUSE.to_string(), v.clone());
|
|
120
140
|
}
|
|
121
141
|
let catalog = runtime()
|
|
122
142
|
.block_on(
|
|
123
143
|
RestCatalogBuilder::default()
|
|
124
|
-
.
|
|
144
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
145
|
+
.with_storage_factory(Arc::new(storage_factory(warehouse)))
|
|
125
146
|
.load("rest", props),
|
|
126
147
|
)
|
|
127
148
|
.map_err(to_rb_err)?;
|
|
@@ -135,7 +156,11 @@ impl RbCatalog {
|
|
|
135
156
|
let mut props = HashMap::new();
|
|
136
157
|
props.insert(S3TABLES_CATALOG_PROP_TABLE_BUCKET_ARN.to_string(), arn);
|
|
137
158
|
let catalog = runtime()
|
|
138
|
-
.block_on(
|
|
159
|
+
.block_on(
|
|
160
|
+
S3TablesCatalogBuilder::default()
|
|
161
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
162
|
+
.load("s3tables", props),
|
|
163
|
+
)
|
|
139
164
|
.map_err(to_rb_err)?;
|
|
140
165
|
Ok(Self {
|
|
141
166
|
catalog: RbCatalogType::S3Tables(catalog.into()).into(),
|
|
@@ -151,7 +176,7 @@ impl RbCatalog {
|
|
|
151
176
|
) -> RbResult<Self> {
|
|
152
177
|
let mut props = props;
|
|
153
178
|
props.insert(SQL_CATALOG_PROP_URI.to_string(), uri);
|
|
154
|
-
props.insert(SQL_CATALOG_PROP_WAREHOUSE.to_string(), warehouse);
|
|
179
|
+
props.insert(SQL_CATALOG_PROP_WAREHOUSE.to_string(), warehouse.clone());
|
|
155
180
|
props.insert(
|
|
156
181
|
SQL_CATALOG_PROP_BIND_STYLE.to_string(),
|
|
157
182
|
SqlBindStyle::DollarNumeric.to_string(),
|
|
@@ -159,7 +184,8 @@ impl RbCatalog {
|
|
|
159
184
|
let catalog = runtime()
|
|
160
185
|
.block_on(
|
|
161
186
|
SqlCatalogBuilder::default()
|
|
162
|
-
.
|
|
187
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
188
|
+
.with_storage_factory(Arc::new(storage_factory(Some(warehouse))))
|
|
163
189
|
.load(name, props),
|
|
164
190
|
)
|
|
165
191
|
.map_err(to_rb_err)?;
|
|
@@ -175,7 +201,8 @@ impl RbCatalog {
|
|
|
175
201
|
let namespaces = runtime()
|
|
176
202
|
.block_on(
|
|
177
203
|
self.catalog
|
|
178
|
-
.
|
|
204
|
+
.read()
|
|
205
|
+
.unwrap()
|
|
179
206
|
.as_catalog()
|
|
180
207
|
.list_namespaces(parent.map(|v| v.0).as_ref()),
|
|
181
208
|
)
|
|
@@ -191,7 +218,8 @@ impl RbCatalog {
|
|
|
191
218
|
runtime()
|
|
192
219
|
.block_on(
|
|
193
220
|
self.catalog
|
|
194
|
-
.
|
|
221
|
+
.read()
|
|
222
|
+
.unwrap()
|
|
195
223
|
.as_catalog()
|
|
196
224
|
.create_namespace(&name.0, props),
|
|
197
225
|
)
|
|
@@ -201,7 +229,13 @@ impl RbCatalog {
|
|
|
201
229
|
|
|
202
230
|
pub fn namespace_exists(&self, name: Wrap<NamespaceIdent>) -> RbResult<bool> {
|
|
203
231
|
let exists = runtime()
|
|
204
|
-
.block_on(
|
|
232
|
+
.block_on(
|
|
233
|
+
self.catalog
|
|
234
|
+
.read()
|
|
235
|
+
.unwrap()
|
|
236
|
+
.as_catalog()
|
|
237
|
+
.namespace_exists(&name.0),
|
|
238
|
+
)
|
|
205
239
|
.map_err(to_rb_err)?;
|
|
206
240
|
Ok(exists)
|
|
207
241
|
}
|
|
@@ -211,7 +245,13 @@ impl RbCatalog {
|
|
|
211
245
|
name: Wrap<NamespaceIdent>,
|
|
212
246
|
) -> RbResult<HashMap<String, String>> {
|
|
213
247
|
let namespace = runtime()
|
|
214
|
-
.block_on(
|
|
248
|
+
.block_on(
|
|
249
|
+
self.catalog
|
|
250
|
+
.read()
|
|
251
|
+
.unwrap()
|
|
252
|
+
.as_catalog()
|
|
253
|
+
.get_namespace(&name.0),
|
|
254
|
+
)
|
|
215
255
|
.map_err(to_rb_err)?;
|
|
216
256
|
Ok(namespace.properties().clone())
|
|
217
257
|
}
|
|
@@ -224,7 +264,8 @@ impl RbCatalog {
|
|
|
224
264
|
runtime()
|
|
225
265
|
.block_on(
|
|
226
266
|
self.catalog
|
|
227
|
-
.
|
|
267
|
+
.read()
|
|
268
|
+
.unwrap()
|
|
228
269
|
.as_catalog()
|
|
229
270
|
.update_namespace(&name.0, props),
|
|
230
271
|
)
|
|
@@ -234,14 +275,26 @@ impl RbCatalog {
|
|
|
234
275
|
|
|
235
276
|
pub fn drop_namespace(&self, name: Wrap<NamespaceIdent>) -> RbResult<()> {
|
|
236
277
|
runtime()
|
|
237
|
-
.block_on(
|
|
278
|
+
.block_on(
|
|
279
|
+
self.catalog
|
|
280
|
+
.read()
|
|
281
|
+
.unwrap()
|
|
282
|
+
.as_catalog()
|
|
283
|
+
.drop_namespace(&name.0),
|
|
284
|
+
)
|
|
238
285
|
.map_err(to_rb_err)?;
|
|
239
286
|
Ok(())
|
|
240
287
|
}
|
|
241
288
|
|
|
242
289
|
pub fn list_tables(&self, namespace: Wrap<NamespaceIdent>) -> RbResult<Vec<Vec<String>>> {
|
|
243
290
|
let tables = runtime()
|
|
244
|
-
.block_on(
|
|
291
|
+
.block_on(
|
|
292
|
+
self.catalog
|
|
293
|
+
.read()
|
|
294
|
+
.unwrap()
|
|
295
|
+
.as_catalog()
|
|
296
|
+
.list_tables(&namespace.0),
|
|
297
|
+
)
|
|
245
298
|
.map_err(to_rb_err)?;
|
|
246
299
|
Ok(tables
|
|
247
300
|
.iter()
|
|
@@ -253,21 +306,47 @@ impl RbCatalog {
|
|
|
253
306
|
.collect())
|
|
254
307
|
}
|
|
255
308
|
|
|
309
|
+
#[allow(clippy::too_many_arguments)]
|
|
256
310
|
pub fn create_table(
|
|
257
|
-
&
|
|
311
|
+
ruby: &Ruby,
|
|
312
|
+
rb_self: &Self,
|
|
258
313
|
name: Wrap<TableIdent>,
|
|
259
|
-
schema:
|
|
314
|
+
schema: &RbSchema,
|
|
260
315
|
location: Option<String>,
|
|
316
|
+
partition_spec: Option<&RbPartitionSpec>,
|
|
317
|
+
sort_order: Option<&RbSortOrder>,
|
|
318
|
+
properties: HashMap<String, String>,
|
|
261
319
|
) -> RbResult<RbTable> {
|
|
320
|
+
let format_version = if let Some(v) = properties.get("format-version") {
|
|
321
|
+
match v.as_str() {
|
|
322
|
+
"1" => FormatVersion::V1,
|
|
323
|
+
"2" => FormatVersion::V2,
|
|
324
|
+
"3" => FormatVersion::V3,
|
|
325
|
+
_ => {
|
|
326
|
+
return Err(RbErr::new(
|
|
327
|
+
ruby.exception_arg_error(),
|
|
328
|
+
"Unsupported format version",
|
|
329
|
+
));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
FormatVersion::V2
|
|
334
|
+
};
|
|
262
335
|
let creation = TableCreation::builder()
|
|
263
336
|
.name(name.0.name)
|
|
264
|
-
.schema(schema.
|
|
337
|
+
.schema(schema.schema.clone())
|
|
265
338
|
.location_opt(location)
|
|
339
|
+
.partition_spec_opt(partition_spec.map(|v| v.spec.clone()))
|
|
340
|
+
.sort_order_opt(sort_order.map(|v| v.order.clone()))
|
|
341
|
+
.properties(properties)
|
|
342
|
+
.format_version(format_version)
|
|
266
343
|
.build();
|
|
267
344
|
let table = runtime()
|
|
268
345
|
.block_on(
|
|
269
|
-
|
|
270
|
-
.
|
|
346
|
+
rb_self
|
|
347
|
+
.catalog
|
|
348
|
+
.read()
|
|
349
|
+
.unwrap()
|
|
271
350
|
.as_catalog()
|
|
272
351
|
.create_table(&name.0.namespace, creation),
|
|
273
352
|
)
|
|
@@ -279,7 +358,13 @@ impl RbCatalog {
|
|
|
279
358
|
|
|
280
359
|
pub fn load_table(&self, name: Wrap<TableIdent>) -> RbResult<RbTable> {
|
|
281
360
|
let table = runtime()
|
|
282
|
-
.block_on(
|
|
361
|
+
.block_on(
|
|
362
|
+
self.catalog
|
|
363
|
+
.read()
|
|
364
|
+
.unwrap()
|
|
365
|
+
.as_catalog()
|
|
366
|
+
.load_table(&name.0),
|
|
367
|
+
)
|
|
283
368
|
.map_err(to_rb_err)?;
|
|
284
369
|
Ok(RbTable {
|
|
285
370
|
table: table.into(),
|
|
@@ -288,14 +373,39 @@ impl RbCatalog {
|
|
|
288
373
|
|
|
289
374
|
pub fn drop_table(&self, name: Wrap<TableIdent>) -> RbResult<()> {
|
|
290
375
|
runtime()
|
|
291
|
-
.block_on(
|
|
376
|
+
.block_on(
|
|
377
|
+
self.catalog
|
|
378
|
+
.read()
|
|
379
|
+
.unwrap()
|
|
380
|
+
.as_catalog()
|
|
381
|
+
.drop_table(&name.0),
|
|
382
|
+
)
|
|
383
|
+
.map_err(to_rb_err)?;
|
|
384
|
+
Ok(())
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
pub fn purge_table(&self, name: Wrap<TableIdent>) -> RbResult<()> {
|
|
388
|
+
runtime()
|
|
389
|
+
.block_on(
|
|
390
|
+
self.catalog
|
|
391
|
+
.read()
|
|
392
|
+
.unwrap()
|
|
393
|
+
.as_catalog()
|
|
394
|
+
.purge_table(&name.0),
|
|
395
|
+
)
|
|
292
396
|
.map_err(to_rb_err)?;
|
|
293
397
|
Ok(())
|
|
294
398
|
}
|
|
295
399
|
|
|
296
400
|
pub fn table_exists(&self, name: Wrap<TableIdent>) -> RbResult<bool> {
|
|
297
401
|
let exists = runtime()
|
|
298
|
-
.block_on(
|
|
402
|
+
.block_on(
|
|
403
|
+
self.catalog
|
|
404
|
+
.read()
|
|
405
|
+
.unwrap()
|
|
406
|
+
.as_catalog()
|
|
407
|
+
.table_exists(&name.0),
|
|
408
|
+
)
|
|
299
409
|
.map_err(to_rb_err)?;
|
|
300
410
|
Ok(exists)
|
|
301
411
|
}
|
|
@@ -304,7 +414,8 @@ impl RbCatalog {
|
|
|
304
414
|
runtime()
|
|
305
415
|
.block_on(
|
|
306
416
|
self.catalog
|
|
307
|
-
.
|
|
417
|
+
.read()
|
|
418
|
+
.unwrap()
|
|
308
419
|
.as_catalog()
|
|
309
420
|
.rename_table(&name.0, &new_name.0),
|
|
310
421
|
)
|
|
@@ -320,7 +431,8 @@ impl RbCatalog {
|
|
|
320
431
|
runtime()
|
|
321
432
|
.block_on(
|
|
322
433
|
self.catalog
|
|
323
|
-
.
|
|
434
|
+
.read()
|
|
435
|
+
.unwrap()
|
|
324
436
|
.as_catalog()
|
|
325
437
|
.register_table(&name.0, metadata_location),
|
|
326
438
|
)
|
|
@@ -329,22 +441,84 @@ impl RbCatalog {
|
|
|
329
441
|
}
|
|
330
442
|
|
|
331
443
|
#[cfg(feature = "datafusion")]
|
|
332
|
-
pub fn
|
|
333
|
-
let
|
|
334
|
-
|
|
335
|
-
// TODO only create context once
|
|
336
|
-
let catalog = self.catalog.borrow().as_arc();
|
|
337
|
-
let provider = runtime
|
|
444
|
+
pub fn session_context(&self, default_namespace: Option<String>) -> RbResult<RbSessionContext> {
|
|
445
|
+
let catalog = self.catalog.read().unwrap().as_arc();
|
|
446
|
+
let provider = runtime()
|
|
338
447
|
.block_on(IcebergCatalogProvider::try_new(catalog))
|
|
339
|
-
.
|
|
340
|
-
let
|
|
448
|
+
.map_err(to_rb_err)?;
|
|
449
|
+
let mut config = SessionConfig::new();
|
|
450
|
+
if let Some(namespace) = default_namespace {
|
|
451
|
+
config = config.with_default_catalog_and_schema("datafusion", namespace);
|
|
452
|
+
}
|
|
453
|
+
let ctx = SessionContext::new_with_config(config);
|
|
341
454
|
ctx.register_catalog("datafusion", Arc::new(provider));
|
|
455
|
+
Ok(RbSessionContext { ctx })
|
|
456
|
+
}
|
|
457
|
+
}
|
|
342
458
|
|
|
343
|
-
|
|
344
|
-
|
|
459
|
+
#[cfg(feature = "datafusion")]
|
|
460
|
+
#[magnus::wrap(class = "Iceberg::SessionContext")]
|
|
461
|
+
pub struct RbSessionContext {
|
|
462
|
+
pub(crate) ctx: SessionContext,
|
|
463
|
+
}
|
|
345
464
|
|
|
346
|
-
|
|
465
|
+
#[cfg(feature = "datafusion")]
|
|
466
|
+
impl RbSessionContext {
|
|
467
|
+
pub fn sql(ruby: &Ruby, rb_self: &Self, sql: String, rb_params: RArray) -> RbResult<Value> {
|
|
468
|
+
let mut params = Vec::new();
|
|
469
|
+
for param in rb_params.into_iter() {
|
|
470
|
+
if param.is_nil() {
|
|
471
|
+
params.push(ScalarValue::Null);
|
|
472
|
+
} else if Qtrue::from_value(param).is_some() {
|
|
473
|
+
params.push(ScalarValue::from(true));
|
|
474
|
+
} else if Qfalse::from_value(param).is_some() {
|
|
475
|
+
params.push(ScalarValue::from(false));
|
|
476
|
+
} else if let Some(v) = Integer::from_value(param) {
|
|
477
|
+
params.push(ScalarValue::from(v.to_i64()?));
|
|
478
|
+
} else if let Some(v) = Float::from_value(param) {
|
|
479
|
+
params.push(ScalarValue::from(v.to_f64()));
|
|
480
|
+
} else if let Some(v) = RString::from_value(param) {
|
|
481
|
+
// TODO support binary strings
|
|
482
|
+
params.push(ScalarValue::from(v.to_string()?));
|
|
483
|
+
} else if unsafe { param.classname() } == "Date" {
|
|
484
|
+
params.push(ScalarValue::Date32(Some(date_to_i32(param)?)));
|
|
485
|
+
} else if unsafe { param.classname() } == "Time" {
|
|
486
|
+
let sec: i64 = param.funcall("to_i", ())?;
|
|
487
|
+
let nsec: i64 = param.funcall("nsec", ())?;
|
|
488
|
+
params.push(ScalarValue::TimestampNanosecond(
|
|
489
|
+
Some(sec * 1_000_000_000 + nsec),
|
|
490
|
+
None,
|
|
491
|
+
));
|
|
492
|
+
} else {
|
|
493
|
+
return Err(todo_error(param));
|
|
494
|
+
}
|
|
495
|
+
}
|
|
347
496
|
|
|
348
|
-
|
|
497
|
+
let runtime = runtime();
|
|
498
|
+
let stream = runtime
|
|
499
|
+
.block_on(rb_self.ctx.sql(&sql))
|
|
500
|
+
.map_err(datafusion_error)?
|
|
501
|
+
.with_param_values(params)
|
|
502
|
+
.map_err(datafusion_error)?;
|
|
503
|
+
let batches = runtime
|
|
504
|
+
.block_on(stream.collect())
|
|
505
|
+
.map_err(datafusion_error)?;
|
|
506
|
+
collect_batches(ruby, batches)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
fn storage_factory(warehouse: Option<String>) -> OpenDalStorageFactory {
|
|
511
|
+
match warehouse {
|
|
512
|
+
Some(ref v) => {
|
|
513
|
+
if v.starts_with("s3://") {
|
|
514
|
+
OpenDalStorageFactory::S3 {
|
|
515
|
+
customized_credential_load: None,
|
|
516
|
+
}
|
|
517
|
+
} else {
|
|
518
|
+
OpenDalStorageFactory::Fs
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// TODO consider OpenDalStorageFactory::Memory
|
|
522
|
+
None => OpenDalStorageFactory::Fs,
|
|
349
523
|
}
|
|
350
524
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
|
|
3
|
+
use iceberg::spec::EncryptedKey;
|
|
4
|
+
use magnus::{IntoValue, Ruby, value::ReprValue};
|
|
5
|
+
|
|
6
|
+
#[magnus::wrap(class = "Iceberg::EncryptedKey")]
|
|
7
|
+
pub struct RbEncryptedKey {
|
|
8
|
+
pub(crate) key: EncryptedKey,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
impl RbEncryptedKey {
|
|
12
|
+
pub fn key_id(&self) -> &str {
|
|
13
|
+
self.key.key_id()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
pub fn encrypted_by_id(&self) -> Option<&str> {
|
|
17
|
+
self.key.encrypted_by_id()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pub fn properties(&self) -> HashMap<String, String> {
|
|
21
|
+
self.key.properties().clone()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn inspect(ruby: &Ruby, rb_self: &Self) -> String {
|
|
25
|
+
format!(
|
|
26
|
+
"#<Iceberg::EncryptedKey key_id={}, encrypted_by_id={}, properties={}>",
|
|
27
|
+
rb_self.key_id().into_value_with(ruby).inspect(),
|
|
28
|
+
rb_self.encrypted_by_id().into_value_with(ruby).inspect(),
|
|
29
|
+
rb_self.properties().into_value_with(ruby).inspect(),
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
}
|
data/ext/iceberg/src/error.rs
CHANGED
|
@@ -1,16 +1,35 @@
|
|
|
1
|
+
use iceberg::{Error, ErrorKind};
|
|
1
2
|
use magnus::{Error as RbErr, RModule, Ruby, prelude::*};
|
|
2
3
|
|
|
3
|
-
pub fn to_rb_err(err:
|
|
4
|
-
let class_name = match err.kind() {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
pub fn to_rb_err(err: Error) -> RbErr {
|
|
5
|
+
let mut class_name = match err.kind() {
|
|
6
|
+
ErrorKind::NamespaceAlreadyExists => "NamespaceAlreadyExistsError",
|
|
7
|
+
ErrorKind::NamespaceNotFound => "NoSuchNamespaceError",
|
|
8
|
+
ErrorKind::TableAlreadyExists => "TableAlreadyExistsError",
|
|
9
|
+
ErrorKind::TableNotFound => "NoSuchTableError",
|
|
10
|
+
ErrorKind::FeatureUnsupported => "UnsupportedFeatureError",
|
|
11
|
+
ErrorKind::DataInvalid => "InvalidDataError",
|
|
11
12
|
_ => "Error",
|
|
12
13
|
};
|
|
13
14
|
|
|
15
|
+
// no way to get context separately
|
|
16
|
+
// https://github.com/apache/iceberg-rust/issues/1071
|
|
17
|
+
let message = err.message().to_string();
|
|
18
|
+
|
|
19
|
+
if class_name == "Error" {
|
|
20
|
+
let s = err.to_string();
|
|
21
|
+
// for Glue
|
|
22
|
+
if s.contains("EntityNotFoundException") {
|
|
23
|
+
class_name = "NoSuchTableError";
|
|
24
|
+
|
|
25
|
+
// for Glue and S3 Tables
|
|
26
|
+
} else if s.contains("AlreadyExistsException")
|
|
27
|
+
|| s.contains("A table with an identical name already exists in the namespace.")
|
|
28
|
+
{
|
|
29
|
+
class_name = "TableAlreadyExistsError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
14
33
|
let class = Ruby::get()
|
|
15
34
|
.unwrap()
|
|
16
35
|
.class_object()
|
|
@@ -19,14 +38,28 @@ pub fn to_rb_err(err: iceberg::Error) -> RbErr {
|
|
|
19
38
|
.const_get(class_name)
|
|
20
39
|
.unwrap();
|
|
21
40
|
|
|
22
|
-
// no way to get context separately
|
|
23
|
-
// https://github.com/apache/iceberg-rust/issues/1071
|
|
24
|
-
let message = err.to_string();
|
|
25
|
-
let message = message
|
|
26
|
-
// TODO improve
|
|
27
|
-
.strip_prefix("Unexpected => ")
|
|
28
|
-
.map(|v| v.to_string())
|
|
29
|
-
.unwrap_or(message);
|
|
30
|
-
|
|
31
41
|
RbErr::new(class, message)
|
|
32
42
|
}
|
|
43
|
+
|
|
44
|
+
pub fn todo_error<T: std::fmt::Debug>(message: T) -> RbErr {
|
|
45
|
+
let class = Ruby::get()
|
|
46
|
+
.unwrap()
|
|
47
|
+
.class_object()
|
|
48
|
+
.const_get::<_, RModule>("Iceberg")
|
|
49
|
+
.unwrap()
|
|
50
|
+
.const_get("Todo")
|
|
51
|
+
.unwrap();
|
|
52
|
+
RbErr::new(class, format!("not implemented yet: {:?}", message))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#[cfg(feature = "datafusion")]
|
|
56
|
+
pub fn datafusion_error(err: datafusion::common::DataFusionError) -> RbErr {
|
|
57
|
+
let class = Ruby::get()
|
|
58
|
+
.unwrap()
|
|
59
|
+
.class_object()
|
|
60
|
+
.const_get::<_, RModule>("Iceberg")
|
|
61
|
+
.unwrap()
|
|
62
|
+
.const_get("Error")
|
|
63
|
+
.unwrap();
|
|
64
|
+
RbErr::new(class, err.to_string())
|
|
65
|
+
}
|