Skip to main content

rupg_func/
compare.rs

1//! The comparison operators of the types, and `GREATEST` and `LEAST` through `larger` and `smaller`. One comparison serves each type, as the B-tree support function `cmp` of the type does.
2
3use std::cmp::Ordering;
4
5use rupg_common::{Error, Result, SqlState};
6use rupg_pgcatalog::builtin::{self, ProcRow};
7use rupg_types::{
8    Array, DATE_INFINITY, DATE_NEGATIVE_INFINITY, POSTGRES_EPOCH_JDATE, Record, RegKind,
9    TIMESTAMP_INFINITY, TIMESTAMP_NEGATIVE_INFINITY, UNIX_TO_POSTGRES_USECS, USECS_PER_DAY,
10    USECS_PER_SEC, Value, format_type, oid,
11};
12
13use crate::cast::{TIMESTAMP_END_JULIAN, infinite, valid_timestamp};
14use crate::io::base_type;
15use crate::{Call, Kernel, bad_value, multirange, range};
16
17/// The groups of types that compare with each other.
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19enum Family {
20    Integer,
21    Float,
22    Numeric,
23    Text,
24    Bool,
25    Char,
26    Oid,
27    Bytea,
28    Uuid,
29    Network,
30    Date,
31    Time,
32    TimeTz,
33    Timestamp,
34    TimestampTz,
35    Interval,
36    Array,
37    Money,
38    Tid,
39    /// `xid8` and `oid8`, which hold a `u64` in the bits of a `bigint`.
40    Xid8,
41    Oid8,
42    Macaddr,
43    /// `macaddr8`, which holds a `u64` in the bits of a `bigint`.
44    Macaddr8,
45    /// `pg_lsn`, which holds a `u64` in the bits of a `bigint`.
46    PgLsn,
47    /// `bit` and `bit varying`, a string of `0` and `1` whose byte order is the order of `bitcmp`.
48    Bit,
49    /// The range types, which compare as `range_cmp` does.
50    Range,
51    /// The multirange types, which compare as `multirange_cmp` does.
52    Multirange,
53    /// `tsvector`, which compares as `tsvector_cmp` does.
54    TsVector,
55    /// `tsquery`, which compares as `tsquery_cmp` does.
56    TsQuery,
57    /// `record`, which compares as `record_cmp` does.
58    Record,
59    /// The enum types. A value is the OID of its label. The even OIDs of the labels of a type are in their sort order, so two even OIDs compare as numbers, as `enum_cmp_internal` compares them. An odd OID comes from `ALTER TYPE ... ADD VALUE` when no even OID sorts correctly, and then the places of the labels in the sort order give the order.
60    Enum,
61}
62
63/// The family of a type, or `None` for a type that has no comparison here.
64fn family(ty: u32) -> Option<Family> {
65    let ty = base_type(ty);
66    Some(match ty {
67        oid::INT2 | oid::INT4 | oid::INT8 => Family::Integer,
68        oid::FLOAT4 | oid::FLOAT8 => Family::Float,
69        oid::NUMERIC => Family::Numeric,
70        oid::TEXT | oid::NAME | oid::VARCHAR | oid::BPCHAR => Family::Text,
71        oid::BOOL => Family::Bool,
72        oid::CHAR => Family::Char,
73        oid::OID => Family::Oid,
74        ty if RegKind::from_oid(ty).is_some() => Family::Oid,
75        oid::BYTEA => Family::Bytea,
76        oid::UUID => Family::Uuid,
77        oid::INET | oid::CIDR => Family::Network,
78        oid::DATE => Family::Date,
79        oid::TIME => Family::Time,
80        oid::TIMETZ => Family::TimeTz,
81        oid::TIMESTAMP => Family::Timestamp,
82        oid::TIMESTAMPTZ => Family::TimestampTz,
83        oid::INTERVAL => Family::Interval,
84        oid::ANYARRAY | oid::INT2VECTOR | oid::OIDVECTOR => Family::Array,
85        oid::MONEY => Family::Money,
86        oid::TID => Family::Tid,
87        oid::XID8 => Family::Xid8,
88        oid::OID8 => Family::Oid8,
89        oid::MACADDR => Family::Macaddr,
90        oid::MACADDR8 => Family::Macaddr8,
91        oid::PG_LSN => Family::PgLsn,
92        oid::BIT | oid::VARBIT => Family::Bit,
93        oid::ANYRANGE => Family::Range,
94        ty if builtin::range_subtype(ty).is_some() => Family::Range,
95        oid::ANYMULTIRANGE => Family::Multirange,
96        ty if builtin::multirange_range(ty).is_some() => Family::Multirange,
97        oid::TSVECTOR => Family::TsVector,
98        oid::TSQUERY => Family::TsQuery,
99        oid::RECORD => Family::Record,
100        oid::ANYENUM => Family::Enum,
101        _ => match builtin::type_by_oid(ty) {
102            Some(row) if row.category == b'A' && row.elem != 0 => Family::Array,
103            Some(row) if row.kind == b'e' => Family::Enum,
104            _ => return None,
105        },
106    })
107}
108
109/// True when one of the two types is an enum type or `anyenum`, so that two OIDs are labels of an enum type.
110fn is_enum_pair(left: u32, right: u32) -> bool {
111    family(left) == Some(Family::Enum) || family(right) == Some(Family::Enum)
112}
113
114/// `enum_cmp_internal` with an odd OID: the order of the places of the two labels in the sort order of their type.
115fn enum_order(x: u32, y: u32) -> Ordering {
116    match (builtin::enum_label_place(x), builtin::enum_label_place(y)) {
117        (Some(a), Some(b)) => a.cmp(&b),
118        _ => x.cmp(&y),
119    }
120}
121
122/// The element type of an array type, or 0.
123fn element(ty: u32) -> u32 {
124    builtin::type_by_oid(base_type(ty)).map_or(0, |row| row.elem)
125}
126
127/// The order of two values that are not null. `left` and `right` are their types: a `bpchar` value compares without its trailing spaces.
128///
129/// # Errors
130///
131/// `0A000` for values of types that do not compare here.
132pub fn compare(left: u32, a: &Value, right: u32, b: &Value) -> Result<Ordering> {
133    let trim = |ty: u32, s: &'_ str| -> usize {
134        if base_type(ty) == oid::BPCHAR { s.trim_end_matches(' ').len() } else { s.len() }
135    };
136    let unsigned =
137        |ty: u32| matches!(base_type(ty), oid::XID8 | oid::OID8 | oid::MACADDR8 | oid::PG_LSN);
138    Ok(match (a, b) {
139        (Value::Int8(x), Value::Int8(y)) if unsigned(left) && unsigned(right) => {
140            x.cast_unsigned().cmp(&y.cast_unsigned())
141        }
142        (Value::Int2(_) | Value::Int4(_) | Value::Int8(_), _) => {
143            let (Some(x), Some(y)) = (a.as_i64(), b.as_i64()) else { return Err(bad_value()) };
144            x.cmp(&y)
145        }
146        (Value::Float4(_) | Value::Float8(_), _) => {
147            let (Some(x), Some(y)) = (float(a), float(b)) else { return Err(bad_value()) };
148            float_order(x, y)
149        }
150        (Value::Numeric(x), Value::Numeric(y)) => x.compare(y),
151        (Value::Text(x), Value::Text(y)) => {
152            x.as_bytes()[..trim(left, x)].cmp(&y.as_bytes()[..trim(right, y)])
153        }
154        (Value::Bool(x), Value::Bool(y)) => x.cmp(y),
155        (Value::Char(x), Value::Char(y)) => x.cmp(y),
156        (Value::Oid(x), Value::Oid(y)) if (x | y) & 1 == 1 && is_enum_pair(left, right) => {
157            enum_order(*x, *y)
158        }
159        (Value::Oid(x), Value::Oid(y)) => x.cmp(y),
160        (Value::Bytea(x), Value::Bytea(y)) => x.cmp(y),
161        (Value::Uuid(x), Value::Uuid(y)) => x.cmp(y),
162        (Value::Inet(x), Value::Inet(y)) => rupg_types::network_cmp(x, y).cmp(&0),
163        (Value::Date(x), Value::Date(y)) => x.cmp(y),
164        (Value::Time(x), Value::Time(y))
165        | (Value::Timestamp(x), Value::Timestamp(y))
166        | (Value::TimestampTz(x), Value::TimestampTz(y)) => x.cmp(y),
167        (Value::TimeTz(t1, z1), Value::TimeTz(t2, z2)) => {
168            let utc1 = i128::from(*t1) + i128::from(*z1) * i128::from(USECS_PER_SEC);
169            let utc2 = i128::from(*t2) + i128::from(*z2) * i128::from(USECS_PER_SEC);
170            utc1.cmp(&utc2).then(z1.cmp(z2))
171        }
172        (Value::Interval(x), Value::Interval(y)) => span(x).cmp(&span(y)),
173        (Value::Array(x), Value::Array(y)) => array_order(element(left), x, element(right), y)?,
174        (Value::Range(x), Value::Range(y)) => {
175            let sub = match range::subtype(left) {
176                0 => range::subtype(right),
177                sub => sub,
178            };
179            range::range_order(sub, x, y)?
180        }
181        (Value::Multirange(x), Value::Multirange(y)) => {
182            let sub = match range::subtype(multirange::range_of(left)) {
183                0 => range::subtype(multirange::range_of(right)),
184                sub => sub,
185            };
186            multirange::multirange_order(sub, x, y)?
187        }
188        (Value::TsVector(x), Value::TsVector(y)) => rupg_types::tsvector_cmp(x, y).cmp(&0),
189        (Value::TsQuery(x), Value::TsQuery(y)) => rupg_types::tsquery_cmp(x, y).cmp(&0),
190        (Value::Record(x), Value::Record(y)) => record_order(x, y, false)?,
191        _ => return Err(bad_value()),
192    })
193}
194
195/// `record_cmp`, or `record_eq` when `equality` is true, which has its own error for a column type with no comparison: the columns in order, with a null column above the others and two null columns equal. The types of the columns must be the same up to the first column that differs, and the records must have the same number of columns when no column differs.
196fn record_order(a: &Record, b: &Record, equality: bool) -> Result<Ordering> {
197    let columns = a.types.iter().zip(&a.values).zip(b.types.iter().zip(&b.values));
198    for (n, ((&left, x), (&right, y))) in columns.enumerate() {
199        if left != right {
200            return Err(Error::new(
201                SqlState::DATATYPE_MISMATCH,
202                format!(
203                    "cannot compare dissimilar column types {} and {} at record column {}",
204                    format_type(left),
205                    format_type(right),
206                    n + 1
207                ),
208            ));
209        }
210        if family(left).is_none() && !matches!(x, Value::Record(_)) {
211            let message = if equality {
212                format!("could not identify an equality operator for type {}", format_type(left))
213            } else {
214                format!("could not identify a comparison function for type {}", format_type(left))
215            };
216            return Err(Error::new(SqlState::UNDEFINED_FUNCTION, message));
217        }
218        let order = match (x.is_null(), y.is_null()) {
219            (true, true) => Ordering::Equal,
220            (true, false) => Ordering::Greater,
221            (false, true) => Ordering::Less,
222            (false, false) => compare(left, x, right, y)?,
223        };
224        if order.is_ne() {
225            return Ok(order);
226        }
227    }
228    if a.values.len() != b.values.len() {
229        return Err(Error::new(
230            SqlState::DATATYPE_MISMATCH,
231            "cannot compare record types with different numbers of columns",
232        ));
233    }
234    Ok(Ordering::Equal)
235}
236
237/// `record_eq` and `record_ne`, which have the error of `record_eq` for a column type with no equality.
238fn record_equal(args: &[Value]) -> Result<bool> {
239    let [Value::Record(a), Value::Record(b)] = args else { return Err(bad_value()) };
240    Ok(record_order(a, b, true)?.is_eq())
241}
242
243/// True when two values that are not null are equal.
244///
245/// # Errors
246///
247/// The errors of [`compare`].
248pub fn equal(left: u32, a: &Value, right: u32, b: &Value) -> Result<bool> {
249    Ok(compare(left, a, right, b)?.is_eq())
250}
251
252/// The value of a `float4` or a `float8` as a double.
253pub(crate) fn float(v: &Value) -> Option<f64> {
254    match v {
255        Value::Float4(x) => Some(f64::from(*x)),
256        Value::Float8(x) => Some(*x),
257        _ => None,
258    }
259}
260
261/// `float8_cmp_internal`: NaN is equal to itself and above every other value.
262fn float_order(x: f64, y: f64) -> Ordering {
263    match (x.is_nan(), y.is_nan()) {
264        (true, true) => Ordering::Equal,
265        (true, false) => Ordering::Greater,
266        (false, true) => Ordering::Less,
267        (false, false) => x.partial_cmp(&y).unwrap_or(Ordering::Equal),
268    }
269}
270
271/// `interval_cmp_value`: the length of an interval in microseconds, with 30 days in a month and 24 hours in a day.
272pub(crate) fn span(period: &rupg_types::Interval) -> i128 {
273    let days = i128::from(period.month) * 30 + i128::from(period.day);
274    i128::from(period.time) + days * i128::from(USECS_PER_DAY)
275}
276
277/// `array_cmp`: the elements in order, a null element above the others, then the number of elements, the number of dimensions, the lengths and the lower bounds.
278fn array_order(left: u32, a: &Array<Value>, right: u32, b: &Array<Value>) -> Result<Ordering> {
279    for (x, y) in a.values.iter().zip(&b.values) {
280        let order = match (x, y) {
281            (None, None) => Ordering::Equal,
282            (None, Some(_)) => Ordering::Greater,
283            (Some(_), None) => Ordering::Less,
284            (Some(x), Some(y)) => compare(left, x, right, y)?,
285        };
286        if order.is_ne() {
287            return Ok(order);
288        }
289    }
290    let lens = |arr: &Array<Value>| arr.dims.iter().map(|d| d.len).collect::<Vec<_>>();
291    let lowers = |arr: &Array<Value>| arr.dims.iter().map(|d| d.lower).collect::<Vec<_>>();
292    Ok(a.values
293        .len()
294        .cmp(&b.values.len())
295        .then(a.dims.len().cmp(&b.dims.len()))
296        .then_with(|| lens(a).cmp(&lens(b)))
297        .then_with(|| lowers(a).cmp(&lowers(b))))
298}
299
300/// The order of the two arguments of a call.
301fn order(call: &Call<'_>, args: &[Value]) -> Result<Ordering> {
302    let [a, b] = args else { return Err(bad_value()) };
303    if let Some(order) = mixed_datetime(call, a, b)? {
304        return Ok(order);
305    }
306    let left = call.args.first().copied().unwrap_or(0);
307    let right = call.args.get(1).copied().unwrap_or(0);
308    if family(left) == Some(Family::Array)
309        && family(right) == Some(Family::Array)
310        && base_type(element(left)) != base_type(element(right))
311    {
312        return Err(Error::new(
313            SqlState::DATATYPE_MISMATCH,
314            "cannot compare arrays of different element types",
315        ));
316    }
317    compare(left, a, right, b)
318}
319
320fn eq(call: &Call<'_>, args: &[Value]) -> Result<Value> {
321    Ok(Value::Bool(order(call, args)?.is_eq()))
322}
323
324fn ne(call: &Call<'_>, args: &[Value]) -> Result<Value> {
325    Ok(Value::Bool(order(call, args)?.is_ne()))
326}
327
328fn lt(call: &Call<'_>, args: &[Value]) -> Result<Value> {
329    Ok(Value::Bool(order(call, args)?.is_lt()))
330}
331
332fn le(call: &Call<'_>, args: &[Value]) -> Result<Value> {
333    Ok(Value::Bool(order(call, args)?.is_le()))
334}
335
336fn gt(call: &Call<'_>, args: &[Value]) -> Result<Value> {
337    Ok(Value::Bool(order(call, args)?.is_gt()))
338}
339
340fn ge(call: &Call<'_>, args: &[Value]) -> Result<Value> {
341    Ok(Value::Bool(order(call, args)?.is_ge()))
342}
343
344/// `larger`: the first argument when it is greater, else the second. So for two equal values, such as `1.0` and `1.00`, the result is the second.
345fn larger(call: &Call<'_>, args: &[Value]) -> Result<Value> {
346    let pick = !order(call, args)?.is_gt();
347    Ok(args[usize::from(pick)].clone())
348}
349
350/// `smaller`: the first argument when it is less, else the second.
351fn smaller(call: &Call<'_>, args: &[Value]) -> Result<Value> {
352    let pick = !order(call, args)?.is_lt();
353    Ok(args[usize::from(pick)].clone())
354}
355
356/// `bpchar_larger` and `tidlarger`: the first argument when it is greater or equal, else the second.
357fn larger_or_first(call: &Call<'_>, args: &[Value]) -> Result<Value> {
358    let pick = order(call, args)?.is_lt();
359    Ok(args[usize::from(pick)].clone())
360}
361
362/// `bpchar_smaller` and `tidsmaller`: the first argument when it is less or equal, else the second.
363fn smaller_or_first(call: &Call<'_>, args: &[Value]) -> Result<Value> {
364    let pick = order(call, args)?.is_gt();
365    Ok(args[usize::from(pick)].clone())
366}
367
368/// `cmp`: -1, 0 or 1.
369fn cmp(call: &Call<'_>, args: &[Value]) -> Result<Value> {
370    Ok(Value::Int4(match order(call, args)? {
371        Ordering::Less => -1,
372        Ordering::Equal => 0,
373        Ordering::Greater => 1,
374    }))
375}
376
377/// `btoidvectorcmp`: an `oidvector` with fewer elements comes first, then the elements in order.
378fn oidvector_order(args: &[Value]) -> Result<Ordering> {
379    let [Value::Array(a), Value::Array(b)] = args else { return Err(bad_value()) };
380    let mut order = a.values.len().cmp(&b.values.len());
381    for (x, y) in a.values.iter().zip(&b.values) {
382        if order.is_ne() {
383            break;
384        }
385        let (Some(Value::Oid(x)), Some(Value::Oid(y))) = (x, y) else { return Err(bad_value()) };
386        order = x.cmp(y);
387    }
388    Ok(order)
389}
390
391fn oidvector_eq(_: &Call<'_>, args: &[Value]) -> Result<Value> {
392    Ok(Value::Bool(oidvector_order(args)?.is_eq()))
393}
394
395fn oidvector_ne(_: &Call<'_>, args: &[Value]) -> Result<Value> {
396    Ok(Value::Bool(oidvector_order(args)?.is_ne()))
397}
398
399fn oidvector_lt(_: &Call<'_>, args: &[Value]) -> Result<Value> {
400    Ok(Value::Bool(oidvector_order(args)?.is_lt()))
401}
402
403fn oidvector_le(_: &Call<'_>, args: &[Value]) -> Result<Value> {
404    Ok(Value::Bool(oidvector_order(args)?.is_le()))
405}
406
407fn oidvector_gt(_: &Call<'_>, args: &[Value]) -> Result<Value> {
408    Ok(Value::Bool(oidvector_order(args)?.is_gt()))
409}
410
411fn oidvector_ge(_: &Call<'_>, args: &[Value]) -> Result<Value> {
412    Ok(Value::Bool(oidvector_order(args)?.is_ge()))
413}
414
415fn oidvector_cmp(_: &Call<'_>, args: &[Value]) -> Result<Value> {
416    Ok(Value::Int4(match oidvector_order(args)? {
417        Ordering::Less => -1,
418        Ordering::Equal => 0,
419        Ordering::Greater => 1,
420    }))
421}
422
423/// The comparison functions of `oidvector`, which do not compare as arrays.
424fn oidvector_by_src(src: &str) -> Option<Kernel> {
425    Some(match src {
426        "oidvectoreq" => oidvector_eq,
427        "oidvectorne" => oidvector_ne,
428        "oidvectorlt" => oidvector_lt,
429        "oidvectorle" => oidvector_le,
430        "oidvectorgt" => oidvector_gt,
431        "oidvectorge" => oidvector_ge,
432        "btoidvectorcmp" => oidvector_cmp,
433        _ => return None,
434    })
435}
436
437/// True when both arguments of the function are of one family that compares here.
438fn comparable(left: u32, right: u32) -> bool {
439    let (left, right) = (family(left), family(right));
440    left.is_some() && (left == right || datetime(left) && datetime(right))
441}
442
443/// True for the families of `date`, `timestamp` and `timestamptz`, which compare with each other.
444fn datetime(family: Option<Family>) -> bool {
445    matches!(family, Some(Family::Date | Family::Timestamp | Family::TimestampTz))
446}
447
448/// The rank of a date, a timestamp and a `timestamptz` for the comparisons across these types: the value of the lower rank is promoted to the type of the higher rank.
449fn datetime_rank(value: &Value) -> Option<u8> {
450    match value {
451        Value::Date(_) => Some(0),
452        Value::Timestamp(_) => Some(1),
453        Value::TimestampTz(_) => Some(2),
454        _ => None,
455    }
456}
457
458/// A date, a timestamp or a `timestamptz` promoted to the type of a rank, as `date2timestamp_safe`, `date2timestamptz_safe` and `timestamp2timestamptz_safe` give it with a soft error. The result is twice the microseconds. A value above the range is one less than twice infinity and a value below the range is one more than twice minus infinity, so it is above or below each finite value and not equal to an infinity, as `date_cmp_timestamp_internal`, `date_cmp_timestamptz_internal` and `timestamp_cmp_timestamptz_internal` give it.
459fn promote(call: &Call<'_>, value: &Value, rank: u8) -> Result<i128> {
460    let above = 2 * i128::from(TIMESTAMP_INFINITY) - 1;
461    let below = 2 * i128::from(TIMESTAMP_NEGATIVE_INFINITY) + 1;
462    let ts = match *value {
463        Value::Date(DATE_NEGATIVE_INFINITY) => TIMESTAMP_NEGATIVE_INFINITY,
464        Value::Date(DATE_INFINITY) => TIMESTAMP_INFINITY,
465        Value::Date(d) if d >= TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE => return Ok(above),
466        Value::Date(d) => i64::from(d) * USECS_PER_DAY,
467        Value::Timestamp(t) | Value::TimestampTz(t) => t,
468        _ => return Err(bad_value()),
469    };
470    if infinite(ts) || rank < 2 || matches!(value, Value::TimestampTz(_)) {
471        return Ok(2 * i128::from(ts));
472    }
473    let zone = call.session.zone()?;
474    let seconds = ts.div_euclid(USECS_PER_SEC) - UNIX_TO_POSTGRES_USECS / USECS_PER_SEC;
475    let (west, _) = rupg_types::local_offset(seconds, zone.as_ref());
476    let utc = ts + i64::from(west) * USECS_PER_SEC;
477    Ok(if valid_timestamp(utc) {
478        2 * i128::from(utc)
479    } else if ts < 0 {
480        below
481    } else {
482        above
483    })
484}
485
486/// The order of a date, a timestamp and a `timestamptz` of two different types, or `None` for other values.
487fn mixed_datetime(call: &Call<'_>, a: &Value, b: &Value) -> Result<Option<Ordering>> {
488    let (Some(x), Some(y)) = (datetime_rank(a), datetime_rank(b)) else { return Ok(None) };
489    if x == y {
490        return Ok(None);
491    }
492    let rank = x.max(y);
493    Ok(Some(promote(call, a, rank)?.cmp(&promote(call, b, rank)?)))
494}
495
496/// The order of two values of one type, or of two values of `date`, `timestamp` and `timestamptz`.
497pub(crate) fn datetime_order(call: &Call<'_>, a: &Value, b: &Value) -> Result<Ordering> {
498    match mixed_datetime(call, a, b)? {
499        Some(order) => Ok(order),
500        None => compare(0, a, 0, b),
501    }
502}
503
504/// The kernel of a comparison operator for its argument types.
505pub(crate) fn by_operator(name: &str, left: u32, right: u32) -> Option<Kernel> {
506    if !comparable(left, right) {
507        return None;
508    }
509    Some(match name {
510        "=" => eq,
511        "<>" => ne,
512        "<" => lt,
513        "<=" => le,
514        ">" => gt,
515        ">=" => ge,
516        _ => return None,
517    })
518}
519
520/// The number of an `xid`, or of the `int4` of `xideqint4` and `xidneqint4`, which compare the bits of the `int4` as an `xid`.
521fn xid_number(value: &Value) -> Result<u32> {
522    match value {
523        Value::Oid(n) => Ok(*n),
524        Value::Int4(n) => Ok(n.cast_unsigned()),
525        _ => Err(bad_value()),
526    }
527}
528
529/// `xideq` and `xidneq` of `xid.c`: the values are equal when the numbers are equal.
530fn xid_eq(_: &Call<'_>, args: &[Value]) -> Result<Value> {
531    let [a, b] = args else { return Err(bad_value()) };
532    Ok(Value::Bool(xid_number(a)? == xid_number(b)?))
533}
534
535fn xid_ne(_: &Call<'_>, args: &[Value]) -> Result<Value> {
536    let [a, b] = args else { return Err(bad_value()) };
537    Ok(Value::Bool(xid_number(a)? != xid_number(b)?))
538}
539
540/// The kernel of `larger`, `smaller` and the B-tree `cmp` functions.
541pub(crate) fn by_src(src: &str, proc: &ProcRow) -> Option<Kernel> {
542    let [left, right] = proc.argtypes else { return None };
543    // `xid` and `cid` have `=` and `<>` and no order, so they are not in a family.
544    match src {
545        "xideq" | "cideq" => return Some(xid_eq),
546        "xidneq" => return Some(xid_ne),
547        "record_eq" => return Some(|_, args| record_equal(args).map(Value::Bool)),
548        "record_ne" => return Some(|_, args| record_equal(args).map(|eq| Value::Bool(!eq))),
549        _ => {}
550    }
551    if !comparable(*left, *right) {
552        return None;
553    }
554    // The operators `~<~`, `~<=~`, `~>=~` and `~>~` compare the bytes, as the comparison of text does here.
555    match src {
556        "text_pattern_lt" | "bpchar_pattern_lt" => return Some(lt),
557        "text_pattern_le" | "bpchar_pattern_le" => return Some(le),
558        "text_pattern_ge" | "bpchar_pattern_ge" => return Some(ge),
559        "text_pattern_gt" | "bpchar_pattern_gt" => return Some(gt),
560        _ => {}
561    }
562    if let Some(kernel) = oidvector_by_src(src) {
563        Some(kernel)
564    } else if src == "bpchar_larger" || src == "tidlarger" {
565        Some(larger_or_first)
566    } else if src == "bpchar_smaller" || src == "tidsmaller" {
567        Some(smaller_or_first)
568    } else if src.ends_with("larger") {
569        Some(larger)
570    } else if src.ends_with("smaller") {
571        Some(smaller)
572    } else if src.starts_with("bt") && src.ends_with("cmp")
573        || matches!(
574            src,
575            "array_cmp"
576                | "cash_cmp"
577                | "xid8cmp"
578                | "macaddr_cmp"
579                | "macaddr8_cmp"
580                | "pg_lsn_cmp"
581                | "range_cmp"
582                | "multirange_cmp"
583                | "bpcharcmp"
584                | "enum_cmp"
585                | "byteacmp"
586                | "date_cmp"
587                | "date_cmp_timestamp"
588                | "date_cmp_timestamptz"
589                | "interval_cmp"
590                | "numeric_cmp"
591                | "time_cmp"
592                | "timestamp_cmp"
593                | "timestamp_cmp_date"
594                | "timestamp_cmp_timestamptz"
595                | "timestamptz_cmp_date"
596                | "timestamptz_cmp_timestamp"
597                | "timetz_cmp"
598                | "uuid_cmp"
599        )
600    {
601        Some(cmp)
602    } else {
603        None
604    }
605}