Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/extension/analyze_sheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::extension::Param;
use crate::extension::Range;
use crate::extension::RangeParam;
use crate::extension::SheetParam;
use crate::extension::SpreadMergedCellsParam;
use crate::spreadsheet::criteria::Criteria;
use crate::spreadsheet::open_spreadsheet;
use duckdb::core::DataChunkHandle;
Expand Down Expand Up @@ -42,6 +43,8 @@ struct AnalyzeSheetParameters {
nulls: Option<HashSet<String>>,
/// Whether to convert errors to null values (default: false)
error_as_null: Option<bool>,
/// Whether to spread merged cells across merged ranges (default: false)
spread_merged_cells: Option<bool>,
}

impl TryFrom<&BindInfo> for AnalyzeSheetParameters {
Expand All @@ -57,6 +60,7 @@ impl TryFrom<&BindInfo> for AnalyzeSheetParameters {
analyze_rows: AnalyzeRowsParam::read(bind)?,
nulls: NullsParam::read(bind)?,
error_as_null: ErrorAsNullParam::read(bind)?,
spread_merged_cells: SpreadMergedCellsParam::read(bind)?
})
}
}
Expand Down Expand Up @@ -89,6 +93,7 @@ impl TryFrom<&AnalyzeSheetParameters> for AnalyzeSheetBindData {
error_as_null: parameters.error_as_null.unwrap_or(false),
skip_empty_rows: false,
end_at_empty_row: false,
spread_merged_cells: parameters.spread_merged_cells.unwrap_or(false),
}, &Vec::new())? {
for column in &table.columns {
columns.push((
Expand Down
5 changes: 5 additions & 0 deletions src/extension/analyze_sheets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::extension::Param;
use crate::extension::Range;
use crate::extension::RangeParam;
use crate::extension::SheetsParam;
use crate::extension::SpreadMergedCellsParam;
use crate::spreadsheet::criteria::Criteria;
use crate::spreadsheet::open_spreadsheet;
use duckdb::core::DataChunkHandle;
Expand Down Expand Up @@ -42,6 +43,8 @@ struct AnalyzeSheetsParameters {
nulls: Option<HashSet<String>>,
/// Whether to convert errors to null values (default: false)
error_as_null: Option<bool>,
/// Whether to spread merged cells across merged ranges (default: false)
spread_merged_cells: Option<bool>,
}

impl TryFrom<&BindInfo> for AnalyzeSheetsParameters {
Expand All @@ -63,6 +66,7 @@ impl TryFrom<&BindInfo> for AnalyzeSheetsParameters {
analyze_rows: AnalyzeRowsParam::read(bind)?,
nulls: NullsParam::read(bind)?,
error_as_null: ErrorAsNullParam::read(bind)?,
spread_merged_cells: SpreadMergedCellsParam::read(bind)?
})
}
}
Expand Down Expand Up @@ -114,6 +118,7 @@ impl TryFrom<&AnalyzeSheetsParameters> for AnalyzeSheetsBindData {
error_as_null: parameters.error_as_null.unwrap_or(false),
skip_empty_rows: false,
end_at_empty_row: false,
spread_merged_cells: parameters.spread_merged_cells.unwrap_or(false),
}, &Vec::new()).with_prefix(spreadsheet.name().as_str())? {
for column in &table.columns {
columns.push((
Expand Down
16 changes: 16 additions & 0 deletions src/extension/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ struct NullsParam;
struct ErrorAsNullParam;
struct SkipEmptyRowsParam;
struct EndAtEmptyRowParam;
struct SpreadMergedCellsParam;
struct FileNameColumnParam;
struct SheetNameColumnParam;

Expand Down Expand Up @@ -316,6 +317,21 @@ impl NamedParam<bool> for EndAtEmptyRowParam {
}
}

/// Parameter handler for spreading data from merged cells across merged ranges.
impl NamedParam<bool> for SpreadMergedCellsParam {
fn name() -> &'static str {
"spread_merged_cells"
}

fn kind() -> LogicalTypeHandle {
LogicalTypeHandle::from(LogicalTypeId::Boolean)
}

fn cast(value: Value) -> Result<bool, RustySheetError> {
Ok(value.to_bool())
}
}

impl NamedParam<String> for FileNameColumnParam {
fn name() -> &'static str {
"file_name_column"
Expand Down
12 changes: 11 additions & 1 deletion src/extension/read_sheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::database::column::Column;
use crate::database::column::ColumnType;
use crate::error::ResultMessage;
use crate::error::RustySheetError;
use crate::extension::SpreadMergedCellsParam;
use crate::extension::writer::write_to_vector;
use crate::extension::AnalyzeRowsParam;
use crate::extension::ColumnsParam;
Expand Down Expand Up @@ -62,6 +63,8 @@ struct ReadSheetParameters {
file_name_column: Option<String>,
/// column name for sheet name of record
sheet_name_column: Option<String>,
/// Whether to spread merged cells across merged ranges (default: false)
spread_merged_cells: Option<bool>,
}

impl TryFrom<&BindInfo> for ReadSheetParameters {
Expand All @@ -83,6 +86,7 @@ impl TryFrom<&BindInfo> for ReadSheetParameters {
end_at_empty_row: EndAtEmptyRowParam::read(bind)?,
file_name_column: FileNameColumnParam::read(bind)?,
sheet_name_column: SheetNameColumnParam::read(bind)?,
spread_merged_cells: SpreadMergedCellsParam::read(bind)?,
})
}
}
Expand All @@ -101,6 +105,8 @@ pub(crate) struct ReadSheetBindData {
sheets: Vec<Sheet>,
/// Shared string table for efficient string storage (XLSX/XLSB format)
shared_strings: Vec<Option<String>>,
/// Whether to spread merged cells across merged ranges (default: false)
spread_merged_cells: bool,
}

impl TryFrom<&ReadSheetParameters> for ReadSheetBindData {
Expand All @@ -122,7 +128,7 @@ impl TryFrom<&ReadSheetParameters> for ReadSheetBindData {
let error_as_null = parameters.error_as_null.unwrap_or(false);
let skip_empty_rows = parameters.skip_empty_rows.unwrap_or(false);
let end_at_empty_row = parameters.end_at_empty_row.unwrap_or(false);

let spread_merged_cells = parameters.spread_merged_cells.unwrap_or(false);
// Analyze the sheet structure to determine column types and bounds
let tables = spreadsheet.analyze_sheets(header, &Criteria {
sheet_name_patterns: sheet_name_pattern.to_owned(),
Expand All @@ -133,6 +139,7 @@ impl TryFrom<&ReadSheetParameters> for ReadSheetBindData {
error_as_null,
skip_empty_rows,
end_at_empty_row,
spread_merged_cells,
}, parameters.columns.as_ref().unwrap_or(&vec![]))?;

// Extract the first matching sheet or return error if no match found
Expand Down Expand Up @@ -171,6 +178,7 @@ impl TryFrom<&ReadSheetParameters> for ReadSheetBindData {
error_as_null,
skip_empty_rows,
end_at_empty_row,
spread_merged_cells,
})?;

let shared_strings = shared_strings
Expand All @@ -189,6 +197,7 @@ impl TryFrom<&ReadSheetParameters> for ReadSheetBindData {
sheet_name_column,
sheets,
shared_strings,
spread_merged_cells,
})
}
}
Expand Down Expand Up @@ -303,6 +312,7 @@ impl VTab for ReadSheetTableFunction {
ErrorAsNullParam::definition(),
SkipEmptyRowsParam::definition(),
EndAtEmptyRowParam::definition(),
SpreadMergedCellsParam::definition(),
FileNameColumnParam::definition(),
SheetNameColumnParam::definition(),
])
Expand Down
12 changes: 11 additions & 1 deletion src/extension/read_sheets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::database::column::ColumnType;
use crate::database::table::Table;
use crate::error::ResultMessage;
use crate::error::RustySheetError;
use crate::extension::SpreadMergedCellsParam;
use crate::extension::writer::write_to_vector;
use crate::extension::AnalyzeRowsParam;
use crate::extension::ColumnsParam;
Expand Down Expand Up @@ -67,6 +68,8 @@ struct ReadSheetsParameters {
file_name_column: Option<String>,
/// column name for sheet name of record
sheet_name_column: Option<String>,
/// Whether to spread merged cells across merged ranges (default: false)
spread_merged_cells: Option<bool>,
}

