diff --git a/Cargo.toml b/Cargo.toml index 513dc7283b04..8a65aaca5f68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,7 @@ tracing = { version = "0.1" } tsify = { version = "0.4.5" } wasm-bindgen = { version = "0.2.89" } wasm-bindgen-futures = { version = "0.4" } -wasm-rs-dbg = { version = "0.1.2" } +wasm-rs-dbg = { version = "0.1.2", default-features = false, features = ["console-error"] } wasm-bindgen-test = { version = "0.3.0" } url = { version = "2.5.0" } diff --git a/quaint/src/connector/connection_info.rs b/quaint/src/connector/connection_info.rs index 4389e5ea2e55..a1c50e2d8673 100644 --- a/quaint/src/connector/connection_info.rs +++ b/quaint/src/connector/connection_info.rs @@ -190,6 +190,19 @@ impl ConnectionInfo { } } + pub fn max_insert_rows(&self) -> Option { + self.sql_family().max_insert_rows() + } + + pub fn max_bind_values(&self) -> usize { + match self { + #[cfg(not(target_arch = "wasm32"))] + ConnectionInfo::Native(_) => self.sql_family().max_bind_values(), + // Wasm connectors can override the default max bind values. + ConnectionInfo::External(info) => info.max_bind_values.unwrap_or(self.sql_family().max_bind_values()), + } + } + /// The family of databases connected. pub fn sql_family(&self) -> SqlFamily { match self { @@ -316,7 +329,7 @@ impl SqlFamily { } /// Get the default max rows for a batch insert. - pub fn max_insert_rows(&self) -> Option { + pub(crate) fn max_insert_rows(&self) -> Option { match self { #[cfg(feature = "postgresql")] SqlFamily::Postgres => None, diff --git a/quaint/src/connector/external.rs b/quaint/src/connector/external.rs index 51d341d8d9c4..92423363fcc8 100644 --- a/quaint/src/connector/external.rs +++ b/quaint/src/connector/external.rs @@ -6,13 +6,15 @@ use super::{SqlFamily, TransactionCapable}; pub struct ExternalConnectionInfo { pub sql_family: SqlFamily, pub schema_name: String, + pub max_bind_values: Option, } impl ExternalConnectionInfo { - pub fn new(sql_family: SqlFamily, schema_name: String) -> Self { + pub fn new(sql_family: SqlFamily, schema_name: String, max_bind_values: Option) -> Self { ExternalConnectionInfo { sql_family, schema_name, + max_bind_values, } } } diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_15607.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_15607.rs index a4b072256ec0..d46fda36abbc 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_15607.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_15607.rs @@ -73,7 +73,16 @@ impl Actor { Some("READ COMMITTED"), ); - let mut runner = Runner::load(datamodel, &[], version, tag, setup_metrics(), log_capture).await?; + let mut runner = Runner::load( + datamodel, + &[], + version, + tag, + CONFIG.max_bind_values(), + setup_metrics(), + log_capture, + ) + .await?; tokio::spawn(async move { while let Some(message) = query_receiver.recv().await { diff --git a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_7434.rs b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_7434.rs index 166e9e1e4a94..61788cd24e40 100644 --- a/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_7434.rs +++ b/query-engine/connector-test-kit-rs/query-engine-tests/tests/new/regressions/prisma_7434.rs @@ -1,6 +1,6 @@ use query_engine_tests::*; -#[test_suite(schema(autoinc_id), capabilities(CreateMany, AutoIncrement), exclude(CockroachDb))] +#[test_suite(schema(autoinc_id), capabilities(AutoIncrement), exclude(CockroachDb))] mod not_in_chunking { use query_engine_tests::Runner; diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs index 5566fe3d717e..f287f3e4782b 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs @@ -69,6 +69,9 @@ pub struct TestConfigFromSerde { /// This is the URL to the mobile emulator which will execute the queries against /// the instances of the engine running on the device. pub(crate) mobile_emulator_url: Option, + + /// The maximum number of bind values to use in a query for a driver adapter test runner. + pub(crate) driver_adapter_max_bind_values: Option, } impl TestConfigFromSerde { @@ -156,6 +159,9 @@ pub(crate) struct WithDriverAdapter { /// The driver adapter configuration to forward as a stringified JSON object to the external /// test executor by setting the `DRIVER_ADAPTER_CONFIG` env var when spawning the executor. pub(crate) config: Option, + + /// The maximum number of bind values to use in a query for a driver adapter test runner. + pub(crate) max_bind_values: Option, } impl WithDriverAdapter { @@ -181,6 +187,7 @@ impl From for TestConfig { adapter, test_executor: config.external_test_executor.unwrap(), config: config.driver_adapter_config, + max_bind_values: config.driver_adapter_max_bind_values, }), None => None, }; @@ -295,6 +302,9 @@ impl TestConfig { let driver_adapter_config = std::env::var("DRIVER_ADAPTER_CONFIG") .map(|config| serde_json::from_str::(config.as_str()).ok()) .unwrap_or_default(); + let driver_adapter_max_bind_values = std::env::var("DRIVER_ADAPTER_MAX_BIND_VALUES") + .ok() + .map(|v| v.parse::().unwrap()); let mobile_emulator_url = std::env::var("MOBILE_EMULATOR_URL").ok(); @@ -310,6 +320,7 @@ impl TestConfig { driver_adapter, driver_adapter_config, mobile_emulator_url, + driver_adapter_max_bind_values, }) .map(Self::from) } @@ -387,7 +398,7 @@ impl TestConfig { } pub fn test_connector(&self) -> TestResult<(ConnectorTag, ConnectorVersion)> { - let version = ConnectorVersion::try_from((self.connector(), self.connector_version()))?; + let version = self.parse_connector_version()?; let tag = match version { ConnectorVersion::SqlServer(_) => &SqlServerConnectorTag as ConnectorTag, ConnectorVersion::Postgres(_) => &PostgresConnectorTag, @@ -401,6 +412,17 @@ impl TestConfig { Ok((tag, version)) } + pub fn max_bind_values(&self) -> Option { + let version = self.parse_connector_version().unwrap(); + let local_mbv = self.with_driver_adapter().and_then(|config| config.max_bind_values); + + local_mbv.or_else(|| version.max_bind_values()) + } + + fn parse_connector_version(&self) -> TestResult { + ConnectorVersion::try_from((self.connector(), self.connector_version())) + } + #[rustfmt::skip] pub fn for_external_executor(&self) -> Vec<(String, String)> { let with_driver_adapter = self.with_driver_adapter().unwrap(); diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/mod.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/mod.rs index 8912c227c079..8390c28d1f66 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/mod.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/connector_tag/mod.rs @@ -290,19 +290,15 @@ impl ConnectorVersion { /// From the PoV of the test binary, the target architecture is that of where the test runs, /// generally x86_64, or aarch64, etc. /// - /// As a consequence there is an mismatch between the the max_bind_values as seen by the test + /// As a consequence there is a mismatch between the max_bind_values as seen by the test /// binary (overriden by the QUERY_BATCH_SIZE env var) and the max_bind_values as seen by the /// WASM engine being exercised in those tests, through the RunnerExecutor::External test runner. /// - /// What we do in here, is returning the number of max_bind_values hat the connector under test + /// What we do in here, is returning the number of max_bind_values that the connector under test /// will use. i.e. if it's a WASM connector, the default, not overridable one. Otherwise the one /// as seen by the test binary (which will be the same as the engine exercised) pub fn max_bind_values(&self) -> Option { - if self.is_wasm() { - self.sql_family().map(|f| f.default_max_bind_values()) - } else { - self.sql_family().map(|f| f.max_bind_values()) - } + self.sql_family().map(|f| f.max_bind_values()) } /// SQL family for the connector @@ -317,17 +313,6 @@ impl ConnectorVersion { _ => None, } } - - /// Determines if the connector uses a driver adapter implemented in Wasm - fn is_wasm(&self) -> bool { - matches!( - self, - Self::Postgres(Some(PostgresVersion::PgJsWasm)) - | Self::Postgres(Some(PostgresVersion::NeonJsWasm)) - | Self::Vitess(Some(VitessVersion::PlanetscaleJsWasm)) - | Self::Sqlite(Some(SqliteVersion::LibsqlJsWasm)) - ) - } } impl fmt::Display for ConnectorVersion { diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs index de7020ce3ec4..9e3aeb20ade0 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/lib.rs @@ -168,7 +168,7 @@ fn run_relation_link_test_impl( run_with_tokio( async move { println!("Used datamodel:\n {}", datamodel.yellow()); - let runner = Runner::load(datamodel.clone(), &[], version, connector_tag, metrics, log_capture) + let runner = Runner::load(datamodel.clone(), &[], version, connector_tag, CONFIG.max_bind_values(), metrics, log_capture) .await .unwrap(); @@ -286,6 +286,7 @@ fn run_connector_test_impl( db_schemas, version, connector_tag, + CONFIG.max_bind_values(), metrics, log_capture, ) diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs index a5b376e4fb68..01654d7c4e78 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/runner/mod.rs @@ -206,6 +206,9 @@ pub struct Runner { metrics: MetricRegistry, protocol: EngineProtocol, log_capture: TestLogCapture, + // This is a local override for the max bind values that can be used in a query. + // It is set in the test config files, specifically for D1 for now, which has a lower limit than the SQLite native connector. + local_max_bind_values: Option, } impl Runner { @@ -221,7 +224,8 @@ impl Runner { } pub fn max_bind_values(&self) -> Option { - self.connector_version().max_bind_values() + self.local_max_bind_values + .or_else(|| self.connector_version().max_bind_values()) } pub async fn load( @@ -229,6 +233,7 @@ impl Runner { db_schemas: &[&str], connector_version: ConnectorVersion, connector_tag: ConnectorTag, + local_max_bind_values: Option, metrics: MetricRegistry, log_capture: TestLogCapture, ) -> TestResult { @@ -240,12 +245,13 @@ impl Runner { let (executor, db_version) = match crate::CONFIG.with_driver_adapter() { Some(with_driver_adapter) => { let external_executor = ExternalExecutor::new(); - let external_initializer: ExternalExecutorInitializer<'_> = - external_executor.init(&datamodel, url.as_str()); - let executor = RunnerExecutor::External(external_executor); + + let external_initializer = external_executor.init(&datamodel, url.as_str()); qe_setup::setup_external(with_driver_adapter.adapter, external_initializer, db_schemas).await?; + let executor = RunnerExecutor::External(external_executor); + let database_version = None; (executor, database_version) } @@ -263,8 +269,8 @@ impl Runner { let connector = query_executor.primary_connector(); let conn = connector.get_connection().await.unwrap(); let database_version = conn.version().await; - let executor = RunnerExecutor::Builtin(query_executor); + (executor, database_version) } }; @@ -283,6 +289,7 @@ impl Runner { metrics, protocol, log_capture, + local_max_bind_values, }) } diff --git a/query-engine/connector-test-kit-rs/test-configs/cloudflare-d1 b/query-engine/connector-test-kit-rs/test-configs/cloudflare-d1 index 51f9a52edea3..75e3e594c688 100644 --- a/query-engine/connector-test-kit-rs/test-configs/cloudflare-d1 +++ b/query-engine/connector-test-kit-rs/test-configs/cloudflare-d1 @@ -2,5 +2,6 @@ "connector": "sqlite", "version": "cfd1", "driver_adapter": "d1", - "external_test_executor": "Wasm" + "external_test_executor": "Wasm", + "driver_adapter_max_bind_value": 100 } \ No newline at end of file diff --git a/query-engine/connectors/sql-query-connector/src/context.rs b/query-engine/connectors/sql-query-connector/src/context.rs index fbb9941f51c4..5b2887451ef1 100644 --- a/query-engine/connectors/sql-query-connector/src/context.rs +++ b/query-engine/connectors/sql-query-connector/src/context.rs @@ -13,9 +13,8 @@ pub(super) struct Context<'a> { impl<'a> Context<'a> { pub(crate) fn new(connection_info: &'a ConnectionInfo, trace_id: Option<&'a str>) -> Self { - let sql_family = connection_info.sql_family(); - let max_insert_rows = sql_family.max_insert_rows(); - let max_bind_values = sql_family.max_bind_values(); + let max_insert_rows = connection_info.max_insert_rows(); + let max_bind_values = connection_info.max_bind_values(); Context { connection_info, diff --git a/query-engine/driver-adapters/src/queryable.rs b/query-engine/driver-adapters/src/queryable.rs index 6250d137fbb6..278c4e4f514e 100644 --- a/query-engine/driver-adapters/src/queryable.rs +++ b/query-engine/driver-adapters/src/queryable.rs @@ -236,6 +236,7 @@ impl std::fmt::Debug for JsQueryable { impl ExternalConnector for JsQueryable { async fn get_connection_info(&self) -> quaint::Result { let conn_info = self.driver_proxy.get_connection_info().await?; + Ok(conn_info.into_external_connection_info(&self.inner.provider)) } } diff --git a/query-engine/driver-adapters/src/types.rs b/query-engine/driver-adapters/src/types.rs index 6a63edd5aebe..329f45f1f911 100644 --- a/query-engine/driver-adapters/src/types.rs +++ b/query-engine/driver-adapters/src/types.rs @@ -40,8 +40,8 @@ impl FromStr for AdapterFlavour { } } -impl From for SqlFamily { - fn from(value: AdapterFlavour) -> Self { +impl From<&AdapterFlavour> for SqlFamily { + fn from(value: &AdapterFlavour) -> Self { match value { #[cfg(feature = "mysql")] AdapterFlavour::Mysql => SqlFamily::Mysql, @@ -59,14 +59,21 @@ impl From for SqlFamily { #[derive(Default)] pub(crate) struct JsConnectionInfo { pub schema_name: Option, + pub max_bind_values: Option, } impl JsConnectionInfo { pub fn into_external_connection_info(self, provider: &AdapterFlavour) -> ExternalConnectionInfo { let schema_name = self.get_schema_name(provider); - let sql_family = provider.to_owned().into(); - ExternalConnectionInfo::new(sql_family, schema_name.to_owned()) + let sql_family = SqlFamily::from(provider); + + ExternalConnectionInfo::new( + sql_family, + schema_name.to_owned(), + self.max_bind_values.map(|v| v as usize), + ) } + fn get_schema_name(&self, provider: &AdapterFlavour) -> &str { match self.schema_name.as_ref() { Some(name) => name,