Skip to content

Commit

Permalink
Implement placeholders
Browse files Browse the repository at this point in the history
  • Loading branch information
aqrln committed Oct 26, 2024
1 parent 1f21ada commit 9a3e7e6
Show file tree
Hide file tree
Showing 24 changed files with 441 additions and 88 deletions.
73 changes: 69 additions & 4 deletions libs/prisma-value/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,45 @@ pub enum PrismaValue {

#[serde(serialize_with = "serialize_bytes")]
Bytes(Vec<u8>),
// Placeholder {
// name: String,
// r#type: Type,
// }

#[serde(serialize_with = "serialize_placeholder")]
Placeholder {
name: String,
r#type: PlaceholderType,
},
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub enum PlaceholderType {
Any,
String,
Int,
BigInt,
Float,
Boolean,
Decimal,
Date,
Array(Box<PlaceholderType>),
Object,
Bytes,
}

impl std::fmt::Display for PlaceholderType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PlaceholderType::Any => write!(f, "Any"),
PlaceholderType::String => write!(f, "String"),
PlaceholderType::Int => write!(f, "Int"),
PlaceholderType::BigInt => write!(f, "BigInt"),
PlaceholderType::Float => write!(f, "Float"),
PlaceholderType::Boolean => write!(f, "Boolean"),
PlaceholderType::Decimal => write!(f, "Decimal"),
PlaceholderType::Date => write!(f, "Date"),
PlaceholderType::Array(t) => write!(f, "Array<{}>", t),
PlaceholderType::Object => write!(f, "Object"),
PlaceholderType::Bytes => write!(f, "Bytes"),
}
}
}

/// Stringify a date to the following format
Expand Down Expand Up @@ -111,6 +146,7 @@ impl TryFrom<serde_json::Value> for PrismaValue {

Ok(PrismaValue::DateTime(date))
}

Some("bigint") => {
let value = obj
.get("prisma__value")
Expand All @@ -121,6 +157,7 @@ impl TryFrom<serde_json::Value> for PrismaValue {
.map(PrismaValue::BigInt)
.map_err(|_| ConversionFailure::new("JSON bigint value", "PrismaValue"))
}

Some("decimal") => {
let value = obj
.get("prisma__value")
Expand All @@ -131,6 +168,7 @@ impl TryFrom<serde_json::Value> for PrismaValue {
.map(PrismaValue::Float)
.map_err(|_| ConversionFailure::new("JSON decimal value", "PrismaValue"))
}

Some("bytes") => {
let value = obj
.get("prisma__value")
Expand All @@ -140,6 +178,19 @@ impl TryFrom<serde_json::Value> for PrismaValue {
decode_bytes(value).map(PrismaValue::Bytes)
}

Some("param") => {
let name = obj
.get("prisma__value")
.and_then(|v| v.as_str())
.ok_or_else(|| ConversionFailure::new("JSON param value", "PrismaValue"))?
.to_owned();

Ok(PrismaValue::Placeholder {
name,
r#type: PlaceholderType::Any,
})
}

_ => Ok(PrismaValue::Json(serde_json::to_string(&obj).unwrap())),
},
}
Expand Down Expand Up @@ -201,6 +252,19 @@ where
map.end()
}

