diff --git a/psl/builtin-connectors/src/lib.rs b/psl/builtin-connectors/src/lib.rs index c6e0f7a210ea..c477386a23ed 100644 --- a/psl/builtin-connectors/src/lib.rs +++ b/psl/builtin-connectors/src/lib.rs @@ -9,15 +9,11 @@ pub use mongodb::MongoDbType; pub use mssql_datamodel_connector::{MsSqlType, MsSqlTypeParameter}; pub use mysql_datamodel_connector::MySqlType; pub use postgres_datamodel_connector::{PostgresDatasourceProperties, PostgresType}; -pub use psl_core::js_connector::JsConnector; mod mongodb; mod mssql_datamodel_connector; mod mysql_datamodel_connector; mod native_type_definition; -mod neon; -mod pg_js; -mod planetscale; mod postgres_datamodel_connector; mod sqlite_datamodel_connector; @@ -29,18 +25,5 @@ pub const MYSQL: &'static dyn Connector = &mysql_datamodel_connector::MySqlDatam pub const SQLITE: &'static dyn Connector = &sqlite_datamodel_connector::SqliteDatamodelConnector; pub const MSSQL: &'static dyn Connector = &mssql_datamodel_connector::MsSqlDatamodelConnector; pub const MONGODB: &'static dyn Connector = &mongodb::MongoDbDatamodelConnector; -pub static PLANETSCALE_SERVERLESS: &'static dyn Connector = &planetscale::PLANETSCALE_SERVERLESS; -pub static NEON_SERVERLESS: &'static dyn Connector = &neon::NEON_SERVERLESS; -pub static PG_JS: &'static dyn Connector = &pg_js::PG_JS; -pub static BUILTIN_CONNECTORS: ConnectorRegistry = &[ - POSTGRES, - MYSQL, - SQLITE, - MSSQL, - COCKROACH, - PG_JS, - MONGODB, - PLANETSCALE_SERVERLESS, - NEON_SERVERLESS, -]; +pub static BUILTIN_CONNECTORS: ConnectorRegistry = &[POSTGRES, MYSQL, SQLITE, MSSQL, COCKROACH, MONGODB]; diff --git a/psl/builtin-connectors/src/neon.rs b/psl/builtin-connectors/src/neon.rs deleted file mode 100644 index 86a842a9504e..000000000000 --- a/psl/builtin-connectors/src/neon.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::postgres_datamodel_connector; -use psl_core::{datamodel_connector::Flavour, js_connector::JsConnector}; - -pub(crate) static NEON_SERVERLESS: JsConnector = JsConnector { - flavour: Flavour::Postgres, - canonical_connector: &postgres_datamodel_connector::PostgresDatamodelConnector, - - provider_name: "@prisma/neon", - name: "neon serverless (pg-compatible)", - allowed_protocols: Some(&["postgres"]), -}; diff --git a/psl/builtin-connectors/src/pg_js.rs b/psl/builtin-connectors/src/pg_js.rs deleted file mode 100644 index fc3be0bcbf96..000000000000 --- a/psl/builtin-connectors/src/pg_js.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::postgres_datamodel_connector; -use psl_core::{datamodel_connector::Flavour, js_connector::JsConnector}; - -pub(crate) static PG_JS: JsConnector = JsConnector { - flavour: Flavour::Postgres, - canonical_connector: &postgres_datamodel_connector::PostgresDatamodelConnector, - - provider_name: "@prisma/pg", - name: "node-postgres (pg) connector", - allowed_protocols: Some(&["postgres", "postgresql"]), -}; diff --git a/psl/builtin-connectors/src/planetscale.rs b/psl/builtin-connectors/src/planetscale.rs deleted file mode 100644 index a97fe48eaa2b..000000000000 --- a/psl/builtin-connectors/src/planetscale.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::mysql_datamodel_connector; -use psl_core::{datamodel_connector::Flavour, js_connector::JsConnector}; - -pub(crate) static PLANETSCALE_SERVERLESS: JsConnector = JsConnector { - flavour: Flavour::Mysql, - canonical_connector: &mysql_datamodel_connector::MySqlDatamodelConnector, - - provider_name: "@prisma/planetscale", - name: "planetscale serverless", - allowed_protocols: Some(&["mysql", "https", "mysqls"]), -}; diff --git a/psl/psl-core/src/datamodel_connector.rs b/psl/psl-core/src/datamodel_connector.rs index b94fc45aaa6d..72671e06688f 100644 --- a/psl/psl-core/src/datamodel_connector.rs +++ b/psl/psl-core/src/datamodel_connector.rs @@ -24,9 +24,7 @@ pub use self::{ relation_mode::RelationMode, }; -use crate::{ - configuration::DatasourceConnectorData, js_connector::JsConnector, Configuration, Datasource, PreviewFeature, -}; +use crate::{configuration::DatasourceConnectorData, Configuration, Datasource, PreviewFeature}; use diagnostics::{DatamodelError, Diagnostics, NativeTypeErrorFactory, Span}; use enumflags2::BitFlags; use lsp_types::CompletionList; @@ -44,11 +42,6 @@ pub const EXTENSIONS_KEY: &str = "extensions"; /// The datamodel connector API. pub trait Connector: Send + Sync { - // Provides safe downcasting to a JsConnector, in case it is one. - fn as_js_connector(&self) -> Option { - None - } - /// The name of the provider, for string comparisons determining which connector we are on. fn provider_name(&self) -> &'static str; @@ -368,7 +361,7 @@ pub trait Connector: Send + Sync { } } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum Flavour { Cockroach, Mongo, diff --git a/psl/psl-core/src/js_connector.rs b/psl/psl-core/src/js_connector.rs deleted file mode 100644 index c8cabc7968f1..000000000000 --- a/psl/psl-core/src/js_connector.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::datamodel_connector::*; -use enumflags2::BitFlags; - -/// JsConnector represents a type of connector that is implemented partially -/// in javascript and used from rust through the js-connectors crate -/// -/// Rather than a unit struct per individual connector, like we have for the rest -/// of the builtin connectors, we have a single struct which state represents the -/// features that vary in this connector with respect to a cannonical connector -/// for the flavour of SQL the particular JsConnector speaks. -/// -/// For example, the _planetscale serverless_ connector is compatible with MySQL, -/// so it reuses the builtin MySQL connector (the cannonical for the MySQL flavour) -/// for most of its features. -#[derive(Copy, Clone)] -pub struct JsConnector { - pub flavour: Flavour, - pub canonical_connector: &'static dyn Connector, - - pub provider_name: &'static str, - pub name: &'static str, - pub allowed_protocols: Option<&'static [&'static str]>, -} - -impl JsConnector { - /// Returns true if the given name is a valid provider name for a JsConnector. - /// We use the convention that if a provider starts with ´@prisma/´ (ex. ´@prisma/planetscale´) - /// then its a provider for a JS connector. - pub fn is_provider(name: &str) -> bool { - name.starts_with("@prisma/") - } -} - -impl Connector for JsConnector { - fn as_js_connector(&self) -> Option { - Some(*self) - } - - fn provider_name(&self) -> &'static str { - self.provider_name - } - - fn name(&self) -> &str { - self.name - } - - fn capabilities(&self) -> ConnectorCapabilities { - self.canonical_connector.capabilities() - } - - fn max_identifier_length(&self) -> usize { - self.canonical_connector.max_identifier_length() - } - - fn referential_actions(&self) -> enumflags2::BitFlags { - self.canonical_connector.referential_actions() - } - - fn available_native_type_constructors(&self) -> &'static [NativeTypeConstructor] { - self.canonical_connector.available_native_type_constructors() - } - - fn scalar_type_for_native_type(&self, native_type: &NativeTypeInstance) -> parser_database::ScalarType { - self.canonical_connector.scalar_type_for_native_type(native_type) - } - - fn default_native_type_for_scalar_type(&self, scalar_type: &parser_database::ScalarType) -> NativeTypeInstance { - self.canonical_connector - .default_native_type_for_scalar_type(scalar_type) - } - - fn native_type_is_default_for_scalar_type( - &self, - native_type: &NativeTypeInstance, - scalar_type: &parser_database::ScalarType, - ) -> bool { - self.canonical_connector - .native_type_is_default_for_scalar_type(native_type, scalar_type) - } - - fn native_type_to_parts(&self, native_type: &NativeTypeInstance) -> (&'static str, Vec) { - self.canonical_connector.native_type_to_parts(native_type) - } - - fn parse_native_type( - &self, - name: &str, - args: &[String], - span: diagnostics::Span, - diagnostics: &mut diagnostics::Diagnostics, - ) -> Option { - self.canonical_connector - .parse_native_type(name, args, span, diagnostics) - } - - fn validate_url(&self, url: &str) -> Result<(), String> { - if let Some(allowed_protocols) = self.allowed_protocols { - let scheme = url.split(':').next().unwrap_or(""); - if allowed_protocols.contains(&scheme) { - Ok(()) - } else { - Err(format!( - "The URL scheme `{}` is not valid for the {} connector. The following schemes are allowed: {}", - scheme, - self.name, - allowed_protocols.join(", ") - )) - } - } else { - self.canonical_connector.validate_url(url) - } - } - - fn default_relation_mode(&self) -> RelationMode { - self.canonical_connector.default_relation_mode() - } - - fn allowed_relation_mode_settings(&self) -> BitFlags { - self.canonical_connector.allowed_relation_mode_settings() - } - - fn flavour(&self) -> Flavour { - self.flavour - } -} diff --git a/psl/psl-core/src/lib.rs b/psl/psl-core/src/lib.rs index 29528b77cc20..bdc234528de3 100644 --- a/psl/psl-core/src/lib.rs +++ b/psl/psl-core/src/lib.rs @@ -3,7 +3,6 @@ #![allow(clippy::derive_partial_eq_without_eq)] pub mod datamodel_connector; -pub mod js_connector; /// `mcf`: Turns a collection of `configuration::Datasource` and `configuration::Generator` into a /// JSON representation. This is the `get_config()` representation. diff --git a/psl/psl/tests/validation/js_connectors/prisma_planetscale.prisma b/psl/psl/tests/validation/js_connectors/prisma_planetscale.prisma deleted file mode 100644 index 42936ac0299c..000000000000 --- a/psl/psl/tests/validation/js_connectors/prisma_planetscale.prisma +++ /dev/null @@ -1,8 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "@prisma/planetscale" - url = "mysql://" -} diff --git a/psl/psl/tests/validation/js_connectors/prisma_planetscale_https_url.prisma b/psl/psl/tests/validation/js_connectors/prisma_planetscale_https_url.prisma deleted file mode 100644 index 65e37cc69a42..000000000000 --- a/psl/psl/tests/validation/js_connectors/prisma_planetscale_https_url.prisma +++ /dev/null @@ -1,8 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "@prisma/planetscale" - url = "https://" -} diff --git a/psl/psl/tests/validation/js_connectors/prisma_planetscale_mysqls_url.prisma b/psl/psl/tests/validation/js_connectors/prisma_planetscale_mysqls_url.prisma deleted file mode 100644 index 67453789be1d..000000000000 --- a/psl/psl/tests/validation/js_connectors/prisma_planetscale_mysqls_url.prisma +++ /dev/null @@ -1,8 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "@prisma/planetscale" - url = "mysqls://" -} diff --git a/psl/psl/tests/validation/js_connectors/prisma_planetscale_supports_relation_mode_fk.prisma b/psl/psl/tests/validation/js_connectors/prisma_planetscale_supports_relation_mode_fk.prisma deleted file mode 100644 index 8fa20455c729..000000000000 --- a/psl/psl/tests/validation/js_connectors/prisma_planetscale_supports_relation_mode_fk.prisma +++ /dev/null @@ -1,9 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "@prisma/planetscale" - url = "mysql://" - relationMode = "foreignKeys" -} diff --git a/psl/psl/tests/validation/js_connectors/prisma_planetscale_supports_relation_mode_prisma.prisma b/psl/psl/tests/validation/js_connectors/prisma_planetscale_supports_relation_mode_prisma.prisma deleted file mode 100644 index 96d63705ef40..000000000000 --- a/psl/psl/tests/validation/js_connectors/prisma_planetscale_supports_relation_mode_prisma.prisma +++ /dev/null @@ -1,9 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "@prisma/planetscale" - url = "mysql://" - relationMode = "prisma" -} 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 6e6c030b7988..e9fce19c2c15 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 @@ -11,8 +11,8 @@ use query_core::{ }; use query_engine_metrics::MetricRegistry; use request_handlers::{ - load_executor, BatchTransactionOption, GraphqlBody, JsonBatchQuery, JsonBody, JsonSingleQuery, MultiQuery, - RequestBody, RequestHandler, + load_executor, BatchTransactionOption, ConnectorMode, GraphqlBody, JsonBatchQuery, JsonBody, JsonSingleQuery, + MultiQuery, RequestBody, RequestHandler, }; use std::{env, sync::Arc}; @@ -52,7 +52,15 @@ impl Runner { let schema = psl::parse_schema(datamodel).unwrap(); let data_source = schema.configuration.datasources.first().unwrap(); let url = data_source.load_url(|key| env::var(key).ok()).unwrap(); - let executor = load_executor(data_source, schema.configuration.preview_features(), &url).await?; + + let connector_mode = ConnectorMode::Rust; + let executor = load_executor( + connector_mode, + data_source, + schema.configuration.preview_features(), + &url, + ) + .await?; let query_schema: QuerySchemaRef = Arc::new(schema::build(Arc::new(schema), true)); Ok(Self { diff --git a/query-engine/connectors/sql-query-connector/src/database/js.rs b/query-engine/connectors/sql-query-connector/src/database/js.rs index ed4415323cc6..dc876bb3b8dc 100644 --- a/query-engine/connectors/sql-query-connector/src/database/js.rs +++ b/query-engine/connectors/sql-query-connector/src/database/js.rs @@ -51,7 +51,6 @@ pub struct Js { connector: JsConnector, connection_info: ConnectionInfo, features: psl::PreviewFeatures, - psl_connector: psl::builtin_connectors::JsConnector, } fn get_connection_info(url: &str) -> connector::Result { @@ -70,23 +69,14 @@ impl FromSource for Js { url: &str, features: psl::PreviewFeatures, ) -> connector_interface::Result { - match source.active_connector.as_js_connector() { - Some(psl_connector) => { - let connector = registered_js_connector(source.active_provider)?; - let connection_info = get_connection_info(url)?; - - Ok(Js { - connector, - connection_info, - features, - psl_connector, - }) - } - None => panic!( - "Connector for provider {} is not a JsConnector", - source.active_connector.provider_name() - ), - } + let connector = registered_js_connector(source.active_provider)?; + let connection_info = get_connection_info(url)?; + + Ok(Js { + connector, + connection_info, + features, + }) } } @@ -101,7 +91,7 @@ impl Connector for Js { } fn name(&self) -> &'static str { - self.psl_connector.name + "js" } fn should_retry_on_transient_error(&self) -> bool { diff --git a/query-engine/js-connectors/js/smoke-test-js/prisma/mysql-planetscale/schema.prisma b/query-engine/js-connectors/js/smoke-test-js/prisma/mysql-planetscale/schema.prisma index 2e5de3f176c0..c13f726baa42 100644 --- a/query-engine/js-connectors/js/smoke-test-js/prisma/mysql-planetscale/schema.prisma +++ b/query-engine/js-connectors/js/smoke-test-js/prisma/mysql-planetscale/schema.prisma @@ -3,7 +3,7 @@ generator client { } datasource db { - provider = "@prisma/planetscale" + provider = "mysql" url = env("JS_PLANETSCALE_DATABASE_URL") } diff --git a/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-neon/schema.prisma b/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-neon/schema.prisma index cb34099493a7..48c2f670a1f4 100644 --- a/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-neon/schema.prisma +++ b/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-neon/schema.prisma @@ -3,7 +3,7 @@ generator client { } datasource db { - provider = "@prisma/neon" + provider = "postgres" url = env("JS_NEON_DATABASE_URL") } diff --git a/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-pg/schema.prisma b/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-pg/schema.prisma index 3c91af0de686..23e66c2f846b 100644 --- a/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-pg/schema.prisma +++ b/query-engine/js-connectors/js/smoke-test-js/prisma/postgres-pg/schema.prisma @@ -3,7 +3,7 @@ generator client { } datasource db { - provider = "@prisma/pg" + provider = "postgres" url = env("JS_PG_DATABASE_URL") } diff --git a/query-engine/js-connectors/src/queryable.rs b/query-engine/js-connectors/src/queryable.rs index d75f3c376988..f65e55d8d7ce 100644 --- a/query-engine/js-connectors/src/queryable.rs +++ b/query-engine/js-connectors/src/queryable.rs @@ -29,7 +29,7 @@ use tracing::{info_span, Instrument}; /// pub struct JsBaseQueryable { pub(crate) proxy: CommonProxy, - pub(crate) flavour: Flavour, + pub flavour: Flavour, } impl JsBaseQueryable { diff --git a/query-engine/query-engine-node-api/src/engine.rs b/query-engine/query-engine-node-api/src/engine.rs index 45986e74368a..f3ec435ec308 100644 --- a/query-engine/query-engine-node-api/src/engine.rs +++ b/query-engine/query-engine-node-api/src/engine.rs @@ -9,7 +9,7 @@ use query_core::{ telemetry, QueryExecutor, TransactionOptions, TxId, }; use query_engine_metrics::{MetricFormat, MetricRegistry}; -use request_handlers::{dmmf, load_executor, render_graphql_schema, RequestBody, RequestHandler}; +use request_handlers::{dmmf, load_executor, render_graphql_schema, ConnectorMode, RequestBody, RequestHandler}; use serde::{Deserialize, Serialize}; use serde_json::json; use std::{ @@ -27,6 +27,7 @@ use user_facing_errors::Error; /// The main query engine used by JS #[napi] pub struct QueryEngine { + connector_mode: ConnectorMode, inner: RwLock, logger: Logger, } @@ -166,19 +167,27 @@ impl QueryEngine { let env = stringify_env_values(env)?; // we cannot trust anything JS sends us from process.env let overrides: Vec<(_, _)> = datasource_overrides.into_iter().collect(); + let mut schema = psl::validate(datamodel.into()); - let config = &mut schema.configuration; - let provider_name = schema.connector.provider_name(); + let mut connector_mode = ConnectorMode::Rust; #[cfg(feature = "js-connectors")] if let Some(driver) = maybe_driver { - let queryable = js_connectors::from_napi(&napi_env, driver); - match sql_connector::register_js_connector(provider_name, Arc::new(queryable)) { - Ok(_) => tracing::info!("Registered js connector for {provider_name}"), + let js_queryable = js_connectors::from_napi(&napi_env, driver); + let provider_name = schema.connector.provider_name(); + + match sql_connector::register_js_connector(provider_name, Arc::new(js_queryable)) { + Ok(_) => { + connector_mode = ConnectorMode::Js; + tracing::info!("Registered js connector for {provider_name}") + } Err(err) => tracing::error!("Failed to registered js connector for {provider_name}. {err}"), } } + let connector_mode = connector_mode; + let config = &mut schema.configuration; + schema .diagnostics .to_result() @@ -226,6 +235,7 @@ impl QueryEngine { } Ok(Self { + connector_mode, inner: RwLock::new(Inner::Builder(builder)), logger, }) @@ -268,7 +278,7 @@ impl QueryEngine { let preview_features = arced_schema.configuration.preview_features(); let executor_fut = async { - let executor = load_executor(data_source, preview_features, &url).await?; + let executor = load_executor(self.connector_mode, data_source, preview_features, &url).await?; let connector = executor.primary_connector(); let conn_span = tracing::info_span!( diff --git a/query-engine/query-engine/src/context.rs b/query-engine/query-engine/src/context.rs index 44a2a215487b..1beb2c5eef7f 100644 --- a/query-engine/query-engine/src/context.rs +++ b/query-engine/query-engine/src/context.rs @@ -9,7 +9,7 @@ use query_core::{ }; use query_engine_metrics::setup as metric_setup; use query_engine_metrics::MetricRegistry; -use request_handlers::load_executor; +use request_handlers::{load_executor, ConnectorMode}; use std::{env, fmt, sync::Arc}; use tracing::Instrument; @@ -63,7 +63,8 @@ impl PrismaContext { let url = data_source.load_url(|key| env::var(key).ok())?; // Load executor - let executor = load_executor(data_source, preview_features, &url).await?; + let connector_mode = ConnectorMode::Rust; + let executor = load_executor(connector_mode, data_source, preview_features, &url).await?; executor.primary_connector().get_connection().await?; PrismaResult::<_>::Ok(executor) }); diff --git a/query-engine/request-handlers/src/connector_mode.rs b/query-engine/request-handlers/src/connector_mode.rs new file mode 100644 index 000000000000..00e0515a596e --- /dev/null +++ b/query-engine/request-handlers/src/connector_mode.rs @@ -0,0 +1,8 @@ +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum ConnectorMode { + /// Indicates that Rust drivers are used in Query Engine. + Rust, + + /// Indicates that JS drivers are used in Query Engine. + Js, +} diff --git a/query-engine/request-handlers/src/lib.rs b/query-engine/request-handlers/src/lib.rs index f3b21008d759..361e5c628bdf 100644 --- a/query-engine/request-handlers/src/lib.rs +++ b/query-engine/request-handlers/src/lib.rs @@ -2,6 +2,7 @@ pub mod dmmf; +mod connector_mode; mod error; mod handler; mod load_executor; @@ -9,6 +10,7 @@ mod protocols; mod response; pub use self::{error::HandlerError, load_executor::load as load_executor}; +pub use connector_mode::ConnectorMode; pub use handler::*; pub use protocols::{graphql::*, json::*, RequestBody}; pub use response::*; diff --git a/query-engine/request-handlers/src/load_executor.rs b/query-engine/request-handlers/src/load_executor.rs index 8232e907e60f..3658885c985a 100644 --- a/query-engine/request-handlers/src/load_executor.rs +++ b/query-engine/request-handlers/src/load_executor.rs @@ -8,12 +8,20 @@ use url::Url; #[cfg(feature = "mongodb")] use mongodb_query_connector::MongoDb; +use super::ConnectorMode; + /// Loads a query executor based on the parsed Prisma schema (datasource). pub async fn load( + connector_mode: ConnectorMode, source: &Datasource, features: PreviewFeatures, url: &str, ) -> query_core::Result> { + if connector_mode == ConnectorMode::Js { + #[cfg(feature = "js-connectors")] + return jsconnector(source, url, features).await; + } + match source.active_provider { p if SQLITE.is_provider(p) => sqlite(source, url, features).await, p if MYSQL.is_provider(p) => mysql(source, url, features).await, @@ -24,9 +32,6 @@ pub async fn load( #[cfg(feature = "mongodb")] p if MONGODB.is_provider(p) => mongodb(source, url, features).await, - #[cfg(feature = "js-connectors")] - p if JsConnector::is_provider(p) => jsconnector(source, url, features).await, - x => Err(query_core::CoreError::ConfigurationError(format!( "Unsupported connector type: {x}" ))),