Skip to content

Commit

Permalink
feat: make json serialization faster
Browse files Browse the repository at this point in the history
  • Loading branch information
Weakky committed Mar 13, 2024
1 parent efd2449 commit 456f88e
Show file tree
Hide file tree
Showing 44 changed files with 975 additions and 567 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Expand Up @@ -80,11 +80,11 @@ path = "quaint"
[profile.dev.package.backtrace]
opt-level = 3

[profile.release.package.query-engine-node-api]
strip = "symbols"
# [profile.release.package.query-engine-node-api]
# strip = "symbols"

[profile.release.package.query-engine]
strip = "symbols"
# [profile.release.package.query-engine]
# strip = "symbols"

[profile.release]
lto = "fat"
Expand Down
117 changes: 106 additions & 11 deletions libs/prisma-value/src/lib.rs
Expand Up @@ -7,6 +7,7 @@ use chrono::prelude::*;
use serde::de::Unexpected;
use serde::ser::SerializeMap;
use serde::{ser::Serializer, Deserialize, Deserializer, Serialize};
use std::borrow::Cow;
use std::{convert::TryFrom, fmt, str::FromStr};
use uuid::Uuid;

Expand All @@ -23,7 +24,7 @@ pub enum PrismaValue {
Int(i64),
Uuid(Uuid),
List(PrismaListValue),
Json(String),
Json(PrismaValueJson),

/// A collections of key-value pairs constituting an object.
#[serde(serialize_with = "serialize_object")]
Expand All @@ -45,6 +46,93 @@ pub enum PrismaValue {
Bytes(Vec<u8>),
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PrismaValueJson {
Serialized(String),
Deserialized(Box<serde_json::Value>),
}

impl std::fmt::Display for PrismaValueJson {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PrismaValueJson::Serialized(s) => write!(f, "{}", s),
PrismaValueJson::Deserialized(v) => write!(f, "{}", v),
}
}
}

impl PrismaValueJson {
pub fn as_str(&self) -> Cow<'_, str> {
match self {
PrismaValueJson::Serialized(s) => Cow::Borrowed(s),
PrismaValueJson::Deserialized(v) => Cow::Owned(v.to_string()),
}
}

pub fn try_as_value(&self) -> serde_json::Result<Cow<'_, serde_json::Value>> {
match self {
PrismaValueJson::Serialized(s) => serde_json::from_str(s).map(Cow::Owned),
PrismaValueJson::Deserialized(v) => Ok(Cow::Borrowed(v)),
}
}

pub fn try_into_value(self) -> serde_json::Result<serde_json::Value> {
match self {
PrismaValueJson::Serialized(s) => serde_json::from_str(&s),
PrismaValueJson::Deserialized(v) => Ok(*v),
}
}
}

impl From<String> for PrismaValueJson {
fn from(value: String) -> Self {
PrismaValueJson::Serialized(value)
}
}

impl From<&String> for PrismaValueJson {
fn from(value: &String) -> Self {
PrismaValueJson::Serialized(value.to_owned())
}
}

impl From<&str> for PrismaValueJson {
fn from(value: &str) -> Self {
PrismaValueJson::Serialized(value.to_owned())
}
}

impl From<Cow<'_, str>> for PrismaValueJson {
fn from(value: Cow<'_, str>) -> Self {
PrismaValueJson::Serialized(value.into_owned())
}
}

impl From<serde_json::Value> for PrismaValueJson {
fn from(value: serde_json::Value) -> Self {
PrismaValueJson::Deserialized(Box::new(value))
}
}

impl PartialOrd for PrismaValueJson {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl Ord for PrismaValueJson {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_str().cmp(&other.as_str())
}
}

impl std::hash::Hash for PrismaValueJson {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}

/// Stringify a date to the following format
/// 1999-05-01T00:00:00.000Z
pub fn stringify_datetime(datetime: &DateTime<FixedOffset>) -> String {
Expand All @@ -61,6 +149,10 @@ pub fn parse_datetime(datetime: &str) -> chrono::ParseResult<DateTime<FixedOffse
DateTime::parse_from_rfc3339(datetime)
}

pub fn stringify_decimal(decimal: &BigDecimal) -> f64 {
decimal.to_string().parse::<f64>().unwrap()
}

