From b3436b6c755e487e7e42834c44d242d083a44cab Mon Sep 17 00:00:00 2001 From: Gabor Gevay Date: Tue, 7 Jul 2026 15:17:31 +0200 Subject: [PATCH 1/3] persist: remove unused MINIMUM_CONSOLIDATED_VERSION MINIMUM_CONSOLIDATED_VERSION gated assumptions about codec-order consolidation: parts written before it could not be assumed to be consolidated or sorted according to the current definition. Its last consumers were removed in f02f202c7e ("Remove codec-order consolidation") when consolidation moved entirely to structured (Arrow) ordering with per-run RunOrder metadata, leaving the constant unreferenced. Also update the comment on the SourceData serialization stability test, which still instructed encoding changes to bump the constant. Co-Authored-By: Claude Fable 5 --- src/persist-client/src/iter.rs | 6 ------ src/storage-types/src/sources.rs | 7 ++++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/persist-client/src/iter.rs b/src/persist-client/src/iter.rs index 050062e4ec68a..6a0c4b5fb7078 100644 --- a/src/persist-client/src/iter.rs +++ b/src/persist-client/src/iter.rs @@ -34,7 +34,6 @@ use mz_persist_types::arrow::{ArrayBound, ArrayIdx, ArrayOrd}; use mz_persist_types::columnar::data_type; use mz_persist_types::part::Part; use mz_persist_types::{Codec, Codec64}; -use semver::Version; use timely::progress::Timestamp; use tracing::{Instrument, debug_span}; @@ -45,11 +44,6 @@ use crate::internal::metrics::{ReadMetrics, ShardMetrics}; use crate::internal::state::{HollowRun, RunMeta, RunOrder, RunPart}; use crate::metrics::Metrics; -/// Versions prior to this had bugs in consolidation, or used a different sort. However, -/// we can assume that consolidated parts at this version or higher were consolidated -/// according to the current definition. -pub const MINIMUM_CONSOLIDATED_VERSION: Version = Version::new(0, 67, 0); - /// The data needed to fetch a batch part, bundled up to make it easy /// to send between threads. #[derive(Debug, Clone)] diff --git a/src/storage-types/src/sources.rs b/src/storage-types/src/sources.rs index 0fcdda02d8783..201578b1e2fd8 100644 --- a/src/storage-types/src/sources.rs +++ b/src/storage-types/src/sources.rs @@ -2180,9 +2180,10 @@ mod tests { // and the two versions would not consolidate out. // This can impact correctness! // - // If you need to change how SourceDatas are encoded, that's still fine... - // but we'll also need to increase - // the MINIMUM_CONSOLIDATED_VERSION as part of the same release. + // If you need to change how SourceDatas are encoded, that can be + // okay, but think through the consequences: a record whose old and + // new encodings differ never consolidates away inside existing + // persist shards. Loop in the persist team. assert_eq!( encoded, reencoded.as_str(), From f13fd0c15bbbd9b957a682eeabc608c3cb095606 Mon Sep 17 00:00:00 2001 From: Gabor Gevay Date: Tue, 7 Jul 2026 12:36:38 +0200 Subject: [PATCH 2/3] repr: canonicalize float encodings in row packing SQL float equality is semantic: -0.0 = 0.0 and NaN = NaN are true, and Datum equality agrees (via OrderedFloat). But packed rows are compared as raw bytes (Row equality, arrangement keys, index lookups), and -0.0 vs +0.0 and different NaN payloads have distinct bit patterns. Equal values therefore landed under distinct arrangement keys, giving wrong results for index lookups (LiteralConstraints), joins, GROUP BY, DISTINCT, DISTINCT ON, and UNION on float columns. Fix this at the single choke point that writes float bytes: row packing now rewrites each float to the canonical representative of its equality class, -0.0 to +0.0 and every NaN to the quiet positive NaN, mirroring the existing Numeric canonicalization. All ingress paths (persist columnar decode, ProtoRow decode, source decoders, pgwire) go through the packer, so pre-existing persisted data is normalized on read. Text formatting renders -0.0 as "0" as well, so text output does not depend on whether a value crossed a packing boundary (a view and a materialized view of the same query render identically). Negative zero is thereby unobservable in Materialize. This deviates from PostgreSQL, which prints "-0". Also correct the warning comment on the "=" operator table, which claimed BinaryFunc::Eq is byte equality. It is Datum equality, and packing must canonicalize any type whose Datum equality is coarser than bit equality. Fixes SQL-452. Co-Authored-By: Claude Fable 5 --- src/repr/src/row.rs | 109 ++++++++++++--- src/repr/src/strconv.rs | 15 ++- src/sql/src/func.rs | 17 ++- test/sqllogictest/float.slt | 115 +++++++++++++++- .../transform/literal_constraints.slt | 127 ++++++++++++++++++ 5 files changed, 350 insertions(+), 33 deletions(-) diff --git a/src/repr/src/row.rs b/src/repr/src/row.rs index 35403dc70b00c..d003aee5a2b50 100644 --- a/src/repr/src/row.rs +++ b/src/repr/src/row.rs @@ -1839,6 +1839,43 @@ const TINY: usize = 1 << 8; const SHORT: usize = 1 << 16; const LONG: usize = 1 << 32; +/// The quiet positive NaN. This is the bit pattern of `f64::NAN` on x86 and +/// aarch64, spelled out because Rust does not guarantee the bit pattern of +/// `f64::NAN` and packed bytes must be identical across platforms. +const CANONICAL_F64_NAN_BITS: u64 = 0x7ff8_0000_0000_0000; +/// The `f32` analogue of [`CANONICAL_F64_NAN_BITS`]. +const CANONICAL_F32_NAN_BITS: u32 = 0x7fc0_0000; + +/// Rewrites `f` to the canonical representative of its `Datum` equality class. +/// +/// `Datum` float equality is semantic (via `OrderedFloat`): -0.0 equals +0.0 +/// and all NaN bit patterns are equal to each other. Packed rows are compared +/// as raw bytes (`Row` equality, arrangement keys, index lookups), so equal +/// datums must pack to equal bytes. Each equality class therefore encodes as a +/// single bit pattern: zeros as +0.0 and NaNs as the quiet positive NaN. +#[inline] +fn canonicalize_float32(f: OrderedFloat) -> f32 { + if f.is_nan() { + f32::from_bits(CANONICAL_F32_NAN_BITS) + } else if *f == 0.0 { + 0.0 + } else { + f.into_inner() + } +} + +/// See [`canonicalize_float32`]. +#[inline] +fn canonicalize_float64(f: OrderedFloat) -> f64 { + if f.is_nan() { + f64::from_bits(CANONICAL_F64_NAN_BITS) + } else if *f == 0.0 { + 0.0 + } else { + f.into_inner() + } +} + fn push_datum(data: &mut D, datum: Datum) where D: Vector, @@ -1906,11 +1943,11 @@ where } Datum::Float32(f) => { data.push(Tag::Float32.into()); - data.extend_from_slice(&f.to_bits().to_le_bytes()); + data.extend_from_slice(&canonicalize_float32(f).to_bits().to_le_bytes()); } Datum::Float64(f) => { data.push(Tag::Float64.into()); - data.extend_from_slice(&f.to_bits().to_le_bytes()); + data.extend_from_slice(&canonicalize_float64(f).to_bits().to_le_bytes()); } Datum::Date(d) => { data.push(Tag::Date.into()); @@ -4149,11 +4186,9 @@ mod tests { //test_list_encoding_inner(LONG + 1); // huge } - /// Demonstrates that DatumList's Eq (bytewise) and Ord (datum-by-datum) are now consistent. - /// A list containing -0.0 and one containing +0.0 have different byte representations - /// (IEEE 754 distinguishes them), originally Eq says they are not equal. But after - /// using the new Datum::cmp, Eq says they are equal, which matches what Ord - /// compares via iter().cmp(other.iter()), and them as equal. + /// DatumList's Eq and Ord (both datum-by-datum) agree that lists + /// containing -0.0 and +0.0 are equal. Packing also canonicalizes the two + /// zeros to the same bytes, see `test_float_packing_canonicalizes`. #[mz_ore::test] fn test_datum_list_eq_ord_consistency() { // Build list containing +0.0 @@ -4170,12 +4205,10 @@ mod tests { }); let list_neg = row_neg.unpack_first().unwrap_list(); - // Eq is bytewise: different encodings => not equal - // This was a bug in the past, so we test it. - assert_eq!( - list_pos, list_neg, - "Eq should see different encodings as equal" - ); + assert_eq!(list_pos, list_neg, "-0.0 and +0.0 lists must be equal"); + + // Packing canonicalizes -0.0 to +0.0, so the rows are equal bytewise. + assert_eq!(row_pos, row_neg, "-0.0 and +0.0 must pack to equal bytes"); // Ord is datum-by-datum: -0.0 and +0.0 compare equal as Datums assert_eq!( @@ -4185,8 +4218,47 @@ mod tests { ); } - /// Demonstrates that DatumMap's derived Eq (bytewise) can make maps with equal keys and - /// values compare equal when values have different encodings (e.g. -0.0 vs +0.0). + /// Equal float datums must pack to identical bytes. Arrangement keys, + /// index lookups, and `Row` equality compare packed rows bytewise, so the + /// packer canonicalizes -0.0 to +0.0 and every NaN to one bit pattern. + #[mz_ore::test] + fn test_float_packing_canonicalizes() { + let f64_classes: &[(f64, f64)] = &[ + (-0.0, 0.0), + // Negative quiet NaN, what x86 produces for e.g. inf - inf. + (f64::NAN, f64::from_bits(0xfff8_0000_0000_0000)), + // Quiet NaN with a payload. + (f64::NAN, f64::from_bits(0x7ff8_0000_dead_beef)), + // Signaling NaN. + (f64::NAN, f64::from_bits(0x7ff0_0000_0000_0001)), + ]; + for (a, b) in f64_classes { + let row_a = Row::pack_slice(&[Datum::Float64(OrderedFloat(*a))]); + let row_b = Row::pack_slice(&[Datum::Float64(OrderedFloat(*b))]); + assert_eq!(row_a, row_b, "{a:?} and {b:?} must pack to equal bytes"); + // Unpacking still yields an equal datum. + assert_eq!(row_a.unpack_first(), Datum::Float64(OrderedFloat(*a))); + } + + let f32_classes: &[(f32, f32)] = &[ + (-0.0, 0.0), + (f32::NAN, f32::from_bits(0xffc0_0000)), + (f32::NAN, f32::from_bits(0x7fc0_dead)), + ]; + for (a, b) in f32_classes { + let row_a = Row::pack_slice(&[Datum::Float32(OrderedFloat(*a))]); + let row_b = Row::pack_slice(&[Datum::Float32(OrderedFloat(*b))]); + assert_eq!(row_a, row_b, "{a:?} and {b:?} must pack to equal bytes"); + assert_eq!(row_a.unpack_first(), Datum::Float32(OrderedFloat(*a))); + } + + // Non-zero, non-NaN values keep their exact bits. + let row = Row::pack_slice(&[Datum::Float64(OrderedFloat(-1.5))]); + assert_eq!(row.unpack_first(), Datum::Float64(OrderedFloat(-1.5))); + } + + /// Maps with equal keys and equal values (-0.0 vs +0.0) compare equal, + /// both datum-by-datum and bytewise (packing canonicalizes the zeros). #[mz_ore::test] fn test_datum_map_eq_bytewise_consistency() { // Build map {"k": +0.0} @@ -4205,11 +4277,8 @@ mod tests { }); let map_neg = row_neg.unpack_first().unwrap_map(); - // Same keys and semantically equal values, but Eq (bytewise) says not equal - assert_eq!( - map_pos, map_neg, - "DatumMap Eq is semantic; -0.0 and +0.0 have different encodings but are equal" - ); + assert_eq!(map_pos, map_neg, "-0.0 and +0.0 maps must be equal"); + assert_eq!(row_pos, row_neg, "-0.0 and +0.0 must pack to equal bytes"); // Verify they have the same logical content let entries_pos: Vec<_> = map_pos.iter().collect(); let entries_neg: Vec<_> = map_neg.iter().collect(); diff --git a/src/repr/src/strconv.rs b/src/repr/src/strconv.rs index ac4ac8fa3d194..e9d3b2168ec08 100644 --- a/src/repr/src/strconv.rs +++ b/src/repr/src/strconv.rs @@ -325,16 +325,21 @@ where // // Note that we have to fix up ryu's formatting in a few cases to match // PostgreSQL. PostgreSQL spells out "Infinity" in full, never emits a - // trailing ".0", formats positive exponents as e.g. "1e+10" rather than - // "1e10", and emits a negative sign for negative zero. If we need to speed - // up float formatting, we can look into forking ryu and making these edits - // directly, but for now it doesn't seem worth it. + // trailing ".0", and formats positive exponents as e.g. "1e+10" rather + // than "1e10". If we need to speed up float formatting, we can look into + // forking ryu and making these edits directly, but for now it doesn't + // seem worth it. match f.classify() { FpCategory::Infinite if f.is_sign_negative() => buf.write_str("-Infinity"), FpCategory::Infinite => buf.write_str("Infinity"), FpCategory::Nan => buf.write_str("NaN"), - FpCategory::Zero if f.is_sign_negative() => buf.write_str("-0"), + // Negative zero renders as "0". Packed rows canonicalize -0.0 to +0.0 + // (see `push_datum`), so a -0.0 here can only be an unpacked + // intermediate. Rendering it as "0" keeps text output identical + // whether or not the value crossed a packing boundary. This + // deliberately deviates from PostgreSQL, which prints "-0". + FpCategory::Zero if f.is_sign_negative() => buf.write_str("0"), _ => { debug_assert!(f.is_finite()); let mut ryu_buf = ryu::Buffer::new(); diff --git a/src/sql/src/func.rs b/src/sql/src/func.rs index 4ecbc80717aee..5dbf6153710b7 100644 --- a/src/sql/src/func.rs +++ b/src/sql/src/func.rs @@ -6057,11 +6057,18 @@ pub static OP_IMPLS: LazyLock> = LazyLock::new(|| { // - If you are writing functions here that do not simply use // `BinaryFunc::Eq`, you will break row equality (used in e.g. // DISTINCT operations and JOINs). In short, this is totally verboten. - // - The implementation of `BinaryFunc::Eq` is byte equality on two - // datums, and we enforce that both inputs to the function are of the - // same type in planning. However, it's possible that we will perform - // equality on types not listed here (e.g. `Varchar`) due to decisions - // made in the optimizer. + // - `BinaryFunc::Eq` is `Datum` equality, and we enforce that both + // inputs to the function are of the same type in planning. `Datum` + // equality must agree with byte equality of packed rows, because + // arrangement keys and index lookups compare packed bytes. Packing + // upholds this by canonicalizing every type whose `Datum` equality + // is coarser than bit equality (see `push_datum` in `mz_repr`: + // floats collapse -0.0/+0.0 and all NaN payloads, numerics are + // reduced). A type whose `Datum` equality identifies values with + // distinct packed encodings must not be listed here without a + // matching canonicalization in packing. + // - It's possible that we will perform equality on types not listed + // here (e.g. `Varchar`) due to decisions made in the optimizer. // - Null inputs are handled by `BinaryFunc::eval` checking `propagates_nulls`. "=" => Scalar { params!(Numeric, Numeric) => BF::from(func::Eq) => Bool, 1752; diff --git a/test/sqllogictest/float.slt b/test/sqllogictest/float.slt index f1b1dd21cf9d4..eb9b5140404f4 100644 --- a/test/sqllogictest/float.slt +++ b/test/sqllogictest/float.slt @@ -12,7 +12,7 @@ mode cockroach query T SELECT '-0'::float::text ---- --0 +0 query T SELECT '+0'::float::text @@ -42,7 +42,7 @@ SELECT '.0'::float::text query T SELECT '-.0'::float::text ---- --0 +0 query T SELECT '+.0'::float::text @@ -57,7 +57,7 @@ SELECT '+0.'::float::text query T SELECT '-0.'::float::text ---- --0 +0 query error invalid input syntax SELECT '++0'::float::text @@ -186,3 +186,112 @@ SELECT 'e'::float::text query error invalid input syntax SELECT 'e10'::float::text + +# Row packing canonicalizes floats so that byte equality of packed rows agrees +# with SQL float equality: -0.0 packs as +0.0 and every NaN packs as one bit +# pattern. Arrangement keys (joins, GROUP BY, DISTINCT) and index lookups +# compare packed bytes, so without this a stored -0.0 and +0.0 would be +# distinct keys even though `-0.0 = 0.0` is true. See SQL-452. +# Text formatting renders -0.0 as "0" as well (see the `'-0'::float::text` +# tests above), so text output does not depend on whether a value crossed a +# packing boundary. Negative zero is thereby unobservable in Materialize. +# This deviates from PostgreSQL, which prints "-0". + +statement ok +CREATE TABLE zeros (id int, f float8) + +statement ok +INSERT INTO zeros VALUES (1, '-0'::float8), (2, '0'::float8) + +# The stored -0.0 reads back as +0.0. +query T rowsort +SELECT f::text FROM zeros +---- +0 +0 + +query I +SELECT count(*) FROM (SELECT DISTINCT f FROM zeros) +---- +1 + +# One group, so a single count of 2. +query I +SELECT count(*) FROM zeros GROUP BY f +---- +2 + +query I +SELECT count(*) FROM (SELECT DISTINCT ON (f) f FROM zeros) +---- +1 + +query I +SELECT count(*) FROM (SELECT f FROM zeros UNION SELECT f FROM zeros) +---- +1 + +statement ok +CREATE TABLE zeros2 (id int, f float8) + +statement ok +INSERT INTO zeros2 VALUES (3, '0'::float8) + +query II +SELECT z1.id, z2.id FROM zeros z1 JOIN zeros2 z2 ON z1.f = z2.f ORDER BY z1.id +---- +1 3 +2 3 + +# NaN bit patterns: parsed NaNs and arithmetic NaNs are one value. +statement ok +CREATE TABLE nans (f float8) + +statement ok +INSERT INTO nans VALUES ('NaN'::float8), ('-NaN'::float8), ('inf'::float8 - 'inf'::float8) + +query I +SELECT count(*) FROM (SELECT DISTINCT f FROM nans) +---- +1 + +query T +SELECT DISTINCT f::text FROM nans +---- +NaN + +# float4 gets the same treatment. +statement ok +CREATE TABLE zeros_f4 (f float4) + +statement ok +INSERT INTO zeros_f4 VALUES ('-0'::float4), ('0'::float4), ('NaN'::float4) + +query I +SELECT count(*) FROM (SELECT DISTINCT f FROM zeros_f4) +---- +2 + +# The same query must render identically through a view (inlined, the text +# cast evaluates on unpacked datums) and a materialized view (the float is +# packed and persisted before the cast). +statement ok +CREATE VIEW v_negzero AS SELECT -1.0::float8 * 0.0 AS f + +statement ok +CREATE MATERIALIZED VIEW mv_negzero AS SELECT -1.0::float8 * 0.0 AS f + +query T +SELECT f::text FROM v_negzero +---- +0 + +query T +SELECT f::text FROM mv_negzero +---- +0 + +query T +SELECT (-1.0::float8 * 0.0)::text +---- +0 diff --git a/test/sqllogictest/transform/literal_constraints.slt b/test/sqllogictest/transform/literal_constraints.slt index 3f6b96eb59daa..022786e118974 100644 --- a/test/sqllogictest/transform/literal_constraints.slt +++ b/test/sqllogictest/transform/literal_constraints.slt @@ -1242,6 +1242,133 @@ SELECT * FROM t3 WHERE t3.c0 = 0.8::INT OR t3.c0 = -0.1; -0.1 1 +# Regression tests for SQL-452: `LiteralConstraints` packs the equality +# literal into a `Row` and the index lookup seeks that exact byte-encoded key. +# SQL float equality treats -0.0 = 0.0 (and NaN = NaN), so row packing must +# canonicalize the encodings that float equality conflates, otherwise a lookup +# for +0.0 misses a stored -0.0. +# NOTE: a genuine -0.0 needs a text->float cast (`'-0'::float8`), a `-0.0` +# literal goes through `numeric` (no signed zero) and arrives as +0.0. + +statement ok +CREATE TABLE t_neg_zero (id int, c0 float8) + +statement ok +CREATE INDEX t_neg_zero_i ON t_neg_zero (c0) + +statement ok +INSERT INTO t_neg_zero VALUES (1, '-0'::float8) + +# `-0.0 = 0.0` is true, so id=1 must be returned. +query I +SELECT id FROM t_neg_zero WHERE c0 = 0.0 +---- +1 + +# Control: without a matching index (a filter scan) the row is also returned. +statement ok +CREATE TABLE t_neg_zero_noidx (id int, c0 float8) + +statement ok +INSERT INTO t_neg_zero_noidx VALUES (1, '-0'::float8) + +query I +SELECT id FROM t_neg_zero_noidx WHERE c0 = 0.0 +---- +1 + +# Multiplicity (also covers float4): both zeros match `c0 = 0.0` and one +# lookup key must find both rows. +statement ok +CREATE TABLE t_both_zero (c0 float4) + +statement ok +CREATE INDEX t_both_zero_i ON t_both_zero (c0) + +statement ok +INSERT INTO t_both_zero VALUES ('0'::float4), ('-0'::float4) + +query I +SELECT count(*) FROM t_both_zero WHERE c0 = 0.0 +---- +2 + +# The mirror image: a -0.0 literal must find a stored +0.0. +statement ok +CREATE TABLE t_pos_zero (c0 float8) + +statement ok +CREATE INDEX t_pos_zero_i ON t_pos_zero (c0) + +statement ok +INSERT INTO t_pos_zero VALUES ('0'::float8) + +query I +SELECT count(*) FROM t_pos_zero WHERE c0 = '-0'::float8 +---- +1 + +statement ok +CREATE TABLE t_neg_zero_f4 (c0 float4) + +statement ok +CREATE INDEX t_neg_zero_f4_i ON t_neg_zero_f4 (c0) + +statement ok +INSERT INTO t_neg_zero_f4 VALUES ('-0'::float4) + +query I +SELECT count(*) FROM t_neg_zero_f4 WHERE c0 = 0.0 +---- +1 + +# NaN: SQL float equality treats NaN = NaN, but NaN bit patterns differ +# (parsed 'NaN' vs arithmetic results like inf - inf), so packing must +# canonicalize them too. +statement ok +CREATE TABLE t_nan (c0 float8) + +statement ok +CREATE INDEX t_nan_i ON t_nan (c0) + +statement ok +INSERT INTO t_nan VALUES ('inf'::float8 - 'inf'::float8) + +query I +SELECT count(*) FROM t_nan WHERE c0 = 'NaN'::float8 +---- +1 + +# Two equal literals that used to have distinct encodings must dedupe into a +# single lookup key. +query T multiline +EXPLAIN OPTIMIZED PLAN WITH(humanized expressions) AS VERBOSE TEXT FOR +SELECT count(*) FROM t_both_zero WHERE c0 = '0'::float4 OR c0 = '-0'::float4; +---- +Explained Query: + With + cte l0 = + Reduce aggregates=[count(*)] + Project () + ReadIndex on=materialize.public.t_both_zero t_both_zero_i=[lookup value=(0)] + Return + Union + Get l0 + Map (0) + Union + Negate + Project () + Get l0 + Constant + - () + +Used Indexes: + - materialize.public.t_both_zero_i (lookup) + +Target cluster: quickstart + +EOF + # Check the nullability- and unique key inference: # The `ReadIndex` should have # - non-nullable first 3 columns From 08a90d5b8bc64408f41479127b1e843adfbb504e Mon Sep 17 00:00:00 2001 From: Gabor Gevay Date: Tue, 7 Jul 2026 14:10:16 +0200 Subject: [PATCH 3/3] repr: follow-ups for float canonicalization Make the remaining sign-of-zero-sensitive scalar functions treat -0.0 as +0.0, so that results do not depend on whether a value crossed a packing boundary (e.g. a view vs a materialized view of the same query): cot(-0.0) now returns +Infinity (PostgreSQL returns -Infinity here), power no longer errors for a -0.0 base with a fractional exponent or a -0.0 exponent with a zero base, and ln/log10 (float and numeric) report the zero error rather than the negative error for -0.0. The ln/log10/power changes also match PostgreSQL, which uses zero-first checks and strict comparisons, and they let NaN inputs propagate to NaN results regardless of the NaN's sign bit. Regenerate the SourceData serialization stability snapshot: old encodings containing non-canonical floats now decode to canonical rows, so re-encoding them produces different bytes. Update the stale comment that pointed at MINIMUM_CONSOLIDATED_VERSION, which has not been consulted since codec-order consolidation was removed. Add platform checks covering the cross-version story for tables, Postgres sources, and Kafka upsert sources: rows written by an old version with raw float bits must cancel against retractions written by a new version, and must land in the same DISTINCT/GROUP BY/index groups. Co-Authored-By: Claude Fable 5 --- .../all_checks/float_canonicalization.py | 220 ++++++++++++++++++ src/expr/src/scalar/func.rs | 17 +- src/expr/src/scalar/func/impls/float64.rs | 23 +- src/expr/src/scalar/func/impls/numeric.rs | 8 +- .../src/snapshots/source-datas.txt | 2 +- src/storage-types/src/sources.rs | 6 +- test/sqllogictest/funcs.slt | 41 +++- 7 files changed, 300 insertions(+), 17 deletions(-) create mode 100644 misc/python/materialize/checks/all_checks/float_canonicalization.py diff --git a/misc/python/materialize/checks/all_checks/float_canonicalization.py b/misc/python/materialize/checks/all_checks/float_canonicalization.py new file mode 100644 index 0000000000000..f622383a5f7f5 --- /dev/null +++ b/misc/python/materialize/checks/all_checks/float_canonicalization.py @@ -0,0 +1,220 @@ +# Copyright Materialize, Inc. and contributors. All rights reserved. +# +# Use of this software is governed by the Business Source License +# included in the LICENSE file at the root of this repository. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0. +from textwrap import dedent + +from materialize.checks.actions import Testdrive +from materialize.checks.checks import Check, externally_idempotent + +# Row packing canonicalizes floats (-0.0 packs as +0.0, every NaN packs as one +# bit pattern) so that byte equality of packed rows agrees with SQL float +# equality. Data written by versions without that canonicalization carries the +# raw bit patterns, so these checks exercise the cross-version story: rows +# written by an older version must cancel against retractions written by a +# newer one, and must land in the same DISTINCT/GROUP BY/index groups. +# +# In multi-version scenarios validate() also runs on versions without the +# canonicalization, where -0.0 is a distinct arrangement key, so the +# assertions that depend on it are gated on version 26.33 (which is when the +# canonicalization was introduced). Row counts and upsert results hold on all +# versions and are asserted unconditionally. +# +# NOTE: a genuine -0.0 needs a text->float cast ('-0'), a -0.0 literal goes +# through numeric (no signed zero) and arrives as +0.0. + + +class FloatCanonicalizationTable(Check): + """-0.0 and NaN in a table across versions: retraction of old-encoding + rows, DISTINCT/GROUP BY collapse, and index point lookups.""" + + def initialize(self) -> Testdrive: + return Testdrive(dedent(""" + > CREATE TABLE float_canon_table (id INT, f DOUBLE PRECISION); + > INSERT INTO float_canon_table VALUES + (1, '-0'), (2, '0'), (3, 'NaN'), (4, '-0'), (5, 1.5); + """)) + + def manipulate(self) -> list[Testdrive]: + return [ + Testdrive(dedent(s)) + for s in [ + """ + > CREATE MATERIALIZED VIEW float_canon_table_mv AS + SELECT f, COUNT(*) AS c FROM float_canon_table GROUP BY f; + > CREATE DEFAULT INDEX ON float_canon_table; + > INSERT INTO float_canon_table VALUES (6, '-0'); + """, + """ + > DELETE FROM float_canon_table WHERE id = 4; + > INSERT INTO float_canon_table VALUES (7, '0'), (8, 'NaN'); + """, + ] + ] + + def validate(self) -> Testdrive: + return Testdrive(dedent(""" + > SELECT count(*) FROM float_canon_table; + 7 + + >[version>=2603300] SELECT count(*) FROM (SELECT DISTINCT f FROM float_canon_table); + 3 + + >[version>=2603300] SELECT f::text, c FROM float_canon_table_mv; + 0 4 + 1.5 1 + NaN 2 + + >[version>=2603300] SELECT id FROM float_canon_table WHERE f = 0; + 1 + 2 + 6 + 7 + + >[version>=2603300] SELECT id FROM float_canon_table WHERE f = 'NaN'; + 3 + 8 + """)) + + +@externally_idempotent(False) +class FloatCanonicalizationPgCdc(Check): + """-0.0 and NaN ingested from a Postgres source across versions: an + upstream DELETE/UPDATE after an upgrade must retract rows whose additions + were written with the old float encoding.""" + + def initialize(self) -> Testdrive: + return Testdrive(dedent(""" + $ postgres-execute connection=postgres://postgres:postgres@postgres + CREATE USER postgres_float_canon WITH SUPERUSER PASSWORD 'postgres'; + ALTER USER postgres_float_canon WITH replication; + DROP PUBLICATION IF EXISTS float_canon_publication; + DROP TABLE IF EXISTS float_canon_pg_table; + CREATE TABLE float_canon_pg_table (id INT PRIMARY KEY, f DOUBLE PRECISION); + ALTER TABLE float_canon_pg_table REPLICA IDENTITY FULL; + INSERT INTO float_canon_pg_table VALUES (1, '-0'), (2, '0'), (3, 'NaN'), (4, '-0'); + CREATE PUBLICATION float_canon_publication FOR ALL TABLES; + + > CREATE SECRET float_canon_pgpass AS 'postgres'; + + > CREATE CONNECTION float_canon_pg_conn FOR POSTGRES + HOST 'postgres', + DATABASE postgres, + USER postgres_float_canon, + PASSWORD SECRET float_canon_pgpass; + + > CREATE SOURCE float_canon_pg_source + FROM POSTGRES CONNECTION float_canon_pg_conn + (PUBLICATION 'float_canon_publication'); + > CREATE TABLE float_canon_pg FROM SOURCE float_canon_pg_source + (REFERENCE float_canon_pg_table); + + # Wait for the snapshot so the initial rows are ingested (and + # thus encoded) by the version running this phase. + > SELECT count(*) FROM float_canon_pg; + 4 + """)) + + def manipulate(self) -> list[Testdrive]: + return [ + Testdrive(dedent(s)) + for s in [ + """ + $ postgres-execute connection=postgres://postgres:postgres@postgres + INSERT INTO float_canon_pg_table VALUES (5, '-0'), (6, 'NaN'); + """, + """ + $ postgres-execute connection=postgres://postgres:postgres@postgres + DELETE FROM float_canon_pg_table WHERE id IN (1, 6); + UPDATE float_canon_pg_table SET f = '0' WHERE id = 4; + """, + ] + ] + + def validate(self) -> Testdrive: + return Testdrive(dedent(""" + > SELECT count(*) FROM float_canon_pg; + 4 + + >[version>=2603300] SELECT count(*) FROM (SELECT DISTINCT f FROM float_canon_pg); + 2 + + >[version>=2603300] SELECT id FROM float_canon_pg WHERE f = 0; + 2 + 4 + 5 + + >[version>=2603300] SELECT id FROM float_canon_pg WHERE f = 'NaN'; + 3 + """)) + + +def float_canon_schemas() -> str: + return dedent(""" + $ set float-canon-keyschema={ + "type": "record", + "name": "Key", + "fields": [ {"name": "key1", "type": "double"} ] + } + + $ set float-canon-schema={ + "type" : "record", + "name" : "test", + "fields" : [ {"name": "f1", "type": "double"} ] + } + """) + + +class FloatCanonicalizationUpsert(Check): + """-0.0 in a Kafka upsert source's key and value across versions: a -0.0 + and a +0.0 key are the same key, and a post-upgrade tombstone must retract + a value row written with the old float encoding.""" + + def initialize(self) -> Testdrive: + return Testdrive(float_canon_schemas() + dedent(""" + $ kafka-create-topic topic=float-canon-upsert + + $ kafka-ingest format=avro key-format=avro topic=float-canon-upsert key-schema=${float-canon-keyschema} schema=${float-canon-schema} + {"key1": -0.0} {"f1": 1.0} + {"key1": 2.0} {"f1": -0.0} + + > CREATE SOURCE float_canon_upsert_src + FROM KAFKA CONNECTION kafka_conn (TOPIC 'testdrive-float-canon-upsert-${testdrive.seed}') + > CREATE TABLE float_canon_upsert FROM SOURCE float_canon_upsert_src (REFERENCE "testdrive-float-canon-upsert-${testdrive.seed}") + FORMAT AVRO USING CONFLUENT SCHEMA REGISTRY CONNECTION csr_conn + ENVELOPE UPSERT + + # Wait for the snapshot so the initial rows are ingested (and + # thus encoded) by the version running this phase. + > SELECT count(*) FROM float_canon_upsert; + 2 + """)) + + def manipulate(self) -> list[Testdrive]: + return [ + Testdrive(float_canon_schemas() + dedent(s)) + for s in [ + """ + # The +0.0 key is the same key as the -0.0 key, so this + # replaces the (0, 1) row rather than adding a third row. + $ kafka-ingest format=avro key-format=avro topic=float-canon-upsert key-schema=${float-canon-keyschema} schema=${float-canon-schema} + {"key1": 0.0} {"f1": 3.0} + """, + """ + # Tombstone the key whose value row (f1 = -0.0) may have been + # written with the old float encoding. + $ kafka-ingest format=avro key-format=avro topic=float-canon-upsert key-schema=${float-canon-keyschema} schema=${float-canon-schema} + {"key1": 2.0} + """, + ] + ] + + def validate(self) -> Testdrive: + return Testdrive(dedent(""" + > SELECT key1::text, f1::text FROM float_canon_upsert; + 0 3 + """)) diff --git a/src/expr/src/scalar/func.rs b/src/expr/src/scalar/func.rs index 34b21e795723e..9b52c50918690 100644 --- a/src/expr/src/scalar/func.rs +++ b/src/expr/src/scalar/func.rs @@ -1305,12 +1305,14 @@ fn neg_interval_inner(a: Interval) -> Result { } fn log_guard_numeric(val: &Numeric, function_name: &str) -> Result<(), EvalError> { - if val.is_negative() { - return Err(EvalError::NegativeOutOfDomain(function_name.into())); - } + // Check zero before the sign, like PostgreSQL, so that a negative zero + // (which the dec crate considers negative) reports the same error as +0. if val.is_zero() { return Err(EvalError::ZeroOutOfDomain(function_name.into())); } + if val.is_negative() { + return Err(EvalError::NegativeOutOfDomain(function_name.into())); + } Ok(()) } @@ -1353,12 +1355,17 @@ fn log_base_numeric(mut a: Numeric, mut b: Numeric) -> Result Result { - if a == 0.0 && b.is_sign_negative() { + // Strict less-than, so that a -0.0 exponent counts as zero (x^0 = 1) + // rather than as negative, like PostgreSQL. + if a == 0.0 && b < 0.0 { return Err(EvalError::Undefined( "zero raised to a negative power".into(), )); } - if a.is_sign_negative() && b.fract() != 0.0 { + // Strict less-than, like PostgreSQL, so that -0.0 does not count as + // negative (row packing canonicalizes -0.0 to +0.0, so the sign of a zero + // must not be observable) and NaN propagates to a NaN result. + if a < 0.0 && b.fract() != 0.0 { // Equivalent to PG error: // > a negative number raised to a non-integer power yields a complex result return Err(EvalError::ComplexOutOfRange("pow".into())); diff --git a/src/expr/src/scalar/func/impls/float64.rs b/src/expr/src/scalar/func/impls/float64.rs index b709703da35b8..cfec89299c2ef 100644 --- a/src/expr/src/scalar/func/impls/float64.rs +++ b/src/expr/src/scalar/func/impls/float64.rs @@ -371,6 +371,12 @@ fn cot(a: f64) -> Result { if a.is_infinite() { return Err(EvalError::InfinityOutOfDomain("cot".into())); } + // -0.0 behaves as +0.0, so cot(-0.0) is +Infinity rather than + // PostgreSQL's -Infinity. Row packing canonicalizes -0.0 to +0.0, so + // honoring the sign here would make the result depend on whether the + // input crossed a packing boundary (e.g. a view vs a materialized view + // of the same query). + let a = if a == 0.0 { 0.0 } else { a }; Ok(1.0 / a.tan()) } @@ -384,25 +390,30 @@ fn degrees(a: f64) -> f64 { a.to_degrees() } +// The guards in `log10` and `ln` use `== 0.0` and `< 0.0` rather than the +// sign bit, so that -0.0 errors like +0.0 and NaN propagates to a NaN result, +// like PostgreSQL. The sign of a zero must not be observable, since row +// packing canonicalizes -0.0 to +0.0. + #[sqlfunc(sqlname = "log10f64")] fn log10(a: f64) -> Result { - if a.is_sign_negative() { - return Err(EvalError::NegativeOutOfDomain("log10".into())); - } if a == 0.0 { return Err(EvalError::ZeroOutOfDomain("log10".into())); } + if a < 0.0 { + return Err(EvalError::NegativeOutOfDomain("log10".into())); + } Ok(a.log10()) } #[sqlfunc(sqlname = "lnf64")] fn ln(a: f64) -> Result { - if a.is_sign_negative() { - return Err(EvalError::NegativeOutOfDomain("ln".into())); - } if a == 0.0 { return Err(EvalError::ZeroOutOfDomain("ln".into())); } + if a < 0.0 { + return Err(EvalError::NegativeOutOfDomain("ln".into())); + } Ok(a.ln()) } diff --git a/src/expr/src/scalar/func/impls/numeric.rs b/src/expr/src/scalar/func/impls/numeric.rs index 6a7bc6d67a57c..a25eabfe7cdd7 100644 --- a/src/expr/src/scalar/func/impls/numeric.rs +++ b/src/expr/src/scalar/func/impls/numeric.rs @@ -79,12 +79,14 @@ fn floor_numeric(mut a: Numeric) -> Numeric { } fn log_guard_numeric(val: &Numeric, function_name: &str) -> Result<(), EvalError> { - if val.is_negative() { - return Err(EvalError::NegativeOutOfDomain(function_name.into())); - } + // Check zero before the sign, like PostgreSQL, so that a negative zero + // (which the dec crate considers negative) reports the same error as +0. if val.is_zero() { return Err(EvalError::ZeroOutOfDomain(function_name.into())); } + if val.is_negative() { + return Err(EvalError::NegativeOutOfDomain(function_name.into())); + } Ok(()) } diff --git a/src/storage-types/src/snapshots/source-datas.txt b/src/storage-types/src/snapshots/source-datas.txt index b1cbc40f37bd8..1df1872496b5e 100644 --- a/src/storage-types/src/snapshots/source-datas.txt +++ b/src/storage-types/src/snapshots/source-datas.txt @@ -4,7 +4,7 @@ ChgKBQoDggEACgYKAioAEAEKBwoDggEAEAESAwoBWBIFCgNcZ2MSBQoDaE1f,ClIKJUIjW/KIr78nbfK Cr4BCgUKA4ICAAoECgJCAAquAQqrAbIBpwEKEQoICgZVel9lRl8SBQoDmgIAChQKCgoITFBrb0tmelASBgoCIgAQAQoYCg8KDV1qdGhXZldLRkpbYFISBQoDsgIACg8KBQoDbW9lEgYKAkIAEAEKGAoECgJTUBIQCgyKAgkKBzoFCgMIigEQAQoUCgkKB0J2Xk5MR2kSBwoDogIAEAEKFAoJCgd2Y09Eb0JCEgcKA/oBABABIgsQseHs9rfmiPfSARIGCgRBcl9lEg0KC2Z6XlN0Tl5ETExiEgQKAnRr,CsIBCgvwAb2/6rGEn63vRQoIggEFCOG2xhcKqAGyAaQBCjFCLwQNYlUJXFzzi5KkAOKArmnyvayh85OTpUT0grmwAFxJ0ahFYOSQne+/vQs0Ij0uCgsg5Zrij/HOorSNAQoTmgEQCNiMDhAFGOqOAiDo08P3AQoHggEECIm1agog+gEdChsiGcIBFgoJORRyZSaQc2JsEOr//////////wEKFYoCEgiqyqF3EJKJ4hkaBgiOgICAFAoL6AHzzI3i+IrGw1Q= ChYKBwoD6gEAEAEKBQoD8gEACgQKAmIAEg8KDVVtVVRgSUtOR1NHbl4SEQoPYHpJYE9ScXp2YHpnXnNjEgQKAnFb,CjEKBeABn5cBCgfQAc/yuY8HCh+iARwIvt6DqwcQhYOLg/z/////ARiI68f3+vWtxJoB CgA=,CgA= -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OVImwrHCk8KUJAkL86eBhfGjrY8mGxIfogEcCJL93LwFELTgte3+/////wEYz8qZyISI99XnAQowCg4vXPK4pIfDqyHwp4CBJRIeogEbCJHv3tD8/////wEQhvSi6gMYgde735qT5O1IClQKLTpmJy/ChVwm77u/0agl77u/PPGVkp46bwlcctGo8pS5qvKTjKvzp62H8ZGXpxIjogEgCLXBjJP//////wEQ9Jvirf3/////ARjvo7au1eTU/S8KWAowOvCsnr8DJXvIuvCdsq3zs4Sg8YCIiWAAOvSPs7vigK7xi5m9Gwt/Ae+7v8KkDX9LEiSiASEInt/Jwfj/////ARCe5uqJ+P////8BGO3i1J6+vurQ7gEKQAodP/KcgIcuOvKBqpbzjb2iACQyw6RmKvCflbTvu78SH6IBHAirw+L++f////8BEIuqxdwGGNj9647N69T4swEKWgo8QHPDjg3yk6SVC/GfuKnwuIeuB++/vfKOvYFxIgtgDe+/vX/zv4mdY/GRg5/yko2j8YKZuiZ8PV7CpVxgEhqiARcI2c+ntwUQhfr8rQQYrpHDpuLF0ur4AQpKCidNfwcLw6444oCudDEk77u/LnwlJCDyv7muL/CppKnypoeV8Jeksw0SH6IBHAjWsqfzBRCJq7Sd+v////8BGIzRhabu3+jw/AEKOQobYRsNRUAL9IW7ii42InvyvaOH85CamQt/e8OIEhqiARcIgI/H8gUQ/5D9zAUYpaeg9cbglPfPAQpeCjZz8KS6gHtyOfCaq41PBAd/Vy9K8biRv/GVl5hp8Yesjj8md07zmY+78J+VtAABXncAYO+7vwkSJKIBIQiMheea+f////8BEOX+nJj9/////wEYyc7x4+W39Y+4AQo6ChN1J9GoWAD0gIqBPPKQo4DwkqK7EiOiASAI+qDE/Pn/////ARCEhei5+P////8BGOWXkPL2j8/VTwpGCh52Ksi6AjzysbC28ZmflCpo77+9yLomfPOziJbCpWASJKIBIQjD65X0/P////8BEKab0KH8/////wEY3avb883fmebvAQpDCiV2PWDIuvCflbReL2A9SiTvu797YOWgkAALVvOrqZs9bHvygqWvEhqiARcIr6SxvQUQvvuSqAIYt7mwwdinptivAQprCkN58aCojCck8r6nsyY885+9vvCimr4/8qWUkQM98pWrkfOvp5ANe/KkmbbxqI60XG/zvri9PfCngqlk4oCu8bm8g9GoEiSiASEI+6Xr4vz/////ARCnn7jX+P////8BGLHvq/aT/PyB8wEKVgo4wqXwoKWFKFwlPNGo8qqSkPKLg6w/Y/G5raBWfDwGV8i6PHZAJPConJ/xrpSEP/Ctto3yprCpL3oSGqIBFwjlxNrrBhDXmIvLBhiu+svdhJOvwbcBCjUKDcOjPyov4oCu0ajigK4SJKIBIQi12ZTP+/////8BEKvQrvb7/////wEYjoG8tM/k353CAQpMCiTIujQbYAkl77+99ImEvCJ6dCcNwph18LWquMOaPTwb8JO9lAESJKIBIQiE+Jze/f////8BELPNsKX5/////wEY7ZaJ2J7k54P0AQoVCg/RqCbIuiEuBMi6atGowrUSAggBCikKI+G8p0xeBPORmK3RqEZjPCrCq3sLQyIG8r6MuRvwoIqHayc1EgIIAQo9ChXigK5gJ0fwkKOkYAUbL/Goh69cWi4SJKIBIQjEoaz9+/////8BEO3Rh874/////wEYm+62x8+e08+3AQo0ChLigK7wkpqHLirvgJ0n7IiSAFUSHqIBGwiL8KncBRDjifWI/P////8BGIm0wdqFk9ncZwo1Chjvv70qe/Ctsavwn5W0JPSGq4XvjJolf38SGaIBFgj2sMS4BBCjxNfNAhjm7auV8ITTmWcKIwoF8b+qjRsSGqIBFwjHg7KzAxDhhOLhARivr+m57fKdpL8BCjIKCvOTt6Zg85ezviUSJKIBIQisubaC//////8BEOOYxeD7/////wEYvtei8sCt0rmkAQo1ChLzvpKP8o+OjD0FPD8mXGI9PyISH6IBHAjf183Z+/////8BEJyj3KQBGIy2iracxqHunAEKSAol9IC6kX0uevCjpZIv87+9miZdT/GcgLX0iLy1wqLwnZy/86yeoRIfogEcCJGchrz5/////wEQ/5iWugIYnra4p6ac8fToAQpRCi70hIW+8r+NmPG8u4888J+VtEdTwqUqc1wkBGDznaKy77u/yLpxw5gbP0nyq7m5Eh+iARwIsZTx8f7/////ARD1/IyoBhjp//3NjuzU+fUBCgzCAQkKBVghUVRMEAgKCIIBBQi20agF diff --git a/src/storage-types/src/sources.rs b/src/storage-types/src/sources.rs index 201578b1e2fd8..370a0dcca0a0b 100644 --- a/src/storage-types/src/sources.rs +++ b/src/storage-types/src/sources.rs @@ -2183,7 +2183,11 @@ mod tests { // If you need to change how SourceDatas are encoded, that can be // okay, but think through the consequences: a record whose old and // new encodings differ never consolidates away inside existing - // persist shards. Loop in the persist team. + // persist shards, so an addition written by an old version and its + // retraction written by a new version both stay in the shard + // forever. Readers stay correct because they consolidate rows after + // decoding, where the two encodings become identical, but every + // reader must tolerate such pairs. Loop in the persist team. assert_eq!( encoded, reencoded.as_str(), diff --git a/test/sqllogictest/funcs.slt b/test/sqllogictest/funcs.slt index 17c1c0fc5509d..a40d6a8068319 100644 --- a/test/sqllogictest/funcs.slt +++ b/test/sqllogictest/funcs.slt @@ -1240,10 +1240,13 @@ SELECT cot(0::double) ---- inf +# cot treats -0.0 as +0.0 so that the result does not depend on whether the +# input crossed a packing boundary (packing canonicalizes -0.0 to +0.0). +# PostgreSQL returns -Infinity here. query R SELECT cot(-0::double) ---- --inf +inf query R SELECT sin(1::double) @@ -1477,6 +1480,42 @@ SELECT ln(-1) query error function ln is not defined for zero SELECT ln(0) +# A float -0.0 input behaves exactly like +0.0 in ln, log10, and power, so +# that results (and error messages) do not depend on whether the input +# crossed a packing boundary (packing canonicalizes -0.0 to +0.0). + +query error function ln is not defined for zero +SELECT ln('-0'::double) + +query error function log10 is not defined for zero +SELECT log10('-0'::double) + +query R +SELECT power('-0'::double, 0.5) +---- +0 + +query R +SELECT power(0::double, '-0'::double) +---- +1 + +# NaN inputs propagate in ln and power regardless of the NaN's sign bit, +# matching PostgreSQL. +query R +SELECT ln('-NaN'::double) +---- +NaN + +query R +SELECT power('-NaN'::double, 0.5) +---- +NaN + +# A numeric negative zero (reachable via rounding) also errors like zero. +query error function log10 is not defined for zero +SELECT log10((-0.1)::decimal(10,0)) + query R SELECT ln(13.0000::decimal(15, 5)) ----