Merge pull request #640 from MarekSpodBiedry:issue-515
This commit is contained in:
commit
02c973536b
|
|
@ -2039,4 +2039,38 @@ mod tests {
|
|||
assert!(e.contains("field name is not valid"), "{e}");
|
||||
assert!(s.execute("insert.pivotTable", json!({"source": "NoSuchTable"})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_many_value_columns_are_refused() {
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
let mut records: Vec<Json> = vec![json!(["Cat", "Sub", "V"])];
|
||||
for r in 0..64 {
|
||||
for c in 0..64 {
|
||||
records.push(json!([format!("c{r}"), format!("s{c}"), 1.0]));
|
||||
}
|
||||
}
|
||||
s.execute("range.setValues", json!({"range": "A1", "values": records})).unwrap();
|
||||
let req = |n: usize| {
|
||||
let values: Vec<Json> = (0..n).map(|_| json!({"field": "V"})).collect();
|
||||
json!({"source": "A1:C4097", "rows": ["Cat"], "columns": ["Sub"], "values": values})
|
||||
};
|
||||
let e = s.execute("insert.pivotTable", req(256)).unwrap_err().to_string();
|
||||
assert!(e.contains("will not fit"), "{e}");
|
||||
assert!(s.execute("insert.pivotTable", req(128)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collapsed_column_items_count_as_one_column_group() {
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
let mut records: Vec<Json> = vec![json!(["Grp", "Sub", "V"])];
|
||||
records.extend((0..64).map(|c| json!(["g", format!("s{c}"), 1.0])));
|
||||
s.execute("range.setValues", json!({"range": "A1", "values": records})).unwrap();
|
||||
let values: Vec<Json> = (0..256).map(|_| json!({"field": "V"})).collect();
|
||||
s.execute("insert.pivotTable", json!({"source": "A1:C65", "columns": ["Grp"], "values": values})).unwrap();
|
||||
s.execute("pivot.collapse", json!({"field": "Grp"})).unwrap();
|
||||
// 64 Sub items under the collapsed "g" show as one column group: (1 + grand total) x 256 columns fit.
|
||||
s.execute("pivot.addField", json!({"field": "Sub", "area": "columns"})).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -693,6 +693,12 @@ enum ValSrc {
|
|||
Calc(usize),
|
||||
}
|
||||
|
||||
/// True when `col_groups` distinct column leaves (plus the grand-total column) times `values` value fields
|
||||
/// (at least one data column per group) already exceed the sheet width. This is a lower bound on the final column count.
|
||||
fn data_cols_exceed_sheet(col_groups: usize, grand_total: bool, values: usize) -> bool {
|
||||
(col_groups + usize::from(grand_total)).saturating_mul(values.max(1)) > MAX_COLS as usize
|
||||
}
|
||||
|
||||
/// Computes a PivotTable report from the workbook.
|
||||
pub fn compute(wb: &Workbook, pt: &PivotTable, pivot_sheet: usize) -> Result<Output, String> {
|
||||
let src = read_source(wb, &pt.source, pivot_sheet)?;
|
||||
|
|
@ -752,6 +758,7 @@ pub fn compute(wb: &Workbook, pt: &PivotTable, pivot_sheet: usize) -> Result<Out
|
|||
let mut aggs: AggMap = HashMap::new();
|
||||
let mut row_set: HashSet<Vec<u32>> = HashSet::new();
|
||||
let mut col_set: HashSet<Vec<u32>> = HashSet::new();
|
||||
let mut col_leaves: HashSet<Vec<u32>> = HashSet::new();
|
||||
let filter_fmts: Vec<String> = filters.iter().map(|f| fmt_of(f.0)).collect();
|
||||
let row_fmts: Vec<String> = rows.iter().map(|(c, _)| fmt_of(*c)).collect();
|
||||
let col_fmts: Vec<String> = cols.iter().map(|(c, _)| fmt_of(*c)).collect();
|
||||
|
|
@ -788,8 +795,22 @@ pub fn compute(wb: &Workbook, pt: &PivotTable, pivot_sheet: usize) -> Result<Out
|
|||
}
|
||||
}
|
||||
}
|
||||
// The column leaf this record lands in: its path cut after the first collapsed item (as in `flatten_cols`).
|
||||
let shown = cols
|
||||
.iter()
|
||||
.zip(col_items.iter())
|
||||
.zip(cp.iter())
|
||||
.position(|(((_, f), fi), &it)| !f.collapsed_items.is_empty() && is_collapsed(f, &fi.label(it)))
|
||||
.map_or(cp.len(), |d| d + 1);
|
||||
if !col_leaves.contains(&cp[..shown]) {
|
||||
col_leaves.insert(cp[..shown].to_vec());
|
||||
}
|
||||
row_set.insert(rp);
|
||||
col_set.insert(cp);
|
||||
// Refuse a report that cannot fit before the accumulators for more column groups are allocated.
|
||||
if !cols.is_empty() && data_cols_exceed_sheet(col_leaves.len(), pt.grand_totals_rows, k) {
|
||||
return Err("The PivotTable report will not fit on the sheet.".into());
|
||||
}
|
||||
if aggs.len() > 5_000_000 {
|
||||
return Err("The PivotTable report is too large.".into());
|
||||
}
|
||||
|
|
@ -1428,3 +1449,17 @@ pub fn clear_output(wb: &mut Workbook, sheet: usize, idx: usize) {
|
|||
p.last_range = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod fit_tests {
|
||||
use super::data_cols_exceed_sheet;
|
||||
|
||||
#[test]
|
||||
fn column_budget_counts_value_fields_and_grand_total() {
|
||||
assert!(!data_cols_exceed_sheet(64, true, 128));
|
||||
assert!(data_cols_exceed_sheet(64, true, 256));
|
||||
assert!(!data_cols_exceed_sheet(64, false, 256));
|
||||
assert!(!data_cols_exceed_sheet(16_384, false, 0));
|
||||
assert!(data_cols_exceed_sheet(16_385, false, 0));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue