Skip to main content

mz_repr/
row.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! In-memory `Tag`-based encoding of a tuple of `Datum`s.
11//!
12//! See `doc/developer/row-encoding.md` for the size limits this encoding and
13//! its datum types impose.
14
15use std::borrow::Borrow;
16use std::cell::{Cell, RefCell};
17use std::cmp::Ordering;
18use std::convert::{TryFrom, TryInto};
19use std::fmt::{self, Debug};
20use std::hash::{Hash, Hasher};
21use std::marker::PhantomData;
22use std::mem::{size_of, transmute};
23use std::ops::Deref;
24use std::str;
25
26use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
27use compact_bytes::CompactBytes;
28use mz_ore::cast::{CastFrom, ReinterpretCast};
29use mz_ore::soft_assert_no_log;
30use mz_ore::vec::Vector;
31use mz_persist_types::Codec64;
32use num_enum::{IntoPrimitive, TryFromPrimitive};
33use ordered_float::OrderedFloat;
34#[cfg(any(test, feature = "proptest"))]
35use proptest::prelude::*;
36#[cfg(any(test, feature = "proptest"))]
37use proptest::strategy::{BoxedStrategy, Strategy};
38use serde::{Deserialize, Serialize};
39use uuid::Uuid;
40
41use crate::adt::array::{
42    Array, ArrayDimension, ArrayDimensions, InvalidArrayError, MAX_ARRAY_DIMENSIONS,
43};
44use crate::adt::date::Date;
45use crate::adt::interval::Interval;
46use crate::adt::mz_acl_item::{AclItem, MzAclItem};
47use crate::adt::numeric;
48use crate::adt::numeric::Numeric;
49use crate::adt::range::{
50    self, InvalidRangeError, Range, RangeBound, RangeInner, RangeLowerBound, RangeUpperBound,
51};
52use crate::adt::timestamp::CheckedTimestamp;
53#[cfg(any(test, feature = "proptest"))]
54use crate::scalar::arb_datum;
55use crate::scalar::{DatumKind, SqlScalarType};
56use crate::{Datum, RelationDesc, Timestamp};
57
58pub(crate) mod encode;
59pub mod iter;
60
61include!(concat!(env!("OUT_DIR"), "/mz_repr.row.rs"));
62
63/// A packed representation for `Datum`s.
64///
65/// `Datum` is easy to work with but very space inefficient. A `Datum::Int32(42)`
66/// is laid out in memory like this:
67///
68///   tag: 3
69///   padding: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
70///   data: 0 0 0 42
71///   padding: 0 0 0 0 0 0 0 0 0 0 0 0
72///
73/// For a total of 32 bytes! The second set of padding is needed in case we were
74/// to write a 16-byte datum into this location. The first set of padding is
75/// needed to align that hypothetical decimal to a 16 bytes boundary.
76///
77/// A `Row` stores zero or more `Datum`s without any padding. We avoid the need
78/// for the first set of padding by only providing access to the `Datum`s via
79/// calls to `ptr::read_unaligned`, which on modern x86 is barely penalized. We
80/// avoid the need for the second set of padding by not providing mutable access
81/// to the `Datum`. Instead, `Row` is append-only.
82///
83/// A `Row` can be built from a collection of `Datum`s using `Row::pack`, but it
84/// is more efficient to use `Row::pack_slice` so that a right-sized allocation
85/// can be created. If that is not possible, consider using the row buffer
86/// pattern: allocate one row, pack into it, and then call [`Row::clone`] to
87/// receive a copy of that row, leaving behind the original allocation to pack
88/// future rows.
89///
90/// Creating a row via [`Row::pack_slice`]:
91///
92/// ```
93/// # use mz_repr::{Row, Datum};
94/// let row = Row::pack_slice(&[Datum::Int32(0), Datum::Int32(1), Datum::Int32(2)]);
95/// assert_eq!(row.unpack(), vec![Datum::Int32(0), Datum::Int32(1), Datum::Int32(2)])
96/// ```
97///
98/// `Row`s can be unpacked by iterating over them:
99///
100/// ```
101/// # use mz_repr::{Row, Datum};
102/// let row = Row::pack_slice(&[Datum::Int32(0), Datum::Int32(1), Datum::Int32(2)]);
103/// assert_eq!(row.iter().nth(1).unwrap(), Datum::Int32(1));
104/// ```
105///
106/// If you want random access to the `Datum`s in a `Row`, use `Row::unpack` to create a `Vec<Datum>`
107/// ```
108/// # use mz_repr::{Row, Datum};
109/// let row = Row::pack_slice(&[Datum::Int32(0), Datum::Int32(1), Datum::Int32(2)]);
110/// let datums = row.unpack();
111/// assert_eq!(datums[1], Datum::Int32(1));
112/// ```
113///
114/// # Performance
115///
116/// Rows are dynamically sized, but up to a fixed size their data is stored in-line.
117/// It is best to re-use a `Row` across multiple `Row` creation calls, as this
118/// avoids the allocations involved in `Row::new()`.
119#[derive(Default, Eq, PartialEq, Serialize, Deserialize)]
120pub struct Row {
121    data: CompactBytes,
122}
123
124impl Row {
125    const SIZE: usize = CompactBytes::MAX_INLINE;
126
127    /// A variant of `Row::from_proto` that allows for reuse of internal allocs
128    /// and validates the decoding against a provided [`RelationDesc`].
129    pub fn decode_from_proto(
130        &mut self,
131        proto: &ProtoRow,
132        desc: &RelationDesc,
133    ) -> Result<(), String> {
134        let mut packer = self.packer();
135        for (col_idx, _, _) in desc.iter_all() {
136            let d = match proto.datums.get(col_idx.to_raw()) {
137                Some(x) => x,
138                None => {
139                    packer.push(Datum::Null);
140                    continue;
141                }
142            };
143            packer.try_push_proto(d)?;
144        }
145
146        Ok(())
147    }
148
149    /// Allocate an empty `Row` with a pre-allocated capacity.
150    #[inline]
151    pub fn with_capacity(cap: usize) -> Self {
152        Self {
153            data: CompactBytes::with_capacity(cap),
154        }
155    }
156
157    /// Create an empty `Row`.
158    #[inline]
159    pub const fn empty() -> Self {
160        Self {
161            data: CompactBytes::empty(),
162        }
163    }
164
165    /// Creates a new row from supplied bytes.
166    ///
167    /// # Safety
168    ///
169    /// This method relies on `data` being an appropriate row encoding, and can
170    /// result in unsafety if this is not the case.
171    pub unsafe fn from_bytes_unchecked(data: &[u8]) -> Self {
172        Row {
173            data: CompactBytes::new(data),
174        }
175    }
176
177    /// Constructs a [`RowPacker`] that will pack datums into this row's
178    /// allocation.
179    ///
180    /// This method clears the existing contents of the row, but retains the
181    /// allocation.
182    pub fn packer(&mut self) -> RowPacker<'_> {
183        self.clear();
184        RowPacker { row: self }
185    }
186
187    /// Take some `Datum`s and pack them into a `Row`.
188    ///
189    /// This method builds a `Row` by repeatedly increasing the backing
190    /// allocation. If the contents of the iterator are known ahead of
191    /// time, consider [`Row::with_capacity`] to right-size the allocation
192    /// first, and then [`RowPacker::extend`] to populate it with `Datum`s.
193    /// This avoids the repeated allocation resizing and copying.
194    pub fn pack<'a, I, D>(iter: I) -> Row
195    where
196        I: IntoIterator<Item = D>,
197        D: Borrow<Datum<'a>>,
198    {
199        let mut row = Row::default();
200        row.packer().extend(iter);
201        row
202    }
203
204    /// Use `self` to pack `iter`, and then clone the result.
205    ///
206    /// This is a convenience method meant to reduce boilerplate around row
207    /// formation.
208    pub fn pack_using<'a, I, D>(&mut self, iter: I) -> Row
209    where
210        I: IntoIterator<Item = D>,
211        D: Borrow<Datum<'a>>,
212    {
213        self.packer().extend(iter);
214        self.clone()
215    }
216
217    /// Like [`Row::pack`], but the provided iterator is allowed to produce an
218    /// error, in which case the packing operation is aborted and the error
219    /// returned.
220    pub fn try_pack<'a, I, D, E>(iter: I) -> Result<Row, E>
221    where
222        I: IntoIterator<Item = Result<D, E>>,
223        D: Borrow<Datum<'a>>,
224    {
225        let mut row = Row::default();
226        row.packer().try_extend(iter)?;
227        Ok(row)
228    }
229
230    /// Pack a slice of `Datum`s into a `Row`.
231    ///
232    /// This method has the advantage over `pack` that it can determine the required
233    /// allocation before packing the elements, ensuring only one allocation and no
234    /// redundant copies required.
235    pub fn pack_slice<'a>(slice: &[Datum<'a>]) -> Row {
236        // Pre-allocate the needed number of bytes.
237        let mut row = Row::with_capacity(datums_size(slice.iter()));
238        row.packer().extend(slice.iter());
239        row
240    }
241
242    /// Returns the total amount of bytes used by this row.
243    pub fn byte_len(&self) -> usize {
244        let heap_size = if self.data.spilled() {
245            self.data.len()
246        } else {
247            0
248        };
249        let inline_size = std::mem::size_of::<Self>();
250        inline_size.saturating_add(heap_size)
251    }
252
253    /// The length of the encoded row in bytes. Does not include the size of the `Row` struct itself.
254    pub fn data_len(&self) -> usize {
255        self.data.len()
256    }
257
258    /// Returns the total capacity in bytes used by this row.
259    pub fn byte_capacity(&self) -> usize {
260        self.data.capacity()
261    }
262
263    /// Extracts a Row slice containing the entire [`Row`].
264    #[inline]
265    pub fn as_row_ref(&self) -> &RowRef {
266        // SAFETY: `Row` contains valid row data, by construction.
267        unsafe { RowRef::from_slice(self.data.as_slice()) }
268    }
269
270    /// Clear the contents of the [`Row`], leaving any allocation in place.
271    #[inline]
272    fn clear(&mut self) {
273        self.data.clear();
274    }
275}
276
277impl Borrow<RowRef> for Row {
278    #[inline]
279    fn borrow(&self) -> &RowRef {
280        self.as_row_ref()
281    }
282}
283
284impl AsRef<RowRef> for Row {
285    #[inline]
286    fn as_ref(&self) -> &RowRef {
287        self.as_row_ref()
288    }
289}
290
291impl Deref for Row {
292    type Target = RowRef;
293
294    #[inline]
295    fn deref(&self) -> &Self::Target {
296        self.as_row_ref()
297    }
298}
299
300// Nothing depends on Row being exactly 24, we just want to add visibility to the size.
301static_assertions::const_assert_eq!(std::mem::size_of::<Row>(), 24);
302
303impl Clone for Row {
304    fn clone(&self) -> Self {
305        Row {
306            data: self.data.clone(),
307        }
308    }
309
310    fn clone_from(&mut self, source: &Self) {
311        self.data.clone_from(&source.data);
312    }
313}
314
315// Row's `Hash` implementation defers to `RowRef` to ensure they hash equivalently.
316impl std::hash::Hash for Row {
317    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
318        self.as_row_ref().hash(state)
319    }
320}
321
322#[cfg(any(test, feature = "proptest"))]
323impl Arbitrary for Row {
324    type Parameters = prop::collection::SizeRange;
325    type Strategy = BoxedStrategy<Row>;
326
327    fn arbitrary_with(size: Self::Parameters) -> Self::Strategy {
328        prop::collection::vec(arb_datum(true), size)
329            .prop_map(|items| {
330                let mut row = Row::default();
331                let mut packer = row.packer();
332                for item in items.iter() {
333                    let datum: Datum<'_> = item.into();
334                    packer.push(datum);
335                }
336                row
337            })
338            .boxed()
339    }
340}
341
342impl PartialOrd for Row {
343    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
344        Some(self.cmp(other))
345    }
346}
347
348impl Ord for Row {
349    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
350        self.as_ref().cmp(other.as_ref())
351    }
352}
353
354/// A [`Row`] that serializes as protobuf-encoded [`ProtoRow`] bytes.
355///
356/// `Row`'s own serde impl emits the raw bytes of the in-memory `Tag` based
357/// datum encoding, which is free to change between releases. Use this wrapper
358/// instead wherever a row is serialized into a durable, cross-version format,
359/// such as the stable LIR plan format. `ProtoRow` already carries the needed
360/// backward compatibility obligation: it is persist's storage codec for
361/// `SourceData`, and `row.proto` is covered by the buf breaking lint.
362#[derive(
363    Clone,
364    Default,
365    Eq,
366    PartialEq,
367    Ord,
368    PartialOrd,
369    Hash,
370    Serialize,
371    Deserialize
372)]
373pub struct StableRow(#[serde(with = "stable_row_proto")] pub Row);
374
375impl From<Row> for StableRow {
376    fn from(row: Row) -> Self {
377        StableRow(row)
378    }
379}
380
381impl Deref for StableRow {
382    type Target = Row;
383
384    fn deref(&self) -> &Row {
385        &self.0
386    }
387}
388
389impl Debug for StableRow {
390    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391        self.0.fmt(f)
392    }
393}
394
395mod stable_row_proto {
396    use mz_proto::RustType;
397    use prost::Message;
398    use serde::de::Error;
399    use serde::{Deserialize, Deserializer, Serializer};
400
401    use crate::row::{ProtoRow, Row};
402
403    pub fn serialize<S: Serializer>(row: &Row, serializer: S) -> Result<S::Ok, S::Error> {
404        serializer.serialize_bytes(&row.into_proto().encode_to_vec())
405    }
406
407    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Row, D::Error> {
408        let bytes = serde_bytes::ByteBuf::deserialize(deserializer)?;
409        let proto = ProtoRow::decode(bytes.as_slice()).map_err(D::Error::custom)?;
410        Row::from_proto(proto).map_err(D::Error::custom)
411    }
412}
413
414#[allow(missing_debug_implementations)]
415mod columnation {
416    use columnation::{Columnation, Region};
417    use mz_ore::region::LgAllocRegion;
418
419    use crate::Row;
420
421    /// Region allocation for `Row` data.
422    ///
423    /// Content bytes are stored in stable contiguous memory locations,
424    /// and then a `Row` referencing them is falsified.
425    pub struct RowStack {
426        region: LgAllocRegion<u8>,
427    }
428
429    impl RowStack {
430        const LIMIT: usize = 2 << 20;
431    }
432
433    // Implement `Default` manually to specify a region allocation limit.
434    impl Default for RowStack {
435        fn default() -> Self {
436            Self {
437                // Limit the region size to 2MiB.
438                region: LgAllocRegion::with_limit(Self::LIMIT),
439            }
440        }
441    }
442
443    impl Columnation for Row {
444        type InnerRegion = RowStack;
445    }
446
447    impl Region for RowStack {
448        type Item = Row;
449        #[inline]
450        fn clear(&mut self) {
451            self.region.clear();
452        }
453        #[inline(always)]
454        unsafe fn copy(&mut self, item: &Row) -> Row {
455            if item.data.spilled() {
456                let bytes = self.region.copy_slice(&item.data[..]);
457                Row {
458                    data: compact_bytes::CompactBytes::from_raw_parts(
459                        bytes.as_mut_ptr(),
460                        item.data.len(),
461                        item.data.capacity(),
462                    ),
463                }
464            } else {
465                item.clone()
466            }
467        }
468
469        fn reserve_items<'a, I>(&mut self, items: I)
470        where
471            Self: 'a,
472            I: Iterator<Item = &'a Self::Item> + Clone,
473        {
474            let size = items
475                .filter(|row| row.data.spilled())
476                .map(|row| row.data.len())
477                .sum();
478            let size = std::cmp::min(size, Self::LIMIT);
479            self.region.reserve(size);
480        }
481
482        fn reserve_regions<'a, I>(&mut self, regions: I)
483        where
484            Self: 'a,
485            I: Iterator<Item = &'a Self> + Clone,
486        {
487            let size = regions.map(|r| r.region.len()).sum();
488            let size = std::cmp::min(size, Self::LIMIT);
489            self.region.reserve(size);
490        }
491
492        fn heap_size(&self, callback: impl FnMut(usize, usize)) {
493            self.region.heap_size(callback)
494        }
495    }
496}
497
498mod columnar {
499    use columnar::common::PushIndexAs;
500    use columnar::{
501        AsBytes, Borrow, Clear, Columnar, Container, FromBytes, Index, IndexAs, Len, Push,
502    };
503    use mz_ore::cast::CastFrom;
504    use std::ops::Range;
505
506    use crate::{Row, RowRef};
507
508    #[derive(
509        Copy,
510        Clone,
511        Debug,
512        Default,
513        PartialEq,
514        serde::Serialize,
515        serde::Deserialize
516    )]
517    pub struct Rows<BC = Vec<u64>, VC = Vec<u8>> {
518        /// Bounds container; provides indexed access to offsets.
519        bounds: BC,
520        /// Values container; provides slice access to bytes.
521        values: VC,
522    }
523
524    impl Columnar for Row {
525        #[inline(always)]
526        fn copy_from(&mut self, other: columnar::Ref<'_, Self>) {
527            self.clear();
528            self.data.extend_from_slice(other.data());
529        }
530        #[inline(always)]
531        fn into_owned(other: columnar::Ref<'_, Self>) -> Self {
532            other.to_owned()
533        }
534        type Container = Rows;
535        #[inline(always)]
536        fn reborrow<'b, 'a: 'b>(thing: columnar::Ref<'a, Self>) -> columnar::Ref<'b, Self>
537        where
538            Self: 'a,
539        {
540            thing
541        }
542    }
543
544    impl<BC: PushIndexAs<u64>> Borrow for Rows<BC, Vec<u8>> {
545        type Ref<'a> = &'a RowRef;
546        type Borrowed<'a>
547            = Rows<BC::Borrowed<'a>, &'a [u8]>
548        where
549            Self: 'a;
550        #[inline(always)]
551        fn borrow<'a>(&'a self) -> Self::Borrowed<'a> {
552            Rows {
553                bounds: self.bounds.borrow(),
554                values: self.values.borrow(),
555            }
556        }
557        #[inline(always)]
558        fn reborrow<'c, 'a: 'c>(item: Self::Borrowed<'a>) -> Self::Borrowed<'c>
559        where
560            Self: 'a,
561        {
562            Rows {
563                bounds: BC::reborrow(item.bounds),
564                values: item.values,
565            }
566        }
567
568        fn reborrow_ref<'b, 'a: 'b>(item: Self::Ref<'a>) -> Self::Ref<'b>
569        where
570            Self: 'a,
571        {
572            item
573        }
574    }
575
576    impl<BC: PushIndexAs<u64>> Container for Rows<BC, Vec<u8>> {
577        fn extend_from_self(&mut self, other: Self::Borrowed<'_>, range: Range<usize>) {
578            if !range.is_empty() {
579                // Imported bounds will be relative to this starting offset.
580                let values_len: u64 = self.values.len().try_into().expect("must fit");
581
582                // Push all bytes that we can, all at once.
583                let other_lower = if range.start == 0 {
584                    0
585                } else {
586                    other.bounds.index_as(range.start - 1)
587                };
588                let other_upper = other.bounds.index_as(range.end - 1);
589                self.values.extend_from_self(
590                    other.values,
591                    usize::try_from(other_lower).expect("must fit")
592                        ..usize::try_from(other_upper).expect("must fit"),
593                );
594
595                // Each bound needs to be shifted by `values_len - other_lower`.
596                if values_len == other_lower {
597                    self.bounds.extend_from_self(other.bounds, range);
598                } else {
599                    for index in range {
600                        let shifted = other.bounds.index_as(index) - other_lower + values_len;
601                        self.bounds.push(&shifted)
602                    }
603                }
604            }
605        }
606        fn reserve_for<'a, I>(&mut self, selves: I)
607        where
608            Self: 'a,
609            I: Iterator<Item = Self::Borrowed<'a>> + Clone,
610        {
611            self.bounds.reserve_for(selves.clone().map(|r| r.bounds));
612            self.values.reserve_for(selves.map(|r| r.values));
613        }
614    }
615
616    impl<'a, BC: AsBytes<'a>, VC: AsBytes<'a>> AsBytes<'a> for Rows<BC, VC> {
617        const SLICE_COUNT: usize = BC::SLICE_COUNT + VC::SLICE_COUNT;
618        #[inline(always)]
619        fn get_byte_slice(&self, index: usize) -> (u64, &'a [u8]) {
620            mz_ore::soft_assert_no_log!(index < Self::SLICE_COUNT);
621            if index < BC::SLICE_COUNT {
622                self.bounds.get_byte_slice(index)
623            } else {
624                self.values.get_byte_slice(index - BC::SLICE_COUNT)
625            }
626        }
627    }
628    impl<'a, BC: FromBytes<'a>, VC: FromBytes<'a>> FromBytes<'a> for Rows<BC, VC> {
629        const SLICE_COUNT: usize = BC::SLICE_COUNT + VC::SLICE_COUNT;
630        #[inline(always)]
631        fn from_bytes(bytes: &mut impl Iterator<Item = &'a [u8]>) -> Self {
632            Self {
633                bounds: FromBytes::from_bytes(bytes),
634                values: FromBytes::from_bytes(bytes),
635            }
636        }
637    }
638
639    impl<BC: Len, VC> Len for Rows<BC, VC> {
640        #[inline(always)]
641        fn len(&self) -> usize {
642            self.bounds.len()
643        }
644    }
645
646    impl<'a, BC: Len + IndexAs<u64>> Index for Rows<BC, &'a [u8]> {
647        type Ref = &'a RowRef;
648        #[inline(always)]
649        fn get(&self, index: usize) -> Self::Ref {
650            let lower = if index == 0 {
651                0
652            } else {
653                self.bounds.index_as(index - 1)
654            };
655            let upper = self.bounds.index_as(index);
656            let lower = usize::cast_from(lower);
657            let upper = usize::cast_from(upper);
658            // SAFETY: self.values contains only valid row data, and self.metadata delimits only ranges
659            // that correspond to the original rows.
660            unsafe { RowRef::from_slice(&self.values[lower..upper]) }
661        }
662    }
663    impl<'a, BC: Len + IndexAs<u64>> Index for &'a Rows<BC, Vec<u8>> {
664        type Ref = &'a RowRef;
665        #[inline(always)]
666        fn get(&self, index: usize) -> Self::Ref {
667            let lower = if index == 0 {
668                0
669            } else {
670                self.bounds.index_as(index - 1)
671            };
672            let upper = self.bounds.index_as(index);
673            let lower = usize::cast_from(lower);
674            let upper = usize::cast_from(upper);
675            // SAFETY: self.values contains only valid row data, and self.metadata delimits only ranges
676            // that correspond to the original rows.
677            unsafe { RowRef::from_slice(&self.values[lower..upper]) }
678        }
679    }
680
681    impl<BC: Push<u64>> Push<&Row> for Rows<BC> {
682        #[inline(always)]
683        fn push(&mut self, item: &Row) {
684            self.values.extend_from_slice(item.data.as_slice());
685            self.bounds.push(u64::cast_from(self.values.len()));
686        }
687    }
688    impl<BC: Push<u64>> Push<Row> for Rows<BC> {
689        #[inline(always)]
690        fn push(&mut self, item: Row) {
691            self.push(&item);
692        }
693    }
694    impl<BC: for<'a> Push<&'a u64>> Push<&RowRef> for Rows<BC> {
695        #[inline(always)]
696        fn push(&mut self, item: &RowRef) {
697            self.values.extend_from_slice(item.data());
698            self.bounds.push(&u64::cast_from(self.values.len()));
699        }
700    }
701    impl<BC: Clear, VC: Clear> Clear for Rows<BC, VC> {
702        #[inline(always)]
703        fn clear(&mut self) {
704            self.bounds.clear();
705            self.values.clear();
706        }
707    }
708}
709
710/// A contiguous slice of bytes that are row data.
711///
712/// A [`RowRef`] is to [`Row`] as [`prim@str`] is to [`String`].
713#[derive(PartialEq, Eq, Hash)]
714#[repr(transparent)]
715pub struct RowRef([u8]);
716
717impl RowRef {
718    /// Create a [`RowRef`] from a slice of data.
719    ///
720    /// # Safety
721    ///
722    /// We do not check that the provided slice is valid [`Row`] data; the caller is required to
723    /// ensure this.
724    pub unsafe fn from_slice(row: &[u8]) -> &RowRef {
725        #[allow(clippy::as_conversions)]
726        let ptr = row as *const [u8] as *const RowRef;
727        // SAFETY: We know `ptr` is non-null and aligned because it came from a &[u8].
728        unsafe { &*ptr }
729    }
730
731    /// Unpack `self` into a `Vec<Datum>` for efficient random access.
732    pub fn unpack(&self) -> Vec<Datum<'_>> {
733        // It's usually cheaper to unpack twice to figure out the right length than it is to grow the vec as we go
734        let len = self.iter().count();
735        let mut vec = Vec::with_capacity(len);
736        vec.extend(self.iter());
737        vec
738    }
739
740    /// Return the first [`Datum`] in `self`
741    ///
742    /// Panics if the [`RowRef`] is empty.
743    pub fn unpack_first(&self) -> Datum<'_> {
744        self.iter().next().unwrap()
745    }
746
747    /// Iterate the [`Datum`] elements of the [`RowRef`].
748    pub fn iter(&self) -> DatumListIter<'_> {
749        DatumListIter { data: &self.0 }
750    }
751
752    /// Return the byte length of this [`RowRef`].
753    pub fn byte_len(&self) -> usize {
754        self.0.len()
755    }
756
757    /// For debugging only.
758    pub fn data(&self) -> &[u8] {
759        &self.0
760    }
761
762    /// True iff there is no data in this [`RowRef`].
763    pub fn is_empty(&self) -> bool {
764        self.0.is_empty()
765    }
766}
767
768impl ToOwned for RowRef {
769    type Owned = Row;
770
771    fn to_owned(&self) -> Self::Owned {
772        // SAFETY: RowRef has the invariant that the wrapped data must be a valid Row encoding.
773        unsafe { Row::from_bytes_unchecked(&self.0) }
774    }
775}
776
777impl<'a> IntoIterator for &'a RowRef {
778    type Item = Datum<'a>;
779    type IntoIter = DatumListIter<'a>;
780
781    fn into_iter(self) -> DatumListIter<'a> {
782        DatumListIter { data: &self.0 }
783    }
784}
785
786/// These implementations order first by length, and then by slice contents.
787/// This allows many comparisons to complete without dereferencing memory.
788/// Warning: These order by the u8 array representation, and NOT by Datum::cmp.
789impl PartialOrd for RowRef {
790    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
791        Some(self.cmp(other))
792    }
793}
794
795impl Ord for RowRef {
796    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
797        match self.0.len().cmp(&other.0.len()) {
798            std::cmp::Ordering::Less => std::cmp::Ordering::Less,
799            std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
800            std::cmp::Ordering::Equal => self.0.cmp(&other.0),
801        }
802    }
803}
804
805impl fmt::Debug for RowRef {
806    /// Debug representation using the internal datums
807    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
808        f.write_str("RowRef{")?;
809        f.debug_list().entries(&*self).finish()?;
810        f.write_str("}")
811    }
812}
813
814/// Packs datums into a [`Row`].
815///
816/// Creating a `RowPacker` via [`Row::packer`] starts a packing operation on the
817/// row. A packing operation always starts from scratch: the existing contents
818/// of the underlying row are cleared.
819///
820/// To complete a packing operation, drop the `RowPacker`.
821#[derive(Debug)]
822pub struct RowPacker<'a> {
823    row: &'a mut Row,
824}
825
826/// Infallible conversion from a [`Datum`] to a typed value.
827///
828/// Used by [`DatumList::typed_iter`] to yield elements as `T` rather than
829/// raw `Datum`s. At runtime, `T` is always `Datum<'a>`, so the conversion
830/// is identity.
831///
832/// See `doc/developer/design/20260311_sqlfunc_generic.md` for the design
833/// behind the generic type parameter and type erasure.
834///
835/// This trait is sealed and cannot be implemented outside of this crate.
836pub trait FromDatum<'a>:
837    Sized + PartialEq + std::borrow::Borrow<Datum<'a>> + sealed::Sealed
838{
839    fn from_datum(datum: Datum<'a>) -> Self;
840}
841
842mod sealed {
843    use crate::Datum;
844
845    pub trait Sealed {}
846    impl<'a> Sealed for Datum<'a> {}
847}
848
849impl<'a> FromDatum<'a> for Datum<'a> {
850    #[inline]
851    fn from_datum(datum: Datum<'a>) -> Self {
852        datum
853    }
854}
855
856#[derive(Debug, Clone)]
857pub struct DatumListIter<'a> {
858    data: &'a [u8],
859}
860
861#[derive(Debug, Clone)]
862pub struct DatumListTypedIter<'a, T> {
863    inner: DatumListIter<'a>,
864    _phantom: PhantomData<fn() -> T>,
865}
866
867#[derive(Debug, Clone)]
868pub struct DatumDictIter<'a> {
869    data: &'a [u8],
870    prev_key: Option<&'a str>,
871}
872
873#[derive(Debug, Clone)]
874pub struct DatumDictTypedIter<'a, T> {
875    inner: DatumDictIter<'a>,
876    _phantom: PhantomData<fn() -> T>,
877}
878
879/// `RowArena` is used to hold on to temporary `Row`s for functions like `eval` that need to create complex `Datum`s but don't have a `Row` to put them in yet.
880#[derive(Debug)]
881pub struct RowArena {
882    // A stack of byte regions, used as a bump allocator. Bytes handed to
883    // `push_bytes` are *copied* into the active (last) region and a reference
884    // into that region is returned.
885    //
886    // The invariant that keeps returned references valid for the arena's
887    // lifetime is that a region is never reallocated once it holds data: when
888    // the active region lacks spare capacity for a push we allocate a *new*,
889    // larger region rather than growing the current one (which would move its
890    // bytes and dangle outstanding references). The outer `Vec` may itself
891    // reallocate as regions are added, but that only moves the `Vec<u8>`
892    // headers, not the heap buffers they own, so references remain valid.
893    //
894    // `clear` retains only the largest region (emptied) to right-size the arena
895    // for reuse; reusing one region across `clear` cycles makes a steady-state
896    // workload (e.g. decoding rows one at a time) allocation-free.
897    inner: RefCell<Vec<Vec<u8>>>,
898    // A single recycled scratch buffer backing `RowArena::writer`. A writer takes ownership of this
899    // buffer (or allocates a fresh one if absent), builds into it, and on drop returns it here for
900    // the next writer to reuse — so building values incrementally does not allocate per use once the
901    // buffer reaches its high-water mark. Holding `Option` (rather than the buffer directly) means
902    // `writer` borrows this cell only transiently, to take and return the buffer, never across the
903    // writer's lifetime. That keeps nested writers sound: a writer obtained while another is live
904    // finds the slot empty and allocates its own buffer instead of double-borrowing.
905    scratch: RefCell<Option<Vec<u8>>>,
906    // Optional ceiling on the bytes this arena will hold, and a running total of what it holds.
907    // `None` is unbounded, which is what every arena in a dataflow must keep using: a budget that
908    // can change mid-run would make dataflow evaluation non-deterministic (see
909    // [`RowArena::with_budget`]). A budget is for evaluating a user-authored expression in a shared
910    // process, where that expression's memory use must be bounded (see `mz_adapter::webhook`).
911    //
912    // NOTE: exceeding the budget does not make a push fail. The pushes are infallible, and a
913    // refused push would hand back a truncated value, i.e. a corrupt datum. The budget is instead a
914    // *reported* condition: `over_budget` is polled by whoever is able to return an error, which
915    // for scalar expressions is the evaluator between calls.
916    budget: Option<usize>,
917    allocated: Cell<usize>,
918}
919
920// DatumList and DatumDict defined here rather than near Datum because we need private access to the unsafe data field
921
922/// A sequence of Datums
923///
924/// The type parameter `T` represents the element type of the list. It is a
925/// phantom parameter that carries no runtime data — the actual elements are
926/// stored as serialized bytes and `T` is not enforced at runtime. It is up
927/// to the caller to ensure `T` matches the actual element type. The default
928/// `T = Datum<'a>` means existing code that writes `DatumList<'a>` continues
929/// to work unchanged.
930///
931/// See `doc/developer/design/20260311_sqlfunc_generic.md` for the design
932/// behind the generic type parameter.
933pub struct DatumList<'a, T = Datum<'a>> {
934    /// Points at the serialized datums
935    data: &'a [u8],
936    _phantom: PhantomData<fn() -> T>,
937}
938
939impl<'a, T> DatumList<'a, T> {
940    /// Private constructor. All `DatumList` values should be created through
941    /// this function to keep the `PhantomData` bookkeeping in one place.
942    pub(crate) fn new(data: &'a [u8]) -> Self {
943        DatumList {
944            data,
945            _phantom: PhantomData,
946        }
947    }
948}
949
950impl<'a, T> Clone for DatumList<'a, T> {
951    fn clone(&self) -> Self {
952        *self
953    }
954}
955
956impl<'a, T> Copy for DatumList<'a, T> {}
957
958impl<'a, T> Debug for DatumList<'a, T> {
959    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
960        f.debug_list().entries(self.iter()).finish()
961    }
962}
963
964impl<'a, T> PartialEq for DatumList<'a, T> {
965    #[inline(always)]
966    fn eq(&self, other: &DatumList<'a, T>) -> bool {
967        self.iter().eq(other.iter())
968    }
969}
970
971impl<'a, T> Eq for DatumList<'a, T> {}
972
973impl<'a, T> Hash for DatumList<'a, T> {
974    #[inline(always)]
975    fn hash<H: Hasher>(&self, state: &mut H) {
976        for d in self.iter() {
977            d.hash(state);
978        }
979    }
980}
981
982impl<T> Ord for DatumList<'_, T> {
983    #[inline(always)]
984    fn cmp(&self, other: &DatumList<'_, T>) -> Ordering {
985        // Grow the stack: lists can be arbitrarily deeply nested (e.g. jsonb).
986        mz_ore::stack::maybe_grow(|| self.iter().cmp(other.iter()))
987    }
988}
989
990impl<T> PartialOrd for DatumList<'_, T> {
991    #[inline(always)]
992    fn partial_cmp(&self, other: &DatumList<'_, T>) -> Option<Ordering> {
993        Some(self.cmp(other))
994    }
995}
996
997/// A mapping from string keys to Datums
998///
999/// The type parameter `T` represents the value type of the map. It is a
1000/// phantom parameter — the actual values are stored as serialized bytes and
1001/// `T` is not enforced at runtime. It is up to the caller to ensure `T`
1002/// matches the actual value type. The default `T = Datum<'a>` means existing
1003/// code that writes `DatumMap<'a>` continues to work unchanged.
1004///
1005/// See `doc/developer/design/20260311_sqlfunc_generic.md` for the design
1006/// behind the generic type parameter.
1007pub struct DatumMap<'a, T = Datum<'a>> {
1008    /// Points at the serialized datums, which should be sorted in key order
1009    data: &'a [u8],
1010    _phantom: PhantomData<fn() -> T>,
1011}
1012
1013impl<'a, T> DatumMap<'a, T> {
1014    /// Private constructor. All `DatumMap` values should be created through
1015    /// this function to keep the `PhantomData` bookkeeping in one place.
1016    pub(crate) fn new(data: &'a [u8]) -> Self {
1017        DatumMap {
1018            data,
1019            _phantom: PhantomData,
1020        }
1021    }
1022}
1023
1024impl<'a, T> Clone for DatumMap<'a, T> {
1025    fn clone(&self) -> Self {
1026        *self
1027    }
1028}
1029
1030impl<'a, T> Copy for DatumMap<'a, T> {}
1031
1032impl<'a, T> PartialEq for DatumMap<'a, T> {
1033    #[inline(always)]
1034    fn eq(&self, other: &DatumMap<'a, T>) -> bool {
1035        self.iter().eq(other.iter())
1036    }
1037}
1038
1039impl<'a, T> Eq for DatumMap<'a, T> {}
1040
1041impl<'a, T> Hash for DatumMap<'a, T> {
1042    #[inline(always)]
1043    fn hash<H: Hasher>(&self, state: &mut H) {
1044        for (k, v) in self.iter() {
1045            k.hash(state);
1046            v.hash(state);
1047        }
1048    }
1049}
1050
1051impl<'a, T> Ord for DatumMap<'a, T> {
1052    #[inline(always)]
1053    fn cmp(&self, other: &DatumMap<'a, T>) -> Ordering {
1054        // Grow the stack: maps can be arbitrarily deeply nested (e.g. jsonb).
1055        mz_ore::stack::maybe_grow(|| self.iter().cmp(other.iter()))
1056    }
1057}
1058
1059impl<'a, T> PartialOrd for DatumMap<'a, T> {
1060    #[inline(always)]
1061    fn partial_cmp(&self, other: &DatumMap<'a, T>) -> Option<Ordering> {
1062        Some(self.cmp(other))
1063    }
1064}
1065
1066impl<'a> crate::scalar::SqlContainerType for DatumList<'a, Datum<'a>> {
1067    fn unwrap_element_type(container: &SqlScalarType) -> &SqlScalarType {
1068        container.unwrap_list_element_type()
1069    }
1070    fn wrap_element_type(element: SqlScalarType) -> SqlScalarType {
1071        SqlScalarType::List {
1072            element_type: Box::new(element),
1073            custom_id: None,
1074        }
1075    }
1076}
1077
1078impl<'a> crate::scalar::SqlContainerType for DatumMap<'a, Datum<'a>> {
1079    fn unwrap_element_type(container: &SqlScalarType) -> &SqlScalarType {
1080        container.unwrap_map_value_type()
1081    }
1082    fn wrap_element_type(element: SqlScalarType) -> SqlScalarType {
1083        SqlScalarType::Map {
1084            value_type: Box::new(element),
1085            custom_id: None,
1086        }
1087    }
1088}
1089
1090/// Represents a single `Datum`, appropriate to be nested inside other
1091/// `Datum`s.
1092#[derive(Clone, Copy, Eq, PartialEq, Hash)]
1093pub struct DatumNested<'a> {
1094    val: &'a [u8],
1095}
1096
1097impl<'a> std::fmt::Display for DatumNested<'a> {
1098    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1099        std::fmt::Display::fmt(&self.datum(), f)
1100    }
1101}
1102
1103impl<'a> std::fmt::Debug for DatumNested<'a> {
1104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1105        f.debug_struct("DatumNested")
1106            .field("val", &self.datum())
1107            .finish()
1108    }
1109}
1110
1111impl<'a> DatumNested<'a> {
1112    // Figure out which bytes `read_datum` returns (e.g. including the tag),
1113    // and then store a reference to those bytes, so we can "replay" this same
1114    // call later on without storing the datum itself.
1115    pub fn extract(data: &mut &'a [u8]) -> DatumNested<'a> {
1116        let prev = *data;
1117        let _ = unsafe { read_datum(data) };
1118        DatumNested {
1119            val: &prev[..(prev.len() - data.len())],
1120        }
1121    }
1122
1123    /// Returns the datum `self` contains.
1124    pub fn datum(&self) -> Datum<'a> {
1125        let mut temp = self.val;
1126        unsafe { read_datum(&mut temp) }
1127    }
1128}
1129
1130impl<'a> Ord for DatumNested<'a> {
1131    fn cmp(&self, other: &Self) -> Ordering {
1132        // Grow the stack: this recurses once per level of nested list/map values.
1133        mz_ore::stack::maybe_grow(|| self.datum().cmp(&other.datum()))
1134    }
1135}
1136
1137impl<'a> PartialOrd for DatumNested<'a> {
1138    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1139        Some(self.cmp(other))
1140    }
1141}
1142
1143// Prefer adding new tags to the end of the enum. Certain behavior, like row ordering and EXPLAIN
1144// PHYSICAL PLAN, rely on the ordering of this enum. Neither of these are breaking changes, but
1145// it's annoying when they change.
1146#[derive(Debug, Clone, Copy, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)]
1147#[repr(u8)]
1148enum Tag {
1149    Null,
1150    False,
1151    True,
1152    Float32,
1153    Float64,
1154    Date,
1155    Time,
1156    Timestamp,
1157    TimestampTz,
1158    Interval,
1159    // The length-prefixed tags: for each of the three kinds, four tags ordered by the width of
1160    // the length prefix in front of the payload, so a tag's distance from its kind's first tag
1161    // selects the width. Each kind has its own `read_datum` arm naming the `Datum` constructor,
1162    // since the kind never varies within a column; the width does, so it stays inside the arm.
1163    BytesTiny,
1164    BytesShort,
1165    BytesLong,
1166    BytesHuge,
1167    StringTiny,
1168    StringShort,
1169    StringLong,
1170    StringHuge,
1171    ListTiny,
1172    ListShort,
1173    ListLong,
1174    ListHuge,
1175    Uuid,
1176    Array,
1177    Dict,
1178    JsonNull,
1179    Dummy,
1180    Numeric,
1181    MzTimestamp,
1182    Range,
1183    MzAclItem,
1184    AclItem,
1185    // Everything except leap seconds and times beyond the range of
1186    // i64 nanoseconds. (Note that Materialize does not support leap
1187    // seconds, but this module does).
1188    CheapTimestamp,
1189    // Everything except leap seconds and times beyond the range of
1190    // i64 nanoseconds. (Note that Materialize does not support leap
1191    // seconds, but this module does).
1192    CheapTimestampTz,
1193    // The next several tags are for variable-length signed integer encoding.
1194    // The basic idea is that `NonNegativeIntN_K` is used to encode a datum of type
1195    // IntN whose actual value is positive or zero and fits in K bits, and similarly for
1196    // NegativeIntN_K with negative values.
1197    //
1198    // The order of these tags matters, because we want to be able to choose the
1199    // tag for a given datum quickly, with arithmetic, rather than slowly, with a
1200    // stack of `if` statements.
1201    //
1202    // Separate tags for non-negative and negative numbers are used to avoid having to
1203    // waste one bit in the actual data space to encode the sign.
1204    // A signed family alternates non-negative and negative at each payload width, so a tag
1205    // splits into both by arithmetic: the width is its distance from the family's first tag
1206    // shifted right by one, and the sign is that distance's low bit. Keeping the two signs
1207    // apart would make the width a subtraction from one of two bases, chosen by a compare.
1208    //
1209    // The family ends at the width where the payload is the whole integer. There the sign is
1210    // already the payload's top bit, so one tag serves both and the alternation stops, which is
1211    // why the fixed-width tag sits there rather than in a family of its own.
1212    NonNegativeInt16_0, // i.e., 0
1213    NegativeInt16_0,    // i.e., -1
1214    NonNegativeInt16_8,
1215    NegativeInt16_8,
1216    Int16,
1217
1218    NonNegativeInt32_0,
1219    NegativeInt32_0,
1220    NonNegativeInt32_8,
1221    NegativeInt32_8,
1222    NonNegativeInt32_16,
1223    NegativeInt32_16,
1224    NonNegativeInt32_24,
1225    NegativeInt32_24,
1226    Int32,
1227
1228    NonNegativeInt64_0,
1229    NegativeInt64_0,
1230    NonNegativeInt64_8,
1231    NegativeInt64_8,
1232    NonNegativeInt64_16,
1233    NegativeInt64_16,
1234    NonNegativeInt64_24,
1235    NegativeInt64_24,
1236    NonNegativeInt64_32,
1237    NegativeInt64_32,
1238    NonNegativeInt64_40,
1239    NegativeInt64_40,
1240    NonNegativeInt64_48,
1241    NegativeInt64_48,
1242    NonNegativeInt64_56,
1243    NegativeInt64_56,
1244    Int64,
1245
1246    // These are like the ones above, but for unsigned types. The situation is slightly simpler
1247    // as we don't have negatives, so the width is the whole distance from the family's first
1248    // tag, and the fixed-width tag is again the widest member.
1249    UInt8_0, // i.e., 0
1250    UInt8,
1251
1252    UInt16_0,
1253    UInt16_8,
1254    UInt16,
1255
1256    UInt32_0,
1257    UInt32_8,
1258    UInt32_16,
1259    UInt32_24,
1260    UInt32,
1261
1262    UInt64_0,
1263    UInt64_8,
1264    UInt64_16,
1265    UInt64_24,
1266    UInt64_32,
1267    UInt64_40,
1268    UInt64_48,
1269    UInt64_56,
1270    UInt64,
1271}
1272
1273impl Tag {
1274    /// The tag's discriminant, usable in const context.
1275    #[allow(clippy::as_conversions)]
1276    const fn byte(self) -> u8 {
1277        self as u8
1278    }
1279}
1280
1281/// Assert that the listed tags are consecutive, in the order given.
1282///
1283/// Every family below is addressed by arithmetic rather than by name: `push_datum` writes
1284/// `first + n` for a payload of `n` bytes, and `read_signed_varint`/`read_unsigned_varint`
1285/// invert that by subtraction. A variant inserted into the middle of a family, or two members
1286/// swapped, silently changes how many bytes a tag claims, which corrupts the datum rather than
1287/// failing to compile. These assertions turn that into a compile error at the definition.
1288macro_rules! assert_consecutive {
1289    ($($tag:ident),+ $(,)?) => {
1290        const _: () = {
1291            let tags: &[u8] = &[$(Tag::$tag.byte()),+];
1292            let mut i = 1;
1293            while i < tags.len() {
1294                assert!(
1295                    tags[i] == tags[i - 1] + 1,
1296                    concat!("tags are not consecutive: ", stringify!($($tag),+))
1297                );
1298                i += 1;
1299            }
1300        };
1301    };
1302}
1303
1304assert_consecutive!(BytesTiny, BytesShort, BytesLong, BytesHuge);
1305assert_consecutive!(StringTiny, StringShort, StringLong, StringHuge);
1306assert_consecutive!(ListTiny, ListShort, ListLong, ListHuge);
1307assert_consecutive!(
1308    NonNegativeInt16_0,
1309    NegativeInt16_0,
1310    NonNegativeInt16_8,
1311    NegativeInt16_8,
1312    Int16,
1313);
1314assert_consecutive!(
1315    NonNegativeInt32_0,
1316    NegativeInt32_0,
1317    NonNegativeInt32_8,
1318    NegativeInt32_8,
1319    NonNegativeInt32_16,
1320    NegativeInt32_16,
1321    NonNegativeInt32_24,
1322    NegativeInt32_24,
1323    Int32,
1324);
1325assert_consecutive!(
1326    NonNegativeInt64_0,
1327    NegativeInt64_0,
1328    NonNegativeInt64_8,
1329    NegativeInt64_8,
1330    NonNegativeInt64_16,
1331    NegativeInt64_16,
1332    NonNegativeInt64_24,
1333    NegativeInt64_24,
1334    NonNegativeInt64_32,
1335    NegativeInt64_32,
1336    NonNegativeInt64_40,
1337    NegativeInt64_40,
1338    NonNegativeInt64_48,
1339    NegativeInt64_48,
1340    NonNegativeInt64_56,
1341    NegativeInt64_56,
1342    Int64,
1343);
1344assert_consecutive!(UInt8_0, UInt8,);
1345assert_consecutive!(UInt16_0, UInt16_8, UInt16,);
1346assert_consecutive!(UInt32_0, UInt32_8, UInt32_16, UInt32_24, UInt32,);
1347assert_consecutive!(
1348    UInt64_0, UInt64_8, UInt64_16, UInt64_24, UInt64_32, UInt64_40, UInt64_48, UInt64_56, UInt64,
1349);
1350
1351// --------------------------------------------------------------------------------
1352// reading data
1353
1354/// Read a byte slice starting at byte `offset`.
1355///
1356/// Updates `offset` to point to the first byte after the end of the read region.
1357fn read_untagged_bytes<'a>(data: &mut &'a [u8]) -> &'a [u8] {
1358    let len = u64::from_le_bytes(read_byte_array(data));
1359    let len = usize::cast_from(len);
1360    let (bytes, next) = data.split_at(len);
1361    *data = next;
1362    bytes
1363}
1364
1365/// Read a byte slice preceded by its length, the width of the length prefix given by the tag's
1366/// distance from `first`, its kind's `*Tiny` tag.
1367///
1368/// Each arm reads the prefix at a constant width, which is a plain load; deriving the width and
1369/// reading that many bytes measured twice as slow on long payloads.
1370///
1371/// # Safety
1372///
1373/// The contents are whatever `push_lengthed_bytes` wrote, so a caller may treat them as UTF-8
1374/// only for a `String` tag.
1375#[inline(always)]
1376fn read_lengthed_bytes<'a>(data: &mut &'a [u8], tag: Tag, first: Tag) -> &'a [u8] {
1377    let len = match u8::from(tag).wrapping_sub(u8::from(first)) {
1378        0 => usize::from(read_byte(data)),
1379        1 => usize::from(u16::from_le_bytes(read_byte_array(data))),
1380        2 => usize::cast_from(u32::from_le_bytes(read_byte_array(data))),
1381        _ => usize::cast_from(u64::from_le_bytes(read_byte_array(data))),
1382    };
1383    let (bytes, next) = data.split_at(len);
1384    *data = next;
1385    bytes
1386}
1387
1388#[inline(always)]
1389fn read_byte(data: &mut &[u8]) -> u8 {
1390    let byte = data[0];
1391    *data = &data[1..];
1392    byte
1393}
1394
1395/// The payload of a variable-length integer whose datum ends within eight bytes of the end of
1396/// `data`, so the wide load in [`read_varint_word`] would run off the end.
1397///
1398/// Out of line and cold: this is at most the tail of a row, or of a nested list or map.
1399#[cold]
1400#[inline(never)]
1401fn read_varint_word_tail(data: &[u8], len: usize) -> u64 {
1402    #[inline(always)]
1403    fn ext<const L: usize>(data: &[u8]) -> u64 {
1404        let mut raw = [0; 8];
1405        raw[..L].copy_from_slice(&data[..L]);
1406        u64::from_le_bytes(raw)
1407    }
1408    // Each arm reads a constant width, which is a load rather than a `memcpy`. This path serves
1409    // the last datum or two of every row, which at low arity is a real share of all datums.
1410    match len {
1411        0 => 0,
1412        1 => u64::from(data[0]),
1413        2 => ext::<2>(data),
1414        3 => ext::<3>(data),
1415        4 => ext::<4>(data),
1416        5 => ext::<5>(data),
1417        6 => ext::<6>(data),
1418        7 => ext::<7>(data),
1419        // An eight-byte payload needs eight bytes past the tag, which this path's caller found
1420        // wanting, so a valid row cannot reach here.
1421        _ => panic!("payload runs past the end of the row"),
1422    }
1423}
1424
1425/// Read the `len` payload bytes of a variable-length integer, returning them in the low
1426/// `len * 8` bits of a word. Bits above that are zero or belong to whatever follows in the row,
1427/// so a caller must mask them off.
1428///
1429/// Loading a fixed eight bytes is what keeps `len` out of the load, and so off a branch. Those
1430/// bytes exist except at the very end of the buffer.
1431#[inline(always)]
1432fn read_varint_word(data: &mut &[u8], len: usize) -> u64 {
1433    let word = match data.first_chunk::<8>() {
1434        Some(chunk) => u64::from_le_bytes(*chunk),
1435        None => read_varint_word_tail(data, len),
1436    };
1437    *data = &data[len..];
1438    word
1439}
1440
1441/// Mask covering the low `len` bytes of a word.
1442///
1443/// `len` reaches eight for a 64-bit value that needs every byte, where a shift of 64 would be
1444/// undefined, so that case saturates instead.
1445#[inline(always)]
1446fn payload_mask(len: usize) -> u64 {
1447    if len >= 8 {
1448        u64::MAX
1449    } else {
1450        (1u64 << (len * 8)) - 1
1451    }
1452}
1453
1454/// The low `N` bytes of `word`, little-endian.
1455#[inline(always)]
1456fn truncate<const N: usize>(word: u64) -> [u8; N] {
1457    word.to_le_bytes()[..N].try_into().expect("N <= 8")
1458}
1459
1460/// Read the payload of a variable-length integer of either sign, extended to `N` bytes.
1461///
1462/// A signed family alternates the two signs at each payload width, so the tag's distance from
1463/// `first` holds the width above its low bit and the sign in it. Both fall out by shifting and
1464/// masking, with no compare and nothing to dispatch on: a column's tag varies with the magnitude
1465/// and sign of every value, so any branch on it is one the predictor cannot learn.
1466///
1467/// # Correctness
1468///
1469/// `tag` must belong to the family starting at `first`, and `data` must hold its payload.
1470#[inline(always)]
1471fn read_signed_varint<const N: usize>(data: &mut &[u8], tag: Tag, first: Tag) -> [u8; N] {
1472    let delta = u8::from(tag).wrapping_sub(u8::from(first));
1473    let len = usize::from(delta >> 1);
1474    // All ones for a negative value, so the bytes above the payload sign-extend, and zero
1475    // otherwise. A `len` of zero leaves the whole word filled, which is the -1 the encoder means.
1476    let negative = delta & 1 == 1;
1477    read_varint_payload(data, len, negative)
1478}
1479
1480/// Read a `len` byte payload and extend it to `N` bytes, with ones above it when `negative`.
1481///
1482/// `len` must not exceed `N`, since `truncate` keeps only the low `N` bytes and a wider payload
1483/// would lose its top ones.
1484#[inline(always)]
1485fn read_varint_payload<const N: usize>(data: &mut &[u8], len: usize, negative: bool) -> [u8; N] {
1486    let mask = payload_mask(len);
1487    let fill = 0u64.wrapping_sub(u64::from(negative));
1488    truncate((read_varint_word(data, len) & mask) | (fill & !mask))
1489}
1490
1491/// Read the payload of an unsigned variable-length integer, zero-extended to `N` bytes.
1492///
1493/// As [`read_signed_varint`], without the sign.
1494///
1495/// # Correctness
1496///
1497/// `tag` must belong to the family starting at `first`, and `data` must hold its payload.
1498#[inline(always)]
1499fn read_unsigned_varint<const N: usize>(data: &mut &[u8], tag: Tag, first: Tag) -> [u8; N] {
1500    let len = usize::from(u8::from(tag).wrapping_sub(u8::from(first)));
1501    read_varint_payload(data, len, false)
1502}
1503
1504#[inline(always)]
1505pub(super) fn read_byte_array<const N: usize>(data: &mut &[u8]) -> [u8; N] {
1506    let (prev, next) = data.split_first_chunk().unwrap();
1507    *data = next;
1508    *prev
1509}
1510
1511pub(super) fn read_date(data: &mut &[u8]) -> Date {
1512    let days = i32::from_le_bytes(read_byte_array(data));
1513    Date::from_pg_epoch(days).expect("unexpected date")
1514}
1515
1516pub(super) fn read_naive_date(data: &mut &[u8]) -> NaiveDate {
1517    let year = i32::from_le_bytes(read_byte_array(data));
1518    let ordinal = u32::from_le_bytes(read_byte_array(data));
1519    NaiveDate::from_yo_opt(year, ordinal).unwrap()
1520}
1521
1522pub(super) fn read_time(data: &mut &[u8]) -> NaiveTime {
1523    let secs = u32::from_le_bytes(read_byte_array(data));
1524    let nanos = u32::from_le_bytes(read_byte_array(data));
1525    NaiveTime::from_num_seconds_from_midnight_opt(secs, nanos).unwrap()
1526}
1527
1528/// Read a datum starting at byte `offset`.
1529///
1530/// Updates `offset` to point to the first byte after the end of the read region.
1531///
1532/// # Safety
1533///
1534/// This function is safe if a `Datum` was previously written at this offset by `push_datum`.
1535/// Otherwise it could return invalid values, which is Undefined Behavior.
1536pub unsafe fn read_datum<'a>(data: &mut &'a [u8]) -> Datum<'a> {
1537    let tag = Tag::try_from_primitive(read_byte(data)).expect("unknown row tag");
1538    match tag {
1539        Tag::Null => Datum::Null,
1540        Tag::False => Datum::False,
1541        Tag::True => Datum::True,
1542        Tag::NonNegativeInt16_0
1543        | Tag::NegativeInt16_0
1544        | Tag::NonNegativeInt16_8
1545        | Tag::NegativeInt16_8
1546        | Tag::Int16 => Datum::Int16(i16::from_le_bytes(read_signed_varint(
1547            data,
1548            tag,
1549            Tag::NonNegativeInt16_0,
1550        ))),
1551        Tag::NonNegativeInt32_0
1552        | Tag::NegativeInt32_0
1553        | Tag::NonNegativeInt32_8
1554        | Tag::NegativeInt32_8
1555        | Tag::NonNegativeInt32_16
1556        | Tag::NegativeInt32_16
1557        | Tag::NonNegativeInt32_24
1558        | Tag::NegativeInt32_24
1559        | Tag::Int32 => Datum::Int32(i32::from_le_bytes(read_signed_varint(
1560            data,
1561            tag,
1562            Tag::NonNegativeInt32_0,
1563        ))),
1564        Tag::NonNegativeInt64_0
1565        | Tag::NegativeInt64_0
1566        | Tag::NonNegativeInt64_8
1567        | Tag::NegativeInt64_8
1568        | Tag::NonNegativeInt64_16
1569        | Tag::NegativeInt64_16
1570        | Tag::NonNegativeInt64_24
1571        | Tag::NegativeInt64_24
1572        | Tag::NonNegativeInt64_32
1573        | Tag::NegativeInt64_32
1574        | Tag::NonNegativeInt64_40
1575        | Tag::NegativeInt64_40
1576        | Tag::NonNegativeInt64_48
1577        | Tag::NegativeInt64_48
1578        | Tag::NonNegativeInt64_56
1579        | Tag::NegativeInt64_56
1580        | Tag::Int64 => Datum::Int64(i64::from_le_bytes(read_signed_varint(
1581            data,
1582            tag,
1583            Tag::NonNegativeInt64_0,
1584        ))),
1585        Tag::UInt8_0 | Tag::UInt8 => Datum::UInt8(u8::from_le_bytes(read_unsigned_varint(
1586            data,
1587            tag,
1588            Tag::UInt8_0,
1589        ))),
1590        Tag::UInt16_0 | Tag::UInt16_8 | Tag::UInt16 => Datum::UInt16(u16::from_le_bytes(
1591            read_unsigned_varint(data, tag, Tag::UInt16_0),
1592        )),
1593        Tag::UInt32_0 | Tag::UInt32_8 | Tag::UInt32_16 | Tag::UInt32_24 | Tag::UInt32 => {
1594            Datum::UInt32(u32::from_le_bytes(read_unsigned_varint(
1595                data,
1596                tag,
1597                Tag::UInt32_0,
1598            )))
1599        }
1600        Tag::UInt64_0
1601        | Tag::UInt64_8
1602        | Tag::UInt64_16
1603        | Tag::UInt64_24
1604        | Tag::UInt64_32
1605        | Tag::UInt64_40
1606        | Tag::UInt64_48
1607        | Tag::UInt64_56
1608        | Tag::UInt64 => Datum::UInt64(u64::from_le_bytes(read_unsigned_varint(
1609            data,
1610            tag,
1611            Tag::UInt64_0,
1612        ))),
1613
1614        Tag::Float32 => {
1615            let f = f32::from_bits(u32::from_le_bytes(read_byte_array(data)));
1616            Datum::Float32(OrderedFloat::from(f))
1617        }
1618        Tag::Float64 => {
1619            let f = f64::from_bits(u64::from_le_bytes(read_byte_array(data)));
1620            Datum::Float64(OrderedFloat::from(f))
1621        }
1622        Tag::Date => Datum::Date(read_date(data)),
1623        Tag::Time => Datum::Time(read_time(data)),
1624        Tag::CheapTimestamp => {
1625            let ts = i64::from_le_bytes(read_byte_array(data));
1626            let secs = ts.div_euclid(1_000_000_000);
1627            let nsecs: u32 = ts.rem_euclid(1_000_000_000).try_into().unwrap();
1628            let ndt = DateTime::from_timestamp(secs, nsecs)
1629                .expect("We only write round-trippable timestamps")
1630                .naive_utc();
1631            Datum::Timestamp(
1632                CheckedTimestamp::from_timestamplike(ndt).expect("unexpected timestamp"),
1633            )
1634        }
1635        Tag::CheapTimestampTz => {
1636            let ts = i64::from_le_bytes(read_byte_array(data));
1637            let secs = ts.div_euclid(1_000_000_000);
1638            let nsecs: u32 = ts.rem_euclid(1_000_000_000).try_into().unwrap();
1639            let dt = DateTime::from_timestamp(secs, nsecs)
1640                .expect("We only write round-trippable timestamps");
1641            Datum::TimestampTz(
1642                CheckedTimestamp::from_timestamplike(dt).expect("unexpected timestamp"),
1643            )
1644        }
1645        Tag::Timestamp => {
1646            let date = read_naive_date(data);
1647            let time = read_time(data);
1648            Datum::Timestamp(
1649                CheckedTimestamp::from_timestamplike(date.and_time(time))
1650                    .expect("unexpected timestamp"),
1651            )
1652        }
1653        Tag::TimestampTz => {
1654            let date = read_naive_date(data);
1655            let time = read_time(data);
1656            Datum::TimestampTz(
1657                CheckedTimestamp::from_timestamplike(DateTime::from_naive_utc_and_offset(
1658                    date.and_time(time),
1659                    Utc,
1660                ))
1661                .expect("unexpected timestamptz"),
1662            )
1663        }
1664        Tag::Interval => {
1665            let months = i32::from_le_bytes(read_byte_array(data));
1666            let days = i32::from_le_bytes(read_byte_array(data));
1667            let micros = i64::from_le_bytes(read_byte_array(data));
1668            Datum::Interval(Interval {
1669                months,
1670                days,
1671                micros,
1672            })
1673        }
1674        Tag::BytesTiny | Tag::BytesShort | Tag::BytesLong | Tag::BytesHuge => {
1675            Datum::Bytes(read_lengthed_bytes(data, tag, Tag::BytesTiny))
1676        }
1677        Tag::StringTiny | Tag::StringShort | Tag::StringLong | Tag::StringHuge => {
1678            // SAFETY: the bytes were written from a `str` under a `String` tag.
1679            Datum::String(str::from_utf8_unchecked(read_lengthed_bytes(
1680                data,
1681                tag,
1682                Tag::StringTiny,
1683            )))
1684        }
1685        Tag::ListTiny | Tag::ListShort | Tag::ListLong | Tag::ListHuge => Datum::List(
1686            DatumList::new(read_lengthed_bytes(data, tag, Tag::ListTiny)),
1687        ),
1688        Tag::Uuid => Datum::Uuid(Uuid::from_bytes(read_byte_array(data))),
1689        Tag::Array => {
1690            // See the comment in `Row::push_array` for details on the encoding
1691            // of arrays.
1692            let ndims = read_byte(data);
1693            let dims_size = usize::from(ndims) * size_of::<u64>() * 2;
1694            let (dims, next) = data.split_at(dims_size);
1695            *data = next;
1696            let bytes = read_untagged_bytes(data);
1697            Datum::Array(Array {
1698                dims: ArrayDimensions { data: dims },
1699                elements: DatumList::new(bytes),
1700            })
1701        }
1702        Tag::Dict => {
1703            let bytes = read_untagged_bytes(data);
1704            Datum::Map(DatumMap::new(bytes))
1705        }
1706        Tag::JsonNull => Datum::JsonNull,
1707        Tag::Dummy => Datum::Dummy,
1708        Tag::Numeric => {
1709            let digits = read_byte(data).into();
1710            let exponent = i8::reinterpret_cast(read_byte(data));
1711            let bits = read_byte(data);
1712
1713            let lsu_u16_len = Numeric::digits_to_lsu_elements_len(digits);
1714            let lsu_u8_len = lsu_u16_len * 2;
1715            let (lsu_u8, next) = data.split_at(lsu_u8_len);
1716            *data = next;
1717
1718            // TODO: if we refactor the decimal library to accept the owned
1719            // array as a parameter to `from_raw_parts` below, we could likely
1720            // avoid a copy because it is exactly the value we want
1721            let mut lsu = [0; numeric::NUMERIC_DATUM_WIDTH_USIZE];
1722            for (i, c) in lsu_u8.chunks(2).enumerate() {
1723                lsu[i] = u16::from_le_bytes(c.try_into().unwrap());
1724            }
1725
1726            let d = Numeric::from_raw_parts(digits, exponent.into(), bits, lsu);
1727            Datum::from(d)
1728        }
1729        Tag::MzTimestamp => {
1730            let t = Timestamp::decode(read_byte_array(data));
1731            Datum::MzTimestamp(t)
1732        }
1733        Tag::Range => {
1734            // See notes on `push_range_with` for details about encoding.
1735            let flag_byte = read_byte(data);
1736            let flags = range::InternalFlags::from_bits(flag_byte)
1737                .expect("range flags must be encoded validly");
1738
1739            if flags.contains(range::InternalFlags::EMPTY) {
1740                assert!(
1741                    flags == range::InternalFlags::EMPTY,
1742                    "empty ranges contain only RANGE_EMPTY flag"
1743                );
1744
1745                return Datum::Range(Range { inner: None });
1746            }
1747
1748            let lower_bound = if flags.contains(range::InternalFlags::LB_INFINITE) {
1749                None
1750            } else {
1751                Some(DatumNested::extract(data))
1752            };
1753
1754            let lower = RangeBound {
1755                inclusive: flags.contains(range::InternalFlags::LB_INCLUSIVE),
1756                bound: lower_bound,
1757            };
1758
1759            let upper_bound = if flags.contains(range::InternalFlags::UB_INFINITE) {
1760                None
1761            } else {
1762                Some(DatumNested::extract(data))
1763            };
1764
1765            let upper = RangeBound {
1766                inclusive: flags.contains(range::InternalFlags::UB_INCLUSIVE),
1767                bound: upper_bound,
1768            };
1769
1770            Datum::Range(Range {
1771                inner: Some(RangeInner { lower, upper }),
1772            })
1773        }
1774        Tag::MzAclItem => {
1775            const N: usize = MzAclItem::binary_size();
1776            let mz_acl_item =
1777                MzAclItem::decode_binary(&read_byte_array::<N>(data)).expect("invalid mz_aclitem");
1778            Datum::MzAclItem(mz_acl_item)
1779        }
1780        Tag::AclItem => {
1781            const N: usize = AclItem::binary_size();
1782            let acl_item =
1783                AclItem::decode_binary(&read_byte_array::<N>(data)).expect("invalid aclitem");
1784            Datum::AclItem(acl_item)
1785        }
1786    }
1787}
1788
1789// --------------------------------------------------------------------------------
1790// writing data
1791
1792fn push_untagged_bytes<D>(data: &mut D, bytes: &[u8])
1793where
1794    D: Vector<u8>,
1795{
1796    let len = u64::cast_from(bytes.len());
1797    data.extend_from_slice(&len.to_le_bytes());
1798    data.extend_from_slice(bytes);
1799}
1800
1801fn push_lengthed_bytes<D>(data: &mut D, bytes: &[u8], tag: Tag)
1802where
1803    D: Vector<u8>,
1804{
1805    match tag {
1806        Tag::BytesTiny | Tag::StringTiny | Tag::ListTiny => {
1807            let len = bytes.len().to_le_bytes();
1808            data.push(len[0]);
1809        }
1810        Tag::BytesShort | Tag::StringShort | Tag::ListShort => {
1811            let len = bytes.len().to_le_bytes();
1812            data.extend_from_slice(&len[0..2]);
1813        }
1814        Tag::BytesLong | Tag::StringLong | Tag::ListLong => {
1815            let len = bytes.len().to_le_bytes();
1816            data.extend_from_slice(&len[0..4]);
1817        }
1818        Tag::BytesHuge | Tag::StringHuge | Tag::ListHuge => {
1819            let len = bytes.len().to_le_bytes();
1820            data.extend_from_slice(&len);
1821        }
1822        _ => unreachable!(),
1823    }
1824    data.extend_from_slice(bytes);
1825}
1826
1827pub(super) fn date_to_array(date: Date) -> [u8; size_of::<i32>()] {
1828    i32::to_le_bytes(date.pg_epoch_days())
1829}
1830
1831fn push_date<D>(data: &mut D, date: Date)
1832where
1833    D: Vector<u8>,
1834{
1835    data.extend_from_slice(&date_to_array(date));
1836}
1837
1838pub(super) fn naive_date_to_arrays(
1839    date: NaiveDate,
1840) -> ([u8; size_of::<i32>()], [u8; size_of::<u32>()]) {
1841    (
1842        i32::to_le_bytes(date.year()),
1843        u32::to_le_bytes(date.ordinal()),
1844    )
1845}
1846
1847fn push_naive_date<D>(data: &mut D, date: NaiveDate)
1848where
1849    D: Vector<u8>,
1850{
1851    let (ds1, ds2) = naive_date_to_arrays(date);
1852    data.extend_from_slice(&ds1);
1853    data.extend_from_slice(&ds2);
1854}
1855
1856pub(super) fn time_to_arrays(time: NaiveTime) -> ([u8; size_of::<u32>()], [u8; size_of::<u32>()]) {
1857    (
1858        u32::to_le_bytes(time.num_seconds_from_midnight()),
1859        u32::to_le_bytes(time.nanosecond()),
1860    )
1861}
1862
1863fn push_time<D>(data: &mut D, time: NaiveTime)
1864where
1865    D: Vector<u8>,
1866{
1867    let (ts1, ts2) = time_to_arrays(time);
1868    data.extend_from_slice(&ts1);
1869    data.extend_from_slice(&ts2);
1870}
1871
1872/// Returns an i64 representing a `NaiveDateTime`, if
1873/// said i64 can be round-tripped back to a `NaiveDateTime`.
1874///
1875/// The only exotic NDTs for which this can't happen are those that
1876/// are hundreds of years in the future or past, or those that
1877/// represent a leap second. (Note that Materialize does not support
1878/// leap seconds, but this module does).
1879// This function is inspired by `NaiveDateTime::timestamp_nanos`,
1880// with extra checking.
1881fn checked_timestamp_nanos(dt: NaiveDateTime) -> Option<i64> {
1882    let subsec_nanos = dt.and_utc().timestamp_subsec_nanos();
1883    if subsec_nanos >= 1_000_000_000 {
1884        return None;
1885    }
1886    let as_ns = dt.and_utc().timestamp().checked_mul(1_000_000_000)?;
1887    as_ns.checked_add(i64::from(subsec_nanos))
1888}
1889
1890// This function is extremely hot, so
1891// we just use `as` to avoid the overhead of
1892// `try_into` followed by `unwrap`.
1893// `leading_ones` and `leading_zeros`
1894// can never return values greater than 64, so the conversion is safe.
1895#[inline(always)]
1896#[allow(clippy::as_conversions)]
1897fn min_bytes_signed<T>(i: T) -> u8
1898where
1899    T: Into<i64>,
1900{
1901    let i: i64 = i.into();
1902
1903    // To fit in n bytes, we require that
1904    // everything but the leading sign bits fits in n*8
1905    // bits.
1906    let n_sign_bits = if i.is_negative() {
1907        i.leading_ones() as u8
1908    } else {
1909        i.leading_zeros() as u8
1910    };
1911
1912    (64 - n_sign_bits + 7) / 8
1913}
1914
1915// In principle we could just use `min_bytes_signed`, rather than
1916// having a separate function here, as long as we made that one take
1917// `T: Into<i128>` instead of 64. But LLVM doesn't seem smart enough
1918// to realize that that function is the same as the current version,
1919// and generates worse code.
1920//
1921// Justification for `as` is the same as in `min_bytes_signed`.
1922#[inline(always)]
1923#[allow(clippy::as_conversions)]
1924fn min_bytes_unsigned<T>(i: T) -> u8
1925where
1926    T: Into<u64>,
1927{
1928    let i: u64 = i.into();
1929
1930    let n_sign_bits = i.leading_zeros() as u8;
1931
1932    (64 - n_sign_bits + 7) / 8
1933}
1934
1935const TINY: usize = 1 << 8;
1936const SHORT: usize = 1 << 16;
1937const LONG: usize = 1 << 32;
1938
1939fn push_datum<D>(data: &mut D, datum: Datum)
1940where
1941    D: Vector<u8>,
1942{
1943    match datum {
1944        Datum::Null => data.push(Tag::Null.into()),
1945        Datum::False => data.push(Tag::False.into()),
1946        Datum::True => data.push(Tag::True.into()),
1947        Datum::Int16(i) => {
1948            // The family alternates the signs at each width, so the width is two tags apart and
1949            // the sign is the low bit. The clamp folds both signs onto the widest tag, where the
1950            // payload carries its own sign. See `read_signed_varint`, which takes this apart.
1951            const WIDEST_DELTA: u8 = Tag::Int16.byte() - Tag::NonNegativeInt16_0.byte();
1952            let mbs = min_bytes_signed(i);
1953            let delta = ((mbs << 1) + u8::from(i.is_negative())).min(WIDEST_DELTA);
1954            let tag = u8::from(Tag::NonNegativeInt16_0) + delta;
1955
1956            data.push(tag);
1957            data.extend_from_slice(&i.to_le_bytes()[0..usize::from(mbs)]);
1958        }
1959        Datum::Int32(i) => {
1960            // The family alternates the signs at each width, so the width is two tags apart and
1961            // the sign is the low bit. The clamp folds both signs onto the widest tag, where the
1962            // payload carries its own sign. See `read_signed_varint`, which takes this apart.
1963            const WIDEST_DELTA: u8 = Tag::Int32.byte() - Tag::NonNegativeInt32_0.byte();
1964            let mbs = min_bytes_signed(i);
1965            let delta = ((mbs << 1) + u8::from(i.is_negative())).min(WIDEST_DELTA);
1966            let tag = u8::from(Tag::NonNegativeInt32_0) + delta;
1967
1968            data.push(tag);
1969            data.extend_from_slice(&i.to_le_bytes()[0..usize::from(mbs)]);
1970        }
1971        Datum::Int64(i) => {
1972            // The family alternates the signs at each width, so the width is two tags apart and
1973            // the sign is the low bit. The clamp folds both signs onto the widest tag, where the
1974            // payload carries its own sign. See `read_signed_varint`, which takes this apart.
1975            const WIDEST_DELTA: u8 = Tag::Int64.byte() - Tag::NonNegativeInt64_0.byte();
1976            let mbs = min_bytes_signed(i);
1977            let delta = ((mbs << 1) + u8::from(i.is_negative())).min(WIDEST_DELTA);
1978            let tag = u8::from(Tag::NonNegativeInt64_0) + delta;
1979
1980            data.push(tag);
1981            data.extend_from_slice(&i.to_le_bytes()[0..usize::from(mbs)]);
1982        }
1983        Datum::UInt8(i) => {
1984            let mbu = min_bytes_unsigned(i);
1985            let tag = u8::from(Tag::UInt8_0) + mbu;
1986            data.push(tag);
1987            data.extend_from_slice(&i.to_le_bytes()[0..usize::from(mbu)]);
1988        }
1989        Datum::UInt16(i) => {
1990            let mbu = min_bytes_unsigned(i);
1991            let tag = u8::from(Tag::UInt16_0) + mbu;
1992            data.push(tag);
1993            data.extend_from_slice(&i.to_le_bytes()[0..usize::from(mbu)]);
1994        }
1995        Datum::UInt32(i) => {
1996            let mbu = min_bytes_unsigned(i);
1997            let tag = u8::from(Tag::UInt32_0) + mbu;
1998            data.push(tag);
1999            data.extend_from_slice(&i.to_le_bytes()[0..usize::from(mbu)]);
2000        }
2001        Datum::UInt64(i) => {
2002            let mbu = min_bytes_unsigned(i);
2003            let tag = u8::from(Tag::UInt64_0) + mbu;
2004            data.push(tag);
2005            data.extend_from_slice(&i.to_le_bytes()[0..usize::from(mbu)]);
2006        }
2007        Datum::Float32(f) => {
2008            data.push(Tag::Float32.into());
2009            data.extend_from_slice(&f.to_bits().to_le_bytes());
2010        }
2011        Datum::Float64(f) => {
2012            data.push(Tag::Float64.into());
2013            data.extend_from_slice(&f.to_bits().to_le_bytes());
2014        }
2015        Datum::Date(d) => {
2016            data.push(Tag::Date.into());
2017            push_date(data, d);
2018        }
2019        Datum::Time(t) => {
2020            data.push(Tag::Time.into());
2021            push_time(data, t);
2022        }
2023        Datum::Timestamp(t) => {
2024            let datetime = t.to_naive();
2025            if let Some(nanos) = checked_timestamp_nanos(datetime) {
2026                data.push(Tag::CheapTimestamp.into());
2027                data.extend_from_slice(&nanos.to_le_bytes());
2028            } else {
2029                data.push(Tag::Timestamp.into());
2030                push_naive_date(data, datetime.date());
2031                push_time(data, datetime.time());
2032            }
2033        }
2034        Datum::TimestampTz(t) => {
2035            let datetime = t.to_naive();
2036            if let Some(nanos) = checked_timestamp_nanos(datetime) {
2037                data.push(Tag::CheapTimestampTz.into());
2038                data.extend_from_slice(&nanos.to_le_bytes());
2039            } else {
2040                data.push(Tag::TimestampTz.into());
2041                push_naive_date(data, datetime.date());
2042                push_time(data, datetime.time());
2043            }
2044        }
2045        Datum::Interval(i) => {
2046            data.push(Tag::Interval.into());
2047            data.extend_from_slice(&i.months.to_le_bytes());
2048            data.extend_from_slice(&i.days.to_le_bytes());
2049            data.extend_from_slice(&i.micros.to_le_bytes());
2050        }
2051        Datum::Bytes(bytes) => {
2052            let tag = match bytes.len() {
2053                0..TINY => Tag::BytesTiny,
2054                TINY..SHORT => Tag::BytesShort,
2055                SHORT..LONG => Tag::BytesLong,
2056                _ => Tag::BytesHuge,
2057            };
2058            data.push(tag.into());
2059            push_lengthed_bytes(data, bytes, tag);
2060        }
2061        Datum::String(string) => {
2062            let tag = match string.len() {
2063                0..TINY => Tag::StringTiny,
2064                TINY..SHORT => Tag::StringShort,
2065                SHORT..LONG => Tag::StringLong,
2066                _ => Tag::StringHuge,
2067            };
2068            data.push(tag.into());
2069            push_lengthed_bytes(data, string.as_bytes(), tag);
2070        }
2071        Datum::List(list) => {
2072            let tag = match list.data.len() {
2073                0..TINY => Tag::ListTiny,
2074                TINY..SHORT => Tag::ListShort,
2075                SHORT..LONG => Tag::ListLong,
2076                _ => Tag::ListHuge,
2077            };
2078            data.push(tag.into());
2079            push_lengthed_bytes(data, list.data, tag);
2080        }
2081        Datum::Uuid(u) => {
2082            data.push(Tag::Uuid.into());
2083            data.extend_from_slice(u.as_bytes());
2084        }
2085        Datum::Array(array) => {
2086            // See the comment in `Row::push_array` for details on the encoding
2087            // of arrays.
2088            data.push(Tag::Array.into());
2089            data.push(array.dims.ndims());
2090            data.extend_from_slice(array.dims.data);
2091            push_untagged_bytes(data, array.elements.data);
2092        }
2093        Datum::Map(dict) => {
2094            data.push(Tag::Dict.into());
2095            push_untagged_bytes(data, dict.data);
2096        }
2097        Datum::JsonNull => data.push(Tag::JsonNull.into()),
2098        Datum::MzTimestamp(t) => {
2099            data.push(Tag::MzTimestamp.into());
2100            data.extend_from_slice(&t.encode());
2101        }
2102        Datum::Dummy => data.push(Tag::Dummy.into()),
2103        Datum::Numeric(mut n) => {
2104            // Pseudo-canonical representation of decimal values with
2105            // insignificant zeroes trimmed. This compresses the number further
2106            // than `Numeric::trim` by removing all zeroes, and not only those in
2107            // the fractional component.
2108            numeric::cx_datum().reduce(&mut n.0);
2109            let (digits, exponent, bits, lsu) = n.0.to_raw_parts();
2110            data.push(Tag::Numeric.into());
2111            data.push(u8::try_from(digits).expect("digits to fit within u8; should not exceed 39"));
2112            data.push(
2113                i8::try_from(exponent)
2114                    .expect("exponent to fit within i8; should not exceed +/- 39")
2115                    .to_le_bytes()[0],
2116            );
2117            data.push(bits);
2118
2119            let lsu = &lsu[..Numeric::digits_to_lsu_elements_len(digits)];
2120
2121            // Little endian machines can take the lsu directly from u16 to u8.
2122            if cfg!(target_endian = "little") {
2123                // SAFETY: `lsu` (returned by `coefficient_units()`) is a `&[u16]`, so
2124                // each element can safely be transmuted into two `u8`s.
2125                let (prefix, lsu_bytes, suffix) = unsafe { lsu.align_to::<u8>() };
2126                // The `u8` aligned version of the `lsu` should have twice as many
2127                // elements as we expect for the `u16` version.
2128                soft_assert_no_log!(
2129                    lsu_bytes.len() == Numeric::digits_to_lsu_elements_len(digits) * 2,
2130                    "u8 version of numeric LSU contained the wrong number of elements; expected {}, but got {}",
2131                    Numeric::digits_to_lsu_elements_len(digits) * 2,
2132                    lsu_bytes.len()
2133                );
2134                // There should be no unaligned elements in the prefix or suffix.
2135                soft_assert_no_log!(prefix.is_empty() && suffix.is_empty());
2136                data.extend_from_slice(lsu_bytes);
2137            } else {
2138                for u in lsu {
2139                    data.extend_from_slice(&u.to_le_bytes());
2140                }
2141            }
2142        }
2143        Datum::Range(range) => {
2144            // See notes on `push_range_with` for details about encoding.
2145            data.push(Tag::Range.into());
2146            data.push(range.internal_flag_bits());
2147
2148            if let Some(RangeInner { lower, upper }) = range.inner {
2149                for bound in [lower.bound, upper.bound] {
2150                    if let Some(bound) = bound {
2151                        match bound.datum() {
2152                            Datum::Null => panic!("cannot push Datum::Null into range"),
2153                            d => push_datum::<D>(data, d),
2154                        }
2155                    }
2156                }
2157            }
2158        }
2159        Datum::MzAclItem(mz_acl_item) => {
2160            data.push(Tag::MzAclItem.into());
2161            data.extend_from_slice(&mz_acl_item.encode_binary());
2162        }
2163        Datum::AclItem(acl_item) => {
2164            data.push(Tag::AclItem.into());
2165            data.extend_from_slice(&acl_item.encode_binary());
2166        }
2167    }
2168}
2169
2170/// Return the number of bytes these Datums would use if packed as a Row.
2171pub fn row_size<'a, I>(a: I) -> usize
2172where
2173    I: IntoIterator<Item = Datum<'a>>,
2174{
2175    // Using datums_size instead of a.data().len() here is safer because it will
2176    // return the size of the datums if they were packed into a Row. Although
2177    // a.data().len() happens to give the correct answer (and is faster), data()
2178    // is documented as for debugging only.
2179    let sz = datums_size::<_, _>(a);
2180    let size_of_row = std::mem::size_of::<Row>();
2181    // The Row struct attempts to inline data until it can't fit in the
2182    // preallocated size. Otherwise it spills to heap, and uses the Row to point
2183    // to that.
2184    if sz > Row::SIZE {
2185        sz + size_of_row
2186    } else {
2187        size_of_row
2188    }
2189}
2190
2191/// Number of bytes required by the datum.
2192/// This is used to optimistically pre-allocate buffers for packing rows.
2193pub fn datum_size(datum: &Datum) -> usize {
2194    match datum {
2195        Datum::Null => 1,
2196        Datum::False => 1,
2197        Datum::True => 1,
2198        Datum::Int16(i) => 1 + usize::from(min_bytes_signed(*i)),
2199        Datum::Int32(i) => 1 + usize::from(min_bytes_signed(*i)),
2200        Datum::Int64(i) => 1 + usize::from(min_bytes_signed(*i)),
2201        Datum::UInt8(i) => 1 + usize::from(min_bytes_unsigned(*i)),
2202        Datum::UInt16(i) => 1 + usize::from(min_bytes_unsigned(*i)),
2203        Datum::UInt32(i) => 1 + usize::from(min_bytes_unsigned(*i)),
2204        Datum::UInt64(i) => 1 + usize::from(min_bytes_unsigned(*i)),
2205        Datum::Float32(_) => 1 + size_of::<f32>(),
2206        Datum::Float64(_) => 1 + size_of::<f64>(),
2207        Datum::Date(_) => 1 + size_of::<i32>(),
2208        Datum::Time(_) => 1 + 8,
2209        Datum::Timestamp(t) => {
2210            1 + if checked_timestamp_nanos(t.to_naive()).is_some() {
2211                8
2212            } else {
2213                16
2214            }
2215        }
2216        Datum::TimestampTz(t) => {
2217            1 + if checked_timestamp_nanos(t.naive_utc()).is_some() {
2218                8
2219            } else {
2220                16
2221            }
2222        }
2223        Datum::Interval(_) => 1 + size_of::<i32>() + size_of::<i32>() + size_of::<i64>(),
2224        Datum::Bytes(bytes) => {
2225            // We use a variable length representation of slice length.
2226            let bytes_for_length = match bytes.len() {
2227                0..TINY => 1,
2228                TINY..SHORT => 2,
2229                SHORT..LONG => 4,
2230                _ => 8,
2231            };
2232            1 + bytes_for_length + bytes.len()
2233        }
2234        Datum::String(string) => {
2235            // We use a variable length representation of slice length.
2236            let bytes_for_length = match string.len() {
2237                0..TINY => 1,
2238                TINY..SHORT => 2,
2239                SHORT..LONG => 4,
2240                _ => 8,
2241            };
2242            1 + bytes_for_length + string.len()
2243        }
2244        Datum::Uuid(_) => 1 + size_of::<uuid::Bytes>(),
2245        Datum::Array(array) => {
2246            1 + size_of::<u8>()
2247                + array.dims.data.len()
2248                + size_of::<u64>()
2249                + array.elements.data.len()
2250        }
2251        Datum::List(list) => 1 + size_of::<u64>() + list.data.len(),
2252        Datum::Map(dict) => 1 + size_of::<u64>() + dict.data.len(),
2253        Datum::JsonNull => 1,
2254        Datum::MzTimestamp(_) => 1 + size_of::<Timestamp>(),
2255        Datum::Dummy => 1,
2256        Datum::Numeric(d) => {
2257            let mut d = d.0.clone();
2258            // Values must be reduced to determine appropriate number of
2259            // coefficient units.
2260            numeric::cx_datum().reduce(&mut d);
2261            // 4 = 1 bit each for tag, digits, exponent, bits
2262            4 + (d.coefficient_units().len() * 2)
2263        }
2264        Datum::Range(Range { inner }) => {
2265            // Tag + flags
2266            2 + match inner {
2267                None => 0,
2268                Some(RangeInner { lower, upper }) => [lower.bound, upper.bound]
2269                    .iter()
2270                    .map(|bound| match bound {
2271                        None => 0,
2272                        Some(bound) => bound.val.len(),
2273                    })
2274                    .sum(),
2275            }
2276        }
2277        Datum::MzAclItem(_) => 1 + MzAclItem::binary_size(),
2278        Datum::AclItem(_) => 1 + AclItem::binary_size(),
2279    }
2280}
2281
2282/// Number of bytes required by a sequence of datums.
2283///
2284/// This method can be used to right-size the allocation for a `Row`
2285/// before calling [`RowPacker::extend`].
2286pub fn datums_size<'a, I, D>(iter: I) -> usize
2287where
2288    I: IntoIterator<Item = D>,
2289    D: Borrow<Datum<'a>>,
2290{
2291    iter.into_iter().map(|d| datum_size(d.borrow())).sum()
2292}
2293
2294/// Number of bytes required by a list of datums. This computes the size that would be required if
2295/// the given datums were packed into a list.
2296///
2297/// This is used to optimistically pre-allocate buffers for packing rows.
2298pub fn datum_list_size<'a, I, D>(iter: I) -> usize
2299where
2300    I: IntoIterator<Item = D>,
2301    D: Borrow<Datum<'a>>,
2302{
2303    1 + size_of::<u64>() + datums_size(iter)
2304}
2305
2306impl RowPacker<'_> {
2307    /// Constructs a row packer that will pack additional datums into the
2308    /// provided row.
2309    ///
2310    /// This function is intentionally somewhat inconvenient to call. You
2311    /// usually want to call [`Row::packer`] instead to start packing from
2312    /// scratch.
2313    pub fn for_existing_row(row: &mut Row) -> RowPacker<'_> {
2314        RowPacker { row }
2315    }
2316
2317    /// Extend an existing `Row` with a `Datum`.
2318    #[inline]
2319    pub fn push<'a, D>(&mut self, datum: D)
2320    where
2321        D: Borrow<Datum<'a>>,
2322    {
2323        push_datum(&mut self.row.data, *datum.borrow());
2324    }
2325
2326    /// Extend an existing `Row` with additional `Datum`s.
2327    #[inline]
2328    pub fn extend<'a, I, D>(&mut self, iter: I)
2329    where
2330        I: IntoIterator<Item = D>,
2331        D: Borrow<Datum<'a>>,
2332    {
2333        for datum in iter {
2334            push_datum(&mut self.row.data, *datum.borrow())
2335        }
2336    }
2337
2338    /// Extend an existing `Row` with additional `Datum`s.
2339    ///
2340    /// In the case the iterator produces an error, the pushing of
2341    /// datums in terminated and the error returned. The `Row` will
2342    /// be incomplete, but it will be safe to read datums from it.
2343    #[inline]
2344    pub fn try_extend<'a, I, E, D>(&mut self, iter: I) -> Result<(), E>
2345    where
2346        I: IntoIterator<Item = Result<D, E>>,
2347        D: Borrow<Datum<'a>>,
2348    {
2349        for datum in iter {
2350            push_datum(&mut self.row.data, *datum?.borrow());
2351        }
2352        Ok(())
2353    }
2354
2355    /// Appends the datums of an entire `Row`.
2356    pub fn extend_by_row(&mut self, row: &Row) {
2357        self.row.data.extend_from_slice(row.data.as_slice());
2358    }
2359
2360    /// Appends the datums of an entire `Row`.
2361    pub fn extend_by_row_ref(&mut self, row: &RowRef) {
2362        self.row.data.extend_from_slice(row.data());
2363    }
2364
2365    /// Appends the slice of data representing an entire `Row`. The data is not validated.
2366    ///
2367    /// # Safety
2368    ///
2369    /// The requirements from [`Row::from_bytes_unchecked`] apply here, too:
2370    /// This method relies on `data` being an appropriate row encoding, and can
2371    /// result in unsafety if this is not the case.
2372    #[inline]
2373    pub unsafe fn extend_by_slice_unchecked(&mut self, data: &[u8]) {
2374        self.row.data.extend_from_slice(data)
2375    }
2376
2377    /// Pushes a [`DatumList`] that is built from a closure.
2378    ///
2379    /// The supplied closure will be invoked once with a `Row` that can be used
2380    /// to populate the list. It is valid to call any method on the
2381    /// [`RowPacker`] except for [`RowPacker::clear`], [`RowPacker::truncate`],
2382    /// or [`RowPacker::truncate_datums`].
2383    ///
2384    /// Returns the value returned by the closure, if any.
2385    ///
2386    /// ```
2387    /// # use mz_repr::{Row, Datum};
2388    /// let mut row = Row::default();
2389    /// row.packer().push_list_with(|row| {
2390    ///     row.push(Datum::String("age"));
2391    ///     row.push(Datum::Int64(42));
2392    /// });
2393    /// assert_eq!(
2394    ///     row.unpack_first().unwrap_list().iter().collect::<Vec<_>>(),
2395    ///     vec![Datum::String("age"), Datum::Int64(42)],
2396    /// );
2397    /// ```
2398    #[inline]
2399    pub fn push_list_with<F, R>(&mut self, f: F) -> R
2400    where
2401        F: FnOnce(&mut RowPacker) -> R,
2402    {
2403        // First, assume that the list will fit in 255 bytes, and thus the length will fit in
2404        // 1 byte. If not, we'll fix it up later.
2405        let start = self.row.data.len();
2406        self.row.data.push(Tag::ListTiny.into());
2407        // Write a dummy len, will fix it up later.
2408        self.row.data.push(0);
2409
2410        let out = f(self);
2411
2412        // The `- 1 - 1` is for the tag and the len.
2413        let len = self.row.data.len() - start - 1 - 1;
2414        // We now know the real len.
2415        if len < TINY {
2416            // If the len fits in 1 byte, we just need to fix up the len.
2417            self.row.data[start + 1] = len.to_le_bytes()[0];
2418        } else {
2419            // Note: We move this code path into its own function, so that the common case can be
2420            // inlined.
2421            long_list(&mut self.row.data, start, len);
2422        }
2423
2424        /// 1. Fix up the tag.
2425        /// 2. Move the actual data a bit (for which we also need to make room at the end).
2426        /// 3. Fix up the len.
2427        /// `data`: The row's backing data.
2428        /// `start`: where `push_list_with` started writing in `data`.
2429        /// `len`: the length of the data, excluding the tag and the length.
2430        #[cold]
2431        fn long_list(data: &mut CompactBytes, start: usize, len: usize) {
2432            // `len_len`: the length of the length. (Possible values are: 2, 4, 8. 1 is handled
2433            // elsewhere.) The other parameters are the same as for `long_list`.
2434            let long_list_inner = |data: &mut CompactBytes, len_len| {
2435                // We'll need memory for the new, bigger length, so make the `CompactBytes` bigger.
2436                // The `- 1` is because the old length was 1 byte.
2437                const ZEROS: [u8; 8] = [0; 8];
2438                data.extend_from_slice(&ZEROS[0..len_len - 1]);
2439                // Move the data to the end of the `CompactBytes`, to make space for the new length.
2440                // Originally, it started after the 1-byte tag and the 1-byte length, now it will
2441                // start after the 1-byte tag and the len_len-byte length.
2442                //
2443                // Note that this is the only operation in `long_list` whose cost is proportional
2444                // to `len`. Since `len` is at least 256 here, the other operations' cost are
2445                // negligible. `copy_within` is a memmove, which is probably a fair bit faster per
2446                // Datum than a Datum encoding in the `f` closure.
2447                data.copy_within(start + 1 + 1..start + 1 + 1 + len, start + 1 + len_len);
2448                // Write the new length.
2449                data[start + 1..start + 1 + len_len]
2450                    .copy_from_slice(&len.to_le_bytes()[0..len_len]);
2451            };
2452            match len {
2453                0..TINY => {
2454                    unreachable!()
2455                }
2456                TINY..SHORT => {
2457                    data[start] = Tag::ListShort.into();
2458                    long_list_inner(data, 2);
2459                }
2460                SHORT..LONG => {
2461                    data[start] = Tag::ListLong.into();
2462                    long_list_inner(data, 4);
2463                }
2464                _ => {
2465                    data[start] = Tag::ListHuge.into();
2466                    long_list_inner(data, 8);
2467                }
2468            };
2469        }
2470
2471        out
2472    }
2473
2474    /// Pushes a [`DatumMap`] that is built from a closure.
2475    ///
2476    /// The supplied closure will be invoked once with a `Row` that can be used
2477    /// to populate the dict.
2478    ///
2479    /// The closure **must** alternate pushing string keys and arbitrary values,
2480    /// otherwise reading the dict will cause a panic.
2481    ///
2482    /// The closure **must** push keys in ascending order, otherwise equality
2483    /// checks on the resulting `Row` may be wrong and reading the dict IN DEBUG
2484    /// MODE will cause a panic.
2485    ///
2486    /// The closure **must not** call [`RowPacker::clear`],
2487    /// [`RowPacker::truncate`], or [`RowPacker::truncate_datums`].
2488    ///
2489    /// # Example
2490    ///
2491    /// ```
2492    /// # use mz_repr::{Row, Datum};
2493    /// let mut row = Row::default();
2494    /// row.packer().push_dict_with(|row| {
2495    ///
2496    ///     // key
2497    ///     row.push(Datum::String("age"));
2498    ///     // value
2499    ///     row.push(Datum::Int64(42));
2500    ///
2501    ///     // key
2502    ///     row.push(Datum::String("name"));
2503    ///     // value
2504    ///     row.push(Datum::String("bob"));
2505    /// });
2506    /// assert_eq!(
2507    ///     row.unpack_first().unwrap_map().iter().collect::<Vec<_>>(),
2508    ///     vec![("age", Datum::Int64(42)), ("name", Datum::String("bob"))]
2509    /// );
2510    /// ```
2511    pub fn push_dict_with<F, R>(&mut self, f: F) -> R
2512    where
2513        F: FnOnce(&mut RowPacker) -> R,
2514    {
2515        self.row.data.push(Tag::Dict.into());
2516        let start = self.row.data.len();
2517        // write a dummy len, will fix it up later
2518        self.row.data.extend_from_slice(&[0; size_of::<u64>()]);
2519
2520        let res = f(self);
2521
2522        let len = u64::cast_from(self.row.data.len() - start - size_of::<u64>());
2523        // fix up the len
2524        self.row.data[start..start + size_of::<u64>()].copy_from_slice(&len.to_le_bytes());
2525
2526        res
2527    }
2528
2529    /// Like [`RowPacker::push_dict_with`], but accepts a fallible closure.
2530    pub fn try_push_dict_with<F, E>(&mut self, f: F) -> Result<(), E>
2531    where
2532        F: FnOnce(&mut RowPacker) -> Result<(), E>,
2533    {
2534        self.push_dict_with(f)
2535    }
2536
2537    /// Convenience function to construct an array from an iter of `Datum`s.
2538    ///
2539    /// Returns an error if the number of elements in `iter` does not match
2540    /// the cardinality of the array as described by `dims`, or if the
2541    /// number of dimensions exceeds [`MAX_ARRAY_DIMENSIONS`]. If an error
2542    /// occurs, the packer's state will be unchanged.
2543    pub fn try_push_array<'a, I, D>(
2544        &mut self,
2545        dims: &[ArrayDimension],
2546        iter: I,
2547    ) -> Result<(), InvalidArrayError>
2548    where
2549        I: IntoIterator<Item = D>,
2550        D: Borrow<Datum<'a>>,
2551    {
2552        // SAFETY: The function returns the exact number of elements pushed into the array.
2553        unsafe {
2554            self.push_array_with_unchecked(dims, |packer| {
2555                let mut nelements = 0;
2556                for datum in iter {
2557                    packer.push(datum);
2558                    nelements += 1;
2559                }
2560                Ok::<_, InvalidArrayError>(nelements)
2561            })
2562        }
2563    }
2564
2565    /// Like [`RowPacker::try_push_array`], but accepts a fallible iterator of
2566    /// elements.
2567    pub fn try_push_array_fallible<'a, I, D, E>(
2568        &mut self,
2569        dims: &[ArrayDimension],
2570        iter: I,
2571    ) -> Result<Result<(), E>, InvalidArrayError>
2572    where
2573        I: IntoIterator<Item = Result<D, E>>,
2574        D: Borrow<Datum<'a>>,
2575    {
2576        enum Error<E> {
2577            Usage(InvalidArrayError),
2578            Inner(E),
2579        }
2580
2581        impl<E> From<InvalidArrayError> for Error<E> {
2582            fn from(e: InvalidArrayError) -> Self {
2583                Self::Usage(e)
2584            }
2585        }
2586
2587        // SAFETY: The function returns the exact number of elements pushed into the array.
2588        let result = unsafe {
2589            self.push_array_with_unchecked(dims, |packer| {
2590                let mut nelements = 0;
2591                for datum in iter {
2592                    packer.push(datum.map_err(Error::Inner)?);
2593                    nelements += 1;
2594                }
2595                Ok(nelements)
2596            })
2597        };
2598        match result {
2599            Ok(()) => Ok(Ok(())),
2600            Err(Error::Usage(e)) => Err(e),
2601            Err(Error::Inner(e)) => Ok(Err(e)),
2602        }
2603    }
2604
2605    /// Convenience function to construct an array from a function. The function must return the
2606    /// number of elements it pushed into the array. It is undefined behavior if the function returns
2607    /// a number different to the number of elements it pushed.
2608    ///
2609    /// Returns an error if the number of elements pushed by `f` does not match
2610    /// the cardinality of the array as described by `dims`, or if the
2611    /// number of dimensions exceeds [`MAX_ARRAY_DIMENSIONS`], or if `f` errors. If an error
2612    /// occurs, the packer's state will be unchanged.
2613    pub unsafe fn push_array_with_unchecked<F, E>(
2614        &mut self,
2615        dims: &[ArrayDimension],
2616        f: F,
2617    ) -> Result<(), E>
2618    where
2619        F: FnOnce(&mut RowPacker) -> Result<usize, E>,
2620        E: From<InvalidArrayError>,
2621    {
2622        // Arrays are encoded as follows.
2623        //
2624        // u8    ndims
2625        // u64   dim_0 lower bound
2626        // u64   dim_0 length
2627        // ...
2628        // u64   dim_n lower bound
2629        // u64   dim_n length
2630        // u64   element data size in bytes
2631        // u8    element data, where elements are encoded in row-major order
2632
2633        if dims.len() > usize::from(MAX_ARRAY_DIMENSIONS) {
2634            return Err(InvalidArrayError::TooManyDimensions(dims.len()).into());
2635        }
2636
2637        let start = self.row.data.len();
2638        self.row.data.push(Tag::Array.into());
2639
2640        // Write dimension information.
2641        self.row
2642            .data
2643            .push(dims.len().try_into().expect("ndims verified to fit in u8"));
2644        for dim in dims {
2645            self.row
2646                .data
2647                .extend_from_slice(&i64::cast_from(dim.lower_bound).to_le_bytes());
2648            self.row
2649                .data
2650                .extend_from_slice(&u64::cast_from(dim.length).to_le_bytes());
2651        }
2652
2653        // Write elements.
2654        let off = self.row.data.len();
2655        self.row.data.extend_from_slice(&[0; size_of::<u64>()]);
2656        let nelements = match f(self) {
2657            Ok(nelements) => nelements,
2658            Err(e) => {
2659                self.row.data.truncate(start);
2660                return Err(e);
2661            }
2662        };
2663        let len = u64::cast_from(self.row.data.len() - off - size_of::<u64>());
2664        self.row.data[off..off + size_of::<u64>()].copy_from_slice(&len.to_le_bytes());
2665
2666        // Check that the number of elements written matches the dimension
2667        // information.
2668        let cardinality = match dims {
2669            [] => 0,
2670            // Saturate the product: a cardinality that overflows `usize` is
2671            // impossibly large (no array can hold that many elements), so it can
2672            // never equal the actual `nelements` and the check below rejects it as
2673            // `WrongCardinality`. A plain `product()` would panic under overflow
2674            // checks (debug/fuzz) and silently wrap in release — and a wrapped
2675            // value could even spuriously match `nelements`, accepting a corrupt
2676            // array (e.g. dims claiming `[2^32, 2^32]` wrap to 0 elements).
2677            dims => dims
2678                .iter()
2679                .map(|d| d.length)
2680                .fold(1usize, usize::saturating_mul),
2681        };
2682        if nelements != cardinality {
2683            self.row.data.truncate(start);
2684            return Err(InvalidArrayError::WrongCardinality {
2685                actual: nelements,
2686                expected: cardinality,
2687            }
2688            .into());
2689        }
2690
2691        Ok(())
2692    }
2693
2694    /// Pushes an [`Array`] that is built from a closure.
2695    ///
2696    /// __WARNING__: This is fairly "sharp" tool that is easy to get wrong. You
2697    /// should prefer [`RowPacker::try_push_array`] when possible.
2698    ///
2699    /// Returns an error if the number of elements pushed does not match
2700    /// the cardinality of the array as described by `dims`, or if the
2701    /// number of dimensions exceeds [`MAX_ARRAY_DIMENSIONS`]. If an error
2702    /// occurs, the packer's state will be unchanged.
2703    pub fn push_array_with_row_major<F, I>(
2704        &mut self,
2705        dims: I,
2706        f: F,
2707    ) -> Result<(), InvalidArrayError>
2708    where
2709        I: IntoIterator<Item = ArrayDimension>,
2710        F: FnOnce(&mut RowPacker) -> usize,
2711    {
2712        let start = self.row.data.len();
2713        self.row.data.push(Tag::Array.into());
2714
2715        // Write dummy dimension length for now, we'll fix it up.
2716        let dims_start = self.row.data.len();
2717        self.row.data.push(42);
2718
2719        let mut num_dims: u8 = 0;
2720        let mut cardinality: usize = 1;
2721        for dim in dims {
2722            num_dims += 1;
2723            // Saturate: an overflowing cardinality is impossibly large and is
2724            // rejected by the `nelements` check below. See the matching note in
2725            // `push_array_with_unchecked`.
2726            cardinality = cardinality.saturating_mul(dim.length);
2727
2728            self.row
2729                .data
2730                .extend_from_slice(&i64::cast_from(dim.lower_bound).to_le_bytes());
2731            self.row
2732                .data
2733                .extend_from_slice(&u64::cast_from(dim.length).to_le_bytes());
2734        }
2735
2736        if num_dims > MAX_ARRAY_DIMENSIONS {
2737            // Reset the packer state so we don't have invalid data.
2738            self.row.data.truncate(start);
2739            return Err(InvalidArrayError::TooManyDimensions(usize::from(num_dims)));
2740        }
2741        // Fix up our dimension length.
2742        self.row.data[dims_start..dims_start + size_of::<u8>()]
2743            .copy_from_slice(&num_dims.to_le_bytes());
2744
2745        // Write elements.
2746        let off = self.row.data.len();
2747        self.row.data.extend_from_slice(&[0; size_of::<u64>()]);
2748
2749        let nelements = f(self);
2750
2751        let len = u64::cast_from(self.row.data.len() - off - size_of::<u64>());
2752        self.row.data[off..off + size_of::<u64>()].copy_from_slice(&len.to_le_bytes());
2753
2754        // Check that the number of elements written matches the dimension
2755        // information.
2756        let cardinality = match num_dims {
2757            0 => 0,
2758            _ => cardinality,
2759        };
2760        if nelements != cardinality {
2761            self.row.data.truncate(start);
2762            return Err(InvalidArrayError::WrongCardinality {
2763                actual: nelements,
2764                expected: cardinality,
2765            });
2766        }
2767
2768        Ok(())
2769    }
2770
2771    /// Convenience function to push a `DatumList` from an iter of `Datum`s
2772    ///
2773    /// See [`RowPacker::push_dict_with`] if you need to be able to handle errors
2774    pub fn push_list<'a, I, D>(&mut self, iter: I)
2775    where
2776        I: IntoIterator<Item = D>,
2777        D: Borrow<Datum<'a>>,
2778    {
2779        self.push_list_with(|packer| {
2780            for elem in iter {
2781                packer.push(*elem.borrow())
2782            }
2783        });
2784    }
2785
2786    /// Convenience function to push a `DatumMap` from an iter of `(&str, Datum)` pairs
2787    pub fn push_dict<'a, I, D>(&mut self, iter: I)
2788    where
2789        I: IntoIterator<Item = (&'a str, D)>,
2790        D: Borrow<Datum<'a>>,
2791    {
2792        self.push_dict_with(|packer| {
2793            for (k, v) in iter {
2794                packer.push(Datum::String(k));
2795                packer.push(*v.borrow())
2796            }
2797        })
2798    }
2799
2800    /// Pushes a `Datum::Range` derived from the `Range<Datum<'a>`.
2801    ///
2802    /// # Panics
2803    /// - If lower and upper express finite values and they are datums of
2804    ///   different types.
2805    /// - If lower or upper express finite values and are equal to
2806    ///   `Datum::Null`. To handle `Datum::Null` properly, use
2807    ///   [`RangeBound::new`].
2808    ///
2809    /// # Notes
2810    /// - This function canonicalizes the range before pushing it to the row.
2811    /// - Prefer this function over `push_range_with` because of its
2812    ///   canonicaliztion.
2813    /// - Prefer creating [`RangeBound`]s using [`RangeBound::new`], which
2814    ///   handles `Datum::Null` in a SQL-friendly way.
2815    pub fn push_range<'a>(&mut self, mut range: Range<Datum<'a>>) -> Result<(), InvalidRangeError> {
2816        range.canonicalize()?;
2817        match range.inner {
2818            None => {
2819                self.row.data.push(Tag::Range.into());
2820                // Untagged bytes only contains the `RANGE_EMPTY` flag value.
2821                self.row.data.push(range::InternalFlags::EMPTY.bits());
2822                Ok(())
2823            }
2824            Some(inner) => self.push_range_with(
2825                RangeLowerBound {
2826                    inclusive: inner.lower.inclusive,
2827                    bound: inner
2828                        .lower
2829                        .bound
2830                        .map(|value| move |row: &mut RowPacker| Ok(row.push(value))),
2831                },
2832                RangeUpperBound {
2833                    inclusive: inner.upper.inclusive,
2834                    bound: inner
2835                        .upper
2836                        .bound
2837                        .map(|value| move |row: &mut RowPacker| Ok(row.push(value))),
2838                },
2839            ),
2840        }
2841    }
2842
2843    /// Pushes a `DatumRange` built from the specified arguments.
2844    ///
2845    /// # Warning
2846    /// Unlike `push_range`, `push_range_with` _does not_ canonicalize its
2847    /// inputs. Consequentially, this means it's possible to generate ranges
2848    /// that will not reflect the proper ordering and equality.
2849    ///
2850    /// # Panics
2851    /// - If lower or upper expresses a finite value and does not push exactly
2852    ///   one value into the `RowPacker`.
2853    /// - If lower and upper express finite values and they are datums of
2854    ///   different types.
2855    /// - If lower or upper express finite values and push `Datum::Null`.
2856    ///
2857    /// # Notes
2858    /// - Prefer `push_range_with` over this function. This function should be
2859    ///   used only when you are not pushing `Datum`s to the inner row.
2860    /// - Range encoding is `[<flag bytes>,<lower>?,<upper>?]`, where `lower`
2861    ///   and `upper` are optional, contingent on the flag value expressing an
2862    ///   empty range (where neither will be present) or infinite bounds (where
2863    ///   each infinite bound will be absent).
2864    /// - To push an emtpy range, use `push_range` using `Range { inner: None }`.
2865    pub fn push_range_with<L, U, E>(
2866        &mut self,
2867        lower: RangeLowerBound<L>,
2868        upper: RangeUpperBound<U>,
2869    ) -> Result<(), E>
2870    where
2871        L: FnOnce(&mut RowPacker) -> Result<(), E>,
2872        U: FnOnce(&mut RowPacker) -> Result<(), E>,
2873        E: From<InvalidRangeError>,
2874    {
2875        let start = self.row.data.len();
2876        self.row.data.push(Tag::Range.into());
2877
2878        let mut flags = range::InternalFlags::empty();
2879
2880        flags.set(range::InternalFlags::LB_INFINITE, lower.bound.is_none());
2881        flags.set(range::InternalFlags::UB_INFINITE, upper.bound.is_none());
2882        flags.set(range::InternalFlags::LB_INCLUSIVE, lower.inclusive);
2883        flags.set(range::InternalFlags::UB_INCLUSIVE, upper.inclusive);
2884
2885        let mut expected_datums = 0;
2886
2887        self.row.data.push(flags.bits());
2888
2889        let datum_check = self.row.data.len();
2890
2891        if let Some(value) = lower.bound {
2892            let start = self.row.data.len();
2893            value(self)?;
2894            assert!(
2895                start < self.row.data.len(),
2896                "finite values must each push exactly one value; expected 1 but got 0"
2897            );
2898            expected_datums += 1;
2899        }
2900
2901        if let Some(value) = upper.bound {
2902            let start = self.row.data.len();
2903            value(self)?;
2904            assert!(
2905                start < self.row.data.len(),
2906                "finite values must each push exactly one value; expected 1 but got 0"
2907            );
2908            expected_datums += 1;
2909        }
2910
2911        // Validate the invariants that 0, 1, or 2 elements were pushed, none are Null,
2912        // and if two are pushed then the second is not less than the first. Panic in
2913        // some cases and error in others.
2914        let mut actual_datums = 0;
2915        let mut seen = None;
2916        let mut dataz = &self.row.data[datum_check..];
2917        while !dataz.is_empty() {
2918            let d = unsafe { read_datum(&mut dataz) };
2919            // These checks only fail when decoding untrusted/corrupted bytes;
2920            // valid callers always push consistent, non-null bounds. Return an
2921            // error rather than asserting so a crafted proto doesn't panic.
2922            if d == Datum::Null {
2923                self.row.data.truncate(start);
2924                return Err(InvalidRangeError::InvalidRangeData.into());
2925            }
2926
2927            match seen {
2928                None => seen = Some(d),
2929                Some(seen) => {
2930                    let seen_kind = DatumKind::from(seen);
2931                    let d_kind = DatumKind::from(d);
2932                    if seen_kind != d_kind {
2933                        self.row.data.truncate(start);
2934                        return Err(InvalidRangeError::InvalidRangeData.into());
2935                    }
2936
2937                    if seen > d {
2938                        self.row.data.truncate(start);
2939                        return Err(InvalidRangeError::MisorderedRangeBounds.into());
2940                    }
2941                }
2942            }
2943            actual_datums += 1;
2944        }
2945
2946        if actual_datums != expected_datums {
2947            self.row.data.truncate(start);
2948            return Err(InvalidRangeError::InvalidRangeData.into());
2949        }
2950
2951        Ok(())
2952    }
2953
2954    /// Clears the contents of the packer without de-allocating its backing memory.
2955    pub fn clear(&mut self) {
2956        self.row.data.clear();
2957    }
2958
2959    /// Truncates the underlying storage to the specified byte position.
2960    ///
2961    /// # Safety
2962    ///
2963    /// `pos` MUST specify a byte offset that lies on a datum boundary.
2964    /// If `pos` specifies a byte offset that is *within* a datum, the row
2965    /// packer will produce an invalid row, the unpacking of which may
2966    /// trigger undefined behavior!
2967    ///
2968    /// To find the byte offset of a datum boundary, inspect the packer's
2969    /// byte length by calling `packer.data().len()` after pushing the desired
2970    /// number of datums onto the packer.
2971    pub unsafe fn truncate(&mut self, pos: usize) {
2972        self.row.data.truncate(pos)
2973    }
2974
2975    /// Truncates the underlying row to contain at most the first `n` datums.
2976    pub fn truncate_datums(&mut self, n: usize) {
2977        let prev_len = self.row.data.len();
2978        let mut iter = self.row.iter();
2979        for _ in iter.by_ref().take(n) {}
2980        let next_len = iter.data.len();
2981        // SAFETY: iterator offsets always lie on a datum boundary.
2982        unsafe { self.truncate(prev_len - next_len) }
2983    }
2984
2985    /// Returns the total amount of bytes used by the underlying row.
2986    pub fn byte_len(&self) -> usize {
2987        self.row.byte_len()
2988    }
2989}
2990
2991impl<'a> IntoIterator for &'a Row {
2992    type Item = Datum<'a>;
2993    type IntoIter = DatumListIter<'a>;
2994    fn into_iter(self) -> DatumListIter<'a> {
2995        self.iter()
2996    }
2997}
2998
2999impl fmt::Debug for Row {
3000    /// Debug representation using the internal datums
3001    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3002        f.write_str("Row{")?;
3003        f.debug_list().entries(self.iter()).finish()?;
3004        f.write_str("}")
3005    }
3006}
3007
3008impl fmt::Display for Row {
3009    /// Display representation using the internal datums
3010    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3011        f.write_str("(")?;
3012        for (i, datum) in self.iter().enumerate() {
3013            if i != 0 {
3014                f.write_str(", ")?;
3015            }
3016            write!(f, "{}", datum)?;
3017        }
3018        f.write_str(")")
3019    }
3020}
3021
3022impl<'a, T> DatumList<'a, T> {
3023    pub fn iter(&self) -> DatumListIter<'a> {
3024        DatumListIter { data: self.data }
3025    }
3026
3027    /// Iterate elements as typed `T` values rather than raw `Datum`s.
3028    ///
3029    /// Each datum is decoded and converted via [`FromDatum`]. Since generic
3030    /// type parameters in `#[sqlfunc]` are erased to `Datum<'a>` before code
3031    /// generation, this is monomorphized to an identity conversion at runtime.
3032    pub fn typed_iter(&self) -> DatumListTypedIter<'a, T>
3033    where
3034        T: FromDatum<'a>,
3035    {
3036        DatumListTypedIter {
3037            inner: self.iter(),
3038            _phantom: PhantomData,
3039        }
3040    }
3041
3042    /// For debugging only
3043    pub fn data(&self) -> &'a [u8] {
3044        self.data
3045    }
3046}
3047
3048impl<T> DatumList<'static, T> {
3049    pub fn empty() -> Self {
3050        DatumList::new(&[])
3051    }
3052}
3053
3054impl<'a> IntoIterator for DatumList<'a> {
3055    type Item = Datum<'a>;
3056    type IntoIter = DatumListIter<'a>;
3057    fn into_iter(self) -> DatumListIter<'a> {
3058        self.iter()
3059    }
3060}
3061
3062impl<'a> Iterator for DatumListIter<'a> {
3063    type Item = Datum<'a>;
3064    fn next(&mut self) -> Option<Self::Item> {
3065        if self.data.is_empty() {
3066            None
3067        } else {
3068            Some(unsafe { read_datum(&mut self.data) })
3069        }
3070    }
3071}
3072
3073impl<'a, T: FromDatum<'a>> Iterator for DatumListTypedIter<'a, T> {
3074    type Item = T;
3075    fn next(&mut self) -> Option<Self::Item> {
3076        self.inner.next().map(T::from_datum)
3077    }
3078}
3079
3080impl<'a, T> DatumMap<'a, T> {
3081    pub fn iter(&self) -> DatumDictIter<'a> {
3082        DatumDictIter {
3083            data: self.data,
3084            prev_key: None,
3085        }
3086    }
3087
3088    /// Iterate entries as `(&str, T)` pairs rather than `(&str, Datum)`.
3089    ///
3090    /// Each value datum is converted via [`FromDatum`]. Since generic type
3091    /// parameters in `#[sqlfunc]` are erased to `Datum<'a>` before code
3092    /// generation, this is monomorphized to an identity conversion at runtime.
3093    pub fn typed_iter(&self) -> DatumDictTypedIter<'a, T>
3094    where
3095        T: FromDatum<'a>,
3096    {
3097        DatumDictTypedIter {
3098            inner: self.iter(),
3099            _phantom: PhantomData,
3100        }
3101    }
3102
3103    /// For debugging only
3104    pub fn data(&self) -> &'a [u8] {
3105        self.data
3106    }
3107}
3108
3109impl<T> DatumMap<'static, T> {
3110    pub fn empty() -> Self {
3111        DatumMap::new(&[])
3112    }
3113}
3114
3115impl<'a, T> Debug for DatumMap<'a, T> {
3116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3117        f.debug_map().entries(self.iter()).finish()
3118    }
3119}
3120
3121impl<'a> IntoIterator for &'a DatumMap<'a> {
3122    type Item = (&'a str, Datum<'a>);
3123    type IntoIter = DatumDictIter<'a>;
3124    fn into_iter(self) -> DatumDictIter<'a> {
3125        self.iter()
3126    }
3127}
3128
3129impl<'a> Iterator for DatumDictIter<'a> {
3130    type Item = (&'a str, Datum<'a>);
3131    fn next(&mut self) -> Option<Self::Item> {
3132        if self.data.is_empty() {
3133            None
3134        } else {
3135            let key_tag =
3136                Tag::try_from_primitive(read_byte(&mut self.data)).expect("unknown row tag");
3137            assert!(
3138                key_tag == Tag::StringTiny
3139                    || key_tag == Tag::StringShort
3140                    || key_tag == Tag::StringLong
3141                    || key_tag == Tag::StringHuge,
3142                "Dict keys must be strings, got {:?}",
3143                key_tag
3144            );
3145            let bytes = read_lengthed_bytes(&mut self.data, key_tag, Tag::StringTiny);
3146            // SAFETY: the bytes were written from a `str` under a `String` tag.
3147            let key = unsafe { str::from_utf8_unchecked(bytes) };
3148            let val = unsafe { read_datum(&mut self.data) };
3149
3150            // Gate the `prev_key` bookkeeping on the same flag as the assert it feeds, so builds
3151            // with soft assertions off pay nothing for it.
3152            if mz_ore::assert::soft_assertions_enabled() {
3153                if let Some(prev_key) = self.prev_key {
3154                    mz_ore::soft_assert_no_log!(
3155                        prev_key < key,
3156                        "Dict keys must be unique and given in ascending order: {} came before {}",
3157                        prev_key,
3158                        key
3159                    );
3160                }
3161                self.prev_key = Some(key);
3162            }
3163
3164            Some((key, val))
3165        }
3166    }
3167}
3168
3169impl<'a, T: FromDatum<'a>> Iterator for DatumDictTypedIter<'a, T> {
3170    type Item = (&'a str, T);
3171    fn next(&mut self) -> Option<Self::Item> {
3172        self.inner.next().map(|(k, v)| (k, T::from_datum(v)))
3173    }
3174}
3175
3176impl RowArena {
3177    pub fn new() -> Self {
3178        RowArena {
3179            inner: RefCell::new(vec![]),
3180            scratch: RefCell::new(None),
3181            budget: None,
3182            allocated: Cell::new(0),
3183        }
3184    }
3185
3186    /// Creates a `RowArena` that reports itself [`RowArena::over_budget`] once it holds more than
3187    /// `budget` bytes.
3188    ///
3189    /// The budget is advisory to the arena itself: pushes still succeed, because handing back a
3190    /// truncated value would corrupt the datum. It is the caller's job to poll `over_budget` at a
3191    /// point where it can fail, so the bytes an arena actually reaches is `budget` plus whatever the
3192    /// operation in flight at the time added.
3193    ///
3194    /// NOTE: a budget bounds a single ad-hoc evaluation in a shared process (see
3195    /// `mz_adapter::webhook`). It must not be given to an arena that feeds a compute dataflow. A
3196    /// dataflow re-evaluates the same expression against the same input and must return the same
3197    /// result every time. Whether an evaluation is over budget depends on what else the arena has
3198    /// accumulated, and the webhook budget is a runtime dyncfg, so a budgeted dataflow arena would
3199    /// make the result depend on when it ran. Differential then turns a changed-but-not-retracted
3200    /// result into non-accumulating diffs that corrupt the collection. A dataflow that ever needs a
3201    /// budget must fix it for the lifetime of a cluster replica.
3202    pub fn with_budget(budget: usize) -> Self {
3203        RowArena {
3204            budget: Some(budget),
3205            ..RowArena::new()
3206        }
3207    }
3208
3209    /// Bytes this arena currently holds.
3210    pub fn allocated_bytes(&self) -> usize {
3211        self.allocated.get()
3212    }
3213
3214    /// Whether this arena holds more than its budget. Always false without one.
3215    pub fn over_budget(&self) -> bool {
3216        self.budget
3217            .is_some_and(|budget| self.allocated.get() > budget)
3218    }
3219
3220    /// Bytes this arena can still take before it is [`RowArena::over_budget`], or `usize::MAX`
3221    /// without a budget.
3222    ///
3223    /// Intended for an operation that can predict its own size and would rather fail than build a
3224    /// value it is about to be told is too big.
3225    pub fn budget_remaining(&self) -> usize {
3226        match self.budget {
3227            None => usize::MAX,
3228            Some(budget) => budget.saturating_sub(self.allocated.get()),
3229        }
3230    }
3231
3232    /// Creates a `RowArena` with an initial region sized to hold `capacity` bytes, to avoid
3233    /// reallocations as the first datums are created in the arena.
3234    pub fn with_capacity(capacity: usize) -> Self {
3235        let mut inner = Vec::new();
3236        if capacity > 0 {
3237            inner.push(Vec::with_capacity(capacity));
3238        }
3239        RowArena {
3240            inner: RefCell::new(inner),
3241            ..RowArena::new()
3242        }
3243    }
3244
3245    /// Ensures the active region can hold at least `additional` more bytes without allocating a
3246    /// new region. Call this when you expect to push roughly `additional` bytes next.
3247    pub fn reserve(&self, additional: usize) {
3248        if additional == 0 {
3249            return;
3250        }
3251        let mut inner = self.inner.borrow_mut();
3252        match inner.last_mut() {
3253            // The active region is empty, so nothing references it yet and it is safe to grow it
3254            // in place (a reallocation cannot dangle a live reference).
3255            Some(active) if active.is_empty() => {
3256                if active.capacity() < additional {
3257                    active.reserve_exact(additional);
3258                }
3259            }
3260            // The active region holds live data; we cannot grow it without moving those bytes, so
3261            // stage a fresh region. Size it like `push_bytes` does (at least double the current
3262            // region) so a sequence of small `reserve`s still yields at most log-many regions
3263            // rather than many small ones.
3264            Some(active) => {
3265                let new_cap = std::cmp::max(additional, active.capacity().saturating_mul(2));
3266                inner.push(Vec::with_capacity(new_cap));
3267            }
3268            None => inner.push(Vec::with_capacity(additional)),
3269        }
3270    }
3271
3272    /// Copies `bytes` into the arena and returns a reference valid for its lifetime.
3273    ///
3274    /// Accepts anything that derefs to `[u8]` (e.g. `Vec<u8>`, `&[u8]`); the bytes are copied, so
3275    /// the caller's allocation is not retained.
3276    #[allow(clippy::transmute_ptr_to_ptr)]
3277    pub fn push_bytes<'a, B: Deref<Target = [u8]>>(&'a self, bytes: B) -> &'a [u8] {
3278        let bytes: &[u8] = &bytes;
3279        let need = bytes.len();
3280        if need == 0 {
3281            return &[];
3282        }
3283        let mut inner = self.inner.borrow_mut();
3284
3285        // Find or create a region with spare capacity for `need` bytes, never growing a region
3286        // that already holds data (see the type-level comment for why this preserves references).
3287        let has_room = inner
3288            .last()
3289            .map_or(false, |region| region.capacity() - region.len() >= need);
3290        if !has_room {
3291            let last_cap = inner.last().map_or(0, |region| region.capacity());
3292            let new_cap = std::cmp::max(need, last_cap.saturating_mul(2));
3293            inner.push(Vec::with_capacity(new_cap));
3294        }
3295
3296        let region = inner.last_mut().expect("region present");
3297        let start = region.len();
3298        region.extend_from_slice(bytes);
3299        self.allocated.set(self.allocated.get() + need);
3300        let copied = &region[start..];
3301        unsafe {
3302            // This is safe because:
3303            //   * `copied` references bytes inside `region`'s heap buffer, which we just sized to
3304            //     fit without reallocating; that buffer is never resized again while it holds data
3305            //     (we allocate a new region instead), so the reference stays valid.
3306            //   * The buffer lives as long as the arena: regions are only dropped by `clear`/`drop`,
3307            //     both of which take `&mut`/ownership, so no `&'a self`-tied reference can outlive
3308            //     them.
3309            //   * Pushing further regions may reallocate `self.inner`, but that moves only the
3310            //     `Vec<u8>` headers, not the heap buffers they own.
3311            transmute::<&[u8], &'a [u8]>(copied)
3312        }
3313    }
3314
3315    /// Moves `bytes` into the arena and returns a reference valid for its lifetime.
3316    ///
3317    /// Prefer this to [`RowArena::push_bytes`] whenever the bytes are already owned: a value large
3318    /// enough that it would get a region to itself has its allocation adopted as that region, rather
3319    /// than a fresh region being allocated and copied into, which for a large value halves the peak.
3320    /// Smaller values are copied, so the arena keeps bump allocating.
3321    pub fn push_owned_bytes<'a>(&'a self, bytes: Vec<u8>) -> &'a [u8] {
3322        /// Never adopt below this, however empty the arena. `last_cap` alone would let every value
3323        /// on a fresh or small arena look big enough, and then each gets an exactly-sized region
3324        /// with no headroom: one region and one `Vec<u8>` header per value.
3325        const MIN_ADOPT_BYTES: usize = 4 * 1024;
3326
3327        let need = bytes.len();
3328        if need == 0 {
3329            return &[];
3330        }
3331
3332        let mut inner = self.inner.borrow_mut();
3333        // Adopt only when `push_bytes` would have given these bytes a dedicated, `need`-sized region
3334        // anyway, i.e. when `need` exceeds the `last_cap * 2` it would otherwise allocate. There's
3335        // no headroom to lose, so adoption saves a copy for free. Below that we copy, because
3336        // `push_bytes` grows a region *with* headroom that later values reuse. Adopting there would
3337        // defeat the bump allocator: adoption leaves an empty region (capacity 0) on top, so nothing
3338        // would ever grow a region with headroom again.
3339        let last_cap = inner.last().map_or(0, |region| region.capacity());
3340        let adopt = need > std::cmp::max(MIN_ADOPT_BYTES, last_cap.saturating_mul(2));
3341        if !adopt {
3342            drop(inner);
3343            return self.push_bytes(&bytes[..]);
3344        }
3345
3346        // `push_bytes` would allocate a fresh region here and copy into it, so adopt the caller's
3347        // allocation as that region. Sound for the same reasons as `push_bytes`: the reference
3348        // points into a heap buffer the arena now owns for `'a`, and the buffer is never resized
3349        // while it holds data.
3350        //
3351        // Inserted *below* the active region rather than appended, because `push_bytes` sizes a new
3352        // region as twice the last one's capacity: leaving a large adopted buffer on top would make
3353        // the next push allocate twice its size.
3354        self.allocated.set(self.allocated.get() + need);
3355        let idx = inner.len().saturating_sub(1);
3356        inner.insert(idx, bytes);
3357        if inner.len() == 1 {
3358            // There was no active region to insert below, so keep an empty one on top for the same
3359            // reason. `Vec::new` does not allocate.
3360            inner.push(Vec::new());
3361        }
3362        let adopted = &inner[idx][..];
3363        unsafe { transmute::<&[u8], &'a [u8]>(adopted) }
3364    }
3365
3366    /// Moves `string` into the arena and returns a reference valid for its lifetime.
3367    pub fn push_string<'a>(&'a self, string: String) -> &'a str {
3368        let copied = self.push_owned_bytes(string.into_bytes());
3369        unsafe {
3370            // This is safe because we just moved in the bytes of a valid `String`.
3371            std::str::from_utf8_unchecked(copied)
3372        }
3373    }
3374
3375    /// Returns a growable, writeable byte buffer for assembling a value incrementally.
3376    ///
3377    /// Write into it with [`RowArenaBuf::push`], [`RowArenaBuf::extend_from_slice`], or
3378    /// [`std::io::Write`], then call [`RowArenaBuf::finish`] to copy the result into the arena and
3379    /// obtain a reference valid for the arena's lifetime. The backing buffer is a single scratch
3380    /// allocation reused across writers, so this lets a producer that builds bytes piecewise (e.g.
3381    /// decoding a row) avoid managing its own scratch.
3382    ///
3383    /// Nested writers are sound but not free: a writer obtained while another is still live can't
3384    /// reuse the (in-use) scratch, so it allocates its own buffer. Steady-state, non-nested use
3385    /// stays allocation-free.
3386    pub fn writer(&self) -> RowArenaBuf<'_> {
3387        // Take the recycled buffer if one is available, else allocate a fresh one. The cell is
3388        // borrowed only for this `take`, never for the writer's lifetime, so a nested `writer` call
3389        // doesn't double-borrow: it simply finds the slot empty and allocates its own buffer.
3390        let mut buf = self.scratch.borrow_mut().take().unwrap_or_default();
3391        buf.clear();
3392        RowArenaBuf { arena: self, buf }
3393    }
3394
3395    /// Take ownership of `row` for the lifetime of the arena, returning a
3396    /// reference to the first datum in the row.
3397    ///
3398    /// If we had an owned datum type, this method would be much clearer, and
3399    /// would be called `push_owned_datum`.
3400    pub fn push_unary_row<'a>(&'a self, row: Row) -> Datum<'a> {
3401        let copied = self.push_bytes(row.data());
3402        unsafe {
3403            // This is safe because `copied` is a valid encoding of a single datum (we just packed
3404            // it into `row`), backed by the arena for the lifetime `'a`. Copying the bytes also
3405            // sidesteps the `Row`'s inline (`SmallVec`) storage entirely.
3406            let datum = read_datum(&mut &copied[..]);
3407            transmute::<Datum<'_>, Datum<'a>>(datum)
3408        }
3409    }
3410
3411    /// Equivalent to `push_unary_row` but returns a `DatumNested` rather than a
3412    /// `Datum`.
3413    fn push_unary_row_datum_nested<'a>(&'a self, row: Row) -> DatumNested<'a> {
3414        let copied = self.push_bytes(row.data());
3415        unsafe {
3416            // Safe for the same reasons as `push_unary_row`.
3417            let nested = DatumNested::extract(&mut &copied[..]);
3418            transmute::<DatumNested<'_>, DatumNested<'a>>(nested)
3419        }
3420    }
3421
3422    /// Convenience function to make a new `Row` containing a single datum, and
3423    /// take ownership of it for the lifetime of the arena
3424    ///
3425    /// ```
3426    /// # use mz_repr::{RowArena, Datum};
3427    /// let arena = RowArena::new();
3428    /// let datum = arena.make_datum(|packer| {
3429    ///   packer.push_list(&[Datum::String("hello"), Datum::String("world")]);
3430    /// });
3431    /// assert_eq!(datum.unwrap_list().iter().collect::<Vec<_>>(), vec![Datum::String("hello"), Datum::String("world")]);
3432    /// ```
3433    pub fn make_datum<'a, F>(&'a self, f: F) -> Datum<'a>
3434    where
3435        F: FnOnce(&mut RowPacker),
3436    {
3437        let mut row = Row::default();
3438        f(&mut row.packer());
3439        self.push_unary_row(row)
3440    }
3441
3442    /// Convenience function to build a list datum from an iterator of typed
3443    /// elements and return it as a `DatumList<'a, T>`.
3444    ///
3445    /// By accepting an iterator of `T: Borrow<Datum>` instead of a raw
3446    /// `RowPacker` closure, this guarantees that only elements of type `T`
3447    /// are pushed.
3448    pub fn make_datum_list<'a, T: std::borrow::Borrow<Datum<'a>>>(
3449        &'a self,
3450        iter: impl IntoIterator<Item = T>,
3451    ) -> DatumList<'a, T> {
3452        let datum = self.make_datum(|packer| {
3453            packer.push_list_with(|packer| {
3454                for elem in iter {
3455                    packer.push(*elem.borrow());
3456                }
3457            });
3458        });
3459        DatumList::new(datum.unwrap_list().data())
3460    }
3461
3462    /// Convenience function identical to `make_datum` but instead returns a
3463    /// `DatumNested`.
3464    pub fn make_datum_nested<'a, F>(&'a self, f: F) -> DatumNested<'a>
3465    where
3466        F: FnOnce(&mut RowPacker),
3467    {
3468        let mut row = Row::default();
3469        f(&mut row.packer());
3470        self.push_unary_row_datum_nested(row)
3471    }
3472
3473    /// Like [`RowArena::make_datum`], but the provided closure can return an error.
3474    pub fn try_make_datum<'a, F, E>(&'a self, f: F) -> Result<Datum<'a>, E>
3475    where
3476        F: FnOnce(&mut RowPacker) -> Result<(), E>,
3477    {
3478        let mut row = Row::default();
3479        f(&mut row.packer())?;
3480        Ok(self.push_unary_row(row))
3481    }
3482
3483    /// Clear the contents of the arena.
3484    ///
3485    /// Retains the single largest region (emptied) so the arena can be reused without
3486    /// reallocating; a workload that clears between uses of similar size becomes allocation-free.
3487    pub fn clear(&mut self) {
3488        let inner = self.inner.get_mut();
3489        // Keep only the largest-capacity region, reset to empty, and drop the rest. Because region
3490        // capacities only ever grow (each new region at least doubles the previous), the largest is
3491        // normally the last; we scan for it defensively, which is cheap given log-many regions.
3492        if let Some(largest) = (0..inner.len()).max_by_key(|&i| inner[i].capacity()) {
3493            inner.swap(0, largest);
3494            inner.truncate(1);
3495            inner[0].clear();
3496        }
3497        self.allocated.set(0);
3498    }
3499}
3500
3501impl Default for RowArena {
3502    fn default() -> RowArena {
3503        RowArena::new()
3504    }
3505}
3506
3507/// A growable, writeable byte buffer that builds a value into a [`RowArena`].
3508///
3509/// Obtained from [`RowArena::writer`]. Behaves like a writeable byte slice (push/extend bytes,
3510/// read back as `&[u8]`); [`RowArenaBuf::finish`] copies the assembled bytes into the arena and
3511/// returns a reference valid for the arena's lifetime. The buffer is owned for the writer's
3512/// lifetime and, on drop, returned to the arena to be reused by the next writer.
3513#[derive(Debug)]
3514pub struct RowArenaBuf<'a> {
3515    arena: &'a RowArena,
3516    buf: Vec<u8>,
3517}
3518
3519impl<'a> RowArenaBuf<'a> {
3520    /// Appends a single byte.
3521    pub fn push(&mut self, byte: u8) {
3522        self.buf.push(byte);
3523    }
3524
3525    /// Appends a slice of bytes.
3526    pub fn extend_from_slice(&mut self, bytes: &[u8]) {
3527        self.buf.extend_from_slice(bytes);
3528    }
3529
3530    /// The bytes written so far.
3531    pub fn as_slice(&self) -> &[u8] {
3532        &self.buf
3533    }
3534
3535    /// The number of bytes written so far.
3536    pub fn len(&self) -> usize {
3537        self.buf.len()
3538    }
3539
3540    /// Whether no bytes have been written.
3541    pub fn is_empty(&self) -> bool {
3542        self.buf.is_empty()
3543    }
3544
3545    /// Copies the written bytes into the arena, returning a reference valid for its lifetime.
3546    pub fn finish(self) -> &'a [u8] {
3547        // `self` is dropped at the end of this call, returning `buf` to the arena for reuse; the
3548        // returned reference points into a committed region, not `buf`, so it stays valid.
3549        self.arena.push_bytes(self.buf.as_slice())
3550    }
3551
3552    /// Like [`RowArenaBuf::finish`], but returns the bytes as a `&str`.
3553    ///
3554    /// Intended for buffers written via [`std::fmt::Write`] (e.g. `write!`), whose contents are
3555    /// valid UTF-8. Panics if the bytes are not valid UTF-8.
3556    pub fn finish_str(self) -> &'a str {
3557        let bytes = self.arena.push_bytes(self.buf.as_slice());
3558        std::str::from_utf8(bytes).expect("RowArenaBuf::finish_str on non-UTF-8 contents")
3559    }
3560}
3561
3562impl<'a> Drop for RowArenaBuf<'a> {
3563    fn drop(&mut self) {
3564        // Return the buffer to the arena so the next writer can reuse its allocation. We keep only
3565        // one buffer: if the slot is already occupied — an outer writer is still live, or a nested
3566        // writer beat us to it — we drop ours rather than growing an unbounded pool. The borrow is
3567        // transient and never overlaps a live writer's, so this can't double-borrow.
3568        let mut slot = self.arena.scratch.borrow_mut();
3569        if slot.is_none() {
3570            *slot = Some(std::mem::take(&mut self.buf));
3571        }
3572    }
3573}
3574
3575impl<'a> std::ops::Deref for RowArenaBuf<'a> {
3576    type Target = [u8];
3577    fn deref(&self) -> &[u8] {
3578        &self.buf
3579    }
3580}
3581
3582impl<'a> std::io::Write for RowArenaBuf<'a> {
3583    fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
3584        self.buf.extend_from_slice(bytes);
3585        Ok(bytes.len())
3586    }
3587
3588    fn flush(&mut self) -> std::io::Result<()> {
3589        Ok(())
3590    }
3591}
3592
3593impl<'a> std::fmt::Write for RowArenaBuf<'a> {
3594    fn write_str(&mut self, s: &str) -> std::fmt::Result {
3595        self.buf.extend_from_slice(s.as_bytes());
3596        Ok(())
3597    }
3598}
3599
3600/// A thread-local row, which can be borrowed and returned.
3601/// # Example
3602///
3603/// Use this type instead of creating a new row:
3604/// ```
3605/// use mz_repr::SharedRow;
3606///
3607/// let mut row_builder = SharedRow::get();
3608/// ```
3609///
3610/// This allows us to reuse an existing row allocation instead of creating a new one or retaining
3611/// an allocation locally. Additionally, we can observe the size of the local row in a central
3612/// place and potentially reallocate to reduce memory needs.
3613///
3614/// # Panic
3615///
3616/// [`SharedRow::get`] panics when trying to obtain multiple references to the shared row.
3617#[derive(Debug)]
3618pub struct SharedRow(Row);
3619
3620impl SharedRow {
3621    thread_local! {
3622        /// A thread-local slot containing a shared Row that can be temporarily used by a function.
3623        /// There can be at most one active user of this Row, which is tracked by the state of the
3624        /// `Option<_>` wrapper. When it is `Some(..)`, the row is available for using. When it
3625        /// is `None`, it is not, and the constructor will panic if a thread attempts to use it.
3626        static SHARED_ROW: Cell<Option<Row>> = const { Cell::new(Some(Row::empty())) }
3627    }
3628
3629    /// Get the shared row.
3630    ///
3631    /// The row's contents are cleared before returning it.
3632    ///
3633    /// # Panic
3634    ///
3635    /// Panics when the row is already borrowed elsewhere.
3636    pub fn get() -> Self {
3637        let mut row = Self::SHARED_ROW
3638            .take()
3639            .expect("attempted to borrow already borrowed SharedRow");
3640        // Clear row
3641        row.packer();
3642        Self(row)
3643    }
3644
3645    /// Gets the shared row and uses it to pack `iter`.
3646    pub fn pack<'a, I, D>(iter: I) -> Row
3647    where
3648        I: IntoIterator<Item = D>,
3649        D: Borrow<Datum<'a>>,
3650    {
3651        let mut row_builder = Self::get();
3652        let mut row_packer = row_builder.packer();
3653        row_packer.extend(iter);
3654        row_builder.clone()
3655    }
3656}
3657
3658impl std::ops::Deref for SharedRow {
3659    type Target = Row;
3660
3661    fn deref(&self) -> &Self::Target {
3662        &self.0
3663    }
3664}
3665
3666impl std::ops::DerefMut for SharedRow {
3667    fn deref_mut(&mut self) -> &mut Self::Target {
3668        &mut self.0
3669    }
3670}
3671
3672impl Drop for SharedRow {
3673    fn drop(&mut self) {
3674        // Take the Row allocation from this instance and put it back in the thread local slot for
3675        // the next user. The Row in `self` is replaced with an empty Row which does not allocate.
3676        Self::SHARED_ROW.set(Some(std::mem::take(&mut self.0)))
3677    }
3678}
3679
3680#[cfg(test)]
3681mod tests {
3682    use std::cmp::Ordering;
3683    use std::collections::hash_map::DefaultHasher;
3684    use std::hash::{Hash, Hasher};
3685
3686    use chrono::{DateTime, NaiveDate};
3687    use itertools::Itertools;
3688    use mz_ore::{assert_err, assert_none};
3689    use ordered_float::OrderedFloat;
3690
3691    use crate::SqlScalarType;
3692
3693    use super::*;
3694
3695    // StableRow's wire format is proto bytes, not the in-memory datum
3696    // encoding, so rows of every column type must roundtrip exactly through
3697    // both a self-describing format (JSON) and a compact binary one
3698    // (bincode). Equality on Row compares the packed in-memory bytes, so
3699    // this also catches any datum normalization sneaking into the
3700    // Row -> ProtoRow -> Row conversion.
3701    proptest! {
3702        #![proptest_config(ProptestConfig::with_cases(1000))]
3703
3704        #[mz_ore::test]
3705        #[cfg_attr(miri, ignore)] // too slow, and decNumber uses FFI
3706        fn stable_row_serde_roundtrip(
3707            stable in crate::relation::arb_relation_desc(1..8)
3708                .prop_flat_map(|desc| crate::relation::arb_row_for_relation(&desc))
3709                .prop_map(StableRow)
3710        ) {
3711            let json = serde_json::to_string(&stable).expect("serializes to JSON");
3712            let from_json: StableRow =
3713                serde_json::from_str(&json).expect("deserializes from JSON");
3714            prop_assert_eq!(&stable, &from_json);
3715
3716            let bytes = bincode::serialize(&stable).expect("serializes to bincode");
3717            let from_bincode: StableRow =
3718                bincode::deserialize(&bytes).expect("deserializes from bincode");
3719            prop_assert_eq!(&stable, &from_bincode);
3720        }
3721    }
3722
3723    /// Every width a variable-length integer can take, at both ends of its range and either
3724    /// side of each byte boundary, plus the values whose payload is empty.
3725    fn varint_edge_cases() -> Vec<Datum<'static>> {
3726        let mut datums = vec![
3727            Datum::Int16(0),
3728            Datum::Int16(-1),
3729            Datum::Int16(i16::MIN),
3730            Datum::Int16(i16::MAX),
3731            Datum::Int32(0),
3732            Datum::Int32(-1),
3733            Datum::Int32(i32::MIN),
3734            Datum::Int32(i32::MAX),
3735            Datum::Int64(0),
3736            Datum::Int64(-1),
3737            Datum::Int64(i64::MIN),
3738            Datum::Int64(i64::MAX),
3739            Datum::UInt8(0),
3740            Datum::UInt8(u8::MAX),
3741            Datum::UInt16(0),
3742            Datum::UInt16(u16::MAX),
3743            Datum::UInt32(0),
3744            Datum::UInt32(u32::MAX),
3745            Datum::UInt64(0),
3746            Datum::UInt64(u64::MAX),
3747        ];
3748        // One below, at, and one above every point where the payload grows a byte.
3749        for bits in 1..64 {
3750            let boundary = 1u64 << bits;
3751            for delta in [-1i64, 0, 1] {
3752                let Some(v) = boundary.checked_add_signed(delta) else {
3753                    continue;
3754                };
3755                datums.push(Datum::UInt64(v));
3756                if let Ok(v) = u32::try_from(v) {
3757                    datums.push(Datum::UInt32(v));
3758                }
3759                if let Ok(v) = u16::try_from(v) {
3760                    datums.push(Datum::UInt16(v));
3761                }
3762                if let Ok(v) = u8::try_from(v) {
3763                    datums.push(Datum::UInt8(v));
3764                }
3765                let Ok(v) = i64::try_from(v) else {
3766                    continue;
3767                };
3768                datums.push(Datum::Int64(v));
3769                datums.push(Datum::Int64(-v));
3770                if let Ok(v) = i32::try_from(v) {
3771                    datums.push(Datum::Int32(v));
3772                    datums.push(Datum::Int32(-v));
3773                }
3774                if let Ok(v) = i16::try_from(v) {
3775                    datums.push(Datum::Int16(v));
3776                    datums.push(Datum::Int16(-v));
3777                }
3778            }
3779        }
3780        datums
3781    }
3782
3783    /// Both signed families of a width share one match arm, which recovers the payload width by
3784    /// subtracting the family's first tag. A value whose tag falls outside the family it is
3785    /// decoded as would read the wrong number of bytes, so check every boundary lands where the
3786    /// arithmetic expects.
3787    #[mz_ore::test]
3788    fn varint_tags_land_in_their_family() {
3789        for datum in varint_edge_cases() {
3790            let row = Row::pack_slice(&[datum]);
3791            let tag = Tag::try_from_primitive(row.data[0]).expect("valid tag");
3792            let (first, len, negative, widest) = match datum {
3793                Datum::Int16(i) => (Tag::NonNegativeInt16_0, min_bytes_signed(i), i < 0, 2),
3794                Datum::Int32(i) => (Tag::NonNegativeInt32_0, min_bytes_signed(i), i < 0, 4),
3795                Datum::Int64(i) => (Tag::NonNegativeInt64_0, min_bytes_signed(i), i < 0, 8),
3796                Datum::UInt8(u) => (Tag::UInt8_0, min_bytes_unsigned(u), false, 1),
3797                Datum::UInt16(u) => (Tag::UInt16_0, min_bytes_unsigned(u), false, 2),
3798                Datum::UInt32(u) => (Tag::UInt32_0, min_bytes_unsigned(u), false, 4),
3799                Datum::UInt64(u) => (Tag::UInt64_0, min_bytes_unsigned(u), false, 8),
3800                other => panic!("not a variable-length integer: {other:?}"),
3801            };
3802            let delta = u8::from(tag) - u8::from(first);
3803            let signed = matches!(datum, Datum::Int16(_) | Datum::Int32(_) | Datum::Int64(_));
3804            if signed {
3805                // Interleaved: the width sits above the low bit, the sign in it. At the widest
3806                // width the alternation stops, one tag serving both signs.
3807                assert_eq!(delta >> 1, len, "wrong payload width in tag for {datum:?}");
3808                let sign_bit = if len == widest { 0 } else { u8::from(negative) };
3809                assert_eq!(delta & 1, sign_bit, "wrong sign in tag for {datum:?}");
3810            } else {
3811                assert_eq!(delta, len, "wrong payload width in tag for {datum:?}");
3812            }
3813            assert_eq!(row.unpack_first(), datum, "did not round-trip: {datum:?}");
3814        }
3815    }
3816
3817    /// The wide load in `read_varint_word` reads eight bytes whatever the payload's width, so
3818    /// the datums at the end of a row, where those bytes do not exist, take the tail path.
3819    #[mz_ore::test]
3820    fn varint_at_end_of_row_reads_exact_width() {
3821        for datum in varint_edge_cases() {
3822            // Alone in a row, and behind padding long enough to push the fast path back in.
3823            for prefix in [None, Some(Datum::String("0123456789abcdef"))] {
3824                let row = match prefix {
3825                    Some(p) => Row::pack_slice(&[p, datum]),
3826                    None => Row::pack_slice(&[datum]),
3827                };
3828                assert_eq!(
3829                    row.iter().last(),
3830                    Some(datum),
3831                    "did not round-trip at end of row: {datum:?}"
3832                );
3833            }
3834            // And nested, where the list's slice ends before the row's data does.
3835            let mut row = Row::default();
3836            let mut packer = row.packer();
3837            packer.push_list_with(|packer| packer.push(datum));
3838            packer.push(Datum::Int64(1));
3839            let list = match row.unpack_first() {
3840                Datum::List(list) => list,
3841                other => panic!("expected a list, got {other:?}"),
3842            };
3843            assert_eq!(list.iter().next(), Some(datum), "did not round-trip nested");
3844        }
3845    }
3846
3847    // Regression: comparing deeply nested list values must not overflow the
3848    // stack (STACK-7). `Datum` ordering recurses once per nesting level.
3849    #[mz_ore::test]
3850    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
3851    fn cmp_deep_nested_list_does_not_overflow() {
3852        fn deep() -> Row {
3853            // `push_list` byte-copies the inner value, so building does not recurse.
3854            let mut row = Row::pack_slice(&[Datum::Int64(1)]);
3855            for _ in 0..50_000 {
3856                let mut next = Row::default();
3857                next.packer().push_list([row.unpack_first()]);
3858                row = next;
3859            }
3860            row
3861        }
3862        let a = deep();
3863        let b = deep();
3864        assert_eq!(a.unpack_first().cmp(&b.unpack_first()), Ordering::Equal);
3865    }
3866
3867    fn hash<T: Hash>(t: &T) -> u64 {
3868        let mut hasher = DefaultHasher::new();
3869        t.hash(&mut hasher);
3870        hasher.finish()
3871    }
3872
3873    #[mz_ore::test]
3874    fn test_assumptions() {
3875        assert_eq!(size_of::<Tag>(), 1);
3876        #[cfg(target_endian = "big")]
3877        {
3878            // if you want to run this on a big-endian cpu, we'll need big-endian versions of the serialization code
3879            assert!(false);
3880        }
3881    }
3882
3883    #[mz_ore::test]
3884    fn miri_test_arena() {
3885        let arena = RowArena::new();
3886
3887        assert_eq!(arena.push_string("".to_owned()), "");
3888        assert_eq!(arena.push_string("العَرَبِيَّة".to_owned()), "العَرَبِيَّة");
3889
3890        let empty: &[u8] = &[];
3891        assert_eq!(arena.push_bytes(vec![]), empty);
3892        assert_eq!(arena.push_bytes(vec![0, 2, 1, 255]), &[0, 2, 1, 255]);
3893
3894        let mut row = Row::default();
3895        let mut packer = row.packer();
3896        packer.push_dict_with(|row| {
3897            row.push(Datum::String("a"));
3898            row.push_list_with(|row| {
3899                row.push(Datum::String("one"));
3900                row.push(Datum::String("two"));
3901                row.push(Datum::String("three"));
3902            });
3903            row.push(Datum::String("b"));
3904            row.push(Datum::String("c"));
3905        });
3906        assert_eq!(arena.push_unary_row(row.clone()), row.unpack_first());
3907    }
3908
3909    #[mz_ore::test]
3910    fn miri_test_arena_growth_keeps_references() {
3911        // References returned by `push_bytes` must stay valid as later pushes allocate new
3912        // regions; this exercises the "never resize a region that holds data" invariant.
3913        let arena = RowArena::new();
3914        let chunks: Vec<Vec<u8>> = (0..128u16)
3915            .map(|i| vec![u8::try_from(i % 256).unwrap(); usize::from(i % 13) + 1])
3916            .collect();
3917        let refs: Vec<&[u8]> = chunks
3918            .iter()
3919            .map(|c| arena.push_bytes(c.as_slice()))
3920            .collect();
3921        for (i, r) in refs.iter().enumerate() {
3922            assert_eq!(*r, chunks[i].as_slice());
3923        }
3924    }
3925
3926    #[mz_ore::test]
3927    fn miri_test_arena_unary_row_at_offset() {
3928        // A row pushed after other bytes lands at a non-zero offset within a region; reading it
3929        // back must not depend on the row starting at offset zero or on any alignment.
3930        let arena = RowArena::new();
3931        arena.reserve(4096);
3932        let _pad = arena.push_bytes(vec![0xAB; 5]);
3933        let row = Row::pack_slice(&[Datum::String("hello"), Datum::Int64(42), Datum::True]);
3934        assert_eq!(arena.push_unary_row(row.clone()), row.unpack_first());
3935    }
3936
3937    #[mz_ore::test]
3938    fn miri_test_arena_clear_reuse() {
3939        // After `clear` the arena retains a region and remains usable across cycles.
3940        let mut arena = RowArena::new();
3941        for i in 0..100u8 {
3942            let _ = arena.push_bytes(vec![i; 16]);
3943        }
3944        arena.clear();
3945        assert_eq!(arena.push_bytes(vec![7u8; 8]), &[7u8; 8]);
3946        assert_eq!(arena.push_string("after clear".to_owned()), "after clear");
3947        arena.clear();
3948        let empty: &[u8] = &[];
3949        assert_eq!(arena.push_bytes(Vec::<u8>::new()), empty);
3950    }
3951
3952    #[mz_ore::test]
3953    fn miri_test_arena_adopts_owned_bytes_and_keeps_references() {
3954        // `push_owned_bytes` adopts a buffer too large for the active region instead of copying it,
3955        // which puts a region the arena never wrote into in the middle of the stack. References
3956        // handed out before and after that must all stay valid.
3957        let arena = RowArena::new();
3958        let before = arena.push_bytes(vec![1u8; 8]);
3959        let adopted = arena.push_owned_bytes(vec![2u8; 64 * 1024]);
3960        let after = arena.push_bytes(vec![3u8; 8]);
3961        // A small buffer fits the active region, so it is copied rather than given a region.
3962        let small = arena.push_owned_bytes(vec![4u8; 4]);
3963
3964        assert_eq!(before, &[1u8; 8]);
3965        assert_eq!(adopted, &vec![2u8; 64 * 1024][..]);
3966        assert_eq!(after, &[3u8; 8]);
3967        assert_eq!(small, &[4u8; 4]);
3968
3969        let empty: &[u8] = &[];
3970        assert_eq!(arena.push_owned_bytes(vec![]), empty);
3971    }
3972
3973    #[mz_ore::test]
3974    fn test_arena_owned_pushes_keep_bump_allocating() {
3975        // Adoption never *creates* a region with headroom: it inserts the caller's buffer, whose
3976        // capacity equals its length, below whatever is on top. Only `push_bytes` grows the arena
3977        // geometrically (`new_cap = max(need, last_cap * 2)`), so once the active region cannot fit
3978        // an incoming value it never can again and every later owned push adopts: one retained
3979        // allocation and one `Vec<u8>` header per value, rather than `O(log n)` regions. That is the
3980        // default path for every `String`- and `Vec<u8>`-returning scalar function, and the arenas
3981        // in the MFP and join paths outlive a single row, so the region list grows with the number
3982        // of string values in a batch. `RowArena::clear` scans every region, so it degrades too.
3983        const VALUES: usize = 500;
3984        const VALUE: &str = "0123456789";
3985
3986        let regions = |arena: &RowArena| arena.inner.borrow().len();
3987        let push_all = |arena: &RowArena, owned: bool| {
3988            for _ in 0..VALUES {
3989                match owned {
3990                    true => _ = arena.push_string(VALUE.to_string()),
3991                    false => _ = arena.push_bytes(VALUE.as_bytes()),
3992                }
3993            }
3994        };
3995
3996        // The bump allocator working as intended, as the baseline to hold the owned path to.
3997        let copied = RowArena::new();
3998        push_all(&copied, false);
3999
4000        let owned = RowArena::new();
4001        push_all(&owned, true);
4002
4003        // Seeding with ordinary copies first must not change the answer. The arena never recovers,
4004        // so this is not just the empty-arena case where the placeholder on top has capacity 0.
4005        let seeded = RowArena::new();
4006        let _ = seeded.push_bytes(VALUE.as_bytes());
4007        push_all(&seeded, true);
4008
4009        // Compare against the copy path rather than an absolute count, so this pins the property (a
4010        // run of small owned pushes still ends with a region that has headroom) and leaves the
4011        // adoption predicate to the fix.
4012        let (copied, owned, seeded) = (regions(&copied), regions(&owned), regions(&seeded));
4013        assert!(
4014            owned <= copied * 2 && seeded <= copied * 2,
4015            "{VALUES} owned pushes left {owned} regions on an empty arena and {seeded} on a seeded \
4016             one, against {copied} for the same bytes copied",
4017        );
4018    }
4019
4020    #[mz_ore::test]
4021    fn miri_test_arena_budget() {
4022        // Without a budget nothing is ever over it, however much is pushed.
4023        let arena = RowArena::new();
4024        let _ = arena.push_bytes(vec![0u8; 1024]);
4025        assert!(!arena.over_budget());
4026        assert_eq!(arena.budget_remaining(), usize::MAX);
4027
4028        let arena = RowArena::with_budget(100);
4029        assert!(!arena.over_budget());
4030        assert_eq!(arena.budget_remaining(), 100);
4031
4032        // Staying within the budget leaves it satisfied, and the remaining count tracks what a
4033        // caller that predicts its own size would consult.
4034        let _ = arena.push_bytes(vec![0u8; 60]);
4035        assert!(!arena.over_budget());
4036        assert_eq!(arena.budget_remaining(), 40);
4037        assert_eq!(arena.allocated_bytes(), 60);
4038
4039        // Crossing it reports, rather than refusing the push: a truncated push would corrupt the
4040        // datum, so the value is intact and it is the caller's job to fail.
4041        let pushed = arena.push_bytes(vec![7u8; 80]);
4042        assert_eq!(pushed, &[7u8; 80]);
4043        assert!(arena.over_budget());
4044        assert_eq!(arena.budget_remaining(), 0);
4045
4046        // An adopted buffer counts against the budget too, or adoption would be a way around it.
4047        // Large enough to actually be adopted rather than copied.
4048        let mut arena = RowArena::with_budget(100);
4049        let _ = arena.push_owned_bytes(vec![0u8; 8 * 1024]);
4050        assert!(arena.over_budget());
4051
4052        arena.clear();
4053        assert!(!arena.over_budget());
4054        assert_eq!(arena.allocated_bytes(), 0);
4055    }
4056
4057    #[mz_ore::test]
4058    fn miri_test_arena_writer() {
4059        use std::io::Write;
4060
4061        let arena = RowArena::new();
4062
4063        // Build a value incrementally and commit it.
4064        let mut w = arena.writer();
4065        let mut expected = Vec::new();
4066        for i in 0..1000u16 {
4067            let byte = u8::try_from(i % 256).unwrap();
4068            w.push(byte);
4069            expected.push(byte);
4070            w.extend_from_slice(&[byte, byte]);
4071            expected.extend_from_slice(&[byte, byte]);
4072        }
4073        assert_eq!(w.as_slice(), expected.as_slice());
4074        assert_eq!(w.len(), expected.len());
4075        let first = w.finish();
4076        assert_eq!(first, expected.as_slice());
4077
4078        // A second writer reuses the scratch; its result is independent of the first, which stays
4079        // valid because `finish` copied it into the arena.
4080        let mut w2 = arena.writer();
4081        write!(w2, "hello").unwrap();
4082        let second = w2.finish();
4083        assert_eq!(second, b"hello");
4084        assert_eq!(first, expected.as_slice());
4085
4086        // An empty writer commits to an empty slice.
4087        let empty: &[u8] = &[];
4088        assert_eq!(arena.writer().finish(), empty);
4089
4090        // Abandoning a writer without finishing is fine; the next writer starts empty.
4091        {
4092            let mut w3 = arena.writer();
4093            w3.extend_from_slice(b"discarded");
4094        }
4095        assert_eq!(arena.writer().as_slice(), empty);
4096    }
4097
4098    #[mz_ore::test]
4099    fn miri_test_arena_writer_nested() {
4100        // Reentrancy: a writer obtained while another is still live must not panic (no `RefCell`
4101        // double-borrow) and must not disturb the outer writer. The nested writer just gets its own
4102        // buffer; the outer one keeps building independently.
4103        let arena = RowArena::new();
4104
4105        let mut outer = arena.writer();
4106        outer.extend_from_slice(b"outer-before-");
4107
4108        // Take a second writer while `outer` is still live -- the case that double-borrowed before.
4109        let inner_bytes = {
4110            let mut inner = arena.writer();
4111            inner.extend_from_slice(b"inner");
4112            // The outer writer is unaffected by the nested one.
4113            assert_eq!(outer.as_slice(), b"outer-before-");
4114            inner.finish()
4115        };
4116        assert_eq!(inner_bytes, b"inner");
4117
4118        // `outer` is intact and still writable after the nested writer committed.
4119        outer.extend_from_slice(b"after");
4120        let outer_bytes = outer.finish();
4121        assert_eq!(outer_bytes, b"outer-before-after");
4122        // Both committed slices stay valid and independent.
4123        assert_eq!(inner_bytes, b"inner");
4124
4125        // Once all writers have dropped, the recycled buffer is reusable (and cleared on acquire).
4126        let mut again = arena.writer();
4127        again.extend_from_slice(b"reused");
4128        assert_eq!(again.finish(), b"reused");
4129    }
4130
4131    #[mz_ore::test]
4132    fn miri_test_arena_writer_fmt() {
4133        use std::fmt::Write;
4134
4135        // Format text into the writer (e.g. building a cast-to-string result) and commit as `&str`.
4136        let arena = RowArena::new();
4137        let mut w = arena.writer();
4138        for i in 0..5 {
4139            write!(w, "{i},").unwrap();
4140        }
4141        assert_eq!(w.finish_str(), "0,1,2,3,4,");
4142    }
4143
4144    #[mz_ore::test]
4145    fn miri_test_round_trip() {
4146        fn round_trip(datums: Vec<Datum>) {
4147            let row = Row::pack(datums.clone());
4148
4149            // When run under miri this catches undefined bytes written to data
4150            // eg by calling push_copy! on a type which contains undefined padding values
4151            println!("{:?}", row.data());
4152
4153            let datums2 = row.iter().collect::<Vec<_>>();
4154            let datums3 = row.unpack();
4155            assert_eq!(datums, datums2);
4156            assert_eq!(datums, datums3);
4157        }
4158
4159        round_trip(vec![]);
4160        round_trip(
4161            SqlScalarType::enumerate()
4162                .iter()
4163                .flat_map(|r#type| r#type.interesting_datums())
4164                .collect(),
4165        );
4166        round_trip(vec![
4167            Datum::Null,
4168            Datum::Null,
4169            Datum::False,
4170            Datum::True,
4171            Datum::Int16(-21),
4172            Datum::Int32(-42),
4173            Datum::Int64(-2_147_483_648 - 42),
4174            Datum::UInt8(0),
4175            Datum::UInt8(1),
4176            Datum::UInt16(0),
4177            Datum::UInt16(1),
4178            Datum::UInt16(1 << 8),
4179            Datum::UInt32(0),
4180            Datum::UInt32(1),
4181            Datum::UInt32(1 << 8),
4182            Datum::UInt32(1 << 16),
4183            Datum::UInt32(1 << 24),
4184            Datum::UInt64(0),
4185            Datum::UInt64(1),
4186            Datum::UInt64(1 << 8),
4187            Datum::UInt64(1 << 16),
4188            Datum::UInt64(1 << 24),
4189            Datum::UInt64(1 << 32),
4190            Datum::UInt64(1 << 40),
4191            Datum::UInt64(1 << 48),
4192            Datum::UInt64(1 << 56),
4193            Datum::Float32(OrderedFloat::from(-42.12)),
4194            Datum::Float64(OrderedFloat::from(-2_147_483_648.0 - 42.12)),
4195            Datum::Date(Date::from_pg_epoch(365 * 45 + 21).unwrap()),
4196            Datum::Timestamp(
4197                CheckedTimestamp::from_timestamplike(
4198                    NaiveDate::from_isoywd_opt(2019, 30, chrono::Weekday::Wed)
4199                        .unwrap()
4200                        .and_hms_opt(14, 32, 11)
4201                        .unwrap(),
4202                )
4203                .unwrap(),
4204            ),
4205            Datum::TimestampTz(
4206                CheckedTimestamp::from_timestamplike(DateTime::from_timestamp(61, 0).unwrap())
4207                    .unwrap(),
4208            ),
4209            Datum::Interval(Interval {
4210                months: 312,
4211                ..Default::default()
4212            }),
4213            Datum::Interval(Interval::new(0, 0, 1_012_312)),
4214            Datum::Bytes(&[]),
4215            Datum::Bytes(&[0, 2, 1, 255]),
4216            Datum::String(""),
4217            Datum::String("العَرَبِيَّة"),
4218        ]);
4219    }
4220
4221    #[mz_ore::test]
4222    fn test_array() {
4223        // Construct an array using `Row::push_array` and verify that it unpacks
4224        // correctly.
4225        const DIM: ArrayDimension = ArrayDimension {
4226            lower_bound: 2,
4227            length: 2,
4228        };
4229        let mut row = Row::default();
4230        let mut packer = row.packer();
4231        packer
4232            .try_push_array(&[DIM], vec![Datum::Int32(1), Datum::Int32(2)])
4233            .unwrap();
4234        let arr1 = row.unpack_first().unwrap_array();
4235        assert_eq!(arr1.dims().into_iter().collect::<Vec<_>>(), vec![DIM]);
4236        assert_eq!(
4237            arr1.elements().into_iter().collect::<Vec<_>>(),
4238            vec![Datum::Int32(1), Datum::Int32(2)]
4239        );
4240
4241        // Pack a previously-constructed `Datum::Array` and verify that it
4242        // unpacks correctly.
4243        let row = Row::pack_slice(&[Datum::Array(arr1)]);
4244        let arr2 = row.unpack_first().unwrap_array();
4245        assert_eq!(arr1, arr2);
4246    }
4247
4248    #[mz_ore::test]
4249    fn test_multidimensional_array() {
4250        let datums = vec![
4251            Datum::Int32(1),
4252            Datum::Int32(2),
4253            Datum::Int32(3),
4254            Datum::Int32(4),
4255            Datum::Int32(5),
4256            Datum::Int32(6),
4257            Datum::Int32(7),
4258            Datum::Int32(8),
4259        ];
4260
4261        let mut row = Row::default();
4262        let mut packer = row.packer();
4263        packer
4264            .try_push_array(
4265                &[
4266                    ArrayDimension {
4267                        lower_bound: 1,
4268                        length: 1,
4269                    },
4270                    ArrayDimension {
4271                        lower_bound: 1,
4272                        length: 4,
4273                    },
4274                    ArrayDimension {
4275                        lower_bound: 1,
4276                        length: 2,
4277                    },
4278                ],
4279                &datums,
4280            )
4281            .unwrap();
4282        let array = row.unpack_first().unwrap_array();
4283        assert_eq!(array.elements().into_iter().collect::<Vec<_>>(), datums);
4284    }
4285
4286    #[mz_ore::test]
4287    fn test_array_max_dimensions() {
4288        let mut row = Row::default();
4289        let max_dims = usize::from(MAX_ARRAY_DIMENSIONS);
4290
4291        // An array with one too many dimensions should be rejected.
4292        let res = row.packer().try_push_array(
4293            &vec![
4294                ArrayDimension {
4295                    lower_bound: 1,
4296                    length: 1
4297                };
4298                max_dims + 1
4299            ],
4300            vec![Datum::Int32(4)],
4301        );
4302        assert_eq!(res, Err(InvalidArrayError::TooManyDimensions(max_dims + 1)));
4303        assert!(row.data.is_empty());
4304
4305        // An array with exactly the maximum allowable dimensions should be
4306        // accepted.
4307        row.packer()
4308            .try_push_array(
4309                &vec![
4310                    ArrayDimension {
4311                        lower_bound: 1,
4312                        length: 1
4313                    };
4314                    max_dims
4315                ],
4316                vec![Datum::Int32(4)],
4317            )
4318            .unwrap();
4319    }
4320
4321    #[mz_ore::test]
4322    fn test_array_wrong_cardinality() {
4323        let mut row = Row::default();
4324        let res = row.packer().try_push_array(
4325            &[
4326                ArrayDimension {
4327                    lower_bound: 1,
4328                    length: 2,
4329                },
4330                ArrayDimension {
4331                    lower_bound: 1,
4332                    length: 3,
4333                },
4334            ],
4335            vec![Datum::Int32(1), Datum::Int32(2)],
4336        );
4337        assert_eq!(
4338            res,
4339            Err(InvalidArrayError::WrongCardinality {
4340                actual: 2,
4341                expected: 6,
4342            })
4343        );
4344        assert!(row.data.is_empty());
4345    }
4346
4347    #[mz_ore::test]
4348    fn test_array_cardinality_overflow() {
4349        // Dimension lengths whose product overflows `usize` must be rejected as
4350        // a `WrongCardinality` error, not panic (under overflow checks) or wrap
4351        // (in release, which could spuriously accept a corrupt array). The
4352        // product saturates to `usize::MAX`, which no real element count matches.
4353        let mut row = Row::default();
4354        let res = row.packer().try_push_array(
4355            &[
4356                ArrayDimension {
4357                    lower_bound: 1,
4358                    length: usize::MAX,
4359                },
4360                ArrayDimension {
4361                    lower_bound: 1,
4362                    length: 2,
4363                },
4364            ],
4365            vec![Datum::Int32(1), Datum::Int32(2)],
4366        );
4367        assert_eq!(
4368            res,
4369            Err(InvalidArrayError::WrongCardinality {
4370                actual: 2,
4371                expected: usize::MAX,
4372            })
4373        );
4374        assert!(row.data.is_empty());
4375    }
4376
4377    #[mz_ore::test]
4378    fn test_nesting() {
4379        let mut row = Row::default();
4380        row.packer().push_dict_with(|row| {
4381            row.push(Datum::String("favourites"));
4382            row.push_list_with(|row| {
4383                row.push(Datum::String("ice cream"));
4384                row.push(Datum::String("oreos"));
4385                row.push(Datum::String("cheesecake"));
4386            });
4387            row.push(Datum::String("name"));
4388            row.push(Datum::String("bob"));
4389        });
4390
4391        let mut iter = row.unpack_first().unwrap_map().iter();
4392
4393        let (k, v) = iter.next().unwrap();
4394        assert_eq!(k, "favourites");
4395        assert_eq!(
4396            v.unwrap_list().iter().collect::<Vec<_>>(),
4397            vec![
4398                Datum::String("ice cream"),
4399                Datum::String("oreos"),
4400                Datum::String("cheesecake"),
4401            ]
4402        );
4403
4404        let (k, v) = iter.next().unwrap();
4405        assert_eq!(k, "name");
4406        assert_eq!(v, Datum::String("bob"));
4407    }
4408
4409    #[mz_ore::test]
4410    fn test_dict_errors() -> Result<(), Box<dyn std::error::Error>> {
4411        let pack = |ok| {
4412            let mut row = Row::default();
4413            row.packer().push_dict_with(|row| {
4414                if ok {
4415                    row.push(Datum::String("key"));
4416                    row.push(Datum::Int32(42));
4417                    Ok(7)
4418                } else {
4419                    Err("fail")
4420                }
4421            })?;
4422            Ok(row)
4423        };
4424
4425        assert_eq!(pack(false), Err("fail"));
4426
4427        let row = pack(true)?;
4428        let mut dict = row.unpack_first().unwrap_map().iter();
4429        assert_eq!(dict.next(), Some(("key", Datum::Int32(42))));
4430        assert_eq!(dict.next(), None);
4431
4432        Ok(())
4433    }
4434
4435    #[mz_ore::test]
4436    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `decNumberFromInt32` on OS `linux`
4437    fn test_datum_sizes() {
4438        let arena = RowArena::new();
4439
4440        // Test the claims about various datum sizes.
4441        let values_of_interest = vec![
4442            Datum::Null,
4443            Datum::False,
4444            Datum::Int16(0),
4445            Datum::Int32(0),
4446            Datum::Int64(0),
4447            Datum::UInt8(0),
4448            Datum::UInt8(1),
4449            Datum::UInt16(0),
4450            Datum::UInt16(1),
4451            Datum::UInt16(1 << 8),
4452            Datum::UInt32(0),
4453            Datum::UInt32(1),
4454            Datum::UInt32(1 << 8),
4455            Datum::UInt32(1 << 16),
4456            Datum::UInt32(1 << 24),
4457            Datum::UInt64(0),
4458            Datum::UInt64(1),
4459            Datum::UInt64(1 << 8),
4460            Datum::UInt64(1 << 16),
4461            Datum::UInt64(1 << 24),
4462            Datum::UInt64(1 << 32),
4463            Datum::UInt64(1 << 40),
4464            Datum::UInt64(1 << 48),
4465            Datum::UInt64(1 << 56),
4466            Datum::Float32(OrderedFloat(0.0)),
4467            Datum::Float64(OrderedFloat(0.0)),
4468            Datum::from(numeric::Numeric::from(0)),
4469            Datum::from(numeric::Numeric::from(1000)),
4470            Datum::from(numeric::Numeric::from(9999)),
4471            Datum::Date(
4472                NaiveDate::from_ymd_opt(1, 1, 1)
4473                    .unwrap()
4474                    .try_into()
4475                    .unwrap(),
4476            ),
4477            Datum::Timestamp(
4478                CheckedTimestamp::from_timestamplike(
4479                    DateTime::from_timestamp(0, 0).unwrap().naive_utc(),
4480                )
4481                .unwrap(),
4482            ),
4483            Datum::TimestampTz(
4484                CheckedTimestamp::from_timestamplike(DateTime::from_timestamp(0, 0).unwrap())
4485                    .unwrap(),
4486            ),
4487            Datum::Interval(Interval::default()),
4488            Datum::Bytes(&[]),
4489            Datum::String(""),
4490            Datum::JsonNull,
4491            Datum::Range(Range { inner: None }),
4492            arena.make_datum(|packer| {
4493                packer
4494                    .push_range(Range::new(Some((
4495                        RangeLowerBound::new(Datum::Int32(-1), true),
4496                        RangeUpperBound::new(Datum::Int32(1), true),
4497                    ))))
4498                    .unwrap();
4499            }),
4500        ];
4501        for value in values_of_interest {
4502            if datum_size(&value) != Row::pack_slice(&[value]).data.len() {
4503                panic!("Disparity in claimed size for {:?}", value);
4504            }
4505        }
4506    }
4507
4508    #[mz_ore::test]
4509    fn test_range_errors() {
4510        fn test_range_errors_inner<'a>(
4511            datums: Vec<Vec<Datum<'a>>>,
4512        ) -> Result<(), InvalidRangeError> {
4513            let mut row = Row::default();
4514            let row_len = row.byte_len();
4515            let mut packer = row.packer();
4516            let r = packer.push_range_with(
4517                RangeLowerBound {
4518                    inclusive: true,
4519                    bound: Some(|row: &mut RowPacker| {
4520                        for d in &datums[0] {
4521                            row.push(d);
4522                        }
4523                        Ok(())
4524                    }),
4525                },
4526                RangeUpperBound {
4527                    inclusive: true,
4528                    bound: Some(|row: &mut RowPacker| {
4529                        for d in &datums[1] {
4530                            row.push(d);
4531                        }
4532                        Ok(())
4533                    }),
4534                },
4535            );
4536
4537            assert_eq!(row_len, row.byte_len());
4538
4539            r
4540        }
4541
4542        // A finite bound whose closure pushes zero values violates the
4543        // `push_range_with` caller contract and still panics. This is
4544        // unreachable when decoding a `ProtoRow`: each decoded bound pushes
4545        // exactly one datum (or fails), so only an in-process caller can hit it.
4546        for panicking_case in [
4547            vec![vec![Datum::Int32(1)], vec![]],
4548            vec![vec![Datum::Int32(1), Datum::Int32(2)], vec![]],
4549        ] {
4550            #[allow(clippy::disallowed_methods)] // not using enhanced panic handler in tests
4551            let result = std::panic::catch_unwind(|| test_range_errors_inner(panicking_case));
4552            assert_err!(result);
4553        }
4554
4555        // Inconsistent bound counts, mismatched datum kinds, and Null bounds are
4556        // all reachable from a crafted/corrupted `ProtoRow`, so they return an
4557        // error instead of panicking.
4558        for error_case in [
4559            vec![
4560                vec![Datum::Int32(1), Datum::Int32(2)],
4561                vec![Datum::Int32(3)],
4562            ],
4563            vec![
4564                vec![Datum::Int32(1)],
4565                vec![Datum::Int32(2), Datum::Int32(3)],
4566            ],
4567            vec![vec![Datum::Int32(1)], vec![Datum::UInt16(2)]],
4568            vec![vec![Datum::Null], vec![Datum::Int32(2)]],
4569            vec![vec![Datum::Int32(1)], vec![Datum::Null]],
4570        ] {
4571            assert_eq!(
4572                test_range_errors_inner(error_case),
4573                Err(InvalidRangeError::InvalidRangeData)
4574            );
4575        }
4576
4577        let e = test_range_errors_inner(vec![vec![Datum::Int32(2)], vec![Datum::Int32(1)]]);
4578        assert_eq!(e, Err(InvalidRangeError::MisorderedRangeBounds));
4579    }
4580
4581    /// Lists have a variable-length encoding for their lengths. We test each case here.
4582    #[mz_ore::test]
4583    #[cfg_attr(miri, ignore)] // slow
4584    fn test_list_encoding() {
4585        fn test_list_encoding_inner(len: usize) {
4586            let list_elem = |i: usize| {
4587                if i % 2 == 0 {
4588                    Datum::False
4589                } else {
4590                    Datum::True
4591                }
4592            };
4593            let mut row = Row::default();
4594            {
4595                // Push some stuff.
4596                let mut packer = row.packer();
4597                packer.push(Datum::String("start"));
4598                packer.push_list_with(|packer| {
4599                    for i in 0..len {
4600                        packer.push(list_elem(i));
4601                    }
4602                });
4603                packer.push(Datum::String("end"));
4604            }
4605            // Check that we read back exactly what we pushed.
4606            let mut row_it = row.iter();
4607            assert_eq!(row_it.next().unwrap(), Datum::String("start"));
4608            match row_it.next().unwrap() {
4609                Datum::List(list) => {
4610                    let mut list_it = list.iter();
4611                    for i in 0..len {
4612                        assert_eq!(list_it.next().unwrap(), list_elem(i));
4613                    }
4614                    assert_none!(list_it.next());
4615                }
4616                _ => panic!("expected Datum::List"),
4617            }
4618            assert_eq!(row_it.next().unwrap(), Datum::String("end"));
4619            assert_none!(row_it.next());
4620        }
4621
4622        test_list_encoding_inner(0);
4623        test_list_encoding_inner(1);
4624        test_list_encoding_inner(10);
4625        test_list_encoding_inner(TINY - 1); // tiny
4626        test_list_encoding_inner(TINY + 1); // short
4627        test_list_encoding_inner(SHORT + 1); // long
4628
4629        // The biggest one takes 40 s on my laptop, probably not worth it.
4630        //test_list_encoding_inner(LONG + 1); // huge
4631    }
4632
4633    /// Demonstrates that DatumList's Eq (bytewise) and Ord (datum-by-datum) are now consistent.
4634    /// A list containing -0.0 and one containing +0.0 have different byte representations
4635    /// (IEEE 754 distinguishes them), originally Eq says they are not equal. But after
4636    /// using the new Datum::cmp, Eq says they are equal, which matches what Ord
4637    /// compares via iter().cmp(other.iter()), and them as equal.
4638    #[mz_ore::test]
4639    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
4640    fn test_datum_list_eq_ord_consistency() {
4641        // Build list containing +0.0
4642        let mut row_pos = Row::default();
4643        row_pos.packer().push_list_with(|p| {
4644            p.push(Datum::Float64(OrderedFloat::from(0.0)));
4645        });
4646        let list_pos = row_pos.unpack_first().unwrap_list();
4647
4648        // Build list containing -0.0 (distinct bit pattern from +0.0)
4649        let mut row_neg = Row::default();
4650        row_neg.packer().push_list_with(|p| {
4651            p.push(Datum::Float64(OrderedFloat::from(-0.0)));
4652        });
4653        let list_neg = row_neg.unpack_first().unwrap_list();
4654
4655        // Eq is bytewise: different encodings => not equal
4656        // This was a bug in the past, so we test it.
4657        assert_eq!(
4658            list_pos, list_neg,
4659            "Eq should see different encodings as equal"
4660        );
4661
4662        // Ord is datum-by-datum: -0.0 and +0.0 compare equal as Datums
4663        assert_eq!(
4664            list_pos.cmp(&list_neg),
4665            Ordering::Equal,
4666            "Ord (datum-by-datum) should see -0.0 and +0.0 as equal"
4667        );
4668    }
4669
4670    /// Demonstrates that DatumMap's derived Eq (bytewise) can make maps with equal keys and
4671    /// values compare equal when values have different encodings (e.g. -0.0 vs +0.0).
4672    #[mz_ore::test]
4673    fn test_datum_map_eq_bytewise_consistency() {
4674        // Build map {"k": +0.0}
4675        let mut row_pos = Row::default();
4676        row_pos.packer().push_dict_with(|p| {
4677            p.push(Datum::String("k"));
4678            p.push(Datum::Float64(OrderedFloat::from(0.0)));
4679        });
4680        let map_pos = row_pos.unpack_first().unwrap_map();
4681
4682        // Build map {"k": -0.0}
4683        let mut row_neg = Row::default();
4684        row_neg.packer().push_dict_with(|p| {
4685            p.push(Datum::String("k"));
4686            p.push(Datum::Float64(OrderedFloat::from(-0.0)));
4687        });
4688        let map_neg = row_neg.unpack_first().unwrap_map();
4689
4690        // Same keys and semantically equal values, but Eq (bytewise) says not equal
4691        assert_eq!(
4692            map_pos, map_neg,
4693            "DatumMap Eq is semantic; -0.0 and +0.0 have different encodings but are equal"
4694        );
4695        // Verify they have the same logical content
4696        let entries_pos: Vec<_> = map_pos.iter().collect();
4697        let entries_neg: Vec<_> = map_neg.iter().collect();
4698        assert_eq!(entries_pos.len(), entries_neg.len());
4699        for ((k1, v1), (k2, v2)) in entries_pos.iter().zip_eq(entries_neg.iter()) {
4700            assert_eq!(k1, k2);
4701            assert_eq!(
4702                v1, v2,
4703                "Datum-level comparison treats -0.0 and +0.0 as equal"
4704            );
4705        }
4706    }
4707
4708    /// Hash must agree with Eq: equal lists must have the same hash.
4709    #[mz_ore::test]
4710    fn test_datum_list_hash_consistency() {
4711        // Equal lists (including -0.0 vs +0.0) must hash the same
4712        let mut row_pos = Row::default();
4713        row_pos.packer().push_list_with(|p| {
4714            p.push(Datum::Float64(OrderedFloat::from(0.0)));
4715        });
4716        let list_pos = row_pos.unpack_first().unwrap_list();
4717
4718        let mut row_neg = Row::default();
4719        row_neg.packer().push_list_with(|p| {
4720            p.push(Datum::Float64(OrderedFloat::from(-0.0)));
4721        });
4722        let list_neg = row_neg.unpack_first().unwrap_list();
4723
4724        assert_eq!(list_pos, list_neg);
4725        assert_eq!(
4726            hash(&list_pos),
4727            hash(&list_neg),
4728            "equal lists must have same hash"
4729        );
4730
4731        // Unequal lists should have different hashes (with asymptotic probability 1)
4732        let mut row_a = Row::default();
4733        row_a.packer().push_list_with(|p| {
4734            p.push(Datum::Int32(1));
4735            p.push(Datum::Int32(2));
4736        });
4737        let list_a = row_a.unpack_first().unwrap_list();
4738
4739        let mut row_b = Row::default();
4740        row_b.packer().push_list_with(|p| {
4741            p.push(Datum::Int32(1));
4742            p.push(Datum::Int32(3));
4743        });
4744        let list_b = row_b.unpack_first().unwrap_list();
4745
4746        assert_ne!(list_a, list_b);
4747        assert_ne!(
4748            hash(&list_a),
4749            hash(&list_b),
4750            "unequal lists must have different hashes"
4751        );
4752    }
4753
4754    /// Ord/PartialOrd for DatumList: less, equal, greater.
4755    #[mz_ore::test]
4756    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
4757    fn test_datum_list_ordering() {
4758        let mut row_12 = Row::default();
4759        row_12.packer().push_list_with(|p| {
4760            p.push(Datum::Int32(1));
4761            p.push(Datum::Int32(2));
4762        });
4763        let list_12 = row_12.unpack_first().unwrap_list();
4764
4765        let mut row_13 = Row::default();
4766        row_13.packer().push_list_with(|p| {
4767            p.push(Datum::Int32(1));
4768            p.push(Datum::Int32(3));
4769        });
4770        let list_13 = row_13.unpack_first().unwrap_list();
4771
4772        let mut row_123 = Row::default();
4773        row_123.packer().push_list_with(|p| {
4774            p.push(Datum::Int32(1));
4775            p.push(Datum::Int32(2));
4776            p.push(Datum::Int32(3));
4777        });
4778        let list_123 = row_123.unpack_first().unwrap_list();
4779
4780        // [1, 2] < [1, 3] due to the second element being different
4781        assert_eq!(list_12.cmp(&list_13), Ordering::Less);
4782        assert_eq!(list_13.cmp(&list_12), Ordering::Greater);
4783        assert_eq!(list_12.cmp(&list_12), Ordering::Equal);
4784        // shorter prefix compares less
4785        assert_eq!(list_12.cmp(&list_123), Ordering::Less);
4786    }
4787
4788    /// Hash must agree with Eq: equal maps must have the same hash.
4789    #[mz_ore::test]
4790    fn test_datum_map_hash_consistency() {
4791        let mut row_pos = Row::default();
4792        row_pos.packer().push_dict_with(|p| {
4793            p.push(Datum::String("x"));
4794            p.push(Datum::Float64(OrderedFloat::from(0.0)));
4795        });
4796        let map_pos = row_pos.unpack_first().unwrap_map();
4797
4798        let mut row_neg = Row::default();
4799        row_neg.packer().push_dict_with(|p| {
4800            p.push(Datum::String("x"));
4801            p.push(Datum::Float64(OrderedFloat::from(-0.0)));
4802        });
4803        let map_neg = row_neg.unpack_first().unwrap_map();
4804
4805        assert_eq!(map_pos, map_neg);
4806        assert_eq!(
4807            hash(&map_pos),
4808            hash(&map_neg),
4809            "equal maps must have same hash"
4810        );
4811
4812        let mut row_a = Row::default();
4813        row_a.packer().push_dict_with(|p| {
4814            p.push(Datum::String("a"));
4815            p.push(Datum::Int32(1));
4816        });
4817        let map_a = row_a.unpack_first().unwrap_map();
4818
4819        let mut row_b = Row::default();
4820        row_b.packer().push_dict_with(|p| {
4821            p.push(Datum::String("a"));
4822            p.push(Datum::Int32(2));
4823        });
4824        let map_b = row_b.unpack_first().unwrap_map();
4825
4826        assert_ne!(map_a, map_b);
4827        assert_ne!(
4828            hash(&map_a),
4829            hash(&map_b),
4830            "unequal maps must have different hashes"
4831        );
4832    }
4833
4834    /// Ord/PartialOrd for DatumMap: less, equal, greater (by key then value).
4835    #[mz_ore::test]
4836    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
4837    fn test_datum_map_ordering() {
4838        let mut row_a1 = Row::default();
4839        row_a1.packer().push_dict_with(|p| {
4840            p.push(Datum::String("a"));
4841            p.push(Datum::Int32(1));
4842        });
4843        let map_a1 = row_a1.unpack_first().unwrap_map();
4844
4845        let mut row_a2 = Row::default();
4846        row_a2.packer().push_dict_with(|p| {
4847            p.push(Datum::String("a"));
4848            p.push(Datum::Int32(2));
4849        });
4850        let map_a2 = row_a2.unpack_first().unwrap_map();
4851
4852        let mut row_b1 = Row::default();
4853        row_b1.packer().push_dict_with(|p| {
4854            p.push(Datum::String("b"));
4855            p.push(Datum::Int32(1));
4856        });
4857        let map_b1 = row_b1.unpack_first().unwrap_map();
4858
4859        assert_eq!(map_a1.cmp(&map_a2), Ordering::Less);
4860        assert_eq!(map_a2.cmp(&map_a1), Ordering::Greater);
4861        assert_eq!(map_a1.cmp(&map_a1), Ordering::Equal);
4862        assert_eq!(map_a1.cmp(&map_b1), Ordering::Less); // "a" < "b"
4863    }
4864
4865    /// Datum puts Null last in the enum so that nulls sort last (PostgreSQL default).
4866    /// This ordering is used when comparing DatumList/DatumMap (e.g. jsonb_agg tiebreaker).
4867    #[mz_ore::test]
4868    #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `rust_psm_stack_pointer` on OS `linux`
4869    fn test_datum_list_and_map_null_sorts_last() {
4870        // DatumList: [1] < [null] so non-null sorts before null
4871        let mut row_list_1 = Row::default();
4872        row_list_1
4873            .packer()
4874            .push_list_with(|p| p.push(Datum::Int32(1)));
4875        let list_1 = row_list_1.unpack_first().unwrap_list();
4876
4877        let mut row_list_null = Row::default();
4878        row_list_null
4879            .packer()
4880            .push_list_with(|p| p.push(Datum::Null));
4881        let list_null = row_list_null.unpack_first().unwrap_list();
4882
4883        assert_eq!(list_1.cmp(&list_null), Ordering::Less);
4884        assert_eq!(list_null.cmp(&list_1), Ordering::Greater);
4885
4886        // DatumMap: {"k": 1} < {"k": null} so non-null sorts before null (same as jsonb_agg)
4887        let mut row_map_1 = Row::default();
4888        row_map_1.packer().push_dict_with(|p| {
4889            p.push(Datum::String("k"));
4890            p.push(Datum::Int32(1));
4891        });
4892        let map_1 = row_map_1.unpack_first().unwrap_map();
4893
4894        let mut row_map_null = Row::default();
4895        row_map_null.packer().push_dict_with(|p| {
4896            p.push(Datum::String("k"));
4897            p.push(Datum::Null);
4898        });
4899        let map_null = row_map_null.unpack_first().unwrap_map();
4900
4901        assert_eq!(map_1.cmp(&map_null), Ordering::Less);
4902        assert_eq!(map_null.cmp(&map_1), Ordering::Greater);
4903    }
4904}