Skip to content

Commit

Permalink
feat(schema-engine-wasm): wasm-compatible sql-schema-describer, `sc…
Browse files Browse the repository at this point in the history
…hema-connector` (#5106)

* feat(schema-engine-wasm): wasm-compatible sql-schema-describer

* fix(schema-engine-wasm): wasm compilation, move "SqlSchemaDescriberBackend" to sqlite-native impl

* fix(schema-engine-wasm): tests

* feat(schema-engine-wasm): wasm-compatible schema-connector

* fix(schema-engine-wasm): tests

* fix(schema-engine-wasm): tests

* fix(sql-schema-describer): tests

* fix(schema-engine-wasm): Cargo.toml features of schema-connector and sql-schema-describer

* chore(quaint): workaround for mongodb-schema-connector

---------

Co-authored-by: jkomyno <[email protected]>
  • Loading branch information
jkomyno and jkomyno authored Jan 8, 2025
1 parent 4123509 commit 169dafa
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 69 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test-schema-engine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ jobs:
CLICOLOR_FORCE: 1
TEST_DATABASE_URL: ${{ matrix.database.url }}

- run: cargo nextest run -p sql-schema-describer
- run: cargo nextest run -p sql-schema-describer --features all-native
if: ${{ !matrix.database.single_threaded }}
env:
CLICOLOR_FORCE: 1
Expand Down Expand Up @@ -179,7 +179,7 @@ jobs:
#
# Single threaded tests (excluding Vitess)
#
- run: cargo nextest run -p sql-schema-describer --test-threads=1
- run: cargo nextest run -p sql-schema-describer --features all-native --test-threads=1
if: ${{ !matrix.database.is_vitess && matrix.database.single_threaded }}
env:
CLICOLOR_FORCE: 1
Expand Down
3 changes: 3 additions & 0 deletions quaint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
//! # }
//! ```
// TODO: remove once `quaint` is no longer a transitive dependency of `mongodb-schema-connector`.
#![allow(dead_code)]

#[cfg(not(any(feature = "sqlite", feature = "postgresql", feature = "mysql", feature = "mssql")))]
compile_error!("one of 'sqlite', 'postgresql', 'mysql' or 'mssql' features must be enabled");

Expand Down
4 changes: 3 additions & 1 deletion schema-engine/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ version = "0.1.0"
edition = "2021"

[dependencies]
schema-connector = { path = "../connectors/schema-connector" }
schema-connector = { path = "../connectors/schema-connector", features = [
"all-native",
] }
schema-core = { path = "../core" }
user-facing-errors = { path = "../../libs/user-facing-errors", features = [
"all-native",
Expand Down
23 changes: 21 additions & 2 deletions schema-engine/connectors/schema-connector/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,32 @@ name = "schema-connector"
version = "0.1.0"
edition = "2021"

[features]
postgresql = ["psl/postgresql", "quaint/postgresql"]
postgresql-native = ["postgresql", "quaint/postgresql-native", "quaint/pooled"]
sqlite = ["psl/sqlite", "quaint/sqlite"]
sqlite-native = ["sqlite", "quaint/sqlite-native", "quaint/pooled", "quaint/expose-drivers"]
mysql = ["psl/mysql", "quaint/mysql"]
mysql-native = ["mysql", "quaint/mysql-native", "quaint/pooled"]
mssql = ["psl/mssql", "quaint/mssql"]
mssql-native = ["mssql", "quaint/mssql-native", "quaint/pooled"]
cockroachdb = ["psl/cockroachdb", "quaint/postgresql"]
cockroachdb-native = ["cockroachdb", "quaint/postgresql-native", "quaint/pooled"]
all-native = [
"postgresql-native",
"sqlite-native",
"mysql-native",
"mssql-native",
"cockroachdb-native",
]

[dependencies]
psl.workspace = true
quaint = { workspace = true, features = ["all-native", "pooled"] }
quaint.workspace = true
serde.workspace = true
serde_json.workspace = true
user-facing-errors = { path = "../../../libs/user-facing-errors", features = [
"all-native",
"quaint",
] }

chrono.workspace = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,15 @@ impl IntrospectionContext {
/// The SQL family we're using currently.
pub fn sql_family(&self) -> SqlFamily {
match self.datasource().active_provider {
#[cfg(feature = "postgresql")]
"postgresql" => SqlFamily::Postgres,
#[cfg(feature = "cockroachdb")]
"cockroachdb" => SqlFamily::Postgres,
#[cfg(feature = "sqlite")]
"sqlite" => SqlFamily::Sqlite,
#[cfg(feature = "mssql")]
"sqlserver" => SqlFamily::Mssql,
#[cfg(feature = "mysql")]
"mysql" => SqlFamily::Mysql,
name => unreachable!("The name `{}` for the datamodel connector is not known", name),
}
Expand Down
8 changes: 6 additions & 2 deletions schema-engine/connectors/sql-schema-connector/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ uuid.workspace = true
indexmap.workspace = true

prisma-value = { path = "../../../libs/prisma-value" }
schema-connector = { path = "../schema-connector" }
sql-schema-describer = { path = "../../sql-schema-describer" }
schema-connector = { path = "../schema-connector", features = [
"all-native",
] }
sql-schema-describer = { path = "../../sql-schema-describer", features = [
"all-native",
] }
datamodel-renderer = { path = "../../datamodel-renderer" }
sql-ddl = { path = "../../../libs/sql-ddl" }
user-facing-errors = { path = "../../../libs/user-facing-errors", features = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl Connection {
}

pub(super) async fn describe_schema(&mut self) -> ConnectorResult<SqlSchema> {
// Note: this relies on quaint::connector::rusqlite::Connection, which is exposed by `quaint/expose-drivers`, and is not Wasm-compatible.
describer::SqlSchemaDescriber::new(&self.0)
.describe_impl()
.await
Expand Down
6 changes: 4 additions & 2 deletions schema-engine/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ version = "0.1.0"

[dependencies]
psl = { workspace = true, features = ["all"] }
schema-connector = { path = "../connectors/schema-connector" }
schema-connector = { path = "../connectors/schema-connector", features = [
"all-native",
] }
mongodb-schema-connector = { path = "../connectors/mongodb-schema-connector" }
sql-schema-connector = { path = "../connectors/sql-schema-connector" }
user-facing-errors = { path = "../../libs/user-facing-errors", features = [
"all-native",
"quaint",
] }

async-trait.workspace = true
Expand Down
8 changes: 6 additions & 2 deletions schema-engine/sql-introspection-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ version = "0.1.0"
edition = "2021"

[dependencies]
schema-connector = { path = "../connectors/schema-connector" }
schema-connector = { path = "../connectors/schema-connector", features = [
"all-native",
] }
sql-schema-connector = { path = "../connectors/sql-schema-connector" }
sql-schema-describer = { path = "../sql-schema-describer" }
sql-schema-describer = { path = "../sql-schema-describer", features = [
"all-native",
] }
psl = { workspace = true, features = ["all"] }
test-macros = { path = "../../libs/test-macros" }
user-facing-errors = { path = "../../libs/user-facing-errors", features = [
Expand Down
4 changes: 3 additions & 1 deletion schema-engine/sql-migration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ edition = "2021"
psl = { workspace = true, features = ["all"] }
schema-core = { path = "../core" }
sql-schema-connector = { path = "../connectors/sql-schema-connector" }
sql-schema-describer = { path = "../sql-schema-describer" }
sql-schema-describer = { path = "../sql-schema-describer", features = [
"all-native",
] }
user-facing-errors = { path = "../../libs/user-facing-errors", features = [
"all-native",
] }
Expand Down
22 changes: 19 additions & 3 deletions schema-engine/sql-schema-describer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@ edition = "2021"
name = "sql-schema-describer"
version = "0.1.0"

[features]
postgresql = ["psl/postgresql", "quaint/postgresql"]
postgresql-native = ["postgresql", "quaint/postgresql-native", "quaint/pooled"]
sqlite = ["psl/sqlite", "quaint/sqlite"]
sqlite-native = ["sqlite", "quaint/sqlite-native", "quaint/pooled", "quaint/expose-drivers"]
mysql = ["psl/mysql", "quaint/mysql"]
mysql-native = ["mysql", "quaint/mysql-native", "quaint/pooled"]
mssql = ["psl/mssql", "quaint/mssql"]
mssql-native = ["mssql", "quaint/mssql-native", "quaint/pooled"]
cockroachdb = ["psl/cockroachdb", "quaint/postgresql"]
cockroachdb-native = ["cockroachdb", "quaint/postgresql-native", "quaint/pooled"]
all-native = [
"postgresql-native",
"sqlite-native",
"mysql-native",
"mssql-native",
"cockroachdb-native",
]

[dependencies]
prisma-value = { path = "../../libs/prisma-value" }
psl = { workspace = true, features = ["all"] }
Expand All @@ -20,9 +39,6 @@ tracing.workspace = true
tracing-error = "0.2"
tracing-futures.workspace = true
quaint = { workspace = true, features = [
"all-native",
"pooled",
"expose-drivers",
"fmt-sql",
] }

Expand Down
58 changes: 4 additions & 54 deletions schema-engine/sql-schema-describer/src/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@
use crate::{
getters::Getter, ids::*, parsers::Parser, Column, ColumnArity, ColumnType, ColumnTypeFamily, DefaultValue,
DescriberResult, ForeignKeyAction, Lazy, PrismaValue, Regex, SQLSortOrder, SqlMetadata, SqlSchema,
SqlSchemaDescriberBackend,
DescriberResult, ForeignKeyAction, Lazy, PrismaValue, Regex, SQLSortOrder, SqlSchema,
};
use either::Either;
use indexmap::IndexMap;
use quaint::{
ast::{Value, ValueType},
connector::{ColumnType as QuaintColumnType, GetRow, ToColumnNames},
prelude::ResultRow,
};
use std::{any::type_name, borrow::Cow, collections::BTreeMap, convert::TryInto, fmt::Debug, path::Path};
use tracing::trace;

#[cfg(feature = "sqlite-native")]
pub(crate) mod native;

#[async_trait::async_trait]
pub trait Connection {
async fn query_raw<'a>(
Expand All @@ -24,31 +25,6 @@ pub trait Connection {
) -> quaint::Result<quaint::prelude::ResultSet>;
}

#[async_trait::async_trait]
impl Connection for std::sync::Mutex<quaint::connector::rusqlite::Connection> {
async fn query_raw<'a>(
&'a self,
sql: &'a str,
params: &'a [quaint::prelude::Value<'a>],
) -> quaint::Result<quaint::prelude::ResultSet> {
let conn = self.lock().unwrap();
let mut stmt = conn.prepare_cached(sql)?;
let column_types = stmt.columns().iter().map(QuaintColumnType::from).collect::<Vec<_>>();
let mut rows = stmt.query(quaint::connector::rusqlite::params_from_iter(params.iter()))?;
let column_names = rows.to_column_names();
let mut converted_rows = Vec::new();
while let Some(row) = rows.next()? {
converted_rows.push(row.get_result_row().unwrap());
}

Ok(quaint::prelude::ResultSet::new(
column_names,
column_types,
converted_rows,
))
}
}

#[async_trait::async_trait]
impl Connection for quaint::single::Quaint {
async fn query_raw<'a>(
Expand All @@ -70,32 +46,6 @@ impl Debug for SqlSchemaDescriber<'_> {
}
}

#[async_trait::async_trait]
impl SqlSchemaDescriberBackend for SqlSchemaDescriber<'_> {
async fn list_databases(&self) -> DescriberResult<Vec<String>> {
Ok(self.get_databases().await?)
}

async fn get_metadata(&self, _schema: &str) -> DescriberResult<SqlMetadata> {
let mut sql_schema = SqlSchema::default();
let table_count = self.get_table_names(&mut sql_schema).await?.len();
let size_in_bytes = self.get_size().await?;

Ok(SqlMetadata {
table_count,
size_in_bytes,
})
}

async fn describe(&self, _schemas: &[&str]) -> DescriberResult<SqlSchema> {
self.describe_impl().await
}

async fn version(&self) -> DescriberResult<Option<String>> {
Ok(Some(quaint::connector::sqlite_version().to_owned()))
}
}

impl Parser for SqlSchemaDescriber<'_> {}

impl<'a> SqlSchemaDescriber<'a> {
Expand Down
51 changes: 51 additions & 0 deletions schema-engine/sql-schema-describer/src/sqlite/native/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::{sqlite::Connection, DescriberResult, SqlMetadata, SqlSchema, SqlSchemaDescriberBackend};

use quaint::{
connector::{rusqlite, ColumnType as QuaintColumnType, GetRow, ToColumnNames},
prelude::{ResultSet, Value},
};

use super::SqlSchemaDescriber;

#[async_trait::async_trait]
impl Connection for std::sync::Mutex<rusqlite::Connection> {
async fn query_raw<'a>(&'a self, sql: &'a str, params: &'a [Value<'a>]) -> quaint::Result<ResultSet> {
let conn = self.lock().unwrap();
let mut stmt = conn.prepare_cached(sql)?;
let column_types = stmt.columns().iter().map(QuaintColumnType::from).collect::<Vec<_>>();
let mut rows = stmt.query(rusqlite::params_from_iter(params.iter()))?;
let column_names = rows.to_column_names();
let mut converted_rows = Vec::new();
while let Some(row) = rows.next()? {
converted_rows.push(row.get_result_row().unwrap());
}

Ok(ResultSet::new(column_names, column_types, converted_rows))
}
}

#[async_trait::async_trait]
impl SqlSchemaDescriberBackend for SqlSchemaDescriber<'_> {
async fn list_databases(&self) -> DescriberResult<Vec<String>> {
Ok(self.get_databases().await?)
}

async fn get_metadata(&self, _schema: &str) -> DescriberResult<SqlMetadata> {
let mut sql_schema = SqlSchema::default();
let table_count = self.get_table_names(&mut sql_schema).await?.len();
let size_in_bytes = self.get_size().await?;

Ok(SqlMetadata {
table_count,
size_in_bytes,
})
}

async fn describe(&self, _schemas: &[&str]) -> DescriberResult<SqlSchema> {
self.describe_impl().await
}

async fn version(&self) -> DescriberResult<Option<String>> {
Ok(Some(quaint::connector::sqlite_version().to_owned()))
}
}
4 changes: 4 additions & 0 deletions schema-engine/sql-schema-describer/tests/test_api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ impl TestApi {

async fn describe_impl(&self, schemas: &[&str]) -> Result<SqlSchema, DescriberError> {
match self.sql_family() {
#[cfg(any(feature = "postgresql", feature = "cockroachdb"))]
SqlFamily::Postgres => {
use postgres::Circumstances;
sql_schema_describer::postgres::SqlSchemaDescriber::new(
Expand All @@ -93,11 +94,13 @@ impl TestApi {
.describe(schemas)
.await
}
#[cfg(feature = "sqlite")]
SqlFamily::Sqlite => {
sql_schema_describer::sqlite::SqlSchemaDescriber::new(&self.database)
.describe_impl()
.await
}
#[cfg(feature = "mysql")]
SqlFamily::Mysql => {
use mysql::Circumstances;
sql_schema_describer::mysql::SqlSchemaDescriber::new(
Expand All @@ -115,6 +118,7 @@ impl TestApi {
.describe(schemas)
.await
}
#[cfg(feature = "mssql")]
SqlFamily::Mssql => {
sql_schema_describer::mssql::SqlSchemaDescriber::new(&self.database)
.describe(schemas)
Expand Down

0 comments on commit 169dafa

Please sign in to comment.