parquet 0.5.12 → 0.6.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/Cargo.lock +295 -98
- data/Cargo.toml +1 -1
- data/Gemfile +1 -0
- data/README.md +94 -3
- data/ext/parquet/Cargo.toml +8 -5
- data/ext/parquet/src/adapter_ffi.rs +156 -0
- data/ext/parquet/src/lib.rs +13 -21
- data/ext/parquet-core/Cargo.toml +23 -0
- data/ext/parquet-core/src/arrow_conversion.rs +1133 -0
- data/ext/parquet-core/src/error.rs +163 -0
- data/ext/parquet-core/src/lib.rs +60 -0
- data/ext/parquet-core/src/reader.rs +263 -0
- data/ext/parquet-core/src/schema.rs +283 -0
- data/ext/parquet-core/src/test_utils.rs +308 -0
- data/ext/parquet-core/src/traits/mod.rs +5 -0
- data/ext/parquet-core/src/traits/schema.rs +151 -0
- data/ext/parquet-core/src/value.rs +209 -0
- data/ext/parquet-core/src/writer.rs +839 -0
- data/ext/parquet-core/tests/arrow_conversion_tests.rs +423 -0
- data/ext/parquet-core/tests/binary_data.rs +437 -0
- data/ext/parquet-core/tests/column_projection.rs +557 -0
- data/ext/parquet-core/tests/complex_types.rs +821 -0
- data/ext/parquet-core/tests/compression_tests.rs +434 -0
- data/ext/parquet-core/tests/concurrent_access.rs +430 -0
- data/ext/parquet-core/tests/decimal_tests.rs +488 -0
- data/ext/parquet-core/tests/edge_cases_corner_cases.rs +322 -0
- data/ext/parquet-core/tests/error_handling_comprehensive_tests.rs +547 -0
- data/ext/parquet-core/tests/null_handling_tests.rs +430 -0
- data/ext/parquet-core/tests/performance_memory.rs +181 -0
- data/ext/parquet-core/tests/primitive_types.rs +547 -0
- data/ext/parquet-core/tests/real_world_patterns.rs +777 -0
- data/ext/parquet-core/tests/roundtrip_correctness.rs +279 -0
- data/ext/parquet-core/tests/schema_comprehensive_tests.rs +534 -0
- data/ext/parquet-core/tests/temporal_tests.rs +518 -0
- data/ext/parquet-core/tests/test_helpers.rs +132 -0
- data/ext/parquet-core/tests/writer_tests.rs +545 -0
- data/ext/parquet-ruby-adapter/Cargo.toml +22 -0
- data/ext/parquet-ruby-adapter/build.rs +5 -0
- data/ext/parquet-ruby-adapter/examples/try_into_value_demo.rs +98 -0
- data/ext/parquet-ruby-adapter/src/batch_manager.rs +116 -0
- data/ext/parquet-ruby-adapter/src/chunk_reader.rs +237 -0
- data/ext/parquet-ruby-adapter/src/converter.rs +1685 -0
- data/ext/parquet-ruby-adapter/src/error.rs +148 -0
- data/ext/{parquet/src/ruby_reader.rs → parquet-ruby-adapter/src/io.rs} +190 -56
- data/ext/parquet-ruby-adapter/src/lib.rs +90 -0
- data/ext/parquet-ruby-adapter/src/logger.rs +64 -0
- data/ext/parquet-ruby-adapter/src/metadata.rs +427 -0
- data/ext/parquet-ruby-adapter/src/reader.rs +317 -0
- data/ext/parquet-ruby-adapter/src/schema.rs +810 -0
- data/ext/parquet-ruby-adapter/src/string_cache.rs +106 -0
- data/ext/parquet-ruby-adapter/src/try_into_value.rs +91 -0
- data/ext/parquet-ruby-adapter/src/types.rs +94 -0
- data/ext/parquet-ruby-adapter/src/utils.rs +186 -0
- data/ext/parquet-ruby-adapter/src/writer.rs +435 -0
- data/lib/parquet/schema.rb +19 -0
- data/lib/parquet/version.rb +1 -1
- metadata +50 -24
- data/ext/parquet/src/enumerator.rs +0 -68
- data/ext/parquet/src/header_cache.rs +0 -99
- data/ext/parquet/src/logger.rs +0 -171
- data/ext/parquet/src/reader/common.rs +0 -111
- data/ext/parquet/src/reader/mod.rs +0 -211
- data/ext/parquet/src/reader/parquet_column_reader.rs +0 -44
- data/ext/parquet/src/reader/parquet_row_reader.rs +0 -43
- data/ext/parquet/src/reader/unified/mod.rs +0 -363
- data/ext/parquet/src/types/core_types.rs +0 -120
- data/ext/parquet/src/types/mod.rs +0 -100
- data/ext/parquet/src/types/parquet_value.rs +0 -1275
- data/ext/parquet/src/types/record_types.rs +0 -603
- data/ext/parquet/src/types/schema_converter.rs +0 -290
- data/ext/parquet/src/types/schema_node.rs +0 -424
- data/ext/parquet/src/types/timestamp.rs +0 -285
- data/ext/parquet/src/types/type_conversion.rs +0 -1949
- data/ext/parquet/src/types/writer_types.rs +0 -329
- data/ext/parquet/src/utils.rs +0 -184
- data/ext/parquet/src/writer/mod.rs +0 -505
- data/ext/parquet/src/writer/write_columns.rs +0 -238
- data/ext/parquet/src/writer/write_rows.rs +0 -488
@@ -0,0 +1,106 @@
|
|
1
|
+
use std::collections::HashMap;
|
2
|
+
use std::sync::{Arc, Mutex};
|
3
|
+
|
4
|
+
use magnus::RString;
|
5
|
+
|
6
|
+
/// A cache for interning strings in the Ruby VM to reduce memory usage
|
7
|
+
/// when there are many repeated strings
|
8
|
+
#[derive(Debug)]
|
9
|
+
pub struct StringCache {
|
10
|
+
/// The actual cache is shared behind an Arc<Mutex> to allow cloning
|
11
|
+
/// while maintaining a single global cache
|
12
|
+
cache: Arc<Mutex<HashMap<String, &'static str>>>,
|
13
|
+
enabled: bool,
|
14
|
+
hits: Arc<Mutex<usize>>,
|
15
|
+
misses: Arc<Mutex<usize>>,
|
16
|
+
}
|
17
|
+
|
18
|
+
impl StringCache {
|
19
|
+
/// Create a new string cache
|
20
|
+
pub fn new(enabled: bool) -> Self {
|
21
|
+
Self {
|
22
|
+
cache: Arc::new(Mutex::new(HashMap::new())),
|
23
|
+
enabled,
|
24
|
+
hits: Arc::new(Mutex::new(0)),
|
25
|
+
misses: Arc::new(Mutex::new(0)),
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
/// Intern a string in Ruby's VM, returning the same string for tracking
|
30
|
+
/// Note: We return the input string to maintain API compatibility,
|
31
|
+
/// but internally we ensure it's interned in Ruby's VM
|
32
|
+
pub fn intern(&mut self, s: String) -> Arc<str> {
|
33
|
+
if !self.enabled {
|
34
|
+
return Arc::from(s.as_str());
|
35
|
+
}
|
36
|
+
|
37
|
+
// Try to get or create the interned string
|
38
|
+
let result = (|| -> Result<(), String> {
|
39
|
+
let mut cache = self.cache.lock().map_err(|e| e.to_string())?;
|
40
|
+
|
41
|
+
if cache.contains_key(&s) {
|
42
|
+
let mut hits = self.hits.lock().map_err(|e| e.to_string())?;
|
43
|
+
*hits += 1;
|
44
|
+
} else {
|
45
|
+
// Create Ruby string and intern it
|
46
|
+
let rstring = RString::new(&s);
|
47
|
+
let interned = rstring.to_interned_str();
|
48
|
+
let static_str = interned.as_str().map_err(|e| e.to_string())?;
|
49
|
+
|
50
|
+
cache.insert(s.clone(), static_str);
|
51
|
+
|
52
|
+
let mut misses = self.misses.lock().map_err(|e| e.to_string())?;
|
53
|
+
*misses += 1;
|
54
|
+
}
|
55
|
+
Ok(())
|
56
|
+
})();
|
57
|
+
|
58
|
+
// Log any errors but don't fail - just return the string
|
59
|
+
if let Err(e) = result {
|
60
|
+
eprintln!("String cache error: {}", e);
|
61
|
+
}
|
62
|
+
|
63
|
+
Arc::from(s.as_str())
|
64
|
+
}
|
65
|
+
|
66
|
+
/// Get cache statistics
|
67
|
+
pub fn stats(&self) -> CacheStats {
|
68
|
+
let cache_size = self.cache.lock().map(|c| c.len()).unwrap_or(0);
|
69
|
+
let hits = self.hits.lock().map(|h| *h).unwrap_or(0);
|
70
|
+
let misses = self.misses.lock().map(|m| *m).unwrap_or(0);
|
71
|
+
|
72
|
+
CacheStats {
|
73
|
+
enabled: self.enabled,
|
74
|
+
size: cache_size,
|
75
|
+
hits,
|
76
|
+
misses,
|
77
|
+
hit_rate: if hits + misses > 0 {
|
78
|
+
hits as f64 / (hits + misses) as f64
|
79
|
+
} else {
|
80
|
+
0.0
|
81
|
+
},
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
/// Clear the cache
|
86
|
+
pub fn clear(&mut self) {
|
87
|
+
if let Ok(mut cache) = self.cache.lock() {
|
88
|
+
cache.clear();
|
89
|
+
}
|
90
|
+
if let Ok(mut hits) = self.hits.lock() {
|
91
|
+
*hits = 0;
|
92
|
+
}
|
93
|
+
if let Ok(mut misses) = self.misses.lock() {
|
94
|
+
*misses = 0;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
#[derive(Debug)]
|
100
|
+
pub struct CacheStats {
|
101
|
+
pub enabled: bool,
|
102
|
+
pub size: usize,
|
103
|
+
pub hits: usize,
|
104
|
+
pub misses: usize,
|
105
|
+
pub hit_rate: f64,
|
106
|
+
}
|
@@ -0,0 +1,91 @@
|
|
1
|
+
use crate::{error::Result, RubyAdapterError};
|
2
|
+
use magnus::{value::ReprValue, IntoValue, Ruby, Value};
|
3
|
+
|
4
|
+
/// Trait for converting Rust values to Ruby values with error handling
|
5
|
+
///
|
6
|
+
/// This is similar to Magnus's `IntoValue` trait but allows for returning errors
|
7
|
+
/// instead of panicking or returning invalid values.
|
8
|
+
pub trait TryIntoValue: Sized {
|
9
|
+
/// Convert `self` to a Ruby value with error handling
|
10
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value>;
|
11
|
+
|
12
|
+
/// Convert `self` to a Ruby value with error handling, using the Ruby runtime from the current thread
|
13
|
+
fn try_into_value_with_current_thread(self) -> Result<Value> {
|
14
|
+
let ruby =
|
15
|
+
Ruby::get().map_err(|_| RubyAdapterError::runtime("Failed to get Ruby runtime"))?;
|
16
|
+
self.try_into_value(&ruby)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
// Note: We don't provide a blanket implementation for all IntoValue types
|
21
|
+
// because some types may want to provide custom error handling.
|
22
|
+
// Types that need TryIntoValue should implement it explicitly.
|
23
|
+
|
24
|
+
// Convenience implementations for common types
|
25
|
+
impl TryIntoValue for String {
|
26
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
27
|
+
Ok(self.into_value_with(handle))
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
impl TryIntoValue for &str {
|
32
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
33
|
+
Ok(self.into_value_with(handle))
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
impl TryIntoValue for i32 {
|
38
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
39
|
+
Ok(self.into_value_with(handle))
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
impl TryIntoValue for i64 {
|
44
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
45
|
+
Ok(self.into_value_with(handle))
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
impl TryIntoValue for f32 {
|
50
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
51
|
+
Ok(self.into_value_with(handle))
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
impl TryIntoValue for f64 {
|
56
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
57
|
+
Ok(self.into_value_with(handle))
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
impl TryIntoValue for bool {
|
62
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
63
|
+
Ok(self.into_value_with(handle))
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
impl<T> TryIntoValue for Vec<T>
|
68
|
+
where
|
69
|
+
T: TryIntoValue,
|
70
|
+
{
|
71
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
72
|
+
let array = handle.ary_new();
|
73
|
+
for item in self {
|
74
|
+
let ruby_value = item.try_into_value(handle)?;
|
75
|
+
array.push(ruby_value)?;
|
76
|
+
}
|
77
|
+
Ok(handle.into_value(array))
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
impl<T> TryIntoValue for Option<T>
|
82
|
+
where
|
83
|
+
T: TryIntoValue,
|
84
|
+
{
|
85
|
+
fn try_into_value(self, handle: &Ruby) -> Result<Value> {
|
86
|
+
match self {
|
87
|
+
Some(value) => value.try_into_value(handle),
|
88
|
+
None => Ok(handle.qnil().as_value()),
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
use magnus::Value;
|
2
|
+
use std::fs::File;
|
3
|
+
use std::str::FromStr;
|
4
|
+
use tempfile::NamedTempFile;
|
5
|
+
|
6
|
+
/// Arguments for writing Parquet files
|
7
|
+
#[derive(Debug)]
|
8
|
+
pub struct ParquetWriteArgs {
|
9
|
+
pub read_from: Value,
|
10
|
+
pub write_to: Value,
|
11
|
+
pub schema_value: Value,
|
12
|
+
pub batch_size: Option<usize>,
|
13
|
+
pub flush_threshold: Option<usize>,
|
14
|
+
pub compression: Option<String>,
|
15
|
+
pub sample_size: Option<usize>,
|
16
|
+
pub logger: Option<Value>,
|
17
|
+
pub string_cache: Option<bool>,
|
18
|
+
}
|
19
|
+
|
20
|
+
/// Arguments for creating row enumerators
|
21
|
+
pub struct RowEnumeratorArgs {
|
22
|
+
pub rb_self: Value,
|
23
|
+
pub to_read: Value,
|
24
|
+
pub result_type: ParserResultType,
|
25
|
+
pub columns: Option<Vec<String>>,
|
26
|
+
pub strict: bool,
|
27
|
+
pub logger: Option<Value>,
|
28
|
+
}
|
29
|
+
|
30
|
+
/// Arguments for creating column enumerators
|
31
|
+
pub struct ColumnEnumeratorArgs {
|
32
|
+
pub rb_self: Value,
|
33
|
+
pub to_read: Value,
|
34
|
+
pub result_type: ParserResultType,
|
35
|
+
pub columns: Option<Vec<String>>,
|
36
|
+
pub batch_size: Option<usize>,
|
37
|
+
pub strict: bool,
|
38
|
+
pub logger: Option<Value>,
|
39
|
+
}
|
40
|
+
|
41
|
+
/// Enum to handle different writer outputs
|
42
|
+
pub enum WriterOutput {
|
43
|
+
File(parquet_core::Writer<File>),
|
44
|
+
TempFile(parquet_core::Writer<File>, NamedTempFile, Value), // Writer, temp file, IO object
|
45
|
+
}
|
46
|
+
|
47
|
+
/// Result type for parser output
|
48
|
+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
49
|
+
pub enum ParserResultType {
|
50
|
+
Hash,
|
51
|
+
Array,
|
52
|
+
}
|
53
|
+
|
54
|
+
impl ParserResultType {
|
55
|
+
pub fn iter() -> impl Iterator<Item = Self> {
|
56
|
+
[Self::Hash, Self::Array].into_iter()
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
impl FromStr for ParserResultType {
|
61
|
+
type Err = String;
|
62
|
+
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
63
|
+
Self::try_from(s)
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
impl TryFrom<&str> for ParserResultType {
|
68
|
+
type Error = String;
|
69
|
+
|
70
|
+
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
71
|
+
match value {
|
72
|
+
"hash" => Ok(ParserResultType::Hash),
|
73
|
+
"array" => Ok(ParserResultType::Array),
|
74
|
+
_ => Err(format!("Invalid parser result type: {}", value)),
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
impl TryFrom<String> for ParserResultType {
|
80
|
+
type Error = String;
|
81
|
+
|
82
|
+
fn try_from(value: String) -> Result<Self, Self::Error> {
|
83
|
+
Self::try_from(value.as_str())
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
impl std::fmt::Display for ParserResultType {
|
88
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
89
|
+
match self {
|
90
|
+
ParserResultType::Hash => write!(f, "hash"),
|
91
|
+
ParserResultType::Array => write!(f, "array"),
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
@@ -0,0 +1,186 @@
|
|
1
|
+
use magnus::value::ReprValue;
|
2
|
+
use magnus::{
|
3
|
+
scan_args::{get_kwargs, scan_args},
|
4
|
+
Error as MagnusError, KwArgs, RArray, RHash, Ruby, Symbol, Value,
|
5
|
+
};
|
6
|
+
use parquet::basic::Compression;
|
7
|
+
use parquet_core::ParquetValue;
|
8
|
+
|
9
|
+
use crate::types::{ColumnEnumeratorArgs, ParquetWriteArgs, RowEnumeratorArgs};
|
10
|
+
|
11
|
+
/// Estimate the memory size of a ParquetValue
|
12
|
+
pub fn estimate_parquet_value_size(value: &ParquetValue) -> usize {
|
13
|
+
match value {
|
14
|
+
ParquetValue::Null => 1,
|
15
|
+
ParquetValue::Boolean(_) => 1,
|
16
|
+
ParquetValue::Int8(_) => 1,
|
17
|
+
ParquetValue::Int16(_) => 2,
|
18
|
+
ParquetValue::Int32(_) => 4,
|
19
|
+
ParquetValue::Int64(_) => 8,
|
20
|
+
ParquetValue::UInt8(_) => 1,
|
21
|
+
ParquetValue::UInt16(_) => 2,
|
22
|
+
ParquetValue::UInt32(_) => 4,
|
23
|
+
ParquetValue::UInt64(_) => 8,
|
24
|
+
ParquetValue::Float16(_) => 4,
|
25
|
+
ParquetValue::Float32(_) => 4,
|
26
|
+
ParquetValue::Float64(_) => 8,
|
27
|
+
ParquetValue::String(s) => s.len() + 24, // String overhead
|
28
|
+
ParquetValue::Bytes(b) => b.len() + 24, // Vec overhead
|
29
|
+
ParquetValue::Date32(_) => 4,
|
30
|
+
ParquetValue::Date64(_) => 8,
|
31
|
+
ParquetValue::Decimal128(_, _) => 16 + 1, // value + scale
|
32
|
+
ParquetValue::Decimal256(_, _) => 32 + 1, // approx size for BigInt + scale
|
33
|
+
ParquetValue::TimestampSecond(_, tz) => 8 + tz.as_ref().map_or(0, |s| s.len() + 24),
|
34
|
+
ParquetValue::TimestampMillis(_, tz) => 8 + tz.as_ref().map_or(0, |s| s.len() + 24),
|
35
|
+
ParquetValue::TimestampMicros(_, tz) => 8 + tz.as_ref().map_or(0, |s| s.len() + 24),
|
36
|
+
ParquetValue::TimestampNanos(_, tz) => 8 + tz.as_ref().map_or(0, |s| s.len() + 24),
|
37
|
+
ParquetValue::TimeMillis(_) => 4,
|
38
|
+
ParquetValue::TimeMicros(_) => 8,
|
39
|
+
ParquetValue::List(items) => {
|
40
|
+
24 + items.iter().map(estimate_parquet_value_size).sum::<usize>()
|
41
|
+
}
|
42
|
+
ParquetValue::Map(entries) => {
|
43
|
+
48 + entries
|
44
|
+
.iter()
|
45
|
+
.map(|(k, v)| estimate_parquet_value_size(k) + estimate_parquet_value_size(v))
|
46
|
+
.sum::<usize>()
|
47
|
+
}
|
48
|
+
ParquetValue::Record(fields) => {
|
49
|
+
48 + fields
|
50
|
+
.iter()
|
51
|
+
.map(|(k, v)| k.len() + 24 + estimate_parquet_value_size(v))
|
52
|
+
.sum::<usize>()
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
/// Estimate the memory size of a row
|
58
|
+
pub fn estimate_row_size(row: &[ParquetValue]) -> usize {
|
59
|
+
row.iter().map(estimate_parquet_value_size).sum()
|
60
|
+
}
|
61
|
+
|
62
|
+
/// Parse compression type from string
|
63
|
+
pub fn parse_compression(compression: Option<String>) -> Result<Compression, MagnusError> {
|
64
|
+
match compression.map(|s| s.to_lowercase()).as_deref() {
|
65
|
+
Some("none") | Some("uncompressed") => Ok(Compression::UNCOMPRESSED),
|
66
|
+
Some("snappy") => Ok(Compression::SNAPPY),
|
67
|
+
Some("gzip") => Ok(Compression::GZIP(parquet::basic::GzipLevel::default())),
|
68
|
+
Some("lz4") => Ok(Compression::LZ4),
|
69
|
+
Some("zstd") => Ok(Compression::ZSTD(parquet::basic::ZstdLevel::default())),
|
70
|
+
Some("brotli") => Ok(Compression::BROTLI(parquet::basic::BrotliLevel::default())),
|
71
|
+
None => Ok(Compression::SNAPPY), // Default to SNAPPY
|
72
|
+
Some(other) => Err(MagnusError::new(
|
73
|
+
magnus::exception::arg_error(),
|
74
|
+
format!("Invalid compression option: '{}'. Valid options are: none, snappy, gzip, lz4, zstd, brotli", other),
|
75
|
+
)),
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
/// Parse arguments for Parquet writing
|
80
|
+
pub fn parse_parquet_write_args(
|
81
|
+
_ruby: &Ruby,
|
82
|
+
args: &[Value],
|
83
|
+
) -> Result<ParquetWriteArgs, MagnusError> {
|
84
|
+
let parsed_args = scan_args::<(Value,), (), (), (), _, ()>(args)?;
|
85
|
+
let (read_from,) = parsed_args.required;
|
86
|
+
|
87
|
+
let kwargs = get_kwargs::<
|
88
|
+
_,
|
89
|
+
(Value, Value),
|
90
|
+
(
|
91
|
+
Option<Option<usize>>,
|
92
|
+
Option<Option<usize>>,
|
93
|
+
Option<Option<String>>,
|
94
|
+
Option<Option<usize>>,
|
95
|
+
Option<Option<Value>>,
|
96
|
+
Option<Option<bool>>,
|
97
|
+
),
|
98
|
+
(),
|
99
|
+
>(
|
100
|
+
parsed_args.keywords,
|
101
|
+
&["schema", "write_to"],
|
102
|
+
&[
|
103
|
+
"batch_size",
|
104
|
+
"flush_threshold",
|
105
|
+
"compression",
|
106
|
+
"sample_size",
|
107
|
+
"logger",
|
108
|
+
"string_cache",
|
109
|
+
],
|
110
|
+
)?;
|
111
|
+
|
112
|
+
Ok(ParquetWriteArgs {
|
113
|
+
read_from,
|
114
|
+
write_to: kwargs.required.1,
|
115
|
+
schema_value: kwargs.required.0,
|
116
|
+
batch_size: kwargs.optional.0.flatten(),
|
117
|
+
flush_threshold: kwargs.optional.1.flatten(),
|
118
|
+
compression: kwargs.optional.2.flatten(),
|
119
|
+
sample_size: kwargs.optional.3.flatten(),
|
120
|
+
logger: kwargs.optional.4.flatten(),
|
121
|
+
string_cache: kwargs.optional.5.flatten(),
|
122
|
+
})
|
123
|
+
}
|
124
|
+
|
125
|
+
/// Handle block or enumerator creation
|
126
|
+
pub fn handle_block_or_enum<F, T>(
|
127
|
+
block_given: bool,
|
128
|
+
create_enum: F,
|
129
|
+
) -> Result<Option<T>, MagnusError>
|
130
|
+
where
|
131
|
+
F: FnOnce() -> Result<T, MagnusError>,
|
132
|
+
{
|
133
|
+
if !block_given {
|
134
|
+
let enum_value = create_enum()?;
|
135
|
+
return Ok(Some(enum_value));
|
136
|
+
}
|
137
|
+
Ok(None)
|
138
|
+
}
|
139
|
+
|
140
|
+
/// Create a row enumerator
|
141
|
+
pub fn create_row_enumerator(args: RowEnumeratorArgs) -> Result<magnus::Enumerator, MagnusError> {
|
142
|
+
let kwargs = RHash::new();
|
143
|
+
kwargs.aset(
|
144
|
+
Symbol::new("result_type"),
|
145
|
+
Symbol::new(args.result_type.to_string()),
|
146
|
+
)?;
|
147
|
+
if let Some(columns) = args.columns {
|
148
|
+
kwargs.aset(Symbol::new("columns"), RArray::from_vec(columns))?;
|
149
|
+
}
|
150
|
+
if args.strict {
|
151
|
+
kwargs.aset(Symbol::new("strict"), true)?;
|
152
|
+
}
|
153
|
+
if let Some(logger) = args.logger {
|
154
|
+
kwargs.aset(Symbol::new("logger"), logger)?;
|
155
|
+
}
|
156
|
+
Ok(args
|
157
|
+
.rb_self
|
158
|
+
.enumeratorize("each_row", (args.to_read, KwArgs(kwargs))))
|
159
|
+
}
|
160
|
+
|
161
|
+
/// Create a column enumerator
|
162
|
+
#[inline]
|
163
|
+
pub fn create_column_enumerator(
|
164
|
+
args: ColumnEnumeratorArgs,
|
165
|
+
) -> Result<magnus::Enumerator, MagnusError> {
|
166
|
+
let kwargs = RHash::new();
|
167
|
+
kwargs.aset(
|
168
|
+
Symbol::new("result_type"),
|
169
|
+
Symbol::new(args.result_type.to_string()),
|
170
|
+
)?;
|
171
|
+
if let Some(columns) = args.columns {
|
172
|
+
kwargs.aset(Symbol::new("columns"), RArray::from_vec(columns))?;
|
173
|
+
}
|
174
|
+
if let Some(batch_size) = args.batch_size {
|
175
|
+
kwargs.aset(Symbol::new("batch_size"), batch_size)?;
|
176
|
+
}
|
177
|
+
if args.strict {
|
178
|
+
kwargs.aset(Symbol::new("strict"), true)?;
|
179
|
+
}
|
180
|
+
if let Some(logger) = args.logger {
|
181
|
+
kwargs.aset(Symbol::new("logger"), logger)?;
|
182
|
+
}
|
183
|
+
Ok(args
|
184
|
+
.rb_self
|
185
|
+
.enumeratorize("each_column", (args.to_read, KwArgs(kwargs))))
|
186
|
+
}
|