impl TryFrom<&BindInfo> for ReadSheetsParameters {
Expand Down Expand Up @@ -94,6 +97,7 @@ impl TryFrom<&BindInfo> for ReadSheetsParameters {
end_at_empty_row: EndAtEmptyRowParam::read(bind)?,
file_name_column: FileNameColumnParam::read(bind)?,
sheet_name_column: SheetNameColumnParam::read(bind)?,
spread_merged_cells: SpreadMergedCellsParam::read(bind)?,
})
}
}
Expand All @@ -109,6 +113,8 @@ pub(crate) struct ReadSheetsBindData {
file_name_column: Option<usize>,
/// sheet name column index
sheet_name_column: Option<usize>,
/// Whether to spread merged cells across merged ranges (default: false)
spread_merged_cells: bool,
}

impl TryFrom<&ReadSheetsParameters> for ReadSheetsBindData {
Expand Down Expand Up @@ -137,7 +143,7 @@ impl TryFrom<&ReadSheetsParameters> for ReadSheetsBindData {
let rows_limit = parameters.analyze_rows.or(Some(10));
let default_preset_columns = vec![];
let preset = parameters.columns.as_ref().unwrap_or(&default_preset_columns);

let spread_merged_cells = parameters.spread_merged_cells.unwrap_or(false);
let mut spreadsheets = Vec::new();
let mut shared_tables = None::<Vec<Table>>;
let mut columns = Vec::<Column>::new();
Expand All @@ -152,6 +158,7 @@ impl TryFrom<&ReadSheetsParameters> for ReadSheetsBindData {
error_as_null,
skip_empty_rows,
end_at_empty_row,
spread_merged_cells: false,
}, preset)?;
if tables.is_empty() {
continue
Expand Down Expand Up @@ -209,6 +216,7 @@ impl TryFrom<&ReadSheetsParameters> for ReadSheetsBindData {
error_as_null,
skip_empty_rows,
end_at_empty_row,
spread_merged_cells: false,
}).with_prefix(table.name.as_str()).with_prefix(spreadsheet.name().as_str())?;
assert_eq!(actual_sheets.len(), 1);
sheets.extend(actual_sheets);
Expand Down Expand Up @@ -254,6 +262,7 @@ impl TryFrom<&ReadSheetsParameters> for ReadSheetsBindData {
columns,
file_name_column,
sheet_name_column,
spread_merged_cells,
})
}
}
Expand Down Expand Up @@ -405,6 +414,7 @@ impl VTab for ReadSheetsTableFunction {
EndAtEmptyRowParam::definition(),
FileNameColumnParam::definition(),
SheetNameColumnParam::definition(),
SpreadMergedCellsParam::definition(),
])
}
}
3 changes: 3 additions & 0 deletions src/spreadsheet/criteria.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub(crate) struct Criteria {

/// Stop reading when encountering a completely empty row.
pub(crate) end_at_empty_row: bool,

/// Spread data from merged cells across all cells in the merged range.
pub(crate) spread_merged_cells: bool,
}

impl Criteria {
Expand Down
8 changes: 7 additions & 1 deletion src/spreadsheet/sheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,13 @@ impl Sheet {
if self.col_upper_bound.map(|col_upper_bound| col_upper_bound < col).unwrap_or(true) {
self.col_upper_bound = Some(col);
}
self.row_upper_bound = Some(row);
// Update row_upper_bound to the maximum row seen
// Optimize for common case: if row_upper_bound is None or row >= current max, update directly
match self.row_upper_bound {
None => self.row_upper_bound = Some(row),
Some(current_max) if row > current_max => self.row_upper_bound = Some(row),
_ => {} // row <= current_max, no update needed
}
}

/// Finalizes chunk creation after all cells have been added.
Expand Down
Loading
Loading