pub fn encode_bytes(bytes: &[u8]) -> String {
base64::encode(bytes)
}
Expand Down Expand Up @@ -132,7 +224,7 @@ impl TryFrom<serde_json::Value> for PrismaValue {
decode_bytes(value).map(PrismaValue::Bytes)
}

_ => Ok(PrismaValue::Json(serde_json::to_string(&obj).unwrap())),
_ => Ok(PrismaValue::new_json(serde_json::Value::Object(obj))),
},
}
}
Expand Down Expand Up @@ -170,7 +262,7 @@ fn serialize_decimal<S>(decimal: &BigDecimal, serializer: S) -> Result<S::Ok, S:
where
S: Serializer,
{
decimal.to_string().parse::<f64>().unwrap().serialize(serializer)
stringify_decimal(decimal).serialize(serializer)
}

fn deserialize_decimal<'de, D>(deserializer: D) -> Result<BigDecimal, D::Error>
Expand Down Expand Up @@ -276,6 +368,13 @@ impl PrismaValue {
}
}

pub fn into_json(self) -> Option<PrismaValueJson> {
match self {
PrismaValue::Json(j) => Some(j),
_ => None,
}
}

pub fn into_list(self) -> Option<PrismaListValue> {
match self {
PrismaValue::List(l) => Some(l),
Expand All @@ -298,20 +397,16 @@ impl PrismaValue {
PrismaValue::DateTime(parse_datetime(datetime).unwrap())
}

pub fn new_json(json: impl Into<PrismaValueJson>) -> PrismaValue {
PrismaValue::Json(json.into())
}

pub fn as_boolean(&self) -> Option<&bool> {
match self {
PrismaValue::Boolean(bool) => Some(bool),
_ => None,
}
}

pub fn as_json(&self) -> Option<&String> {
if let Self::Json(v) = self {
Some(v)
} else {
None
}
}
}

impl fmt::Display for PrismaValue {
Expand Down
Expand Up @@ -361,14 +361,14 @@ impl MongoReadQueryBuilder {
) -> crate::Result<Self> {
for aggr in virtual_selections {
let join = match aggr {
VirtualSelection::RelationCount(rf, filter) => {
let filter = filter
.as_ref()
VirtualSelection::RelationCount(x) => {
let filter = x
.filter()
.map(|f| MongoFilterVisitor::new(FilterPrefix::default(), false).visit(f.clone()))
.transpose()?;

JoinStage {
source: rf.clone(),
source: x.field().clone(),
alias: Some(aggr.db_alias()),
nested: vec![],
filter,
Expand Down
Expand Up @@ -120,8 +120,7 @@ impl QueryRawConversionExtension for &PrismaValue {
fn try_as_bson(&self, arg_name: &str) -> crate::Result<Bson> {
match self {
PrismaValue::Json(json) => {
let json: serde_json::Value = serde_json::from_str(json.as_str())?;
let bson = Bson::try_from(json)?;
let bson = Bson::try_from(json.try_as_value()?.into_owned())?;

Ok(bson)
}
Expand Down
8 changes: 4 additions & 4 deletions query-engine/connectors/mongodb-query-connector/src/value.rs
Expand Up @@ -11,7 +11,6 @@ use psl::builtin_connectors::MongoDbType;
use query_structure::{
CompositeFieldRef, Field, PrismaValue, RelationFieldRef, ScalarFieldRef, SelectedField, TypeIdentifier,
};
use serde_json::Value;
use std::{convert::TryFrom, fmt::Display};

/// Transforms a `PrismaValue` of a specific selected field into the BSON mapping as prescribed by
Expand Down Expand Up @@ -190,7 +189,7 @@ impl IntoBson for (&MongoDbType, PrismaValue) {
}),

(MongoDbType::Json, PrismaValue::Json(json)) => {
let val: Value = serde_json::from_str(&json)?;
let val = json.try_as_value()?.into_owned();

Bson::try_from(val).map_err(|_| MongoError::ConversionError {
from: "Stringified JSON".to_owned(),
Expand Down Expand Up @@ -259,7 +258,8 @@ impl IntoBson for (&TypeIdentifier, PrismaValue) {

// Json
(TypeIdentifier::Json, PrismaValue::Json(json)) => {
let val: Value = serde_json::from_str(&json)?;
let val = json.try_as_value()?.into_owned();

Bson::try_from(val).map_err(|_| MongoError::ConversionError {
from: "Stringified JSON".to_owned(),
to: "Mongo BSON (extJSON)".to_owned(),
Expand Down Expand Up @@ -375,7 +375,7 @@ fn read_scalar_value(bson: Bson, meta: &ScalarOutputMeta) -> crate::Result<Prism
(TypeIdentifier::Bytes, Bson::ObjectId(oid)) => PrismaValue::Bytes(oid.bytes().to_vec()),

// Json
(TypeIdentifier::Json, bson) => PrismaValue::Json(serde_json::to_string(&bson.into_relaxed_extjson())?),
(TypeIdentifier::Json, bson) => PrismaValue::new_json(bson.into_relaxed_extjson()),

(ident, bson) => {
return Err(MongoError::ConversionError {
Expand Down
59 changes: 50 additions & 9 deletions query-engine/connectors/sql-query-connector/src/column_metadata.rs
@@ -1,19 +1,30 @@
use query_structure::{FieldArity, TypeIdentifier};
use query_structure::{
FieldArity, FieldSelection, GroupedSelectedField, GroupedVirtualSelection, RelationSelection, TypeIdentifier,
};

#[derive(Clone, Debug)]
pub enum MetadataFieldKind<'a> {
Scalar,
Relation(&'a RelationSelection),
Virtual(GroupedVirtualSelection<'a>),
}

/// Helps dealing with column value conversion and possible error resolution.
#[derive(Clone, Debug, Copy)]
#[derive(Clone, Debug)]
pub(crate) struct ColumnMetadata<'a> {
identifier: &'a TypeIdentifier,
identifier: TypeIdentifier,
name: Option<&'a str>,
arity: FieldArity,
kind: MetadataFieldKind<'a>,
}

impl<'a> ColumnMetadata<'a> {
fn new(identifier: &'a TypeIdentifier, arity: FieldArity) -> Self {
fn new(identifier: TypeIdentifier, arity: FieldArity, kind: MetadataFieldKind<'a>) -> Self {
Self {
identifier,
name: None,
arity,
kind,
}
}

Expand All @@ -24,19 +35,23 @@ impl<'a> ColumnMetadata<'a> {
}

/// The type of the column.
pub fn identifier(self) -> &'a TypeIdentifier {
pub fn identifier(&self) -> TypeIdentifier {
self.identifier
}

/// The name of the column.
pub fn name(self) -> Option<&'a str> {
pub fn name(&self) -> Option<&'a str> {
self.name
}

/// The arity of the column.
pub fn arity(self) -> FieldArity {
pub fn arity(&self) -> FieldArity {
self.arity
}

pub(crate) fn kind(&self) -> &MetadataFieldKind<'_> {
&self.kind
}
}

/// Create a set of metadata objects, combining column names and type
Expand All @@ -50,14 +65,40 @@ where
idents
.iter()
.zip(field_names.iter())
.map(|((identifier, arity), name)| ColumnMetadata::new(identifier, *arity).set_name(name.as_ref()))
.map(|((identifier, arity), name)| {
ColumnMetadata::new(*identifier, *arity, MetadataFieldKind::Scalar).set_name(name.as_ref())
})
.collect()
}

pub(crate) fn create_from_selection_for_json<'a, T>(
selection: &'a FieldSelection,
field_names: &'a [T],
) -> Vec<ColumnMetadata<'a>>
where
T: AsRef<str>,
{
selection
.grouped_selections()
.zip(field_names.iter())
.map(|(field, name)| {
let (type_identifier, arity) = field.type_identifier_with_arity_for_json();

let kind = match field {
GroupedSelectedField::Scalar(_) => MetadataFieldKind::Scalar,
GroupedSelectedField::Relation(rs) => MetadataFieldKind::Relation(rs),
GroupedSelectedField::Virtual(vs) => MetadataFieldKind::Virtual(vs),
};

ColumnMetadata::new(type_identifier, arity, kind).set_name(name.as_ref())
})
.collect()
}

/// Create a set of metadata objects.
pub(crate) fn create_anonymous(idents: &[(TypeIdentifier, FieldArity)]) -> Vec<ColumnMetadata<'_>> {
idents
.iter()
.map(|(identifier, arity)| ColumnMetadata::new(identifier, *arity))
.map(|(identifier, arity)| ColumnMetadata::new(*identifier, *arity, MetadataFieldKind::Scalar))
.collect()
}

0 comments on commit 456f88e

Please sign in to comment.