cratestack_sql/values/sql_value.rs
1use cratestack_core::Value;
2
3use super::decimal_like::DecimalLike;
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum SqlValue {
7 Bool(bool),
8 Int(i64),
9 Float(f64),
10 String(String),
11 Bytes(Vec<u8>),
12 Uuid(uuid::Uuid),
13 DateTime(chrono::DateTime<chrono::Utc>),
14 Json(Value),
15 /// Holds whichever concrete decimal type the originating schema chose
16 /// (cratestack#505 Direction 2 — `rust_decimal::Decimal`,
17 /// `bigdecimal::BigDecimal`, or any other [`DecimalLike`]
18 /// implementer), boxed rather than a fixed concrete type so two
19 /// schemas that chose different backends can share this one compiled
20 /// `SqlValue` without a Cargo-feature union collision. Unconditional —
21 /// no `#[cfg]` gate, since this variant no longer names a concrete
22 /// backend type at all.
23 Decimal(Box<dyn DecimalLike>),
24 /// A `Vector(n)` field's value (see `docs/design/extensions.md`
25 /// §6). Defined unconditionally — no `pgvector` dependency is
26 /// needed to hold a `Vec<f32>` — but only ever constructed by
27 /// generated code gated on the `pgvector` Cargo feature (#161's
28 /// compile-time check), and only ever bound to a real column by
29 /// `cratestack-sqlx`'s own `pgvector`-gated encode path.
30 Vector(Vec<f32>),
31 NullBool,
32 NullInt,
33 NullFloat,
34 NullString,
35 NullBytes,
36 NullUuid,
37 NullDateTime,
38 NullJson,
39 NullDecimal,
40 NullVector,
41}
42
43#[derive(Debug, Clone, PartialEq)]
44pub enum FilterValue {
45 None,
46 Single(SqlValue),
47 Many(Vec<SqlValue>),
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub struct SqlColumnValue {
52 pub column: &'static str,
53 pub value: SqlValue,
54}
55
56/// Detect the first duplicate value in a list of `SqlValue`s, used for
57/// batch_upsert input deduplication. Linear-scan with `PartialEq` rather
58/// than the hashed variant in `cratestack-core` because `SqlValue::Float`
59/// and `SqlValue::Decimal` don't admit a sound `Hash` impl.
60///
61/// At the documented batch cap (≤ 1000 items) the O(N²) cost is on the
62/// order of a million `PartialEq` comparisons, which dominates nothing
63/// next to a single round-trip to Postgres. Returns `(first_index,
64/// duplicate_index)` on collision, matching `cratestack_core::find_duplicate_position`.
65pub fn find_duplicate_sql_value(values: &[SqlValue]) -> Option<(usize, usize)> {
66 for (index, value) in values.iter().enumerate() {
67 if let Some(earlier) = values[..index].iter().position(|prior| prior == value) {
68 return Some((earlier, index));
69 }
70 }
71 None
72}