Skip to content

Commit

Permalink
Implement a type for different client connectors and expose first cla…
Browse files Browse the repository at this point in the history
…ss citizens (#4074)

for each of the connectors that are built-in.
  • Loading branch information
Miguel Fernández authored Jul 24, 2023
1 parent 6a4762d commit f5c61f9
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 12 deletions.
12 changes: 11 additions & 1 deletion psl/builtin-connectors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod mongodb;
mod mssql_datamodel_connector;
mod mysql_datamodel_connector;
mod native_type_definition;
mod planetscale;
mod postgres_datamodel_connector;
mod sqlite_datamodel_connector;

Expand All @@ -25,5 +26,14 @@ 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 const BUILTIN_CONNECTORS: ConnectorRegistry = &[POSTGRES, MYSQL, SQLITE, MSSQL, COCKROACH, MONGODB];
pub static BUILTIN_CONNECTORS: ConnectorRegistry = &[
POSTGRES,
MYSQL,
SQLITE,
MSSQL,
COCKROACH,
MONGODB,
PLANETSCALE_SERVERLESS,
];
2 changes: 1 addition & 1 deletion psl/builtin-connectors/src/mysql_datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl Connector for MySqlDatamodelConnector {
}

fn is_provider(&self, name: &str) -> bool {
name == "mysql" || name == "@prisma/mysql"
name == "mysql"
}

fn capabilities(&self) -> ConnectorCapabilities {
Expand Down
15 changes: 15 additions & 0 deletions psl/builtin-connectors/src/planetscale.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::mysql_datamodel_connector;
use psl_core::{
datamodel_connector::RelationMode,
js_connector::{Flavor, JsConnector},
};

pub(crate) static PLANETSCALE_SERVERLESS: JsConnector = JsConnector {
flavor: Flavor::MySQL,
canonical_connector: &mysql_datamodel_connector::MySqlDatamodelConnector,

provider_name: "@prisma/planetscale",
name: "planetscale serverless",
enforced_relation_mode: Some(RelationMode::Prisma),
allowed_protocols: Some(&["mysql", "https", "mysqls"]),
};
9 changes: 8 additions & 1 deletion psl/psl-core/src/datamodel_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ pub use self::{
relation_mode::RelationMode,
};

use crate::{configuration::DatasourceConnectorData, Configuration, Datasource, PreviewFeature};
use crate::{
configuration::DatasourceConnectorData, js_connector::JsConnector, Configuration, Datasource, PreviewFeature,
};
use diagnostics::{DatamodelError, Diagnostics, NativeTypeErrorFactory, Span};
use enumflags2::BitFlags;
use lsp_types::CompletionList;
Expand All @@ -41,6 +43,11 @@ 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<JsConnector> {
None
}

/// The name of the provider, for string comparisons determining which connector we are on.
fn provider_name(&self) -> &'static str;

Expand Down
126 changes: 126 additions & 0 deletions psl/psl-core/src/js_connector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
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 flavor 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 flavor)
/// for most of its features.
#[derive(Copy, Clone)]
pub struct JsConnector {
pub flavor: Flavor,
pub canonical_connector: &'static dyn Connector,

pub provider_name: &'static str,
pub name: &'static str,
pub enforced_relation_mode: Option<RelationMode>,
pub allowed_protocols: Option<&'static [&'static str]>,
}

#[derive(Copy, Clone)]
pub enum Flavor {
MySQL,
}

impl Connector for JsConnector {
fn as_js_connector(&self) -> Option<JsConnector> {
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<parser_database::ReferentialAction> {
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<String>) {
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<NativeTypeInstance> {
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 {
if let Some(relation_mode) = self.enforced_relation_mode {
relation_mode
} else {
self.canonical_connector.default_relation_mode()
}
}

fn allowed_relation_mode_settings(&self) -> BitFlags<RelationMode> {
if let Some(relation_mode) = self.enforced_relation_mode {
BitFlags::from(relation_mode)
} else {
self.canonical_connector.allowed_relation_mode_settings()
}
}
}
1 change: 1 addition & 0 deletions psl/psl-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "@prisma/planetscale"
url = "mysql://"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "@prisma/planetscale"
url = "mysql://"
relationMode = "foreignKeys"
}
// error: Error validating datasource `relationMode`: Invalid relation mode setting: "foreignKeys". Supported values: "prisma"
// --> schema.prisma:8
//  | 
//  7 |  url = "mysql://"
//  8 |  relationMode = "foreignKeys"
//  | 
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "@prisma/planetscale"
url = "https://"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "@prisma/planetscale"
url = "mysqls://"
}

This file was deleted.

0 comments on commit f5c61f9

Please sign in to comment.