Push a row with full bitemporal metadata. Sentinel rows (`Tombstone`, `GdprErased`) may have an empty `attrs` slice — the attribute columns receive no entry for these rows, which is correct because sentinel rows carry no payload.
(&mut self, row: SparseRow<'_>)
| 251 | /// slice — the attribute columns receive no entry for these rows, which |
| 252 | /// is correct because sentinel rows carry no payload. |
| 253 | pub fn push_row(&mut self, row: SparseRow<'_>) -> ArrayResult<()> { |
| 254 | let SparseRow { |
| 255 | coord, |
| 256 | attrs, |
| 257 | surrogate, |
| 258 | valid_from_ms, |
| 259 | valid_until_ms, |
| 260 | kind, |
| 261 | } = row; |
| 262 | if coord.len() != self.schema.arity() { |
| 263 | return Err(ArrayError::CoordArityMismatch { |
| 264 | array: self.schema.name.clone(), |
| 265 | expected: self.schema.arity(), |
| 266 | got: coord.len(), |
| 267 | }); |
| 268 | } |
| 269 | // Sentinel rows carry no payload — allow empty attrs. Live rows must |
| 270 | // match the schema attr count exactly. |
| 271 | if kind == RowKind::Live && attrs.len() != self.schema.attrs.len() { |
| 272 | return Err(ArrayError::CellTypeMismatch { |
| 273 | array: self.schema.name.clone(), |
| 274 | attr: "<all>".to_string(), |
| 275 | detail: format!( |
| 276 | "expected {} attr columns, got {}", |
| 277 | self.schema.attrs.len(), |
| 278 | attrs.len() |
| 279 | ), |
| 280 | }); |
| 281 | } |
| 282 | for (i, c) in coord.iter().enumerate() { |
| 283 | self.dim_dicts[i].push(c); |
| 284 | } |
| 285 | // For sentinel rows `attrs` is empty — no entries added to attr_cols. |
| 286 | // For live rows, every attr col gets one entry. |
| 287 | for (i, a) in attrs.iter().enumerate() { |
| 288 | self.attr_cols[i].push(a.clone()); |
| 289 | } |
| 290 | self.surrogates.push(surrogate); |
| 291 | self.valid_from_ms.push(valid_from_ms); |
| 292 | self.valid_until_ms.push(valid_until_ms); |
| 293 | self.row_kinds.push(kind.as_u8()); |
| 294 | // Only fold MBR for live rows; sentinel rows have no meaningful attrs |
| 295 | // and may have coords outside the declared domain. |
| 296 | if kind == RowKind::Live { |
| 297 | self.mbr.fold(coord, attrs); |
| 298 | } |
| 299 | Ok(()) |
| 300 | } |
| 301 | |
| 302 | /// Push a live row whose surrogate is unknown to the caller (recovery, |
| 303 | /// pure-shape ops). The slot is filled with [`Surrogate::ZERO`]; |