|
| 1 | +use spin_world::spin::postgres4_0_0::postgres::{DbDataType, DbValue, ParameterValue}; |
| 2 | +use tokio_postgres::types::{FromSql, Type}; |
| 3 | +use tokio_postgres::{types::ToSql, Row}; |
| 4 | + |
| 5 | +mod convert; |
| 6 | +mod decimal; |
| 7 | +mod interval; |
| 8 | +mod pg_null; |
| 9 | + |
| 10 | +use convert::{ |
| 11 | + date_pg_to_wit, date_wit_to_pg, datetime_pg_to_wit, datetime_wit_to_pg, |
| 12 | + decimal_array_pg_to_wit, decimal_array_wit_to_pg, decimal_range_pg_to_wit, |
| 13 | + decimal_range_wit_to_pg, decimal_wit_to_pg, jsonb_pg_to_wit, jsonb_wit_to_pg, range_pg_to_wit, |
| 14 | + range_wit_to_pg, time_pg_to_wit, time_wit_to_pg, timestamp_wit_to_pg, uuid_wit_to_pg, |
| 15 | +}; |
| 16 | +use interval::Interval; |
| 17 | +use pg_null::PgNull; |
| 18 | + |
| 19 | +pub fn convert_data_type(pg_type: &Type) -> DbDataType { |
| 20 | + match *pg_type { |
| 21 | + Type::BOOL => DbDataType::Boolean, |
| 22 | + Type::BYTEA => DbDataType::Binary, |
| 23 | + Type::FLOAT4 => DbDataType::Floating32, |
| 24 | + Type::FLOAT8 => DbDataType::Floating64, |
| 25 | + Type::INT2 => DbDataType::Int16, |
| 26 | + Type::INT4 => DbDataType::Int32, |
| 27 | + Type::INT8 => DbDataType::Int64, |
| 28 | + Type::TEXT | Type::VARCHAR | Type::BPCHAR => DbDataType::Str, |
| 29 | + Type::TIMESTAMP | Type::TIMESTAMPTZ => DbDataType::Datetime, |
| 30 | + Type::DATE => DbDataType::Date, |
| 31 | + Type::TIME => DbDataType::Time, |
| 32 | + Type::UUID => DbDataType::Uuid, |
| 33 | + Type::JSONB => DbDataType::Jsonb, |
| 34 | + Type::NUMERIC => DbDataType::Decimal, |
| 35 | + Type::INT4_RANGE => DbDataType::RangeInt32, |
| 36 | + Type::INT8_RANGE => DbDataType::RangeInt64, |
| 37 | + Type::NUM_RANGE => DbDataType::RangeDecimal, |
| 38 | + Type::INT4_ARRAY => DbDataType::ArrayInt32, |
| 39 | + Type::INT8_ARRAY => DbDataType::ArrayInt64, |
| 40 | + Type::NUMERIC_ARRAY => DbDataType::ArrayDecimal, |
| 41 | + Type::TEXT_ARRAY | Type::VARCHAR_ARRAY | Type::BPCHAR_ARRAY => DbDataType::ArrayStr, |
| 42 | + Type::INTERVAL => DbDataType::Interval, |
| 43 | + _ => { |
| 44 | + tracing::debug!("Couldn't convert Postgres type {} to WIT", pg_type.name(),); |
| 45 | + DbDataType::Other |
| 46 | + } |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +fn db_value<'a, T: FromSql<'a>>( |
| 51 | + row: &'a Row, |
| 52 | + index: usize, |
| 53 | + convert_fn: impl Fn(T) -> DbValue, |
| 54 | +) -> anyhow::Result<DbValue> { |
| 55 | + let value: Option<T> = row.try_get(index)?; |
| 56 | + Ok(match value { |
| 57 | + Some(v) => convert_fn(v), |
| 58 | + None => DbValue::DbNull, |
| 59 | + }) |
| 60 | +} |
| 61 | + |
| 62 | +fn map_db_value<'a, T: FromSql<'a>, W>( |
| 63 | + row: &'a Row, |
| 64 | + index: usize, |
| 65 | + ctor: impl Fn(W) -> DbValue, |
| 66 | + convert_fn: impl Fn(T) -> W, |
| 67 | +) -> anyhow::Result<DbValue> { |
| 68 | + let value: Option<T> = row.try_get(index)?; |
| 69 | + Ok(match value { |
| 70 | + Some(v) => ctor(convert_fn(v)), |
| 71 | + None => DbValue::DbNull, |
| 72 | + }) |
| 73 | +} |
| 74 | + |
| 75 | +fn try_map_db_value<'a, T: FromSql<'a>, W>( |
| 76 | + row: &'a Row, |
| 77 | + index: usize, |
| 78 | + ctor: impl Fn(W) -> DbValue, |
| 79 | + convert_fn: impl Fn(T) -> anyhow::Result<W>, |
| 80 | +) -> anyhow::Result<DbValue> { |
| 81 | + let value: Option<T> = row.try_get(index)?; |
| 82 | + Ok(match value { |
| 83 | + Some(v) => ctor(convert_fn(v)?), |
| 84 | + None => DbValue::DbNull, |
| 85 | + }) |
| 86 | +} |
| 87 | + |
| 88 | +pub fn convert_entry(row: &Row, index: usize) -> anyhow::Result<DbValue> { |
| 89 | + let column = &row.columns()[index]; |
| 90 | + match column.type_() { |
| 91 | + &Type::BOOL => db_value(row, index, DbValue::Boolean), |
| 92 | + &Type::BYTEA => db_value(row, index, DbValue::Binary), |
| 93 | + &Type::FLOAT4 => db_value(row, index, DbValue::Floating32), |
| 94 | + &Type::FLOAT8 => db_value(row, index, DbValue::Floating64), |
| 95 | + &Type::INT2 => db_value(row, index, DbValue::Int16), |
| 96 | + &Type::INT4 => db_value(row, index, DbValue::Int32), |
| 97 | + &Type::INT8 => db_value(row, index, DbValue::Int64), |
| 98 | + &Type::TEXT | &Type::VARCHAR | &Type::BPCHAR => db_value(row, index, DbValue::Str), |
| 99 | + &Type::TIMESTAMP | &Type::TIMESTAMPTZ => { |
| 100 | + try_map_db_value(row, index, DbValue::Datetime, datetime_pg_to_wit) |
| 101 | + } |
| 102 | + &Type::DATE => try_map_db_value(row, index, DbValue::Date, date_pg_to_wit), |
| 103 | + &Type::TIME => try_map_db_value(row, index, DbValue::Time, time_pg_to_wit), |
| 104 | + &Type::UUID => map_db_value(row, index, DbValue::Uuid, |v: uuid::Uuid| v.to_string()), |
| 105 | + &Type::JSONB => try_map_db_value(row, index, DbValue::Jsonb, jsonb_pg_to_wit), |
| 106 | + &Type::NUMERIC => map_db_value(row, index, DbValue::Decimal, |v: rust_decimal::Decimal| { |
| 107 | + v.to_string() |
| 108 | + }), |
| 109 | + &Type::INT4_RANGE => map_db_value(row, index, DbValue::RangeInt32, range_pg_to_wit), |
| 110 | + &Type::INT8_RANGE => map_db_value(row, index, DbValue::RangeInt64, range_pg_to_wit), |
| 111 | + &Type::NUM_RANGE => { |
| 112 | + map_db_value(row, index, DbValue::RangeDecimal, decimal_range_pg_to_wit) |
| 113 | + } |
| 114 | + &Type::INT4_ARRAY => db_value(row, index, DbValue::ArrayInt32), |
| 115 | + &Type::INT8_ARRAY => db_value(row, index, DbValue::ArrayInt64), |
| 116 | + &Type::NUMERIC_ARRAY => { |
| 117 | + map_db_value(row, index, DbValue::ArrayDecimal, decimal_array_pg_to_wit) |
| 118 | + } |
| 119 | + &Type::TEXT_ARRAY | &Type::VARCHAR_ARRAY | &Type::BPCHAR_ARRAY => { |
| 120 | + db_value(row, index, DbValue::ArrayStr) |
| 121 | + } |
| 122 | + &Type::INTERVAL => map_db_value(row, index, DbValue::Interval, |v: Interval| v.into()), |
| 123 | + t => { |
| 124 | + tracing::debug!( |
| 125 | + "Couldn't convert Postgres type {} in column {}", |
| 126 | + t.name(), |
| 127 | + column.name() |
| 128 | + ); |
| 129 | + Ok(DbValue::Unsupported) |
| 130 | + } |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +pub fn to_sql_parameter(value: &ParameterValue) -> anyhow::Result<Box<dyn ToSql + Send + Sync>> { |
| 135 | + match value { |
| 136 | + ParameterValue::Boolean(v) => Ok(Box::new(*v)), |
| 137 | + ParameterValue::Int32(v) => Ok(Box::new(*v)), |
| 138 | + ParameterValue::Int64(v) => Ok(Box::new(*v)), |
| 139 | + ParameterValue::Int8(v) => Ok(Box::new(*v)), |
| 140 | + ParameterValue::Int16(v) => Ok(Box::new(*v)), |
| 141 | + ParameterValue::Floating32(v) => Ok(Box::new(*v)), |
| 142 | + ParameterValue::Floating64(v) => Ok(Box::new(*v)), |
| 143 | + ParameterValue::Str(v) => Ok(Box::new(v.clone())), |
| 144 | + ParameterValue::Binary(v) => Ok(Box::new(v.clone())), |
| 145 | + ParameterValue::Date(v) => Ok(Box::new(date_wit_to_pg(v)?)), |
| 146 | + ParameterValue::Time(v) => Ok(Box::new(time_wit_to_pg(v)?)), |
| 147 | + ParameterValue::Datetime(v) => Ok(Box::new(datetime_wit_to_pg(v)?)), |
| 148 | + ParameterValue::Timestamp(v) => Ok(Box::new(timestamp_wit_to_pg(*v)?)), |
| 149 | + ParameterValue::Uuid(v) => Ok(Box::new(uuid_wit_to_pg(v)?)), |
| 150 | + ParameterValue::Jsonb(v) => Ok(Box::new(jsonb_wit_to_pg(v)?)), |
| 151 | + ParameterValue::Decimal(v) => Ok(Box::new(decimal_wit_to_pg(v)?)), |
| 152 | + ParameterValue::RangeInt32(v) => Ok(Box::new(range_wit_to_pg(*v))), |
| 153 | + ParameterValue::RangeInt64(v) => Ok(Box::new(range_wit_to_pg(*v))), |
| 154 | + ParameterValue::RangeDecimal(v) => Ok(Box::new(decimal_range_wit_to_pg(v)?)), |
| 155 | + ParameterValue::ArrayInt32(vs) => Ok(Box::new(vs.to_owned())), |
| 156 | + ParameterValue::ArrayInt64(vs) => Ok(Box::new(vs.to_owned())), |
| 157 | + ParameterValue::ArrayDecimal(vs) => Ok(Box::new(decimal_array_wit_to_pg(vs)?)), |
| 158 | + ParameterValue::ArrayStr(vs) => Ok(Box::new(vs.to_owned())), |
| 159 | + ParameterValue::Interval(v) => Ok(Box::new(Interval(*v))), |
| 160 | + ParameterValue::DbNull => Ok(Box::new(PgNull)), |
| 161 | + } |
| 162 | +} |
0 commit comments