Skip to content

Commit 95ecf73

Browse files
authored
fix: ORM-1269 fix table recreation through default schema (#5561)
This PR fixes an issue when enabling the multi schema preview feature but not using explicit schema names and relying on the default schema instead. During diffing the existing tables in the database/applied migrations were introspected with a namespace (the db default one from the connection string). But the tables from the schema file (the target state) didn't have a namespace. => actually identical tables were treated as different => tables got dropped and created This PR fixes this by making the schema generated from the schema file aware of the default schema name. Tables in both schemas now have the same schema name and diffing works as intended.
1 parent 6fa05d5 commit 95ecf73

54 files changed

Lines changed: 551 additions & 388 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test-query-engine.yml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ jobs:
3535
threads: 8
3636

3737
postgres-push:
38-
if: github.event_name == 'push'
38+
if: github.event_name == 'push' ||
39+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
3940
strategy:
4041
fail-fast: false
4142
matrix:
@@ -80,7 +81,8 @@ jobs:
8081
relation_load_strategy: ${{ matrix.database.relation_load_strategy }}
8182

8283
mysql-push:
83-
if: github.event_name == 'push'
84+
if: github.event_name == 'push' ||
85+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
8486
strategy:
8587
fail-fast: false
8688
matrix:
@@ -107,7 +109,8 @@ jobs:
107109
relation_load_strategy: ${{ matrix.database.relation_load_strategy }}
108110

109111
cockroachdb-push:
110-
if: github.event_name == 'push'
112+
if: github.event_name == 'push' ||
113+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
111114
strategy:
112115
fail-fast: false
113116
matrix:
@@ -145,7 +148,8 @@ jobs:
145148
relation_load_strategy: '["query"]'
146149

147150
mongodb-push:
148-
if: github.event_name == 'push'
151+
if: github.event_name == 'push' ||
152+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
149153
strategy:
150154
fail-fast: false
151155
matrix:
@@ -183,7 +187,8 @@ jobs:
183187
relation_load_strategy: '["query"]'
184188

185189
mssql-push:
186-
if: github.event_name == 'push'
190+
if: github.event_name == 'push' ||
191+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
187192
strategy:
188193
fail-fast: false
189194
matrix:

.github/workflows/test-schema-engine.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ concurrency:
2020

2121
jobs:
2222
test-mongodb-schema-connector:
23-
if: github.event_name == 'push'
23+
if: github.event_name == 'push' ||
24+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
2425
strategy:
2526
fail-fast: false
2627
matrix:
@@ -82,7 +83,9 @@ jobs:
8283
database_url: ${{ matrix.database.url }}
8384

8485
test-linux-push:
85-
if: github.event_name == 'push'
86+
if: |
87+
github.event_name == 'push' ||
88+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
8689
strategy:
8790
fail-fast: false
8891
matrix:
@@ -133,7 +136,8 @@ jobs:
133136
single_threaded: ${{ matrix.database.single_threaded }}
134137

135138
test-windows:
136-
if: github.event_name == 'push'
139+
if: github.event_name == 'push' ||
140+
(github.event_name == 'pull_request' && contains(github.event.pull_request.body, '/test-all'))
137141
strategy:
138142
fail-fast: false
139143
matrix:

libs/test-setup/src/postgres.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ pub async fn create_postgres_database(database_url: &str, db_name: &str) -> Resu
8383

8484
url.query_pairs_mut()
8585
.append_pair("statement_cache_size", "0")
86-
.append_pair("schema", "prisma-tests");
86+
.append_pair("schema", "public");
8787

8888
let url_str = url.to_string();
8989

9090
let conn = Quaint::new(&url_str).await?;
9191

92-
conn.raw_cmd("CREATE SCHEMA \"prisma-tests\"").await?;
92+
conn.raw_cmd("CREATE SCHEMA IF NOT EXISTS \"public\"").await?;
9393

9494
Ok((conn, url_str))
9595
}

libs/user-facing-errors/src/schema_engine.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,14 +289,14 @@ pub struct DirectDdlNotAllowed;
289289
#[derive(Debug, SimpleUserFacingError)]
290290
#[user_facing(
291291
code = "P3023",
292-
message = "When using an explicit schemas list in your datasource, `externalTables` in your prisma config must contain only fully qualified table names (e.g. `schema_name.table_name`)."
292+
message = "For the current database, `externalTables` & `externalEnums` in your prisma config must contain only fully qualified identifiers (e.g. `schema_name.table_name`)."
293293
)]
294294
pub struct MissingNamespaceInExternalTables;
295295

296296
#[derive(Debug, SimpleUserFacingError)]
297297
#[user_facing(
298298
code = "P3024",
299-
message = "When using no explicit schemas list in your datasource, `externalTables` in your prisma config must contain only simple table names without a schema name."
299+
message = "For the current database, `externalTables` & `externalEnums` in your prisma config must contain only simple identifiers without a schema name."
300300
)]
301301
pub struct UnexpectedNamespaceInExternalTables;
302302

query-engine/connector-test-kit-rs/qe-setup/src/lib.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub async fn setup_external<'a>(
7878
// 1. Compute the diff migration script.
7979
std::fs::remove_file(source.url.as_literal().unwrap().trim_start_matches("file:")).ok();
8080
let dialect = sql_schema_connector::SqlSchemaDialect::sqlite();
81-
let migration_script = crate::diff(prisma_schema, &dialect).await?;
81+
let migration_script = crate::diff(prisma_schema, &dialect, None).await?;
8282

