faucet_source_redshift/
convert.rs1use serde_json::Value;
8use sqlx::{Column, Row};
9
10pub(crate) fn pg_value_to_json(row: &sqlx::postgres::PgRow, col_name: &str) -> Value {
15 if let Ok(v) = row.try_get::<Value, _>(col_name) {
16 return v;
17 }
18 if let Ok(v) = row.try_get::<String, _>(col_name) {
19 return Value::String(v);
20 }
21 if let Ok(v) = row.try_get::<i64, _>(col_name) {
22 return Value::Number(v.into());
23 }
24 if let Ok(v) = row.try_get::<i32, _>(col_name) {
25 return Value::Number(v.into());
26 }
27 if let Ok(v) = row.try_get::<i16, _>(col_name) {
28 return Value::Number(v.into());
29 }
30 if let Ok(v) = row.try_get::<f64, _>(col_name) {
31 return serde_json::Number::from_f64(v)
32 .map(Value::Number)
33 .unwrap_or(Value::Null);
34 }
35 if let Ok(v) = row.try_get::<f32, _>(col_name) {
36 return serde_json::Number::from_f64(v as f64)
37 .map(Value::Number)
38 .unwrap_or(Value::Null);
39 }
40 if let Ok(v) = row.try_get::<bool, _>(col_name) {
41 return Value::Bool(v);
42 }
43 if let Ok(v) =
45 row.try_get::<sqlx::types::chrono::DateTime<sqlx::types::chrono::Utc>, _>(col_name)
46 {
47 return Value::String(v.to_rfc3339());
48 }
49 if let Ok(v) = row.try_get::<sqlx::types::chrono::NaiveDateTime, _>(col_name) {
50 return Value::String(v.to_string());
51 }
52 if let Ok(v) = row.try_get::<sqlx::types::chrono::NaiveDate, _>(col_name) {
53 return Value::String(v.to_string());
54 }
55 if let Ok(v) = row.try_get::<sqlx::types::chrono::NaiveTime, _>(col_name) {
56 return Value::String(v.to_string());
57 }
58 if let Ok(v) = row.try_get::<sqlx::types::Uuid, _>(col_name) {
59 return Value::String(v.to_string());
60 }
61 if let Ok(v) = row.try_get::<sqlx::types::BigDecimal, _>(col_name) {
63 return Value::String(v.to_string());
64 }
65 if let Ok(v) = row.try_get::<Vec<u8>, _>(col_name) {
67 use base64::Engine as _;
68 return Value::String(base64::engine::general_purpose::STANDARD.encode(v));
69 }
70 Value::Null
71}
72
73pub(crate) fn row_to_json(row: &sqlx::postgres::PgRow) -> Value {
75 let mut map = serde_json::Map::new();
76 for col in row.columns() {
77 let name = col.name().to_string();
78 let value = pg_value_to_json(row, &name);
79 map.insert(name, value);
80 }
81 Value::Object(map)
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub(crate) enum NumberBind {
89 I64,
91 U64,
93 F64,
95}
96
97pub(crate) fn classify_number(n: &serde_json::Number) -> NumberBind {
99 if n.is_i64() {
100 NumberBind::I64
101 } else if n.is_u64() {
102 NumberBind::U64
103 } else {
104 NumberBind::F64
105 }
106}
107
108pub(crate) fn bind_params<'q>(
113 mut query: sqlx::query::Query<'q, sqlx::Postgres, sqlx::postgres::PgArguments>,
114 binds: &'q [Value],
115) -> sqlx::query::Query<'q, sqlx::Postgres, sqlx::postgres::PgArguments> {
116 for value in binds {
117 query = match value {
118 Value::String(s) => query.bind(s.clone()),
119 Value::Number(n) => match classify_number(n) {
120 NumberBind::I64 => query.bind(n.as_i64().unwrap()),
121 NumberBind::U64 => query.bind(n.as_u64().unwrap() as i64),
122 NumberBind::F64 => query.bind(n.as_f64().unwrap_or(0.0)),
123 },
124 Value::Bool(b) => query.bind(*b),
125 Value::Null => query.bind(None::<String>),
126 _ => query.bind(value.to_string()),
127 };
128 }
129 query
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use serde_json::json;
136
137 fn num(v: serde_json::Value) -> serde_json::Number {
138 match v {
139 serde_json::Value::Number(n) => n,
140 _ => panic!("not a number"),
141 }
142 }
143
144 #[test]
145 fn classify_small_int_is_i64() {
146 assert_eq!(classify_number(&num(json!(42))), NumberBind::I64);
147 assert_eq!(classify_number(&num(json!(-7))), NumberBind::I64);
148 }
149
150 #[test]
151 fn classify_above_2_pow_53_stays_i64() {
152 let v = 9_007_199_254_740_993i64; assert_eq!(classify_number(&num(json!(v))), NumberBind::I64);
154 }
155
156 #[test]
157 fn classify_above_i64_max_is_u64() {
158 let v: u64 = i64::MAX as u64 + 1;
159 assert_eq!(classify_number(&num(json!(v))), NumberBind::U64);
160 }
161
162 #[test]
163 fn classify_float_is_f64() {
164 assert_eq!(classify_number(&num(json!(3.5))), NumberBind::F64);
165 }
166}