diff --git a/.envrc b/.envrc index d2e4c1b0ca92..752703e98339 100644 --- a/.envrc +++ b/.envrc @@ -34,6 +34,12 @@ else export INIT_WAIT_SEC="2" fi +# Source the gitignored .envrc.local if it exists. +if test -f .envrc.local; then + watch_file .envrc.local + source .envrc.local +fi + # (Example env vars if you're not using the make commands, i.e. the config files, to set up query engine tests) # export TEST_RUNNER="direct" # export TEST_CONNECTOR="postgres" @@ -50,9 +56,3 @@ then use flake fi fi - -# Source the gitignored .envrc.local if it exists. -if test -f .envrc.local; then - watch_file .envrc.local - source .envrc.local -fi diff --git a/Makefile b/Makefile index 82f3a81147ab..cbf9a6bf07ee 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CONFIG_FILE = .test_config SCHEMA_EXAMPLES_PATH = ./query-engine/example_schemas DEV_SCHEMA_FILE = dev_datamodel.prisma DRIVER_ADAPTERS_BRANCH ?= main -NIX := $(shell command -v nix 2> /dev/null) +NIX := $(shell type nix 2> /dev/null) LIBRARY_EXT := $(shell \ case "$$(uname -s)" in \ @@ -335,6 +335,10 @@ else cd query-engine/query-engine-wasm && ./build.sh endif +measure-qe-wasm: build-qe-wasm + @cd query-engine/query-engine-wasm/pkg; \ + gzip -k -c query_engine_bg.wasm | wc -c | awk '{$$1/=(1024*1024); printf "Current wasm query-engine size compressed: %.3fMB\n", $$1}' + build-driver-adapters-kit: build-driver-adapters cd query-engine/driver-adapters && pnpm i && pnpm build diff --git a/libs/user-facing-errors/src/quaint.rs b/libs/user-facing-errors/src/quaint.rs index c2e73948dcfb..b125f2114618 100644 --- a/libs/user-facing-errors/src/quaint.rs +++ b/libs/user-facing-errors/src/quaint.rs @@ -229,6 +229,12 @@ pub fn render_quaint_error(kind: &ErrorKind, connection_info: &ConnectionInfo) - database_host: url.host().to_owned(), })) } + (NativeErrorKind::ConnectionError(_), ConnectionInfo::Native(NativeConnectionInfo::Mssql(url))) => { + Some(KnownError::new(common::DatabaseNotReachable { + database_port: url.port(), + database_host: url.host().to_owned(), + })) + } (NativeErrorKind::TlsError { message }, _) => Some(KnownError::new(common::TlsConnectionError { message: message.into(), })), diff --git a/psl/psl-core/src/builtin_connectors/sqlite_datamodel_connector.rs b/psl/psl-core/src/builtin_connectors/sqlite_datamodel_connector.rs index 89c91d5e4c8c..b8e6c69b8fb0 100644 --- a/psl/psl-core/src/builtin_connectors/sqlite_datamodel_connector.rs +++ b/psl/psl-core/src/builtin_connectors/sqlite_datamodel_connector.rs @@ -25,10 +25,9 @@ const CAPABILITIES: ConnectorCapabilities = enumflags2::make_bitflags!(Connector NativeUpsert | FilteredInlineChildNestedToOneDisconnect | RowIn | - // InsertReturning, DeleteReturning, UpdateReturning - While SQLite does support RETURNING, it does not return column information on the - // way back from the database. This column type information is necessary in order to preserve consistency for some data types such as int, - // where values could overflow. - // Since we care to stay consistent with reads, it is not enabled. + InsertReturning | + DeleteReturning | + UpdateReturning | SupportsFiltersOnRelationsWithoutJoins }); diff --git a/quaint/src/visitor/sqlite.rs b/quaint/src/visitor/sqlite.rs index 8d1fa74e536c..d6289078393a 100644 --- a/quaint/src/visitor/sqlite.rs +++ b/quaint/src/visitor/sqlite.rs @@ -419,6 +419,41 @@ impl<'a> Visitor<'a> for Sqlite<'a> { Ok(()) } + + fn visit_update(&mut self, update: Update<'a>) -> visitor::Result { + self.write("UPDATE ")?; + self.visit_table(update.table, true)?; + + { + self.write(" SET ")?; + let pairs = update.columns.into_iter().zip(update.values); + let len = pairs.len(); + + for (i, (key, value)) in pairs.enumerate() { + self.visit_column(key)?; + self.write(" = ")?; + self.visit_expression(value)?; + + if i < (len - 1) { + self.write(", ")?; + } + } + } + + if let Some(conditions) = update.conditions { + self.write(" WHERE ")?; + self.visit_conditions(conditions)?; + } + + self.returning(update.returning)?; + + if let Some(comment) = update.comment { + self.write(" ")?; + self.visit_comment(comment)?; + } + + Ok(()) + } } #[cfg(test)] diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/metrics.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/metrics.rs index dff1ecdb03a5..827a35daeac7 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/metrics.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/metrics.rs @@ -34,7 +34,7 @@ mod metrics { let total_operations = get_counter(&json, PRISMA_CLIENT_QUERIES_TOTAL); match runner.connector_version() { - Sqlite(_) => assert_eq!(total_queries, 9), + Sqlite(_) => assert_eq!(total_queries, 2), SqlServer(_) => assert_eq!(total_queries, 17), MongoDb(_) => assert_eq!(total_queries, 5), CockroachDb(_) => (), // not deterministic diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/bytes.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/bytes.rs index 265a75763794..a4957d75e1ab 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/bytes.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/data_types/bytes.rs @@ -1,14 +1,6 @@ use query_engine_tests::*; -#[test_suite( - schema(common_nullable_types), - exclude( - Postgres("pg.js.wasm"), - Postgres("neon.js.wasm"), - Sqlite("libsql.js.wasm"), - Vitess("planetscale.js.wasm") - ) -)] +#[test_suite(schema(common_nullable_types))] mod bytes { use query_engine_tests::{run_query, EngineProtocol, Runner}; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bigint_filter.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bigint_filter.rs index 0ef65c7af43a..7234fcc253d0 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bigint_filter.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bigint_filter.rs @@ -143,11 +143,7 @@ mod bigint_filter { Ok(()) } - #[connector_test( - schema(setup::common_list_types), - exclude(Postgres("pg.js.wasm", "neon.js.wasm")), - capabilities(ScalarLists) - )] + #[connector_test(schema(setup::common_list_types), capabilities(ScalarLists))] async fn scalar_list_filters(runner: Runner) -> TestResult<()> { setup::test_data_list_common(&runner).await?; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bytes_filter.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bytes_filter.rs index a77bf6e765b2..bcb4a76c6158 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bytes_filter.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/bytes_filter.rs @@ -6,10 +6,7 @@ mod bytes_filter { use super::setup; use query_engine_tests::run_query; - #[connector_test( - schema(setup::common_types), - exclude(Sqlite("libsql.js.wasm"), Vitess("planetscale.js.wasm")) - )] + #[connector_test(schema(setup::common_types))] async fn basic_where(runner: Runner) -> TestResult<()> { setup::test_data_common_types(&runner).await?; @@ -31,11 +28,7 @@ mod bytes_filter { Ok(()) } - #[connector_test( - schema(setup::common_mixed_types), - exclude(Postgres("pg.js.wasm", "neon.js.wasm")), - capabilities(ScalarLists) - )] + #[connector_test(schema(setup::common_mixed_types), capabilities(ScalarLists))] async fn inclusion_filter(runner: Runner) -> TestResult<()> { setup::test_data_common_mixed_types(&runner).await?; @@ -57,11 +50,7 @@ mod bytes_filter { Ok(()) } - #[connector_test( - schema(setup::common_list_types), - exclude(Postgres("pg.js.wasm", "neon.js.wasm")), - capabilities(ScalarLists) - )] + #[connector_test(schema(setup::common_list_types), capabilities(ScalarLists))] async fn scalar_list_filters(runner: Runner) -> TestResult<()> { setup::test_data_list_common(&runner).await?; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/datetime_filter.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/datetime_filter.rs index 2753471bc635..327379bd4903 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/datetime_filter.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/datetime_filter.rs @@ -6,10 +6,7 @@ mod datetime_filter { use super::setup; use query_engine_tests::run_query; - #[connector_test( - schema(setup::common_types), - exclude(Sqlite("libsql.js.wasm"), Vitess("planetscale.js.wasm")) - )] + #[connector_test(schema(setup::common_types))] async fn basic_where(runner: Runner) -> TestResult<()> { setup::test_data_common_types(&runner).await?; @@ -31,10 +28,7 @@ mod datetime_filter { Ok(()) } - #[connector_test( - schema(setup::common_types), - exclude(Sqlite("libsql.js.wasm"), Vitess("planetscale.js.wasm")) - )] + #[connector_test(schema(setup::common_types))] async fn numeric_comparison_filters(runner: Runner) -> TestResult<()> { setup::test_data_common_types(&runner).await?; @@ -143,11 +137,7 @@ mod datetime_filter { Ok(()) } - #[connector_test( - schema(setup::common_list_types), - capabilities(ScalarLists), - exclude(Postgres("pg.js.wasm", "neon.js.wasm")) - )] + #[connector_test(schema(setup::common_list_types), capabilities(ScalarLists))] async fn scalar_list_filters(runner: Runner) -> TestResult<()> { setup::test_data_list_common(&runner).await?; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/float_filter.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/float_filter.rs index f40f73bbc180..580b7f31bc39 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/float_filter.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/float_filter.rs @@ -6,10 +6,7 @@ mod float_filter { use super::setup; use query_engine_tests::run_query; - #[connector_test( - schema(setup::common_types), - exclude(Sqlite("libsql.js.wasm"), Vitess("planetscale.js.wasm")) - )] + #[connector_test(schema(setup::common_types))] async fn basic_where(runner: Runner) -> TestResult<()> { setup::test_data_common_types(&runner).await?; @@ -31,10 +28,7 @@ mod float_filter { Ok(()) } - #[connector_test( - schema(setup::common_types), - exclude(Sqlite("libsql.js.wasm"), Vitess("planetscale.js.wasm")) - )] + #[connector_test(schema(setup::common_types))] async fn numeric_comparison_filters(runner: Runner) -> TestResult<()> { setup::test_data_common_types(&runner).await?; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/int_filter.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/int_filter.rs index cedbb81c3a1f..972539ec1f15 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/int_filter.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/int_filter.rs @@ -6,10 +6,7 @@ mod int_filter { use super::setup; use query_engine_tests::run_query; - #[connector_test( - schema(setup::common_types), - exclude(Sqlite("libsql.js.wasm"), Vitess("planetscale.js.wasm")) - )] + #[connector_test(schema(setup::common_types))] async fn basic_where(runner: Runner) -> TestResult<()> { setup::test_data_common_types(&runner).await?; @@ -31,10 +28,7 @@ mod int_filter { Ok(()) } - #[connector_test( - schema(setup::common_types), - exclude(Sqlite("libsql.js.wasm"), Vitess("planetscale.js.wasm")) - )] + #[connector_test(schema(setup::common_types))] async fn numeric_comparison_filters(runner: Runner) -> TestResult<()> { setup::test_data_common_types(&runner).await?; @@ -143,11 +137,7 @@ mod int_filter { Ok(()) } - #[connector_test( - schema(setup::common_list_types), - exclude(Postgres("pg.js.wasm", "neon.js.wasm")), - capabilities(ScalarLists) - )] + #[connector_test(schema(setup::common_list_types), capabilities(ScalarLists))] async fn scalar_list_filters(runner: Runner) -> TestResult<()> { setup::test_data_list_common(&runner).await?; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/string_filter.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/string_filter.rs index c62821ef4604..708f2d15e83e 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/string_filter.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/field_reference/string_filter.rs @@ -435,11 +435,7 @@ mod string_filter { Ok(()) } - #[connector_test( - schema(setup::common_list_types), - exclude(Postgres("pg.js.wasm", "neon.js.wasm")), - capabilities(ScalarLists) - )] + #[connector_test(schema(setup::common_list_types), capabilities(ScalarLists))] async fn scalar_list_filters_sensitive(runner: Runner) -> TestResult<()> { setup::test_data_list_common(&runner).await?; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/list_filters.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/list_filters.rs index f34675ba3ff1..16b9a0ab0437 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/list_filters.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/queries/filters/list_filters.rs @@ -1,10 +1,6 @@ use query_engine_tests::*; -#[test_suite( - schema(common_list_types), - exclude(Postgres("pg.js.wasm", "neon.js.wasm")), - capabilities(ScalarLists) -)] +#[test_suite(schema(common_list_types), capabilities(ScalarLists))] mod lists { use indoc::indoc; use query_engine_tests::run_query; @@ -627,7 +623,7 @@ mod lists { } // Cockroachdb does not like the bytes empty array check in v21 but this will be fixed in 22. - #[connector_test(exclude(CockroachDB), exclude(Postgres("pg.js.wasm", "neon.js.wasm")))] + #[connector_test(exclude(CockroachDB))] async fn is_empty_bytes(runner: Runner) -> TestResult<()> { test_data(&runner).await?; diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/bytes.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/bytes.rs index 654463f491f7..a5f8b2d230d9 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/bytes.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/bytes.rs @@ -1,10 +1,6 @@ use query_engine_tests::*; -#[test_suite(exclude( - Postgres("pg.js.wasm", "neon.js.wasm"), - Sqlite("libsql.js.wasm"), - Vitess("planetscale.js.wasm") -))] +#[test_suite] mod bytes { use indoc::indoc; use query_engine_tests::run_query; @@ -81,16 +77,7 @@ mod bytes { Ok(()) } - #[connector_test( - schema(bytes_id), - exclude( - MySQL, - Vitess, - SqlServer, - Postgres("pg.js.wasm", "neon.js.wasm"), - Sqlite("libsql.js.wasm") - ) - )] + #[connector_test(schema(bytes_id), exclude(MySQL, Vitess, SqlServer,))] async fn byte_id_coercion(runner: Runner) -> TestResult<()> { insta::assert_snapshot!( run_query!(runner, r#" diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/native_types/postgres.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/native_types/postgres.rs index e24245745991..f47f3e528b17 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/native_types/postgres.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/native_types/postgres.rs @@ -191,11 +191,7 @@ mod postgres { } // "Other Postgres native types" should "work" - #[connector_test( - schema(schema_other_types), - only(Postgres), - exclude(CockroachDb, Postgres("pg.js.wasm", "neon.js.wasm")) - )] + #[connector_test(schema(schema_other_types), only(Postgres), exclude(CockroachDb,))] async fn native_other_types(runner: Runner) -> TestResult<()> { insta::assert_snapshot!( run_query!(&runner, r#"mutation { diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/base.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/base.rs index 2bd989573da8..9a5e74dd8547 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/base.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/base.rs @@ -28,7 +28,7 @@ mod basic_types { schema.to_owned() } - #[connector_test(exclude(Postgres("pg.js.wasm", "neon.js.wasm")))] + #[connector_test] async fn set_base(runner: Runner) -> TestResult<()> { insta::assert_snapshot!( run_query!(&runner, format!(r#"mutation {{ @@ -59,7 +59,7 @@ mod basic_types { // "Scalar lists" should "be behave like regular values for create and update operations" // Skipped for CockroachDB as enum array concatenation is not supported (https://github.com/cockroachdb/cockroach/issues/71388). - #[connector_test(exclude(CockroachDb, Postgres("pg.js.wasm", "neon.js.wasm")))] + #[connector_test(exclude(CockroachDb))] async fn behave_like_regular_val_for_create_and_update(runner: Runner) -> TestResult<()> { insta::assert_snapshot!( run_query!(&runner, format!(r#"mutation {{ @@ -158,7 +158,7 @@ mod basic_types { } // "A Create Mutation" should "create and return items with list values with shorthand notation" - #[connector_test(exclude(Postgres("pg.js.wasm", "neon.js.wasm")))] + #[connector_test] async fn create_mut_work_with_list_vals(runner: Runner) -> TestResult<()> { insta::assert_snapshot!( run_query!(&runner, format!(r#"mutation {{ diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/defaults.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/defaults.rs index c216b36ae458..39370e62c572 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/defaults.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/writes/data_types/scalar_list/defaults.rs @@ -29,7 +29,7 @@ mod basic { schema.to_owned() } - #[connector_test(exclude(Postgres("pg.js.wasm", "neon.js.wasm")))] + #[connector_test] async fn basic_write(runner: Runner) -> TestResult<()> { insta::assert_snapshot!( run_query!(&runner, r#"mutation { diff --git a/query-engine/driver-adapters/src/conversion/js_arg.rs b/query-engine/driver-adapters/src/conversion/js_arg.rs index c5b65e80882a..d6f67ed7716d 100644 --- a/query-engine/driver-adapters/src/conversion/js_arg.rs +++ b/query-engine/driver-adapters/src/conversion/js_arg.rs @@ -1,8 +1,6 @@ -use serde::Serialize; use serde_json::value::Value as JsonValue; -#[derive(Debug, PartialEq, Serialize)] -#[serde(untagged)] +#[derive(Debug, PartialEq)] pub enum JSArg { Value(serde_json::Value), Buffer(Vec), diff --git a/query-engine/driver-adapters/src/types.rs b/query-engine/driver-adapters/src/types.rs index bae6024ede3f..3f5a12b58f63 100644 --- a/query-engine/driver-adapters/src/types.rs +++ b/query-engine/driver-adapters/src/types.rs @@ -243,7 +243,6 @@ pub enum ColumnType { } #[cfg_attr(not(target_arch = "wasm32"), napi_derive::napi(object))] -#[cfg_attr(target_arch = "wasm32", derive(Serialize))] #[derive(Debug, Default)] pub struct Query { pub sql: String, diff --git a/query-engine/driver-adapters/src/wasm/adapter_method.rs b/query-engine/driver-adapters/src/wasm/adapter_method.rs index 0e405bd30580..410d3d06899e 100644 --- a/query-engine/driver-adapters/src/wasm/adapter_method.rs +++ b/query-engine/driver-adapters/src/wasm/adapter_method.rs @@ -1,28 +1,19 @@ use js_sys::{Function as JsFunction, Promise as JsPromise}; -use serde::Serialize; -use serde_wasm_bindgen::Serializer; use std::marker::PhantomData; use wasm_bindgen::convert::FromWasmAbi; use wasm_bindgen::describe::WasmDescribe; -use wasm_bindgen::{JsCast, JsError, JsValue}; +use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen_futures::JsFuture; use super::error::into_quaint_error; use super::from_js::FromJsValue; +use super::to_js::ToJsValue; use crate::AdapterResult; -// - `serialize_missing_as_null` is required to make sure that "empty" values (e.g., `None` and `()`) -// are serialized as `null` and not `undefined`. -// This is due to certain drivers (e.g., LibSQL) not supporting `undefined` values. -// - `serialize_large_number_types_as_bigints` is required to allow reading bigints from Prisma Client. -pub(crate) static SERIALIZER: Serializer = Serializer::new() - .serialize_large_number_types_as_bigints(true) - .serialize_missing_as_null(true); - #[derive(Clone)] pub(crate) struct AdapterMethod where - ArgType: Serialize, + ArgType: ToJsValue, ReturnType: FromJsValue, { fn_: JsFunction, @@ -33,7 +24,7 @@ where impl From for AdapterMethod where - T: Serialize, + T: ToJsValue, R: FromJsValue, { fn from(js_value: JsValue) -> Self { @@ -43,7 +34,7 @@ where impl From for AdapterMethod where - T: Serialize, + T: ToJsValue, R: FromJsValue, { fn from(js_fn: JsFunction) -> Self { @@ -57,7 +48,7 @@ where impl AdapterMethod where - T: Serialize, + T: ToJsValue, R: FromJsValue, { pub(crate) async fn call_as_async(&self, arg1: T) -> quaint::Result { @@ -85,14 +76,12 @@ where } async fn call_internal(&self, arg1: T) -> Result { - let arg1 = arg1 - .serialize(&SERIALIZER) - .map_err(|err| JsValue::from(JsError::from(&err)))?; + let arg1 = arg1.to_js_value()?; self.fn_.call1(&JsValue::null(), &arg1) } pub(crate) fn call_non_blocking(&self, arg: T) { - if let Ok(arg) = serde_wasm_bindgen::to_value(&arg) { + if let Ok(arg) = arg.to_js_value() { _ = self.fn_.call1(&JsValue::null(), &arg); } } @@ -100,7 +89,7 @@ where impl WasmDescribe for AdapterMethod where - ArgType: Serialize, + ArgType: ToJsValue, ReturnType: FromJsValue, { fn describe() { @@ -110,7 +99,7 @@ where impl FromWasmAbi for AdapterMethod where - ArgType: Serialize, + ArgType: ToJsValue, ReturnType: FromJsValue, { type Abi = ::Abi; diff --git a/query-engine/driver-adapters/src/wasm/conversion.rs b/query-engine/driver-adapters/src/wasm/conversion.rs new file mode 100644 index 000000000000..c41ff8a23107 --- /dev/null +++ b/query-engine/driver-adapters/src/wasm/conversion.rs @@ -0,0 +1,52 @@ +use crate::conversion::JSArg; + +use super::to_js::{serde_serialize, ToJsValue}; +use crate::types::Query; +use js_sys::{Array, JsString, Object, Reflect, Uint8Array}; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Object)] + pub type Buffer; + + #[wasm_bindgen(static_method_of = Buffer)] + pub fn from(array: &Uint8Array) -> Buffer; +} + +impl ToJsValue for Query { + fn to_js_value(&self) -> Result { + let object = Object::new(); + let sql = self.sql.to_js_value()?; + Reflect::set(&object, &JsValue::from(JsString::from("sql")), &sql)?; + let args = Array::new(); + for arg in &self.args { + let value = arg.to_js_value()?; + args.push(&value); + } + Reflect::set(&object, &JsValue::from(JsString::from("args")), &args)?; + + Ok(JsValue::from(object)) + } +} + +impl ToJsValue for JSArg { + fn to_js_value(&self) -> Result { + match self { + JSArg::Value(value) => serde_serialize(value), + JSArg::Buffer(buf) => { + let array = Uint8Array::from(buf.as_slice()); + Ok(Buffer::from(&array).into()) + } + JSArg::Array(value) => { + let array = Array::new(); + for arg in value { + let js_arg = arg.to_js_value()?; + array.push(&js_arg); + } + + Ok(JsValue::from(array)) + } + } + } +} diff --git a/query-engine/driver-adapters/src/wasm/mod.rs b/query-engine/driver-adapters/src/wasm/mod.rs index 8a75f81d9280..7f51b1ee0236 100644 --- a/query-engine/driver-adapters/src/wasm/mod.rs +++ b/query-engine/driver-adapters/src/wasm/mod.rs @@ -1,10 +1,12 @@ //! Query Engine Driver Adapters: `wasm`-specific implementation. mod adapter_method; +mod conversion; mod error; mod from_js; mod js_object_extern; pub(crate) mod result; +mod to_js; pub(crate) use adapter_method::AdapterMethod; pub(crate) use from_js::FromJsValue; diff --git a/query-engine/driver-adapters/src/wasm/to_js.rs b/query-engine/driver-adapters/src/wasm/to_js.rs new file mode 100644 index 000000000000..a97b7a0f70c2 --- /dev/null +++ b/query-engine/driver-adapters/src/wasm/to_js.rs @@ -0,0 +1,30 @@ +use serde::Serialize; +use serde_wasm_bindgen::Serializer; +use wasm_bindgen::{JsError, JsValue}; + +// - `serialize_missing_as_null` is required to make sure that "empty" values (e.g., `None` and `()`) +// are serialized as `null` and not `undefined`. +// This is due to certain drivers (e.g., LibSQL) not supporting `undefined` values. +// - `serialize_large_number_types_as_bigints` is required to allow reading bigints from Prisma Client. +static DEFAULT_SERIALIZER: Serializer = Serializer::new() + .serialize_large_number_types_as_bigints(true) + .serialize_missing_as_null(true); + +pub(crate) trait ToJsValue: Sized { + fn to_js_value(&self) -> Result; +} + +impl ToJsValue for T +where + T: Serialize, +{ + fn to_js_value(&self) -> Result { + serde_serialize(self) + } +} + +pub(crate) fn serde_serialize(value: T) -> Result { + value + .serialize(&DEFAULT_SERIALIZER) + .map_err(|err| JsValue::from(JsError::from(err))) +} diff --git a/query-engine/query-engine-wasm/build.sh b/query-engine/query-engine-wasm/build.sh index b6cf5b685733..037f3415f65f 100755 --- a/query-engine/query-engine-wasm/build.sh +++ b/query-engine/query-engine-wasm/build.sh @@ -29,7 +29,7 @@ then fi echo "Building query-engine-wasm using $WASM_BUILD_PROFILE profile" -wasm-pack build "--$WASM_BUILD_PROFILE" --target $OUT_TARGET --out-name query_engine +CARGO_PROFILE_RELEASE_OPT_LEVEL="z" wasm-pack build "--$WASM_BUILD_PROFILE" --target $OUT_TARGET --out-name query_engine WASM_OPT_ARGS=( "-Os" # execute size-focused optimization passes diff --git a/schema-engine/sql-migration-tests/tests/errors/error_tests.rs b/schema-engine/sql-migration-tests/tests/errors/error_tests.rs index 76a18c984fd0..7d60c3ee8052 100644 --- a/schema-engine/sql-migration-tests/tests/errors/error_tests.rs +++ b/schema-engine/sql-migration-tests/tests/errors/error_tests.rs @@ -90,6 +90,10 @@ fn authentication_failure_must_return_a_known_error_on_mysql(api: TestApi) { assert_eq!(json_error, expected); } +// TODO(tech-debt): get rid of provider-specific PSL `dm` declaration, and use `test_api::datamodel_with_provider` utility instead. +// See: https://github.com/prisma/team-orm/issues/835. +// This issue also currently prevents us from defining an `Mssql`-specific copy of this `unreachable_database_*` test case, +// due to url parsing differences between the `url` crate and `quaint`'s `MssqlUrl` struct. #[test_connector(tags(Mysql))] fn unreachable_database_must_return_a_proper_error_on_mysql(api: TestApi) { let mut url: Url = api.connection_string().parse().unwrap();