fn serialize_placeholder<S>(name: &str, r#type: &PlaceholderType, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(3))?;

map.serialize_entry("prisma__type", "param")?;
map.serialize_entry("prisma__value", name)?;
map.serialize_entry("prisma__paramType", r#type)?;

map.end()
}

struct BigDecimalVisitor;

impl<'de> serde::de::Visitor<'de> for BigDecimalVisitor {
Expand Down Expand Up @@ -349,6 +413,7 @@ impl fmt::Display for PrismaValue {

write!(f, "{{ {joined} }}")
}
PrismaValue::Placeholder { name, r#type } => write!(f, "var({name}: {type})"),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion quaint/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ pub use select::{DistinctType, Select};
pub use table::*;
pub use union::Union;
pub use update::*;
pub use values::{IntoRaw, Raw, Value, ValueType, Values};
pub use values::{IntoRaw, Raw, Value, ValueType, Values, VarType};
pub(crate) use values::{NativeColumnType, Params};
68 changes: 67 additions & 1 deletion quaint/src/ast/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub struct Value<'a> {

impl<'a> Value<'a> {
/// Returns the native column type of the value, if any, in the form
/// of an UPCASE string. ex: "VARCHAR, BYTEA, DATE, TIMEZ"
/// of an UPCASE string. ex: "VARCHAR, BYTEA, DATE, TIMEZ"
pub fn native_column_type_name(&'a self) -> Option<&'a str> {
self.native_column_type.as_deref()
}
Expand Down Expand Up @@ -225,6 +225,11 @@ impl<'a> Value<'a> {
ValueType::xml(value).into_value()
}

/// Creates a new variable.
pub fn var(name: impl Into<Cow<'a, str>>, ty: VarType) -> Self {
ValueType::var(name, ty).into_value()
}

/// `true` if the `Value` is null.
pub fn is_null(&self) -> bool {
self.typed.is_null()
Expand Down Expand Up @@ -556,6 +561,59 @@ pub enum ValueType<'a> {
Date(Option<NaiveDate>),
/// A time value.
Time(Option<NaiveTime>),
/// A variable that doesn't have a value assigned yet.
Var(Cow<'a, str>, VarType),
}

#[derive(Debug, Clone, PartialEq)]
pub enum VarType {
Unknown,
Int32,
Int64,
Float,
Double,
Text,
Enum,
Bytes,
Boolean,
Char,
Array(Box<VarType>),
Numeric,
Json,
Xml,
Uuid,
DateTime,
Date,
Time,
}

impl fmt::Display for VarType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VarType::Unknown => write!(f, "Unknown"),
VarType::Int32 => write!(f, "Int32"),
VarType::Int64 => write!(f, "Int64"),
VarType::Float => write!(f, "Float"),
VarType::Double => write!(f, "Double"),
VarType::Text => write!(f, "Text"),
VarType::Enum => write!(f, "Enum"),
VarType::Bytes => write!(f, "Bytes"),
VarType::Boolean => write!(f, "Boolean"),
VarType::Char => write!(f, "Char"),
VarType::Array(t) => {
write!(f, "Array<")?;
t.fmt(f)?;
write!(f, ">")
}
VarType::Numeric => write!(f, "Numeric"),
VarType::Json => write!(f, "Json"),
VarType::Xml => write!(f, "Xml"),
VarType::Uuid => write!(f, "Uuid"),
VarType::DateTime => write!(f, "DateTime"),
VarType::Date => write!(f, "Date"),
VarType::Time => write!(f, "Time"),
}
}
}

pub(crate) struct Params<'a>(pub(crate) &'a [Value<'a>]);
Expand Down Expand Up @@ -622,6 +680,7 @@ impl<'a> fmt::Display for ValueType<'a> {
ValueType::DateTime(val) => val.map(|v| write!(f, "\"{v}\"")),
ValueType::Date(val) => val.map(|v| write!(f, "\"{v}\"")),
ValueType::Time(val) => val.map(|v| write!(f, "\"{v}\"")),
ValueType::Var(name, ty) => Some(write!(f, "${name} as {ty}")),
};

match res {
Expand Down Expand Up @@ -680,6 +739,7 @@ impl<'a> From<ValueType<'a>> for serde_json::Value {
ValueType::DateTime(dt) => dt.map(|dt| serde_json::Value::String(dt.to_rfc3339())),
ValueType::Date(date) => date.map(|date| serde_json::Value::String(format!("{date}"))),
ValueType::Time(time) => time.map(|time| serde_json::Value::String(format!("{time}"))),
ValueType::Var(_, _) => todo!(),
};

match res {
Expand Down Expand Up @@ -834,6 +894,11 @@ impl<'a> ValueType<'a> {
Self::Xml(Some(value.into()))
}

/// Creates a new variable.
pub fn var(name: impl Into<Cow<'a, str>>, ty: VarType) -> Self {
Self::Var(name.into(), ty)
}

/// `true` if the `Value` is null.
pub fn is_null(&self) -> bool {
match self {
Expand All @@ -855,6 +920,7 @@ impl<'a> ValueType<'a> {
Self::Date(d) => d.is_none(),
Self::Time(t) => t.is_none(),
Self::Json(json) => json.is_none(),
Self::Var(_, _) => false,
}
}

Expand Down
57 changes: 40 additions & 17 deletions quaint/src/connector/column_type.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(not(target_arch = "wasm32"))]
use super::TypeIdentifier;

use crate::{Value, ValueType};
use crate::{ast::VarType, Value, ValueType};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColumnType {
Expand Down Expand Up @@ -99,23 +99,24 @@ impl From<&Value<'_>> for ColumnType {
impl From<&ValueType<'_>> for ColumnType {
fn from(value: &ValueType) -> Self {
match value {
ValueType::Int32(_) => ColumnType::Int32,
ValueType::Int64(_) => ColumnType::Int64,
ValueType::Float(_) => ColumnType::Float,
ValueType::Double(_) => ColumnType::Double,
ValueType::Text(_) => ColumnType::Text,
ValueType::Enum(_, _) => ColumnType::Enum,
ValueType::Int32(_) | ValueType::Var(_, VarType::Int32) => ColumnType::Int32,
ValueType::Int64(_) | ValueType::Var(_, VarType::Int64) => ColumnType::Int64,
ValueType::Float(_) | ValueType::Var(_, VarType::Float) => ColumnType::Float,
ValueType::Double(_) | ValueType::Var(_, VarType::Double) => ColumnType::Double,
ValueType::Text(_) | ValueType::Var(_, VarType::Text) => ColumnType::Text,
ValueType::Enum(_, _) | ValueType::Var(_, VarType::Enum) => ColumnType::Enum,
ValueType::EnumArray(_, _) => ColumnType::TextArray,
ValueType::Bytes(_) => ColumnType::Bytes,
ValueType::Boolean(_) => ColumnType::Boolean,
ValueType::Char(_) => ColumnType::Char,
ValueType::Numeric(_) => ColumnType::Numeric,
ValueType::Json(_) => ColumnType::Json,
ValueType::Xml(_) => ColumnType::Xml,
ValueType::Uuid(_) => ColumnType::Uuid,
ValueType::DateTime(_) => ColumnType::DateTime,
ValueType::Date(_) => ColumnType::Date,
ValueType::Time(_) => ColumnType::Time,
ValueType::Var(_, VarType::Array(vt)) if **vt == VarType::Enum => ColumnType::TextArray,
ValueType::Bytes(_) | ValueType::Var(_, VarType::Bytes) => ColumnType::Bytes,
ValueType::Boolean(_) | ValueType::Var(_, VarType::Boolean) => ColumnType::Boolean,
ValueType::Char(_) | ValueType::Var(_, VarType::Char) => ColumnType::Char,
ValueType::Numeric(_) | ValueType::Var(_, VarType::Numeric) => ColumnType::Numeric,
ValueType::Json(_) | ValueType::Var(_, VarType::Json) => ColumnType::Json,
ValueType::Xml(_) | ValueType::Var(_, VarType::Xml) => ColumnType::Xml,
ValueType::Uuid(_) | ValueType::Var(_, VarType::Uuid) => ColumnType::Uuid,
ValueType::DateTime(_) | ValueType::Var(_, VarType::DateTime) => ColumnType::DateTime,
ValueType::Date(_) | ValueType::Var(_, VarType::Date) => ColumnType::Date,
ValueType::Time(_) | ValueType::Var(_, VarType::Time) => ColumnType::Time,
ValueType::Array(Some(vals)) if !vals.is_empty() => match &vals[0].typed {
ValueType::Int32(_) => ColumnType::Int32Array,
ValueType::Int64(_) => ColumnType::Int64Array,
Expand All @@ -135,8 +136,30 @@ impl From<&ValueType<'_>> for ColumnType {
ValueType::Time(_) => ColumnType::TimeArray,
ValueType::Array(_) => ColumnType::Unknown,
ValueType::EnumArray(_, _) => ColumnType::Unknown,
ValueType::Var(_, _) => ColumnType::Unknown,
},
ValueType::Array(_) => ColumnType::Unknown,
ValueType::Var(_, VarType::Unknown) => ColumnType::Unknown,
ValueType::Var(_, VarType::Array(vt)) => match **vt {
VarType::Int32 => ColumnType::Int32Array,
VarType::Int64 => ColumnType::Int64Array,
VarType::Float => ColumnType::FloatArray,
VarType::Double => ColumnType::DoubleArray,
VarType::Text => ColumnType::TextArray,
VarType::Enum => ColumnType::TextArray,
VarType::Bytes => ColumnType::BytesArray,
VarType::Boolean => ColumnType::BooleanArray,
VarType::Char => ColumnType::CharArray,
VarType::Numeric => ColumnType::NumericArray,
VarType::Json => ColumnType::JsonArray,
VarType::Xml => ColumnType::TextArray,
VarType::Uuid => ColumnType::UuidArray,
VarType::DateTime => ColumnType::DateTimeArray,
VarType::Date => ColumnType::DateArray,
VarType::Time => ColumnType::TimeArray,
VarType::Unknown => ColumnType::Unknown,
VarType::Array(_) => ColumnType::Unknown,
},
}
}
}
Expand Down
11 changes: 10 additions & 1 deletion quaint/src/connector/mssql/native/conversion.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::ast::{Value, ValueType};
use crate::{
ast::{Value, ValueType},
error::{Error, ErrorKind},
};

use bigdecimal::BigDecimal;
use std::{borrow::Cow, convert::TryFrom};
Expand All @@ -25,6 +28,12 @@ impl<'a> IntoSql<'a> for &'a Value<'a> {
ValueType::DateTime(val) => val.into_sql(),
ValueType::Date(val) => val.into_sql(),
ValueType::Time(val) => val.into_sql(),
ValueType::Var(name, _) => {
panic!(
"conversion error: {:?}",
Error::builder(ErrorKind::RanQueryWithVarParam(name.clone().into_owned())).build()
)
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions quaint/src/connector/mysql/native/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ pub fn conv_params(params: &[Value<'_>]) -> crate::Result<my::Params> {
dt.timestamp_subsec_micros(),
)
}),

ValueType::Var(name, _) => {
Err(Error::builder(ErrorKind::RanQueryWithVarParam(name.clone().into_owned())).build())?
}
};

match res {
Expand Down
Loading

0 comments on commit 9a3e7e6

Please sign in to comment.