Skip to main content

cratestack_sql/values/
sql_value.rs

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