1mod convert;
6mod database;
7mod host;
8mod postgres;
9mod replay;
10mod run;
11
12pub use convert::{ReadError, Written, read, write};
13pub use database::{Abandoned, Answer, Call, Database, OpenCursor, Outcome, Session};
14pub use postgres::{Postgres, Stream, Tls};
15pub use replay::{Recorder, Replay};
16pub use run::{Ran, SqlHost, run};
17
18use crate::vocab::SignClause;
19
20#[derive(Clone, Debug, PartialEq)]
22pub enum Value {
23 Null,
24 Int(i64),
25 Decimal { value: i128, scale: u32 },
27 Double(f64),
28 Char(String),
29 Binary(Vec<u8>),
30}
31
32impl Value {
33 pub fn decimal_text(value: i128, scale: u32) -> String {
35 let s = scale as usize;
36 let digits = format!("{:0>width$}", value.unsigned_abs(), width = s + 1);
37 let (whole, places) = digits.split_at(digits.len() - s);
38 let sign = if value < 0 { "-" } else { "" };
39 if s == 0 { format!("{sign}{whole}") } else { format!("{sign}{whole}.{places}") }
40 }
41
42 pub fn parse_decimal(text: &str) -> Option<Value> {
44 let (negative, digits) = match text.strip_prefix('-') {
45 Some(d) => (true, d),
46 None => (false, text.strip_prefix('+').unwrap_or(text)),
47 };
48 let (whole, places) = digits.split_once('.').unwrap_or((digits, ""));
49 if whole.is_empty() || !whole.bytes().chain(places.bytes()).all(|b| b.is_ascii_digit()) {
50 return None;
51 }
52 let magnitude: i128 = format!("{whole}{places}").parse().ok()?;
53 Some(Value::Decimal { value: if negative { -magnitude } else { magnitude }, scale: places.len() as u32 })
54 }
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub struct SqlError {
60 pub code: i32,
61 pub state: &'static str,
62}
63
64pub const NOT_ASSIGNABLE: SqlError = SqlError { code: -303, state: "42806" };
65pub const OUT_OF_RANGE: SqlError = SqlError { code: -304, state: "22003" };
66pub const NULL_WITHOUT_INDICATOR: SqlError = SqlError { code: -305, state: "22002" };
67pub const BAD_LENGTH: SqlError = SqlError { code: -311, state: "22501" };
68pub const UNCONVERTIBLE: SqlError = SqlError { code: -330, state: "22021" };
69
70#[derive(Clone, Debug, PartialEq, Eq)]
72pub enum HostType {
73 SmallInt { signed: bool },
74 Integer { signed: bool },
75 BigInt { signed: bool },
76 Decimal { digits: u32, scale: u32, signed: bool },
77 Zoned { digits: u32, scale: u32, signed: bool, sign: Option<SignClause> },
78 Real,
79 Double,
80 Char(u32),
81 VarChar(u32),
82 Structure(Vec<(usize, HostType)>),
84}
85
86pub fn fingerprint(text: &str) -> u32 {
88 text.bytes().fold(0x811c_9dc5, |h, b| (h ^ u32::from(b)).wrapping_mul(0x0100_0193))
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn the_fingerprint_of_nothing_is_the_fnv_offset() {
97 assert_eq!(fingerprint(""), 0x811c_9dc5);
98 assert_ne!(fingerprint("COMMIT"), fingerprint("ROLLBACK"));
99 }
100}