Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(schema-engine-wasm): wasm-compatible sql-schema-describer, schema-connector #5106

Merged
merged 9 commits into from
Jan 8, 2025
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
Loading