iceberg 0.11.2 → 0.12.1
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 +17 -0
- data/Cargo.lock +1180 -495
- data/Cargo.toml +7 -0
- data/NOTICE.txt +2 -2
- data/README.md +60 -15
- data/ext/iceberg/Cargo.toml +18 -15
- 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 +157 -33
- data/ext/iceberg/src/encryption.rs +32 -0
- data/ext/iceberg/src/error.rs +63 -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 +306 -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 +223 -164
- data/lib/iceberg/catalog.rb +59 -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/catalog.rs
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
use std::sync::{Arc, RwLock};
|
|
3
|
+
|
|
1
4
|
#[cfg(feature = "datafusion")]
|
|
2
|
-
use datafusion::
|
|
3
|
-
|
|
5
|
+
use datafusion::common::ScalarValue;
|
|
6
|
+
#[cfg(feature = "datafusion")]
|
|
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,13 +27,23 @@ use iceberg_catalog_sql::{
|
|
|
21
27
|
};
|
|
22
28
|
#[cfg(feature = "datafusion")]
|
|
23
29
|
use iceberg_datafusion::IcebergCatalogProvider;
|
|
24
|
-
use
|
|
25
|
-
use
|
|
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
|
+
};
|
|
26
36
|
|
|
27
37
|
use crate::error::to_rb_err;
|
|
28
|
-
|
|
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};
|
|
29
43
|
use crate::utils::Wrap;
|
|
30
|
-
|
|
44
|
+
#[cfg(feature = "datafusion")]
|
|
45
|
+
use crate::utils::date_to_i32;
|
|
46
|
+
use crate::{RbPartitionSpec, RbResult, RbSchema, RbSortOrder, RbTable};
|
|
31
47
|
|
|
32
48
|
pub enum RbCatalogType {
|
|
33
49
|
#[cfg(feature = "glue")]
|
|
@@ -82,7 +98,11 @@ impl RbCatalog {
|
|
|
82
98
|
pub fn new_glue(warehouse: String) -> RbResult<Self> {
|
|
83
99
|
let props = HashMap::from([(GLUE_CATALOG_PROP_WAREHOUSE.to_string(), warehouse)]);
|
|
84
100
|
let catalog = runtime()
|
|
85
|
-
.block_on(
|
|
101
|
+
.block_on(
|
|
102
|
+
GlueCatalogBuilder::default()
|
|
103
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
104
|
+
.load("glue", props),
|
|
105
|
+
)
|
|
86
106
|
.map_err(to_rb_err)?;
|
|
87
107
|
Ok(Self {
|
|
88
108
|
catalog: RbCatalogType::Glue(catalog.into()).into(),
|
|
@@ -91,13 +111,14 @@ impl RbCatalog {
|
|
|
91
111
|
|
|
92
112
|
pub fn new_memory(warehouse: Option<String>) -> RbResult<Self> {
|
|
93
113
|
let mut props = HashMap::new();
|
|
94
|
-
if let Some(v) = warehouse {
|
|
95
|
-
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());
|
|
96
116
|
}
|
|
97
117
|
let catalog = runtime()
|
|
98
118
|
.block_on(
|
|
99
119
|
MemoryCatalogBuilder::default()
|
|
100
|
-
.
|
|
120
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
121
|
+
.with_storage_factory(Arc::new(storage_factory(warehouse)))
|
|
101
122
|
.load("memory", props),
|
|
102
123
|
)
|
|
103
124
|
.map_err(to_rb_err)?;
|
|
@@ -114,13 +135,14 @@ impl RbCatalog {
|
|
|
114
135
|
) -> RbResult<Self> {
|
|
115
136
|
let mut props = props;
|
|
116
137
|
props.insert(REST_CATALOG_PROP_URI.to_string(), uri);
|
|
117
|
-
if let Some(v) = warehouse {
|
|
118
|
-
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());
|
|
119
140
|
}
|
|
120
141
|
let catalog = runtime()
|
|
121
142
|
.block_on(
|
|
122
143
|
RestCatalogBuilder::default()
|
|
123
|
-
.
|
|
144
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
145
|
+
.with_storage_factory(Arc::new(storage_factory(warehouse)))
|
|
124
146
|
.load("rest", props),
|
|
125
147
|
)
|
|
126
148
|
.map_err(to_rb_err)?;
|
|
@@ -134,7 +156,11 @@ impl RbCatalog {
|
|
|
134
156
|
let mut props = HashMap::new();
|
|
135
157
|
props.insert(S3TABLES_CATALOG_PROP_TABLE_BUCKET_ARN.to_string(), arn);
|
|
136
158
|
let catalog = runtime()
|
|
137
|
-
.block_on(
|
|
159
|
+
.block_on(
|
|
160
|
+
S3TablesCatalogBuilder::default()
|
|
161
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
162
|
+
.load("s3tables", props),
|
|
163
|
+
)
|
|
138
164
|
.map_err(to_rb_err)?;
|
|
139
165
|
Ok(Self {
|
|
140
166
|
catalog: RbCatalogType::S3Tables(catalog.into()).into(),
|
|
@@ -150,7 +176,7 @@ impl RbCatalog {
|
|
|
150
176
|
) -> RbResult<Self> {
|
|
151
177
|
let mut props = props;
|
|
152
178
|
props.insert(SQL_CATALOG_PROP_URI.to_string(), uri);
|
|
153
|
-
props.insert(SQL_CATALOG_PROP_WAREHOUSE.to_string(), warehouse);
|
|
179
|
+
props.insert(SQL_CATALOG_PROP_WAREHOUSE.to_string(), warehouse.clone());
|
|
154
180
|
props.insert(
|
|
155
181
|
SQL_CATALOG_PROP_BIND_STYLE.to_string(),
|
|
156
182
|
SqlBindStyle::DollarNumeric.to_string(),
|
|
@@ -158,7 +184,8 @@ impl RbCatalog {
|
|
|
158
184
|
let catalog = runtime()
|
|
159
185
|
.block_on(
|
|
160
186
|
SqlCatalogBuilder::default()
|
|
161
|
-
.
|
|
187
|
+
.with_runtime(Runtime::new(tokio_runtime()))
|
|
188
|
+
.with_storage_factory(Arc::new(storage_factory(Some(warehouse))))
|
|
162
189
|
.load(name, props),
|
|
163
190
|
)
|
|
164
191
|
.map_err(to_rb_err)?;
|
|
@@ -279,20 +306,42 @@ impl RbCatalog {
|
|
|
279
306
|
.collect())
|
|
280
307
|
}
|
|
281
308
|
|
|
309
|
+
#[allow(clippy::too_many_arguments)]
|
|
282
310
|
pub fn create_table(
|
|
283
|
-
&
|
|
311
|
+
ruby: &Ruby,
|
|
312
|
+
rb_self: &Self,
|
|
284
313
|
name: Wrap<TableIdent>,
|
|
285
|
-
schema:
|
|
314
|
+
schema: &RbSchema,
|
|
286
315
|
location: Option<String>,
|
|
316
|
+
partition_spec: Option<&RbPartitionSpec>,
|
|
317
|
+
sort_order: Option<&RbSortOrder>,
|
|
318
|
+
properties: HashMap<String, String>,
|
|
319
|
+
format_version: u8,
|
|
287
320
|
) -> RbResult<RbTable> {
|
|
321
|
+
let format_version = match format_version {
|
|
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
|
+
};
|
|
288
332
|
let creation = TableCreation::builder()
|
|
289
333
|
.name(name.0.name)
|
|
290
|
-
.schema(schema.
|
|
334
|
+
.schema(schema.schema.clone())
|
|
291
335
|
.location_opt(location)
|
|
336
|
+
.partition_spec_opt(partition_spec.map(|v| v.spec.clone()))
|
|
337
|
+
.sort_order_opt(sort_order.map(|v| v.order.clone()))
|
|
338
|
+
.properties(properties)
|
|
339
|
+
.format_version(format_version)
|
|
292
340
|
.build();
|
|
293
341
|
let table = runtime()
|
|
294
342
|
.block_on(
|
|
295
|
-
|
|
343
|
+
rb_self
|
|
344
|
+
.catalog
|
|
296
345
|
.read()
|
|
297
346
|
.unwrap()
|
|
298
347
|
.as_catalog()
|
|
@@ -332,6 +381,19 @@ impl RbCatalog {
|
|
|
332
381
|
Ok(())
|
|
333
382
|
}
|
|
334
383
|
|
|
384
|
+
pub fn purge_table(&self, name: Wrap<TableIdent>) -> RbResult<()> {
|
|
385
|
+
runtime()
|
|
386
|
+
.block_on(
|
|
387
|
+
self.catalog
|
|
388
|
+
.read()
|
|
389
|
+
.unwrap()
|
|
390
|
+
.as_catalog()
|
|
391
|
+
.purge_table(&name.0),
|
|
392
|
+
)
|
|
393
|
+
.map_err(to_rb_err)?;
|
|
394
|
+
Ok(())
|
|
395
|
+
}
|
|
396
|
+
|
|
335
397
|
pub fn table_exists(&self, name: Wrap<TableIdent>) -> RbResult<bool> {
|
|
336
398
|
let exists = runtime()
|
|
337
399
|
.block_on(
|
|
@@ -376,22 +438,84 @@ impl RbCatalog {
|
|
|
376
438
|
}
|
|
377
439
|
|
|
378
440
|
#[cfg(feature = "datafusion")]
|
|
379
|
-
pub fn
|
|
380
|
-
let runtime = runtime();
|
|
381
|
-
|
|
382
|
-
// TODO only create context once
|
|
441
|
+
pub fn session_context(&self, default_namespace: Option<String>) -> RbResult<RbSessionContext> {
|
|
383
442
|
let catalog = self.catalog.read().unwrap().as_arc();
|
|
384
|
-
let provider = runtime
|
|
443
|
+
let provider = runtime()
|
|
385
444
|
.block_on(IcebergCatalogProvider::try_new(catalog))
|
|
386
|
-
.
|
|
387
|
-
let
|
|
445
|
+
.map_err(to_rb_err)?;
|
|
446
|
+
let mut config = SessionConfig::new();
|
|
447
|
+
if let Some(namespace) = default_namespace {
|
|
448
|
+
config = config.with_default_catalog_and_schema("datafusion", namespace);
|
|
449
|
+
}
|
|
450
|
+
let ctx = SessionContext::new_with_config(config);
|
|
388
451
|
ctx.register_catalog("datafusion", Arc::new(provider));
|
|
452
|
+
Ok(RbSessionContext { ctx })
|
|
453
|
+
}
|
|
454
|
+
}
|
|
389
455
|
|
|
390
|
-
|
|
391
|
-
|
|
456
|
+
#[cfg(feature = "datafusion")]
|
|
457
|
+
#[magnus::wrap(class = "Iceberg::SessionContext")]
|
|
458
|
+
pub struct RbSessionContext {
|
|
459
|
+
pub(crate) ctx: SessionContext,
|
|
460
|
+
}
|
|
392
461
|
|
|
393
|
-
|
|
462
|
+
#[cfg(feature = "datafusion")]
|
|
463
|
+
impl RbSessionContext {
|
|
464
|
+
pub fn sql(ruby: &Ruby, rb_self: &Self, sql: String, rb_params: RArray) -> RbResult<Value> {
|
|
465
|
+
let mut params = Vec::new();
|
|
466
|
+
for param in rb_params.into_iter() {
|
|
467
|
+
if param.is_nil() {
|
|
468
|
+
params.push(ScalarValue::Null);
|
|
469
|
+
} else if Qtrue::from_value(param).is_some() {
|
|
470
|
+
params.push(ScalarValue::from(true));
|
|
471
|
+
} else if Qfalse::from_value(param).is_some() {
|
|
472
|
+
params.push(ScalarValue::from(false));
|
|
473
|
+
} else if let Some(v) = Integer::from_value(param) {
|
|
474
|
+
params.push(ScalarValue::from(v.to_i64()?));
|
|
475
|
+
} else if let Some(v) = Float::from_value(param) {
|
|
476
|
+
params.push(ScalarValue::from(v.to_f64()));
|
|
477
|
+
} else if let Some(v) = RString::from_value(param) {
|
|
478
|
+
// TODO support binary strings
|
|
479
|
+
params.push(ScalarValue::from(v.to_string()?));
|
|
480
|
+
} else if unsafe { param.classname() } == "Date" {
|
|
481
|
+
params.push(ScalarValue::Date32(Some(date_to_i32(param)?)));
|
|
482
|
+
} else if unsafe { param.classname() } == "Time" {
|
|
483
|
+
let sec: i64 = param.funcall("to_i", ())?;
|
|
484
|
+
let nsec: i64 = param.funcall("nsec", ())?;
|
|
485
|
+
params.push(ScalarValue::TimestampNanosecond(
|
|
486
|
+
Some(sec * 1_000_000_000 + nsec),
|
|
487
|
+
None,
|
|
488
|
+
));
|
|
489
|
+
} else {
|
|
490
|
+
return Err(todo_error(param));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
394
493
|
|
|
395
|
-
|
|
494
|
+
let runtime = runtime();
|
|
495
|
+
let stream = runtime
|
|
496
|
+
.block_on(rb_self.ctx.sql(&sql))
|
|
497
|
+
.map_err(datafusion_error)?
|
|
498
|
+
.with_param_values(params)
|
|
499
|
+
.map_err(datafusion_error)?;
|
|
500
|
+
let batches = runtime
|
|
501
|
+
.block_on(stream.collect())
|
|
502
|
+
.map_err(datafusion_error)?;
|
|
503
|
+
collect_batches(ruby, batches)
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
fn storage_factory(warehouse: Option<String>) -> OpenDalStorageFactory {
|
|
508
|
+
match warehouse {
|
|
509
|
+
Some(ref v) => {
|
|
510
|
+
if v.starts_with("s3://") {
|
|
511
|
+
OpenDalStorageFactory::S3 {
|
|
512
|
+
customized_credential_load: None,
|
|
513
|
+
}
|
|
514
|
+
} else {
|
|
515
|
+
OpenDalStorageFactory::Fs
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// TODO consider OpenDalStorageFactory::Memory
|
|
519
|
+
None => OpenDalStorageFactory::Fs,
|
|
396
520
|
}
|
|
397
521
|
}
|
|
@@ -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,17 +1,49 @@
|
|
|
1
|
+
use std::error::Error as _;
|
|
2
|
+
|
|
3
|
+
use iceberg::{Error, ErrorKind};
|
|
1
4
|
use magnus::{Error as RbErr, RModule, Ruby, prelude::*};
|
|
2
5
|
|
|
3
|
-
pub fn to_rb_err(err:
|
|
4
|
-
let class_name = match err.kind() {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
pub fn to_rb_err(err: Error) -> RbErr {
|
|
7
|
+
let mut class_name = match err.kind() {
|
|
8
|
+
ErrorKind::NamespaceAlreadyExists => "NamespaceAlreadyExistsError",
|
|
9
|
+
ErrorKind::NamespaceNotFound => "NoSuchNamespaceError",
|
|
10
|
+
ErrorKind::TableAlreadyExists => "TableAlreadyExistsError",
|
|
11
|
+
ErrorKind::TableNotFound => "NoSuchTableError",
|
|
12
|
+
ErrorKind::FeatureUnsupported => "UnsupportedFeatureError",
|
|
13
|
+
ErrorKind::DataInvalid => "InvalidDataError",
|
|
11
14
|
_ => "Error",
|
|
12
15
|
};
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
// no way to get context separately
|
|
18
|
+
// https://github.com/apache/iceberg-rust/issues/1071
|
|
19
|
+
let mut message = err.message().to_string();
|
|
20
|
+
|
|
21
|
+
let mut source = err.source();
|
|
22
|
+
while let Some(s) = source {
|
|
23
|
+
let m = s.to_string();
|
|
24
|
+
if message.contains(&m) {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
message.push_str(": ");
|
|
28
|
+
message.push_str(&m);
|
|
29
|
+
source = s.source();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if class_name == "Error" {
|
|
33
|
+
let s = err.to_string();
|
|
34
|
+
// for Glue
|
|
35
|
+
if s.contains("EntityNotFoundException") {
|
|
36
|
+
class_name = "NoSuchTableError";
|
|
37
|
+
|
|
38
|
+
// for Glue and S3 Tables
|
|
39
|
+
} else if s.contains("AlreadyExistsException")
|
|
40
|
+
|| s.contains("A table with an identical name already exists in the namespace.")
|
|
41
|
+
{
|
|
42
|
+
class_name = "TableAlreadyExistsError";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let class = Ruby::get()
|
|
15
47
|
.unwrap()
|
|
16
48
|
.class_object()
|
|
17
49
|
.const_get::<_, RModule>("Iceberg")
|
|
@@ -19,20 +51,28 @@ pub fn to_rb_err(err: iceberg::Error) -> RbErr {
|
|
|
19
51
|
.const_get(class_name)
|
|
20
52
|
.unwrap();
|
|
21
53
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let message = err.to_string();
|
|
25
|
-
|
|
26
|
-
// TODO remove in 0.12.0
|
|
27
|
-
if message.contains("target schema is not superset of current schema") {
|
|
28
|
-
class = Ruby::get().unwrap().exception_arg_error();
|
|
29
|
-
}
|
|
54
|
+
RbErr::new(class, message)
|
|
55
|
+
}
|
|
30
56
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.
|
|
34
|
-
.
|
|
35
|
-
.
|
|
57
|
+
pub fn todo_error<T: std::fmt::Debug>(message: T) -> RbErr {
|
|
58
|
+
let class = Ruby::get()
|
|
59
|
+
.unwrap()
|
|
60
|
+
.class_object()
|
|
61
|
+
.const_get::<_, RModule>("Iceberg")
|
|
62
|
+
.unwrap()
|
|
63
|
+
.const_get("Todo")
|
|
64
|
+
.unwrap();
|
|
65
|
+
RbErr::new(class, format!("not implemented yet: {:?}", message))
|
|
66
|
+
}
|
|
36
67
|
|
|
37
|
-
|
|
68
|
+
#[cfg(feature = "datafusion")]
|
|
69
|
+
pub fn datafusion_error(err: datafusion::common::DataFusionError) -> RbErr {
|
|
70
|
+
let class = Ruby::get()
|
|
71
|
+
.unwrap()
|
|
72
|
+
.class_object()
|
|
73
|
+
.const_get::<_, RModule>("Iceberg")
|
|
74
|
+
.unwrap()
|
|
75
|
+
.const_get("Error")
|
|
76
|
+
.unwrap();
|
|
77
|
+
RbErr::new(class, err.to_string())
|
|
38
78
|
}
|