Skip to content

Commit

Permalink
driver-adapters: Don't read URL from schema (#4548)
Browse files Browse the repository at this point in the history
* driver-adapters: Don't read URL from schema

By desing, adapter itself is responsible for establishing connection and
users are expected to provide url to JS driver manually. Yet, we
continued to validate and read URL from schema file upon initialization.

Information, derived from connection URL used in a bunch of places in
quaint and other crates. Most of them are optional, such as error
messages.

However, one important thing - default schema name - is used for
building SQL queries. We need an alternative way to provide that
information, that does not include URL parsing.

This PR expands adapter API by adding optional method,
`getConnectionInfo`. Right now, it can only return an object wiht `schemaName`
property. If it does, this name will be used as a prefix for SQL
queries. If it does not, or `getConnectionInfo` is ommited, defaults
will be used:
- `main` for postgres and sqlite
- `mysql` for mysql

Generally, it is up for adapter to decide on where to get this info
from: if URL on JS side contains it, it might get it from there or it
can have an optional constructor option with a schema name.

This PR will require a follow up on JS side too: at the minimum,
expanding public interface of a driver.

Contributes to prisma/prisma-orm#662

* Fix test runner

* Fix clippy & migrate tests

* Remove commented out code

* Correct default schema for postgres

* Provide schema URL to the driver adapters

DRIVER_ADAPTERS_BRANCH=feat/getConnectionInfo

* Rename SQLITE_DEFAULT_SCHEMA to DATABASE

* Add a way to call JS functions without promises to both WASM and NAPI

* Use newer adapters
  • Loading branch information
SevInf committed Dec 12, 2023
1 parent faa6559 commit 8a6f85f
Show file tree
Hide file tree
Showing 34 changed files with 319 additions and 173 deletions.
2 changes: 2 additions & 0 deletions quaint/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

mod connection_info;

pub mod external;
pub mod metrics;
mod queryable;
mod result_set;
Expand All @@ -21,6 +22,7 @@ mod type_identifier;

pub use self::result_set::*;
pub use connection_info::*;
pub use external::*;
pub use queryable::*;
pub use transaction::*;
#[cfg(any(feature = "mssql-native", feature = "postgresql-native", feature = "mysql-native"))]
Expand Down
16 changes: 15 additions & 1 deletion quaint/src/connector/connection_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use crate::connector::SqliteParams;
#[cfg(feature = "sqlite")]
use std::convert::TryFrom;

use super::ExternalConnectionInfo;

/// General information about a SQL connection.
#[derive(Debug, Clone)]
pub enum ConnectionInfo {
Expand All @@ -34,7 +36,10 @@ pub enum ConnectionInfo {
db_name: String,
},
#[cfg(feature = "sqlite")]
InMemorySqlite { db_name: String },
InMemorySqlite {
db_name: String,
},
External(ExternalConnectionInfo),
}

impl ConnectionInfo {
Expand Down Expand Up @@ -104,6 +109,7 @@ impl ConnectionInfo {
ConnectionInfo::Mssql(url) => Some(url.dbname()),
#[cfg(feature = "sqlite")]
ConnectionInfo::Sqlite { .. } | ConnectionInfo::InMemorySqlite { .. } => None,
ConnectionInfo::External(_) => None,
}
}

Expand All @@ -124,6 +130,7 @@ impl ConnectionInfo {
ConnectionInfo::Sqlite { db_name, .. } => db_name,
#[cfg(feature = "sqlite")]
ConnectionInfo::InMemorySqlite { db_name } => db_name,
ConnectionInfo::External(info) => &info.schema_name,
}
}

Expand All @@ -138,6 +145,8 @@ impl ConnectionInfo {
ConnectionInfo::Mssql(url) => url.host(),
#[cfg(feature = "sqlite")]
ConnectionInfo::Sqlite { .. } | ConnectionInfo::InMemorySqlite { .. } => "localhost",

ConnectionInfo::External(_) => "external",
}
}

Expand All @@ -152,6 +161,7 @@ impl ConnectionInfo {
ConnectionInfo::Mssql(url) => url.username().map(Cow::from),
#[cfg(feature = "sqlite")]
ConnectionInfo::Sqlite { .. } | ConnectionInfo::InMemorySqlite { .. } => None,
ConnectionInfo::External(_) => None,
}
}