8383
// 2. Tell JavaScript to take care of the schema migration.
8484
// This results in a JSON-RPC call to the JS runtime.
@@ -148,15 +148,25 @@ pub async fn teardown(prisma_schema: &str, db_schemas: &[&str]) -> ConnectorResu
148148

149149
/// Compute an initialisation migration script via
150150
/// `prisma migrate diff --from-empty --to-schema-datamodel $SCHEMA_PATH --script`.
151-
pub(crate) async fn diff(schema: &str, dialect: &dyn SchemaDialect) -> ConnectorResult<String> {
151+
pub(crate) async fn diff(
152+
schema: &str,
153+
dialect: &dyn SchemaDialect,
154+
default_namespace: Option<&str>,
155+
) -> ConnectorResult<String> {
152156
let from = dialect.empty_database_schema();
153-
let to = dialect.schema_from_datamodel(vec![("schema.prisma".to_string(), schema.into())])?;
157+
let to = dialect.schema_from_datamodel(vec![("schema.prisma".to_string(), schema.into())], default_namespace)?;
154158
let migration = dialect.diff(from, to, &SchemaFilter::default());
155159
dialect.render_script(&migration, &Default::default())
156160
}
157161

158162
/// Apply the script returned by [`diff`] against the database.
159163
pub(crate) async fn diff_and_apply(schema: &str, connector: &mut dyn SchemaConnector) -> ConnectorResult<()> {
160-
let script = diff(schema, &*connector.schema_dialect()).await.unwrap();
164+
let script = diff(
165+
schema,
166+
&*connector.schema_dialect(),
167+
connector.default_runtime_namespace(),
168+
)
169+
.await
170+
.unwrap();
161171
connector.db_execute(script).await
162172
}

schema-engine/commands/src/commands/create_migration.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ pub async fn create_migration(
3232
let sources: Vec<_> = input.schema.to_psl_input();
3333
let dialect = connector.schema_dialect();
3434
let filter: schema_connector::SchemaFilter = input.filters.into();
35+
let default_namespace = connector.default_runtime_namespace();
3536
// We need to start with the 'to', which is the Schema, in order to grab the
3637
// namespaces, in case we've got MultiSchema enabled.
37-
let to = dialect.schema_from_datamodel(sources)?;
38+
let to = dialect.schema_from_datamodel(sources, default_namespace)?;
3839
let namespaces = dialect.extract_namespaces(&to);
3940
filter.validate(namespaces.as_ref())?;
4041

schema-engine/commands/src/commands/diff.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ async fn diff_target_to_dialect(
138138
DiffTarget::SchemaDatamodel(schemas) => {
139139
let sources = schemas.to_psl_input();
140140
let dialect = schema_to_dialect(&sources)?;
141-
let schema = dialect.schema_from_datamodel(sources)?;
141+
let schema = dialect.schema_from_datamodel(sources, connector.default_runtime_namespace())?;
142142
Ok(Some((dialect, schema)))
143143
}
144144
DiffTarget::Url(UrlContainer { .. }) => Err(ConnectorError::from_msg(

schema-engine/commands/src/commands/evaluate_data_loss.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub async fn evaluate_data_loss(
1818
let dialect = connector.schema_dialect();
1919
let filter: schema_connector::SchemaFilter = input.filters.into();
2020

21-
let to = dialect.schema_from_datamodel(sources)?;
21+
let to = dialect.schema_from_datamodel(sources, connector.default_runtime_namespace())?;
2222

2323
let from = migration_schema_cache
2424
.get_or_insert(&input.migrations_list.migration_directories, || async {

schema-engine/commands/src/commands/schema_push.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub async fn schema_push(input: SchemaPushInput, connector: &mut dyn SchemaConne
2424
let dialect = connector.schema_dialect();
2525
let filter: schema_connector::SchemaFilter = input.filters.into();
2626

27-
let to = dialect.schema_from_datamodel(sources)?;
27+
let to = dialect.schema_from_datamodel(sources, connector.default_runtime_namespace())?;
2828
// We only consider the namespaces present in the "to" schema aka the PSL file for the introspection of the "from" schema.
2929
// So when the user removes a previously existing namespace from their PSL file we will not modify that namespace in the database.
3030
let namespaces = dialect.extract_namespaces(&to);

schema-engine/connectors/mongodb-schema-connector/src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,15 @@ impl SchemaDialect for MongoDbSchemaDialect {
9090
DatabaseSchema::new(MongoSchema::default())
9191
}
9292

93-
fn schema_from_datamodel(&self, sources: Vec<(String, psl::SourceFile)>) -> ConnectorResult<DatabaseSchema> {
93+
fn default_namespace(&self) -> Option<&str> {
94+
None
95+
}
96+
97+
fn schema_from_datamodel(
98+
&self,
99+
sources: Vec<(String, psl::SourceFile)>,
100+
_default_namespace: Option<&str>,
101+
) -> ConnectorResult<DatabaseSchema> {
94102
let validated_schema = psl::parse_schema_multi(&sources).map_err(ConnectorError::new_schema_parser_error)?;
95103
Ok(DatabaseSchema::new(schema_calculator::calculate(&validated_schema)))
96104
}
@@ -121,6 +129,10 @@ impl SchemaConnector for MongoDbSchemaConnector {
121129
Box::new(MongoDbSchemaDialect)
122130
}
123131

132+
fn default_runtime_namespace(&self) -> Option<&str> {
133+
None
134+
}
135+
124136
fn host(&self) -> &Arc<dyn ConnectorHost> {
125137
&self.host
126138
}

0 commit comments

Comments
 (0)