Skip to content

Commit 4d84597

Browse files
authored
feat(qc): implement query arg conversions (#5593)
[ORM-1327](https://linear.app/prisma-company/issue/ORM-1327/implement-conversions-for-query-arguments) /prisma-branch feat/query-arg-conversions
1 parent 717184b commit 4d84597

File tree

39 files changed

+616
-410
lines changed

39 files changed

+616
-410
lines changed
Lines changed: 80 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,96 @@
1-
/// `JSArgType` is a 1:1 mapping of [`quaint::ValueType`] that:
2-
/// - only includes the type tag (e.g. `Int32`, `Text`, `Enum`, etc.)
3-
/// - doesn't care for the optionality of the actual value (e.g., `quaint::Value::Int32(None)` -> `JSArgType::Int32`)
4-
/// - is used to guide the JS side on how to serialize the query argument value before sending it to the JS driver.
51
#[derive(Debug, PartialEq)]
6-
pub enum JSArgType {
7-
/// 32-bit signed integer.
8-
Int32,
9-
/// 64-bit signed integer.
10-
Int64,
11-
/// 32-bit floating point.
2+
pub struct JSArgType {
3+
pub scalar_type: JSArgScalarType,
4+
pub db_type: Option<String>,
5+
pub arity: JSArgArity,
6+
}
7+
8+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9+
pub enum JSArgScalarType {
10+
String,
11+
Int,
12+
BigInt,
1213
Float,
13-
/// 64-bit floating point.
14-
Double,
15-
/// String value.
16-
Text,
17-
/// Database enum value.
18-
Enum,
19-
/// Database enum array (PostgreSQL specific).
20-
EnumArray,
21-
/// Bytes value.
22-
Bytes,
23-
/// Boolean value.
14+
Decimal,
2415
Boolean,
25-
/// A single character.
26-
Char,
27-
/// An array value (PostgreSQL).
28-
Array,
29-
/// A numeric value.
30-
Numeric,
31-
/// A JSON value.
32-
Json,
33-
/// A XML value.
34-
Xml,
35-
/// An UUID value.
16+
Enum,
3617
Uuid,
37-
/// A datetime value.
18+
Json,
3819
DateTime,
39-
/// A date value.
40-
Date,
41-
/// A time value.
42-
Time,
20+
Bytes,
21+
Unknown,
4322
}
4423

45-
impl core::fmt::Display for JSArgType {
46-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47-
let s = match self {
48-
JSArgType::Int32 => "Int32",
49-
JSArgType::Int64 => "Int64",
50-
JSArgType::Float => "Float",
51-
JSArgType::Double => "Double",
52-
JSArgType::Text => "Text",
53-
JSArgType::Enum => "Enum",
54-
JSArgType::EnumArray => "EnumArray",
55-
JSArgType::Bytes => "Bytes",
56-
JSArgType::Boolean => "Boolean",
57-
JSArgType::Char => "Char",
58-
JSArgType::Array => "Array",
59-
JSArgType::Numeric => "Numeric",
60-
JSArgType::Json => "Json",
61-
JSArgType::Xml => "Xml",
62-
JSArgType::Uuid => "Uuid",
63-
JSArgType::DateTime => "DateTime",
64-
JSArgType::Date => "Date",
65-
JSArgType::Time => "Time",
66-
};
24+
impl From<JSArgScalarType> for &'static str {
25+
fn from(arg: JSArgScalarType) -> Self {
26+
match arg {
27+
JSArgScalarType::String => "string",
28+
JSArgScalarType::Int => "int",
29+
JSArgScalarType::BigInt => "bigint",
30+
JSArgScalarType::Float => "float",
31+
JSArgScalarType::Decimal => "decimal",
32+
JSArgScalarType::Boolean => "boolean",
33+
JSArgScalarType::Enum => "enum",
34+
JSArgScalarType::Uuid => "uuid",
35+
JSArgScalarType::Json => "json",
36+
JSArgScalarType::DateTime => "datetime",
37+
JSArgScalarType::Bytes => "bytes",
38+
JSArgScalarType::Unknown => "unknown",
39+
}
40+
}
41+
}
42+
43+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44+
pub enum JSArgArity {
45+
Scalar,
46+
List,
47+
}
6748

68-
write!(f, "{s}")
49+
impl From<JSArgArity> for &'static str {
50+
fn from(arg: JSArgArity) -> Self {
51+
match arg {
52+
JSArgArity::Scalar => "scalar",
53+
JSArgArity::List => "list",
54+
}
6955
}
7056
}
7157

7258
pub fn value_to_js_arg_type(value: &quaint::Value) -> JSArgType {
59+
JSArgType {
60+
scalar_type: value_to_js_arg_scalar_type(value),
61+
db_type: value.native_column_type_name().map(|nt| nt.to_string()),
62+
arity: if matches!(
63+
value.typed,
64+
quaint::ValueType::Array(_) | quaint::ValueType::EnumArray(_, _)
65+
) {
66+
JSArgArity::List
67+
} else {
68+
JSArgArity::Scalar
69+
},
70+
}
71+
}
72+
73+
fn value_to_js_arg_scalar_type(value: &quaint::Value) -> JSArgScalarType {
7374
match &value.typed {
74-
quaint::ValueType::Int32(_) => JSArgType::Int32,
75-
quaint::ValueType::Int64(_) => JSArgType::Int64,
76-
quaint::ValueType::Float(_) => JSArgType::Float,
77-
quaint::ValueType::Double(_) => JSArgType::Double,
78-
quaint::ValueType::Text(_) => JSArgType::Text,
79-
quaint::ValueType::Enum(_, _) => JSArgType::Enum,
80-
quaint::ValueType::EnumArray(_, _) => JSArgType::EnumArray,
81-
quaint::ValueType::Bytes(_) => JSArgType::Bytes,
82-
quaint::ValueType::Boolean(_) => JSArgType::Boolean,
83-
quaint::ValueType::Char(_) => JSArgType::Char,
84-
quaint::ValueType::Array(_) => JSArgType::Array,
85-
quaint::ValueType::Numeric(_) => JSArgType::Numeric,
86-
quaint::ValueType::Json(_) => JSArgType::Json,
87-
quaint::ValueType::Xml(_) => JSArgType::Xml,
88-
quaint::ValueType::Uuid(_) => JSArgType::Uuid,
89-
quaint::ValueType::DateTime(_) => JSArgType::DateTime,
90-
quaint::ValueType::Date(_) => JSArgType::Date,
91-
quaint::ValueType::Time(_) => JSArgType::Time,
75+
quaint::ValueType::Int32(_) => JSArgScalarType::Int,
76+
quaint::ValueType::Int64(_) => JSArgScalarType::BigInt,
77+
quaint::ValueType::Float(_) => JSArgScalarType::Float,
78+
quaint::ValueType::Double(_) => JSArgScalarType::Float,
79+
quaint::ValueType::Text(_) | quaint::ValueType::Char(_) | quaint::ValueType::Xml(_) => JSArgScalarType::String,
80+
quaint::ValueType::Enum(_, _) | quaint::ValueType::EnumArray(_, _) => JSArgScalarType::Enum,
81+
quaint::ValueType::Bytes(_) => JSArgScalarType::Bytes,
82+
quaint::ValueType::Boolean(_) => JSArgScalarType::Boolean,
83+
quaint::ValueType::Array(vals) => vals
84+
.as_deref()
85+
.unwrap_or_default()
86+
.first()
87+
.map_or(JSArgScalarType::Unknown, value_to_js_arg_scalar_type),
88+
quaint::ValueType::Numeric(_) => JSArgScalarType::Decimal,
89+
quaint::ValueType::Json(_) => JSArgScalarType::Json,
90+
quaint::ValueType::Uuid(_) => JSArgScalarType::Uuid,
91+
quaint::ValueType::DateTime(_) | quaint::ValueType::Date(_) | quaint::ValueType::Time(_) => {
92+
JSArgScalarType::DateTime
93+
}
9294
quaint::ValueType::Opaque(_) => unreachable!("Opaque values are not supposed to be converted to JSON"),
9395
}
9496
}

libs/driver-adapters/src/conversion/mssql.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,18 @@ use serde_json::value::Value as JsonValue;
33

44
#[rustfmt::skip]
55
pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result<JSArg> {
6-
let res = match (&value.typed, value.native_column_type_name()) {
7-
(quaint::ValueType::DateTime(Some(dt)), _) => JSArg::Value(JsonValue::String(dt.naive_utc().to_string())),
8-
(quaint::ValueType::Json(Some(s)), _) => JSArg::Value(JsonValue::String(serde_json::to_string(s)?)),
9-
(quaint::ValueType::Bytes(Some(bytes)), _) => JSArg::Buffer(bytes.to_vec()),
10-
(quaint::ValueType::Int32(Some(value)), _) => JSArg::SafeInt(*value),
11-
(quaint::ValueType::Numeric(Some(bd)), _) => JSArg::Value(JsonValue::String(bd.to_string())),
12-
(quaint::ValueType::Array(Some(items)), _) => JSArg::Array(
6+
let res = match &value.typed {
7+
quaint::ValueType::Json(Some(s)) => JSArg::Value(JsonValue::String(serde_json::to_string(s)?)),
8+
quaint::ValueType::Bytes(Some(bytes)) => JSArg::Buffer(bytes.to_vec()),
9+
quaint::ValueType::Int32(Some(value)) => JSArg::SafeInt(*value),
10+
quaint::ValueType::Numeric(Some(bd)) => JSArg::Value(JsonValue::String(bd.to_string())),
11+
quaint::ValueType::Array(Some(items)) => JSArg::Array(
1312
items
1413
.iter()
1514
.map(value_to_js_arg)
1615
.collect::<serde_json::Result<Vec<JSArg>>>()?,
1716
),
18-
(quaint_value, _) => JSArg::from(JsonValue::from(quaint_value.clone())),
17+
quaint_value => JSArg::from(JsonValue::from(quaint_value.clone())),
1918
};
2019

2120
Ok(res)

libs/driver-adapters/src/conversion/mysql.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
use super::JSArg;
22
use serde_json::value::Value as JsonValue;
33

4-
const DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.f";
5-
const DATE_FORMAT: &str = "%Y-%m-%d";
6-
const TIME_FORMAT: &str = "%H:%M:%S%.f";
7-
84
#[rustfmt::skip]
95
pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result<JSArg> {
106
let res = match &value.typed {
117
quaint::ValueType::Numeric(Some(bd)) => JSArg::Value(JsonValue::String(bd.to_string())),
128
quaint::ValueType::Json(Some(s)) => JSArg::Value(JsonValue::String(serde_json::to_string(s)?)),
139
quaint::ValueType::Bytes(Some(bytes)) => JSArg::Buffer(bytes.to_vec()),
14-
quaint::ValueType::Date(Some(d)) => JSArg::Value(JsonValue::String(d.format(DATE_FORMAT).to_string())),
15-
quaint::ValueType::DateTime(Some(dt)) => JSArg::Value(JsonValue::String(dt.format(DATETIME_FORMAT).to_string())),
1610
quaint::ValueType::Int32(Some(value)) => JSArg::SafeInt(*value),
17-
quaint::ValueType::Time(Some(t)) => JSArg::Value(JsonValue::String(t.format(TIME_FORMAT).to_string())),
1811
quaint::ValueType::Array(Some(items)) => JSArg::Array(
1912
items
2013
.iter()
@@ -68,7 +61,7 @@ mod test {
6861
),
6962
(
7063
ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap().with_nanosecond(100).unwrap())),
71-
JSArg::Value(JsonValue::String("2020-01-01 23:13:01.000000100".to_string()))
64+
JSArg::Value(JsonValue::String("2020-01-01T23:13:01.000000100+00:00".to_string()))
7265
),
7366
(
7467
ValueType::DateTime(None),

libs/driver-adapters/src/conversion/postgres.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
use std::sync::LazyLock;
2-
31
use crate::conversion::JSArg;
4-
use quaint::chrono::format::StrftimeItems;
52
use serde_json::value::Value as JsonValue;
63

7-
static TIME_FMT: LazyLock<StrftimeItems> = LazyLock::new(|| StrftimeItems::new("%H:%M:%S%.f"));
8-
94
#[rustfmt::skip]
105
pub fn value_to_js_arg(value: &quaint::Value) -> serde_json::Result<JSArg> {
116
let res = match (&value.typed, value.native_column_type_name()) {
12-
(quaint::ValueType::DateTime(Some(dt)), Some("DATE")) => JSArg::Value(JsonValue::String(dt.date_naive().to_string())),
13-
(quaint::ValueType::DateTime(Some(dt)), Some("TIME")) => JSArg::Value(JsonValue::String(dt.time().to_string())),
14-
(quaint::ValueType::DateTime(Some(dt)), Some("TIMETZ")) => JSArg::Value(JsonValue::String(dt.time().format_with_items(TIME_FMT.clone()).to_string())),
15-
(quaint::ValueType::DateTime(Some(dt)), _) => JSArg::Value(JsonValue::String(dt.naive_utc().to_string())),
167
(quaint::ValueType::Json(Some(s)), _) => JSArg::Value(JsonValue::String(serde_json::to_string(s)?)),
178
(quaint::ValueType::Bytes(Some(bytes)), _) => JSArg::Buffer(bytes.to_vec()),
189
(quaint::ValueType::Int32(Some(value)), _) => JSArg::SafeInt(*value),
@@ -70,16 +61,8 @@ mod test {
7061
JSArg::Value(JsonValue::Null)
7162
),
7263
(
73-
ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())).into_value().with_native_column_type(Some("DATE")),
74-
JSArg::Value(JsonValue::String("2020-01-01".to_string()))
75-
),
76-
(
77-
ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())).into_value().with_native_column_type(Some("TIME")),
78-
JSArg::Value(JsonValue::String("23:13:01".to_string()))
79-
),
80-
(
81-
ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())).into_value().with_native_column_type(Some("TIMETZ")),
82-
JSArg::Value(JsonValue::String("23:13:01".to_string()))
64+
ValueType::DateTime(Some(Utc.with_ymd_and_hms(2020, 1, 1, 23, 13, 1).unwrap())).into_value(),
65+
JSArg::Value(JsonValue::String("2020-01-01T23:13:01+00:00".to_string()))
8366
),
8467
(
8568
ValueType::DateTime(None).into_value(),

libs/driver-adapters/src/napi/conversion.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,14 @@ impl ToNapiValue for JSArg {
5858

5959
impl ToNapiValue for JSArgType {
6060
unsafe fn to_napi_value(env: napi::sys::napi_env, value: Self) -> napi::Result<napi::sys::napi_value> {
61-
unsafe { ToNapiValue::to_napi_value(env, value.to_string()) }
61+
let env = unsafe { napi::Env::from_raw(env) };
62+
63+
let mut obj = env.create_object()?;
64+
obj.set_named_property("scalarType", <&str>::from(value.scalar_type))?;
65+
obj.set_named_property("dbType", MaybeDefined(value.db_type))?;
66+
obj.set_named_property("arity", <&str>::from(value.arity))?;
67+
68+
unsafe { ToNapiValue::to_napi_value(env.raw(), obj) }
6269
}
6370
}
6471

libs/driver-adapters/src/wasm/conversion.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,18 @@ impl ToJsValue for JSArg {
5252

5353
impl ToJsValue for JSArgType {
5454
fn to_js_value(&self) -> Result<wasm_bindgen::prelude::JsValue, wasm_bindgen::prelude::JsValue> {
55-
Ok(JsValue::from(self.to_string()))
55+
let object = Object::new();
56+
57+
let scalar_type = <&str>::from(self.scalar_type).to_js_value()?;
58+
Reflect::set(&object, &JsValue::from(JsString::from("scalarType")), &scalar_type)?;
59+
60+
let db_type = MaybeDefined(self.db_type.as_ref()).to_js_value()?;
61+
Reflect::set(&object, &JsValue::from(JsString::from("dbType")), &db_type)?;
62+
63+
let arity = <&str>::from(self.arity).to_js_value()?;
64+
Reflect::set(&object, &JsValue::from(JsString::from("arity")), &arity)?;
65+
66+
Ok(JsValue::from(object))
5667
}
5768
}
5869

0 commit comments

Comments
 (0)