parquet 0.5.13 → 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 +3 -0
- 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 -605
- 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
@@ -1,363 +0,0 @@
|
|
1
|
-
use crate::header_cache::StringCache;
|
2
|
-
use crate::logger::RubyLogger;
|
3
|
-
use crate::types::TryIntoValue;
|
4
|
-
use crate::{
|
5
|
-
create_column_enumerator, create_row_enumerator, ColumnEnumeratorArgs, ColumnRecord,
|
6
|
-
ParquetField, ParquetGemError, ParquetValueVec, ParserResultType, RowEnumeratorArgs, RowRecord,
|
7
|
-
};
|
8
|
-
use ahash::RandomState;
|
9
|
-
use either::Either;
|
10
|
-
use magnus::IntoValue;
|
11
|
-
use magnus::{Error as MagnusError, Ruby, Value};
|
12
|
-
use std::collections::HashMap;
|
13
|
-
use std::rc::Rc;
|
14
|
-
use std::sync::OnceLock;
|
15
|
-
|
16
|
-
use super::common::{
|
17
|
-
create_batch_reader, handle_block_or_enum, handle_empty_file, open_parquet_source,
|
18
|
-
};
|
19
|
-
use crate::types::ArrayWrapper;
|
20
|
-
|
21
|
-
/// A unified parser configuration that can be used for both row and column parsing
|
22
|
-
pub enum ParserType {
|
23
|
-
Row {
|
24
|
-
strict: bool,
|
25
|
-
},
|
26
|
-
Column {
|
27
|
-
batch_size: Option<usize>,
|
28
|
-
strict: bool,
|
29
|
-
},
|
30
|
-
}
|
31
|
-
|
32
|
-
/// Unified parser arguments structure
|
33
|
-
pub struct UnifiedParserArgs {
|
34
|
-
pub to_read: Value,
|
35
|
-
pub result_type: ParserResultType,
|
36
|
-
pub columns: Option<Vec<String>>,
|
37
|
-
pub parser_type: ParserType,
|
38
|
-
pub logger: Option<Value>,
|
39
|
-
}
|
40
|
-
|
41
|
-
/// Unified implementation for parsing Parquet data (both rows and columns)
|
42
|
-
pub fn parse_parquet_unified(
|
43
|
-
ruby: Rc<Ruby>,
|
44
|
-
rb_self: Value,
|
45
|
-
args: UnifiedParserArgs,
|
46
|
-
) -> Result<Value, ParquetGemError> {
|
47
|
-
let UnifiedParserArgs {
|
48
|
-
to_read,
|
49
|
-
result_type,
|
50
|
-
columns,
|
51
|
-
parser_type,
|
52
|
-
logger,
|
53
|
-
} = args;
|
54
|
-
|
55
|
-
// Initialize the logger if provided
|
56
|
-
let ruby_logger = RubyLogger::new(&ruby, logger)?;
|
57
|
-
|
58
|
-
// Clone values for the closure to avoid move issues
|
59
|
-
let columns_clone = columns.clone();
|
60
|
-
|
61
|
-
// Determine if we're handling rows or columns for enumerator creation
|
62
|
-
match &parser_type {
|
63
|
-
ParserType::Row { strict } => {
|
64
|
-
// Handle block or create row enumerator
|
65
|
-
if let Some(enum_value) = handle_block_or_enum(&ruby, ruby.block_given(), || {
|
66
|
-
create_row_enumerator(RowEnumeratorArgs {
|
67
|
-
rb_self,
|
68
|
-
to_read,
|
69
|
-
result_type,
|
70
|
-
columns: columns_clone,
|
71
|
-
strict: *strict,
|
72
|
-
logger,
|
73
|
-
})
|
74
|
-
.map(|yield_enum| yield_enum.into_value_with(&ruby))
|
75
|
-
})? {
|
76
|
-
return Ok(enum_value);
|
77
|
-
}
|
78
|
-
}
|
79
|
-
ParserType::Column { batch_size, strict } => {
|
80
|
-
// For column-based parsing, log the batch size if present
|
81
|
-
if let Some(ref bs) = batch_size {
|
82
|
-
ruby_logger.debug(|| format!("Using batch size: {}", bs))?;
|
83
|
-
}
|
84
|
-
|
85
|
-
// Handle block or create column enumerator
|
86
|
-
if let Some(enum_value) = handle_block_or_enum(&ruby, ruby.block_given(), || {
|
87
|
-
create_column_enumerator(ColumnEnumeratorArgs {
|
88
|
-
rb_self,
|
89
|
-
to_read,
|
90
|
-
result_type,
|
91
|
-
columns: columns_clone,
|
92
|
-
batch_size: *batch_size,
|
93
|
-
strict: *strict,
|
94
|
-
logger: logger.as_ref().map(|_| to_read),
|
95
|
-
})
|
96
|
-
.map(|yield_enum| yield_enum.into_value_with(&ruby))
|
97
|
-
})? {
|
98
|
-
return Ok(enum_value);
|
99
|
-
}
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
// Open the Parquet source
|
104
|
-
let source = open_parquet_source(ruby.clone(), to_read)?;
|
105
|
-
|
106
|
-
// Based on the parser type, handle the data differently
|
107
|
-
match parser_type {
|
108
|
-
ParserType::Row { strict } => {
|
109
|
-
// Handle row-based parsing
|
110
|
-
process_row_data(
|
111
|
-
ruby.clone(),
|
112
|
-
source,
|
113
|
-
&columns,
|
114
|
-
result_type,
|
115
|
-
strict,
|
116
|
-
&ruby_logger,
|
117
|
-
)?;
|
118
|
-
}
|
119
|
-
ParserType::Column { batch_size, strict } => {
|
120
|
-
// Handle column-based parsing
|
121
|
-
process_column_data(
|
122
|
-
ruby.clone(),
|
123
|
-
source,
|
124
|
-
&columns,
|
125
|
-
result_type,
|
126
|
-
batch_size,
|
127
|
-
strict,
|
128
|
-
&ruby_logger,
|
129
|
-
)?;
|
130
|
-
}
|
131
|
-
}
|
132
|
-
|
133
|
-
Ok(ruby.qnil().into_value_with(&ruby))
|
134
|
-
}
|
135
|
-
|
136
|
-
/// Process row-based Parquet data
|
137
|
-
fn process_row_data(
|
138
|
-
ruby: Rc<Ruby>,
|
139
|
-
source: Either<std::fs::File, crate::ruby_reader::ThreadSafeRubyReader>,
|
140
|
-
columns: &Option<Vec<String>>,
|
141
|
-
result_type: ParserResultType,
|
142
|
-
strict: bool,
|
143
|
-
ruby_logger: &RubyLogger,
|
144
|
-
) -> Result<(), ParquetGemError> {
|
145
|
-
use parquet::file::reader::{FileReader, SerializedFileReader};
|
146
|
-
use parquet::record::reader::RowIter as ParquetRowIter;
|
147
|
-
|
148
|
-
// Create the row-based reader
|
149
|
-
let reader: Box<dyn FileReader> = match source {
|
150
|
-
Either::Left(file) => {
|
151
|
-
Box::new(SerializedFileReader::new(file).map_err(ParquetGemError::from)?)
|
152
|
-
}
|
153
|
-
Either::Right(readable) => {
|
154
|
-
Box::new(SerializedFileReader::new(readable).map_err(ParquetGemError::from)?)
|
155
|
-
}
|
156
|
-
};
|
157
|
-
|
158
|
-
let schema = reader.metadata().file_metadata().schema().clone();
|
159
|
-
ruby_logger.debug(|| format!("Schema loaded: {:?}", schema))?;
|
160
|
-
|
161
|
-
let mut iter = ParquetRowIter::from_file_into(reader);
|
162
|
-
if let Some(cols) = columns {
|
163
|
-
ruby_logger.debug(|| format!("Projecting columns: {:?}", cols))?;
|
164
|
-
let projection = create_projection_schema(&schema, cols);
|
165
|
-
iter = iter.project(Some(projection.to_owned())).map_err(|e| {
|
166
|
-
MagnusError::new(
|
167
|
-
ruby.exception_runtime_error(),
|
168
|
-
format!("Failed to create projection: {}", e),
|
169
|
-
)
|
170
|
-
})?;
|
171
|
-
}
|
172
|
-
|
173
|
-
match result_type {
|
174
|
-
ParserResultType::Hash => {
|
175
|
-
let headers = OnceLock::new();
|
176
|
-
let headers_clone = headers.clone();
|
177
|
-
let iter = iter.map(move |row| {
|
178
|
-
row.map(|row| {
|
179
|
-
let headers = headers_clone.get_or_init(|| {
|
180
|
-
let column_count = row.get_column_iter().count();
|
181
|
-
|
182
|
-
let mut header_string = Vec::with_capacity(column_count);
|
183
|
-
for (k, _) in row.get_column_iter() {
|
184
|
-
header_string.push(k.to_owned());
|
185
|
-
}
|
186
|
-
|
187
|
-
StringCache::intern_many(&header_string).expect("Failed to intern headers")
|
188
|
-
});
|
189
|
-
|
190
|
-
let mut map =
|
191
|
-
HashMap::with_capacity_and_hasher(headers.len(), RandomState::default());
|
192
|
-
for (i, ((_, v), t)) in
|
193
|
-
row.get_column_iter().zip(schema.get_fields()).enumerate()
|
194
|
-
{
|
195
|
-
let type_info = t.get_basic_info();
|
196
|
-
map.insert(
|
197
|
-
headers[i],
|
198
|
-
ParquetField {
|
199
|
-
field: v.clone(),
|
200
|
-
converted_type: type_info.converted_type(),
|
201
|
-
logical_type: type_info.logical_type().clone(),
|
202
|
-
strict,
|
203
|
-
},
|
204
|
-
);
|
205
|
-
}
|
206
|
-
map
|
207
|
-
})
|
208
|
-
.map(RowRecord::Map::<RandomState>)
|
209
|
-
.map_err(ParquetGemError::from)
|
210
|
-
});
|
211
|
-
|
212
|
-
for result in iter {
|
213
|
-
let record = result?;
|
214
|
-
let _: Value = ruby.yield_value(record.try_into_value_with(&ruby)?)?;
|
215
|
-
}
|
216
|
-
}
|
217
|
-
ParserResultType::Array => {
|
218
|
-
let iter = iter.map(|row| {
|
219
|
-
row.map(|row| {
|
220
|
-
let column_count = row.get_column_iter().count();
|
221
|
-
let mut vec = Vec::with_capacity(column_count);
|
222
|
-
for ((_, v), t) in row.get_column_iter().zip(schema.get_fields()) {
|
223
|
-
let type_info = t.get_basic_info();
|
224
|
-
vec.push(ParquetField {
|
225
|
-
field: v.clone(),
|
226
|
-
converted_type: type_info.converted_type(),
|
227
|
-
logical_type: type_info.logical_type().clone(),
|
228
|
-
strict,
|
229
|
-
});
|
230
|
-
}
|
231
|
-
vec
|
232
|
-
})
|
233
|
-
.map(RowRecord::Vec::<RandomState>)
|
234
|
-
.map_err(ParquetGemError::from)
|
235
|
-
});
|
236
|
-
|
237
|
-
for result in iter {
|
238
|
-
let record = result?;
|
239
|
-
let _: Value = ruby.yield_value(record.try_into_value_with(&ruby)?)?;
|
240
|
-
}
|
241
|
-
}
|
242
|
-
}
|
243
|
-
|
244
|
-
Ok(())
|
245
|
-
}
|
246
|
-
|
247
|
-
/// Process column-based Parquet data
|
248
|
-
fn process_column_data(
|
249
|
-
ruby: Rc<Ruby>,
|
250
|
-
source: Either<std::fs::File, crate::ruby_reader::ThreadSafeRubyReader>,
|
251
|
-
columns: &Option<Vec<String>>,
|
252
|
-
result_type: ParserResultType,
|
253
|
-
batch_size: Option<usize>,
|
254
|
-
strict: bool,
|
255
|
-
_ruby_logger: &RubyLogger,
|
256
|
-
) -> Result<(), ParquetGemError> {
|
257
|
-
// Create the batch reader
|
258
|
-
let (batch_reader, schema, num_rows) = match source {
|
259
|
-
Either::Left(file) => create_batch_reader(file, columns, batch_size)?,
|
260
|
-
Either::Right(readable) => create_batch_reader(readable, columns, batch_size)?,
|
261
|
-
};
|
262
|
-
|
263
|
-
match result_type {
|
264
|
-
ParserResultType::Hash => {
|
265
|
-
// For hash return type, we need to return a hash with column names pointing at empty arrays
|
266
|
-
if handle_empty_file(&ruby, &schema, num_rows)? {
|
267
|
-
return Ok(());
|
268
|
-
}
|
269
|
-
|
270
|
-
let headers = OnceLock::new();
|
271
|
-
let headers_clone = headers.clone();
|
272
|
-
let iter = batch_reader.map(move |batch| {
|
273
|
-
batch.map_err(ParquetGemError::Arrow).and_then(|batch| {
|
274
|
-
let local_headers = headers_clone
|
275
|
-
.get_or_init(|| {
|
276
|
-
let schema = batch.schema();
|
277
|
-
let fields = schema.fields();
|
278
|
-
let mut header_string = Vec::with_capacity(fields.len());
|
279
|
-
for field in fields {
|
280
|
-
header_string.push(field.name().to_owned());
|
281
|
-
}
|
282
|
-
StringCache::intern_many(&header_string)
|
283
|
-
})
|
284
|
-
.as_ref()
|
285
|
-
.map_err(|e| ParquetGemError::HeaderIntern(e.clone()))?;
|
286
|
-
|
287
|
-
let mut map = HashMap::with_capacity_and_hasher(
|
288
|
-
local_headers.len(),
|
289
|
-
RandomState::default(),
|
290
|
-
);
|
291
|
-
|
292
|
-
batch
|
293
|
-
.columns()
|
294
|
-
.iter()
|
295
|
-
.enumerate()
|
296
|
-
.try_for_each(|(i, column)| {
|
297
|
-
let header = local_headers[i];
|
298
|
-
let values = ParquetValueVec::try_from(ArrayWrapper {
|
299
|
-
array: column,
|
300
|
-
strict,
|
301
|
-
})?;
|
302
|
-
map.insert(header, values.into_inner());
|
303
|
-
Ok::<_, ParquetGemError>(())
|
304
|
-
})?;
|
305
|
-
|
306
|
-
Ok(ColumnRecord::Map::<RandomState>(map))
|
307
|
-
})
|
308
|
-
});
|
309
|
-
|
310
|
-
for result in iter {
|
311
|
-
let record = result?;
|
312
|
-
let _: Value = ruby.yield_value(record.try_into_value_with(&ruby)?)?;
|
313
|
-
}
|
314
|
-
}
|
315
|
-
ParserResultType::Array => {
|
316
|
-
let iter = batch_reader.map(|batch| {
|
317
|
-
batch.map_err(ParquetGemError::Arrow).and_then(|batch| {
|
318
|
-
let vec = batch
|
319
|
-
.columns()
|
320
|
-
.iter()
|
321
|
-
.map(|column| {
|
322
|
-
let values = ParquetValueVec::try_from(ArrayWrapper {
|
323
|
-
array: column,
|
324
|
-
strict,
|
325
|
-
})?;
|
326
|
-
Ok::<_, ParquetGemError>(values.into_inner())
|
327
|
-
})
|
328
|
-
.collect::<Result<Vec<_>, _>>()?;
|
329
|
-
Ok(ColumnRecord::Vec::<RandomState>(vec))
|
330
|
-
})
|
331
|
-
});
|
332
|
-
|
333
|
-
for result in iter {
|
334
|
-
let record = result?;
|
335
|
-
let _: Value = ruby.yield_value(record.try_into_value_with(&ruby)?)?;
|
336
|
-
}
|
337
|
-
}
|
338
|
-
}
|
339
|
-
|
340
|
-
Ok(())
|
341
|
-
}
|
342
|
-
|
343
|
-
/// Helper function to create a projection schema
|
344
|
-
fn create_projection_schema(
|
345
|
-
schema: &parquet::schema::types::Type,
|
346
|
-
columns: &[String],
|
347
|
-
) -> parquet::schema::types::Type {
|
348
|
-
if let parquet::schema::types::Type::GroupType { fields, .. } = schema {
|
349
|
-
let projected_fields: Vec<std::sync::Arc<parquet::schema::types::Type>> = fields
|
350
|
-
.iter()
|
351
|
-
.filter(|field| columns.contains(&field.name().to_string()))
|
352
|
-
.cloned()
|
353
|
-
.collect();
|
354
|
-
|
355
|
-
parquet::schema::types::Type::GroupType {
|
356
|
-
basic_info: schema.get_basic_info().clone(),
|
357
|
-
fields: projected_fields,
|
358
|
-
}
|
359
|
-
} else {
|
360
|
-
// Return original schema if not a group type
|
361
|
-
schema.clone()
|
362
|
-
}
|
363
|
-
}
|
@@ -1,120 +0,0 @@
|
|
1
|
-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
2
|
-
pub enum ParserResultType {
|
3
|
-
Hash,
|
4
|
-
Array,
|
5
|
-
}
|
6
|
-
|
7
|
-
impl ParserResultType {
|
8
|
-
pub fn iter() -> impl Iterator<Item = Self> {
|
9
|
-
[Self::Hash, Self::Array].into_iter()
|
10
|
-
}
|
11
|
-
}
|
12
|
-
|
13
|
-
impl TryFrom<&str> for ParserResultType {
|
14
|
-
type Error = String;
|
15
|
-
|
16
|
-
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
17
|
-
match value {
|
18
|
-
"hash" => Ok(ParserResultType::Hash),
|
19
|
-
"array" => Ok(ParserResultType::Array),
|
20
|
-
_ => Err(format!("Invalid parser result type: {}", value)),
|
21
|
-
}
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
impl TryFrom<String> for ParserResultType {
|
26
|
-
type Error = String;
|
27
|
-
|
28
|
-
fn try_from(value: String) -> Result<Self, Self::Error> {
|
29
|
-
Self::try_from(value.as_str())
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|
33
|
-
impl std::fmt::Display for ParserResultType {
|
34
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
35
|
-
match self {
|
36
|
-
ParserResultType::Hash => write!(f, "hash"),
|
37
|
-
ParserResultType::Array => write!(f, "array"),
|
38
|
-
}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
|
42
|
-
#[derive(Debug, Clone)]
|
43
|
-
pub struct ListField<'a> {
|
44
|
-
pub item_type: ParquetSchemaType<'a>,
|
45
|
-
pub format: Option<&'a str>,
|
46
|
-
pub nullable: bool,
|
47
|
-
}
|
48
|
-
|
49
|
-
#[derive(Debug, Clone)]
|
50
|
-
pub struct MapField<'a> {
|
51
|
-
pub key_type: ParquetSchemaType<'a>,
|
52
|
-
pub value_type: ParquetSchemaType<'a>,
|
53
|
-
pub key_format: Option<&'a str>,
|
54
|
-
pub value_format: Option<&'a str>,
|
55
|
-
pub value_nullable: bool,
|
56
|
-
}
|
57
|
-
|
58
|
-
#[derive(Debug, Clone)]
|
59
|
-
pub struct StructField<'a> {
|
60
|
-
pub fields: Vec<super::writer_types::SchemaField<'a>>,
|
61
|
-
}
|
62
|
-
|
63
|
-
#[derive(Clone, Debug)]
|
64
|
-
pub enum ParquetSchemaType<'a> {
|
65
|
-
Primitive(PrimitiveType),
|
66
|
-
List(Box<ListField<'a>>),
|
67
|
-
Map(Box<MapField<'a>>),
|
68
|
-
Struct(Box<StructField<'a>>),
|
69
|
-
}
|
70
|
-
|
71
|
-
// New schema representation for the DSL-based approach
|
72
|
-
#[derive(Debug, Clone)]
|
73
|
-
pub enum SchemaNode {
|
74
|
-
Struct {
|
75
|
-
name: String,
|
76
|
-
nullable: bool,
|
77
|
-
fields: Vec<SchemaNode>,
|
78
|
-
},
|
79
|
-
List {
|
80
|
-
name: String,
|
81
|
-
nullable: bool,
|
82
|
-
item: Box<SchemaNode>,
|
83
|
-
},
|
84
|
-
Map {
|
85
|
-
name: String,
|
86
|
-
nullable: bool,
|
87
|
-
key: Box<SchemaNode>,
|
88
|
-
value: Box<SchemaNode>,
|
89
|
-
},
|
90
|
-
Primitive {
|
91
|
-
name: String,
|
92
|
-
parquet_type: PrimitiveType,
|
93
|
-
nullable: bool,
|
94
|
-
format: Option<String>,
|
95
|
-
},
|
96
|
-
}
|
97
|
-
|
98
|
-
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
99
|
-
pub enum PrimitiveType {
|
100
|
-
Int8,
|
101
|
-
Int16,
|
102
|
-
Int32,
|
103
|
-
Int64,
|
104
|
-
UInt8,
|
105
|
-
UInt16,
|
106
|
-
UInt32,
|
107
|
-
UInt64,
|
108
|
-
Float32,
|
109
|
-
Float64,
|
110
|
-
Decimal128(u8, i8),
|
111
|
-
Decimal256(u8, i8),
|
112
|
-
Boolean,
|
113
|
-
String,
|
114
|
-
Binary,
|
115
|
-
Date32,
|
116
|
-
TimestampMillis,
|
117
|
-
TimestampMicros,
|
118
|
-
TimeMillis,
|
119
|
-
TimeMicros,
|
120
|
-
}
|
@@ -1,100 +0,0 @@
|
|
1
|
-
// Re-export all public items from submodules
|
2
|
-
mod core_types;
|
3
|
-
mod parquet_value;
|
4
|
-
mod record_types;
|
5
|
-
pub mod schema_converter;
|
6
|
-
pub mod schema_node;
|
7
|
-
mod timestamp;
|
8
|
-
pub mod type_conversion;
|
9
|
-
mod writer_types;
|
10
|
-
|
11
|
-
pub use core_types::*;
|
12
|
-
pub use parquet_value::*;
|
13
|
-
pub use record_types::*;
|
14
|
-
// Explicitly export schema-related items
|
15
|
-
pub use schema_converter::{
|
16
|
-
infer_schema_from_first_row, legacy_schema_to_dsl, parse_legacy_schema,
|
17
|
-
};
|
18
|
-
pub use schema_node::parse_schema_node;
|
19
|
-
pub use timestamp::*;
|
20
|
-
pub use type_conversion::*;
|
21
|
-
pub use writer_types::*;
|
22
|
-
|
23
|
-
// Common imports used across the module
|
24
|
-
use arrow_array::cast::downcast_array;
|
25
|
-
use arrow_array::{
|
26
|
-
Array, BinaryArray, BooleanArray, Date32Array, Date64Array, Decimal128Array, Decimal256Array,
|
27
|
-
Float16Array, Float32Array, Float64Array, Int16Array, Int32Array, Int64Array, Int8Array,
|
28
|
-
ListArray, NullArray, StringArray, StructArray, Time32MillisecondArray, Time64MicrosecondArray,
|
29
|
-
TimestampMicrosecondArray, TimestampMillisecondArray, TimestampNanosecondArray,
|
30
|
-
TimestampSecondArray, UInt16Array, UInt32Array, UInt64Array, UInt8Array,
|
31
|
-
};
|
32
|
-
use arrow_schema::{DataType, TimeUnit};
|
33
|
-
use magnus::{value::ReprValue, Error as MagnusError, IntoValue, Ruby, Value};
|
34
|
-
use parquet::data_type::Decimal;
|
35
|
-
use parquet::record::Field;
|
36
|
-
use std::{collections::HashMap, hash::BuildHasher, sync::Arc};
|
37
|
-
|
38
|
-
use crate::header_cache::StringCacheKey;
|
39
|
-
|
40
|
-
use crate::header_cache::CacheError;
|
41
|
-
|
42
|
-
use std::io;
|
43
|
-
|
44
|
-
use thiserror::Error;
|
45
|
-
|
46
|
-
#[derive(Error, Debug)]
|
47
|
-
pub enum ParquetGemError {
|
48
|
-
#[error("Failed to open file: {0}")]
|
49
|
-
FileOpen(#[from] io::Error),
|
50
|
-
#[error("Failed to intern headers: {0}")]
|
51
|
-
HeaderIntern(#[from] CacheError),
|
52
|
-
#[error("Ruby error: {0}")]
|
53
|
-
Ruby(#[from] MagnusErrorWrapper),
|
54
|
-
#[error("Parquet error: {0}")]
|
55
|
-
Parquet(#[from] parquet::errors::ParquetError),
|
56
|
-
#[error("Arrow error: {0}")]
|
57
|
-
Arrow(#[from] arrow_schema::ArrowError),
|
58
|
-
#[error("UTF-8 error: {0}")]
|
59
|
-
Utf8Error(#[from] simdutf8::basic::Utf8Error),
|
60
|
-
#[error("Jiff error: {0}")]
|
61
|
-
Jiff(#[from] jiff::Error),
|
62
|
-
#[error("Failed to cast slice to array: {0}")]
|
63
|
-
InvalidDecimal(String),
|
64
|
-
#[error("Failed to parse UUID: {0}")]
|
65
|
-
UuidError(#[from] uuid::Error),
|
66
|
-
#[error("Decimals larger than 128 bits are not supported")]
|
67
|
-
DecimalWouldBeTruncated,
|
68
|
-
}
|
69
|
-
|
70
|
-
#[derive(Debug)]
|
71
|
-
pub struct MagnusErrorWrapper(pub MagnusError);
|
72
|
-
|
73
|
-
impl From<MagnusError> for MagnusErrorWrapper {
|
74
|
-
fn from(err: MagnusError) -> Self {
|
75
|
-
Self(err)
|
76
|
-
}
|
77
|
-
}
|
78
|
-
|
79
|
-
impl std::fmt::Display for MagnusErrorWrapper {
|
80
|
-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
81
|
-
write!(f, "{}", self.0)
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
impl std::error::Error for MagnusErrorWrapper {}
|
86
|
-
|
87
|
-
impl From<MagnusError> for ParquetGemError {
|
88
|
-
fn from(err: MagnusError) -> Self {
|
89
|
-
Self::Ruby(MagnusErrorWrapper(err))
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
impl From<ParquetGemError> for MagnusError {
|
94
|
-
fn from(val: ParquetGemError) -> Self {
|
95
|
-
match val {
|
96
|
-
ParquetGemError::Ruby(MagnusErrorWrapper(err)) => err,
|
97
|
-
_ => MagnusError::new(magnus::exception::runtime_error(), val.to_string()),
|
98
|
-
}
|
99
|
-
}
|
100
|
-
}
|