Expand All @@ -168,6 +178,7 @@ impl ConnectionInfo {
ConnectionInfo::Sqlite { file_path, .. } => Some(file_path),
#[cfg(feature = "sqlite")]
ConnectionInfo::InMemorySqlite { .. } => None,
ConnectionInfo::External(_) => None,
}
}

Expand All @@ -182,6 +193,7 @@ impl ConnectionInfo {
ConnectionInfo::Mssql(_) => SqlFamily::Mssql,
#[cfg(feature = "sqlite")]
ConnectionInfo::Sqlite { .. } | ConnectionInfo::InMemorySqlite { .. } => SqlFamily::Sqlite,
ConnectionInfo::External(info) => info.sql_family.to_owned(),
}
}

Expand All @@ -196,6 +208,7 @@ impl ConnectionInfo {
ConnectionInfo::Mssql(url) => Some(url.port()),
#[cfg(feature = "sqlite")]
ConnectionInfo::Sqlite { .. } | ConnectionInfo::InMemorySqlite { .. } => None,
ConnectionInfo::External(_) => None,
}
}

Expand Down Expand Up @@ -223,6 +236,7 @@ impl ConnectionInfo {
ConnectionInfo::Sqlite { file_path, .. } => file_path.clone(),
#[cfg(feature = "sqlite")]
ConnectionInfo::InMemorySqlite { .. } => "in-memory".into(),
ConnectionInfo::External(_) => "external".into(),
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions quaint/src/connector/external.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use async_trait::async_trait;

use super::{SqlFamily, TransactionCapable};

#[derive(Debug, Clone)]
pub struct ExternalConnectionInfo {
pub sql_family: SqlFamily,
pub schema_name: String,
}

impl ExternalConnectionInfo {
pub fn new(sql_family: SqlFamily, schema_name: String) -> Self {
ExternalConnectionInfo {
sql_family,
schema_name,
}
}
}

#[async_trait]
pub trait ExternalConnector: TransactionCapable {
async fn get_connection_info(&self) -> crate::Result<ExternalConnectionInfo>;
}
2 changes: 2 additions & 0 deletions quaint/src/connector/mysql.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Wasm-compatible definitions for the MySQL connector.
//! This module is only available with the `mysql` feature.
mod defaults;
pub(crate) mod error;
pub(crate) mod url;

pub use self::url::*;
pub use error::MysqlError;

pub use defaults::*;
#[cfg(feature = "mysql-native")]
pub(crate) mod native;
1 change: 1 addition & 0 deletions quaint/src/connector/mysql/defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const DEFAULT_MYSQL_DB: &str = "mysql";
4 changes: 2 additions & 2 deletions quaint/src/connector/mysql/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ impl MysqlUrl {
/// Name of the database connected. Defaults to `mysql`.
pub fn dbname(&self) -> &str {
match self.url.path_segments() {
Some(mut segments) => segments.next().unwrap_or("mysql"),
None => "mysql",
Some(mut segments) => segments.next().unwrap_or(super::defaults::DEFAULT_MYSQL_DB),
None => super::defaults::DEFAULT_MYSQL_DB,
}
}

Expand Down
2 changes: 2 additions & 0 deletions quaint/src/connector/postgres.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Wasm-compatible definitions for the PostgreSQL connector.
//! This module is only available with the `postgresql` feature.
mod defaults;
pub(crate) mod error;
pub(crate) mod url;

pub use self::url::*;
pub use defaults::*;
pub use error::PostgresError;

#[cfg(feature = "postgresql-native")]
Expand Down
1 change: 1 addition & 0 deletions quaint/src/connector/postgres/defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const DEFAULT_POSTGRES_SCHEMA: &str = "public";
7 changes: 4 additions & 3 deletions quaint/src/connector/postgres/url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ pub struct PostgresUrl {
pub(crate) flavour: PostgresFlavour,
}

pub(crate) const DEFAULT_SCHEMA: &str = "public";

impl PostgresUrl {
/// Parse `Url` to `PostgresUrl`. Returns error for mistyped connection
/// parameters.
Expand Down Expand Up @@ -157,7 +155,10 @@ impl PostgresUrl {

/// The database schema, defaults to `public`.
pub fn schema(&self) -> &str {
self.query_params.schema.as_deref().unwrap_or(DEFAULT_SCHEMA)
self.query_params
.schema
.as_deref()
.unwrap_or(super::defaults::DEFAULT_POSTGRES_SCHEMA)
}

/// Whether the pgbouncer mode is enabled.
Expand Down
2 changes: 2 additions & 0 deletions quaint/src/connector/sqlite.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Wasm-compatible definitions for the SQLite connector.
//! This module is only available with the `sqlite` feature.
mod defaults;
pub(crate) mod error;
mod ffi;
pub(crate) mod params;

pub use defaults::*;
pub use error::SqliteError;
pub use params::*;

Expand Down
1 change: 1 addition & 0 deletions quaint/src/connector/sqlite/defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const DEFAULT_SQLITE_DATABASE: &str = "main";
4 changes: 1 addition & 3 deletions quaint/src/connector/sqlite/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
use crate::error::{Error, ErrorKind};
use std::{convert::TryFrom, path::Path, time::Duration};

pub(crate) const DEFAULT_SQLITE_SCHEMA_NAME: &str = "main";

/// Wraps a connection url and exposes the parsing logic used by Quaint,
/// including default values.
#[derive(Debug)]
Expand Down Expand Up @@ -95,7 +93,7 @@ impl TryFrom<&str> for SqliteParams {
Ok(Self {
connection_limit,
file_path: path_str.to_owned(),
db_name: DEFAULT_SQLITE_SCHEMA_NAME.to_owned(),
db_name: super::DEFAULT_SQLITE_DATABASE.to_owned(),
socket_timeout,
max_connection_lifetime,
max_idle_connection_lifetime,
Expand Down
3 changes: 2 additions & 1 deletion quaint/src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! A "prelude" for users of the `quaint` crate.
pub use crate::ast::*;
pub use crate::connector::{
ConnectionInfo, DefaultTransaction, Queryable, ResultRow, ResultSet, SqlFamily, TransactionCapable,
ConnectionInfo, DefaultTransaction, ExternalConnectionInfo, Queryable, ResultRow, ResultSet, SqlFamily,
TransactionCapable,
};
pub use crate::{col, val, values};
4 changes: 2 additions & 2 deletions quaint/src/single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ impl Quaint {
#[cfg(feature = "sqlite-native")]
/// Open a new SQLite database in memory.
pub fn new_in_memory() -> crate::Result<Quaint> {
use crate::connector::DEFAULT_SQLITE_SCHEMA_NAME;
use crate::connector::sqlite::DEFAULT_SQLITE_DATABASE;

Ok(Quaint {
inner: Arc::new(connector::Sqlite::new_in_memory()?),
connection_info: Arc::new(ConnectionInfo::InMemorySqlite {
db_name: DEFAULT_SQLITE_SCHEMA_NAME.to_owned(),
db_name: DEFAULT_SQLITE_DATABASE.to_owned(),
}),
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use query_core::{
};
use query_engine_metrics::MetricRegistry;
use request_handlers::{
BatchTransactionOption, ConnectorMode, GraphqlBody, JsonBatchQuery, JsonBody, JsonSingleQuery, MultiQuery,
BatchTransactionOption, ConnectorKind, GraphqlBody, JsonBatchQuery, JsonBody, JsonSingleQuery, MultiQuery,
RequestBody, RequestHandler,
};
use serde_json::json;
Expand Down Expand Up @@ -126,10 +126,9 @@ impl Runner {
Some(_) => RunnerExecutor::new_external(&url, &datamodel).await?,
None => RunnerExecutor::Builtin(
request_handlers::load_executor(
ConnectorMode::Rust,
ConnectorKind::Rust { url: url.to_owned() },
data_source,
schema.configuration.preview_features(),
&url,
)
.await?,
),
Expand Down
3 changes: 3 additions & 0 deletions query-engine/connectors/query-connector/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ pub enum ErrorKind {

#[error("External connector error")]
ExternalError(i32),

#[error("Invalid driver adapter: {0}")]
InvalidDriverAdapter(String),
}

impl From<DomainError> for ConnectorError {
Expand Down
12 changes: 6 additions & 6 deletions query-engine/connectors/sql-query-connector/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use quaint::prelude::ConnectionInfo;
use quaint::{connector::SqlFamily, prelude::ConnectionInfo};

pub(super) struct Context<'a> {
connection_info: &'a ConnectionInfo,
Expand All @@ -13,12 +13,12 @@ pub(super) struct Context<'a> {

impl<'a> Context<'a> {
pub(crate) fn new(connection_info: &'a ConnectionInfo, trace_id: Option<&'a str>) -> Self {
let (max_rows, default_batch_size) = match connection_info {
ConnectionInfo::Postgres(_) => (None, 32766),
let (max_rows, default_batch_size) = match connection_info.sql_family() {
SqlFamily::Postgres => (None, 32766),
// See https://stackoverflow.com/a/11131824/788562
ConnectionInfo::Mysql(_) => (None, 65535),
ConnectionInfo::Mssql(_) => (Some(1000), 2099),
ConnectionInfo::Sqlite { .. } | ConnectionInfo::InMemorySqlite { .. } => (Some(999), 999),
SqlFamily::Mysql => (None, 65535),
SqlFamily::Mssql => (Some(1000), 2099),
SqlFamily::Sqlite => (Some(999), 999),
};
Context {
connection_info,
Expand Down
42 changes: 20 additions & 22 deletions query-engine/connectors/sql-query-connector/src/database/js.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::connection::SqlConnection;
use crate::FromSource;
use async_trait::async_trait;
use connector_interface::{
self as connector,
Expand All @@ -8,7 +7,7 @@ use connector_interface::{
};
use once_cell::sync::Lazy;
use quaint::{
connector::{IsolationLevel, Transaction},
connector::{ExternalConnector, IsolationLevel, Transaction},
prelude::{Queryable as QuaintQueryable, *},
};
use std::sync::{Arc, Mutex};
Expand All @@ -30,7 +29,7 @@ fn active_driver_adapter(provider: &str) -> connector::Result<DriverAdapter> {
))))
}

pub fn activate_driver_adapter(connector: Arc<dyn TransactionCapable>) {
pub fn activate_driver_adapter(connector: Arc<dyn ExternalConnector>) {
let mut lock = ACTIVE_DRIVER_ADAPTER.lock().unwrap();

*lock = Some(DriverAdapter { connector });
Expand All @@ -42,29 +41,21 @@ pub struct Js {
features: psl::PreviewFeatures,
}

fn get_connection_info(url: &str) -> connector::Result<ConnectionInfo> {
ConnectionInfo::from_url(url).map_err(|err| {
ConnectorError::from_kind(ErrorKind::InvalidDatabaseUrl {
details: err.to_string(),
url: url.to_string(),
})
})
}

#[async_trait]
impl FromSource for Js {
async fn from_source(
source: &psl::Datasource,
url: &str,
features: psl::PreviewFeatures,
) -> connector_interface::Result<Js> {
impl Js {
pub async fn new(source: &psl::Datasource, features: psl::PreviewFeatures) -> connector_interface::Result<Self> {
let connector = active_driver_adapter(source.active_provider)?;
let connection_info = get_connection_info(url)?;

let external_conn_info = connector.get_connection_info().await.map_err(|e| match e.kind() {
&quaint::error::ErrorKind::ExternalError(id) => ConnectorError::from_kind(ErrorKind::ExternalError(id)),
_ => ConnectorError::from_kind(ErrorKind::InvalidDriverAdapter(
"Error while calling getConnectionInfo()".into(),
)),
})?;

Ok(Js {
connector,
connection_info,
features,
connection_info: ConnectionInfo::External(external_conn_info),
})
}
}
Expand Down Expand Up @@ -105,7 +96,14 @@ impl Connector for Js {
/// in this object, and implementing TransactionCapable (and quaint::Queryable) explicitly for it.
#[derive(Clone)]
pub struct DriverAdapter {
connector: Arc<dyn TransactionCapable>,
connector: Arc<dyn ExternalConnector>,
}

#[async_trait]
impl ExternalConnector for DriverAdapter {
async fn get_connection_info(&self) -> quaint::Result<ExternalConnectionInfo> {
self.connector.get_connection_info().await
}
}

#[async_trait]
Expand Down
Loading

0 comments on commit 8a6f85f

Please sign in to comment.