From 1e1c08c78cc57ee1b2687bae70f958bcff123aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Houl=C3=A9?= Date: Fri, 17 Dec 2021 15:02:19 +0100 Subject: [PATCH] Unnest migration_core module structure We probably also would want JSON-RPC on wasm (wasi). --- .github/workflows/build-wasm.yml | 4 +- migration-engine/core/Cargo.toml | 1 + migration-engine/core/src/lib.rs | 104 +++++++++++++++++- migration-engine/core/src/query_engine.rs | 86 +++++++++++++++ migration-engine/core/src/{native => }/rpc.rs | 0 5 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 migration-engine/core/src/query_engine.rs rename migration-engine/core/src/{native => }/rpc.rs (100%) diff --git a/.github/workflows/build-wasm.yml b/.github/workflows/build-wasm.yml index 8a71d8eaa83a..422f52acecd9 100644 --- a/.github/workflows/build-wasm.yml +++ b/.github/workflows/build-wasm.yml @@ -36,11 +36,11 @@ jobs: command: build args: -p migration-connector --release --target=wasm32-unknown-unknown - - name: Build the migration-core crate with default features + - name: Build the migration-core crate uses: actions-rs/cargo@v1 with: command: build - args: -p migration-core --release --target=wasm32-unknown-unknown + args: -p migration-core --release --target=wasm32-unknown-unknown --no-default-features - name: Build the prisma-fmt crate uses: actions-rs/cargo@v1 diff --git a/migration-engine/core/Cargo.toml b/migration-engine/core/Cargo.toml index 79a130c1b046..0d98263c460c 100644 --- a/migration-engine/core/Cargo.toml +++ b/migration-engine/core/Cargo.toml @@ -22,5 +22,6 @@ tracing-futures = "0.2" url = "2.1.1" [features] +default = ["sql"] mongodb = ["mongodb-migration-connector"] sql = ["sql-migration-connector"] diff --git a/migration-engine/core/src/lib.rs b/migration-engine/core/src/lib.rs index 1fdfcc11dab8..055d95320d2e 100644 --- a/migration-engine/core/src/lib.rs +++ b/migration-engine/core/src/lib.rs @@ -3,22 +3,120 @@ //! The top-level library crate for the migration engine. +pub mod commands; +#[doc(hidden)] +pub mod query_engine; + mod api; mod core_error; +mod rpc; -pub mod commands; +pub use self::{api::GenericApi, core_error::*, rpc::rpc_api}; pub use core_error::*; pub use migration_connector; #[cfg(not(target_arch = "wasm32"))] -mod native; +pub use native::*; #[cfg(not(target_arch = "wasm32"))] -pub use native::*; +mod native; + +use datamodel::{ + common::{ + preview_features::PreviewFeature, + provider_names::{MSSQL_SOURCE_NAME, MYSQL_SOURCE_NAME, POSTGRES_SOURCE_NAME, SQLITE_SOURCE_NAME}, + }, + Datasource, +}; +use enumflags2::BitFlags; +use std::env; +use user_facing_errors::{common::InvalidConnectionString, KnownError}; use datamodel::{Configuration, Datamodel}; fn parse_schema(schema: &str) -> CoreResult<(Configuration, Datamodel)> { datamodel::parse_schema(schema).map_err(CoreError::new_schema_parser_error) } + +#[cfg(feature = "mongodb")] +use datamodel::common::provider_names::MONGODB_SOURCE_NAME; +#[cfg(feature = "mongodb")] +use mongodb_migration_connector::MongoDbMigrationConnector; +#[cfg(feature = "sql")] +use sql_migration_connector::SqlMigrationConnector; + +/// Top-level constructor for the migration engine API. +pub async fn migration_api(datamodel: &str) -> CoreResult> { + let (source, url, preview_features, shadow_database_url) = parse_configuration(datamodel)?; + + match source.active_provider.as_str() { + #[cfg(feature = "sql")] + POSTGRES_SOURCE_NAME => { + let mut u = url::Url::parse(&url).map_err(|err| { + let details = user_facing_errors::quaint::invalid_connection_string_description(&format!( + "Error parsing connection string: {}", + err + )); + + CoreError::from(KnownError::new(InvalidConnectionString { details })) + })?; + + let params: Vec<(String, String)> = u.query_pairs().map(|(k, v)| (k.to_string(), v.to_string())).collect(); + + u.query_pairs_mut().clear(); + + for (k, v) in params.into_iter() { + if k == "statement_cache_size" { + u.query_pairs_mut().append_pair("statement_cache_size", "0"); + } else { + u.query_pairs_mut().append_pair(&k, &v); + } + } + + if !u.query_pairs().any(|(k, _)| k == "statement_cache_size") { + u.query_pairs_mut().append_pair("statement_cache_size", "0"); + } + + let connector = SqlMigrationConnector::new(u.to_string(), preview_features, shadow_database_url)?; + + Ok(Box::new(connector)) + } + #[cfg(feature = "sql")] + MYSQL_SOURCE_NAME | SQLITE_SOURCE_NAME | MSSQL_SOURCE_NAME => { + let connector = SqlMigrationConnector::new(url, preview_features, shadow_database_url)?; + + Ok(Box::new(connector)) + } + #[cfg(feature = "mongodb")] + MONGODB_SOURCE_NAME => Ok(Box::new(MongoDbMigrationConnector::new(url, preview_features))), + provider => Err(CoreError::from_msg(format!( + "`{}` is not a supported connector.", + provider + ))), + } +} + +fn parse_configuration(datamodel: &str) -> CoreResult<(Datasource, String, BitFlags, Option)> { + let config = datamodel::parse_configuration(datamodel) + .map(|validated_config| validated_config.subject) + .map_err(|err| CoreError::new_schema_parser_error(err.to_pretty_string("schema.prisma", datamodel)))?; + + let url = config.datasources[0] + .load_url(|key| env::var(key).ok()) + .map_err(|err| CoreError::new_schema_parser_error(err.to_pretty_string("schema.prisma", datamodel)))?; + + let shadow_database_url = config.datasources[0] + .load_shadow_database_url() + .map_err(|err| CoreError::new_schema_parser_error(err.to_pretty_string("schema.prisma", datamodel)))?; + + let preview_features = config.preview_features(); + + let source = config + .datasources + .into_iter() + .next() + .ok_or_else(|| CoreError::from_msg("There is no datasource in the schema.".into()))?; + + Ok((source, url, preview_features, shadow_database_url)) +} diff --git a/migration-engine/core/src/query_engine.rs b/migration-engine/core/src/query_engine.rs new file mode 100644 index 000000000000..7f068690e210 --- /dev/null +++ b/migration-engine/core/src/query_engine.rs @@ -0,0 +1,86 @@ +//! Query Engine test setup. + +use crate::SqlMigrationConnector; +#[cfg(feature = "mongodb")] +use datamodel::common::provider_names::MONGODB_SOURCE_NAME; +use datamodel::common::provider_names::{ + COCKROACHDB_SOURCE_NAME, MSSQL_SOURCE_NAME, MYSQL_SOURCE_NAME, POSTGRES_SOURCE_NAME, SQLITE_SOURCE_NAME, +}; +use migration_connector::{ConnectorResult, DiffTarget, MigrationConnector}; +#[cfg(feature = "mongodb")] +use mongodb_migration_connector::MongoDbMigrationConnector; + +/// Database setup for connector-test-kit-rs. +pub async fn setup(prisma_schema: &str) -> ConnectorResult<()> { + let (source, url, preview_features, _shadow_database_url) = super::parse_configuration(prisma_schema)?; + + match &source.active_provider { + provider + if [ + MYSQL_SOURCE_NAME, + POSTGRES_SOURCE_NAME, + SQLITE_SOURCE_NAME, + MSSQL_SOURCE_NAME, + COCKROACHDB_SOURCE_NAME, + ] + .contains(&provider.as_str()) => + { + // 1. creates schema & database + SqlMigrationConnector::qe_setup(&url).await?; + let api = SqlMigrationConnector::new(url, preview_features, None)?; + + // 2. create the database schema for given Prisma schema + { + let (config, schema) = crate::parse_schema(prisma_schema).unwrap(); + let migration = api + .diff(DiffTarget::Empty, DiffTarget::Datamodel((&config, &schema))) + .await + .unwrap(); + api.database_migration_step_applier() + .apply_migration(&migration) + .await + .unwrap(); + }; + } + + #[cfg(feature = "mongodb")] + provider if provider == MONGODB_SOURCE_NAME => { + let connector = MongoDbMigrationConnector::new(url, preview_features); + // Drop database. Creation is automatically done when collections are created. + connector.drop_database().await?; + let (_, schema) = crate::parse_schema(prisma_schema).unwrap(); + connector.create_collections(&schema).await?; + } + + x => unimplemented!("Connector {} is not supported yet", x), + }; + + Ok(()) +} + +/// Database teardown for connector-test-kit-rs. +pub async fn teardown(prisma_schema: &str) -> ConnectorResult<()> { + let (source, url, _, _) = super::parse_configuration(prisma_schema)?; + + match &source.active_provider { + provider + if [ + MYSQL_SOURCE_NAME, + POSTGRES_SOURCE_NAME, + SQLITE_SOURCE_NAME, + MSSQL_SOURCE_NAME, + COCKROACHDB_SOURCE_NAME, + ] + .contains(&provider.as_str()) => + { + SqlMigrationConnector::qe_teardown(&url).await?; + } + + #[cfg(feature = "mongodb")] + provider if provider == MONGODB_SOURCE_NAME => {} + + x => unimplemented!("Connector {} is not supported yet", x), + }; + + Ok(()) +} diff --git a/migration-engine/core/src/native/rpc.rs b/migration-engine/core/src/rpc.rs similarity index 100% rename from migration-engine/core/src/native/rpc.rs rename to migration-engine/core/src/rpc.rs