Skip to content

Commit

Permalink
fix(query-engine-wasm): reduce size of postgres Query Engine after …
Browse files Browse the repository at this point in the history
…regression (#5018)

* feat(crosstarget-utils): add Wasm-friendly regex util

* fix(query-engine-wasm): reduce size of postgres Driver Adapter via Wasm-friendly regex util

* chore: fix clippy

* chore: fix clippy

* chore: review comments

* feat(crosstarget-utils): introduce common trait "RegExprCompact" for "regex" util

* chore(crosstarget-utils): rewrite comment

* chore(review): use derive_more Display

Co-authored-by: Alexey Orlenko <alex@aqrln.net>

* fix(crosstarget-utils): don't change the number of captured groups in regex

* fix(crosstarget-utils): fixes on regex and derive_more

* chore: apply nits

* feat(crosstarget-utils): replace every manual "Display" instance with "derive_more"

---------

Co-authored-by: Alexey Orlenko <alex@aqrln.net>
  • Loading branch information
jkomyno and aqrln authored Oct 15, 2024
1 parent edd552c commit 08713a9
Show file tree
Hide file tree
Showing 20 changed files with 156 additions and 33 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ tokio = { version = "1", features = [
"time",
] }
chrono = { version = "0.4.38", features = ["serde"] }
derive_more = "0.99.17"
user-facing-errors = { path = "./libs/user-facing-errors" }
uuid = { version = "1", features = ["serde", "v4", "v7", "js"] }
indoc = "2.0.1"
Expand Down
3 changes: 3 additions & 0 deletions libs/crosstarget-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
derive_more.workspace = true
enumflags2.workspace = true
futures = "0.3"

[target.'cfg(target_arch = "wasm32")'.dependencies]
Expand All @@ -17,3 +19,4 @@ pin-project = "1"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio.workspace = true
regex.workspace = true
23 changes: 0 additions & 23 deletions libs/crosstarget-utils/src/common.rs

This file was deleted.

3 changes: 3 additions & 0 deletions libs/crosstarget-utils/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod regex;
pub mod spawn;
pub mod timeout;
37 changes: 37 additions & 0 deletions libs/crosstarget-utils/src/common/regex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use derive_more::Display;

#[derive(Debug, Display)]
#[display(fmt = "Regular expression error: {message}")]
pub struct RegExpError {
pub message: String,
}

impl std::error::Error for RegExpError {}

/// Flag modifiers for regular expressions.
#[enumflags2::bitflags]
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum RegExpFlags {
IgnoreCase = 0b0001,
Multiline = 0b0010,
}

impl RegExpFlags {
pub fn as_str(&self) -> &'static str {
match self {
Self::IgnoreCase => "i",
Self::Multiline => "m",
}
}
}

pub trait RegExpCompat {
/// Searches for the first match of this regex in the haystack given, and if found,
/// returns not only the overall match but also the matches of each capture group in the regex.
/// If no match is found, then None is returned.
fn captures(&self, message: &str) -> Option<Vec<String>>;

/// Tests if the regex matches the input string.
fn test(&self, message: &str) -> bool;
}
8 changes: 8 additions & 0 deletions libs/crosstarget-utils/src/common/spawn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use derive_more::Display;

#[derive(Debug, Display)]
#[display(fmt = "Failed to spawn a future")]

pub struct SpawnError;

impl std::error::Error for SpawnError {}
7 changes: 7 additions & 0 deletions libs/crosstarget-utils/src/common/timeout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use derive_more::Display;

#[derive(Debug, Display)]
#[display(fmt = "Operation timed out")]
pub struct TimeoutError;

impl std::error::Error for TimeoutError {}
3 changes: 2 additions & 1 deletion libs/crosstarget-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ mod native;
#[cfg(not(target_arch = "wasm32"))]
pub use crate::native::*;

pub use common::SpawnError;
pub use crate::common::regex::RegExpCompat;
pub use crate::common::spawn::SpawnError;
1 change: 1 addition & 0 deletions libs/crosstarget-utils/src/native/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod regex;
pub mod spawn;
pub mod task;
pub mod time;
41 changes: 41 additions & 0 deletions libs/crosstarget-utils/src/native/regex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use enumflags2::BitFlags;
use regex::{Regex as NativeRegex, RegexBuilder};

use crate::common::regex::{RegExpCompat, RegExpError, RegExpFlags};

pub struct RegExp {
inner: NativeRegex,
}

impl RegExp {
pub fn new(pattern: &str, flags: BitFlags<RegExpFlags>) -> Result<Self, RegExpError> {
let mut builder = RegexBuilder::new(pattern);

if flags.contains(RegExpFlags::Multiline) {
builder.multi_line(true);
}

if flags.contains(RegExpFlags::IgnoreCase) {
builder.case_insensitive(true);
}

let inner = builder.build().map_err(|e| RegExpError { message: e.to_string() })?;

Ok(Self { inner })
}
}

impl RegExpCompat for RegExp {
fn captures(&self, message: &str) -> Option<Vec<String>> {
self.inner.captures(message).map(|captures| {
captures
.iter()
.flat_map(|capture| capture.map(|cap| cap.as_str().to_owned()))
.collect()
})
}

fn test(&self, message: &str) -> bool {
self.inner.is_match(message)
}
}
2 changes: 1 addition & 1 deletion libs/crosstarget-utils/src/native/spawn.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use futures::TryFutureExt;
use std::future::Future;

use crate::common::SpawnError;
use crate::common::spawn::SpawnError;

pub fn spawn_if_possible<F>(future: F) -> impl Future<Output = Result<F::Output, SpawnError>>
where
Expand Down
2 changes: 1 addition & 1 deletion libs/crosstarget-utils/src/native/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
time::{Duration, Instant},
};

use crate::common::TimeoutError;
use crate::common::timeout::TimeoutError;

pub struct ElapsedTimeCounter {
instant: Instant,
Expand Down
1 change: 1 addition & 0 deletions libs/crosstarget-utils/src/wasm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod regex;
pub mod spawn;
pub mod task;
pub mod time;
38 changes: 38 additions & 0 deletions libs/crosstarget-utils/src/wasm/regex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use enumflags2::BitFlags;
use js_sys::RegExp as JSRegExp;

use crate::common::regex::{RegExpCompat, RegExpError, RegExpFlags};

pub struct RegExp {
inner: JSRegExp,
}

impl RegExp {
pub fn new(pattern: &str, flags: BitFlags<RegExpFlags>) -> Result<Self, RegExpError> {
let mut flags: String = flags.into_iter().map(|flag| flag.as_str()).collect();

// Global flag is implied in `regex::Regex`, so we match that behavior for consistency.
flags.push('g');

Ok(Self {
inner: JSRegExp::new(pattern, &flags),
})
}
}

impl RegExpCompat for RegExp {
fn captures(&self, message: &str) -> Option<Vec<String>> {
self.inner.exec(message).map(|matches| {
// We keep the same number of captures as the number of groups in the regex pattern,
// but we guarantee that the captures are always strings.
matches
.iter()
.map(|match_value| match_value.try_into().ok().unwrap_or_default())
.collect()
})
}

fn test(&self, input: &str) -> bool {
self.inner.test(input)
}
}
2 changes: 1 addition & 1 deletion libs/crosstarget-utils/src/wasm/spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use futures::TryFutureExt;
use tokio::sync::oneshot;
use wasm_bindgen_futures::spawn_local;

use crate::common::SpawnError;
use crate::common::spawn::SpawnError;

pub fn spawn_if_possible<F>(future: F) -> impl Future<Output = Result<F::Output, SpawnError>>
where
Expand Down
2 changes: 1 addition & 1 deletion libs/crosstarget-utils/src/wasm/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::time::Duration;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;

use crate::common::TimeoutError;
use crate::common::timeout::TimeoutError;

#[wasm_bindgen]
extern "C" {
Expand Down
1 change: 1 addition & 0 deletions quaint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ url.workspace = true
hex = "0.4"
itertools.workspace = true
regex.workspace = true
enumflags2.workspace = true

either = { version = "1.6" }
base64 = { version = "0.12.3" }
Expand Down
8 changes: 4 additions & 4 deletions quaint/src/connector/postgres/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use regex::Regex;
use crosstarget_utils::{regex::RegExp, RegExpCompat};
use enumflags2::BitFlags;
use std::fmt::{Display, Formatter};

use crate::error::{DatabaseConstraint, Error, ErrorKind, Name};
Expand Down Expand Up @@ -30,9 +31,8 @@ impl Display for PostgresError {
}

fn extract_fk_constraint_name(message: &str) -> Option<String> {
let re = Regex::new(r#"foreign key constraint "([^"]+)""#).unwrap();
re.captures(message)
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
let re = RegExp::new(r#"foreign key constraint "([^"]+)""#, BitFlags::empty()).unwrap();
re.captures(message).and_then(|caps| caps.get(1).cloned())
}

impl From<PostgresError> for Error {
Expand Down
2 changes: 1 addition & 1 deletion query-engine/connectors/mongodb-query-connector/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ uuid.workspace = true
indexmap.workspace = true
query-engine-metrics = { path = "../../metrics" }
cuid = { git = "https://github.com/prisma/cuid-rust", branch = "wasm32-support" }
derive_more = "0.99.17"
derive_more.workspace = true

[dependencies.query-structure]
path = "../../query-structure"
Expand Down

0 comments on commit 08713a9

Please sign in to comment.