Skip to content

Commit

Permalink
Referential action support for IE, ME and PSL
Browse files Browse the repository at this point in the history
Supported actions: `Cascade`, `Restrict`, `NoAction`, `SetNull` and
`SetDefault`.

Per-database validation, e.g. `Restrict` doesn't validate on SQL Server.

Defaults to:

- `onUpdate`: `SetNull` on optional and `Cascade` on required relations.
- `onDelete`: `SetNull` on optional and `Restrict` on required relations.

Currently ALWAYS renders the actions on introspection, due to the
database always has some action set.
  • Loading branch information
Julius de Bruijn committed May 28, 2021
1 parent 9b2b3f8 commit 9fccafd
Show file tree
Hide file tree
Showing 50 changed files with 1,328 additions and 444 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -1,11 +1,10 @@
use crate::commenting_out_guardrails::commenting_out_guardrails;
use crate::introspection::introspect;
use crate::introspection_helpers::*;
use crate::prisma_1_defaults::*;
use crate::re_introspection::enrich;
use crate::sanitize_datamodel_names::{sanitization_leads_to_duplicate_names, sanitize_datamodel_names};
use crate::version_checker::VersionChecker;
use crate::SqlIntrospectionResult;
use crate::{commenting_out_guardrails::commenting_out_guardrails, introspection::introspect};
use datamodel::Datamodel;
use introspection_connector::IntrospectionResult;
use quaint::connector::SqlFamily;
Expand Down Expand Up @@ -63,7 +62,7 @@ mod tests {
use super::*;
use datamodel::{
dml, Datamodel, DefaultValue as DMLDefault, Field, FieldArity, FieldType, Model, NativeTypeInstance,
OnDeleteStrategy, RelationField, RelationInfo, ScalarField, ScalarType, ValueGenerator,
RelationField, RelationInfo, ScalarField, ScalarType, ValueGenerator,
};
use native_types::{NativeType, PostgresType};
use pretty_assertions::assert_eq;
Expand Down Expand Up @@ -470,7 +469,8 @@ mod tests {
fields: vec![],
references: vec![],
name: "CityToUser".to_string(),
on_delete: OnDeleteStrategy::None,
on_delete: None,
on_update: None,
},
)),
],
Expand Down Expand Up @@ -557,7 +557,8 @@ mod tests {
to: "City".to_string(),
fields: vec!["city_id".to_string(), "city_name".to_string()],
references: vec!["id".to_string(), "name".to_string()],
on_delete: OnDeleteStrategy::None,
on_delete: None,
on_update: None,
},
)),
],
Expand Down Expand Up @@ -854,7 +855,8 @@ mod tests {
fields: vec![],
references: vec![],
name: "CityToUser".to_string(),
on_delete: OnDeleteStrategy::None,
on_delete: None,
on_update: None,
},
)),
],
Expand Down Expand Up @@ -911,7 +913,8 @@ mod tests {
to: "City".to_string(),
fields: vec!["city_id".to_string()],
references: vec!["id".to_string()],
on_delete: OnDeleteStrategy::None,
on_delete: None,
on_update: None,
},
)),
],
Expand Down
Expand Up @@ -2,13 +2,13 @@ use crate::Dedup;
use crate::SqlError;
use datamodel::{
common::RelationNames, Datamodel, DefaultValue as DMLDef, FieldArity, FieldType, IndexDefinition, Model,
OnDeleteStrategy, RelationField, RelationInfo, ScalarField, ScalarType, ValueGenerator as VG,
ReferentialAction, RelationField, RelationInfo, ScalarField, ScalarType, ValueGenerator as VG,
};
use datamodel_connector::Connector;
use quaint::connector::SqlFamily;
use sql_datamodel_connector::SqlDatamodelConnectors;
use sql_schema_describer::DefaultKind;
use sql_schema_describer::{Column, ColumnArity, ColumnTypeFamily, ForeignKey, Index, IndexType, SqlSchema, Table};
use sql_schema_describer::{DefaultKind, ForeignKeyAction};
use tracing::debug;

//checks
Expand Down Expand Up @@ -107,7 +107,8 @@ pub fn calculate_many_to_many_field(
fields: vec![],
to: opposite_foreign_key.referenced_table.clone(),
references: opposite_foreign_key.referenced_columns.clone(),
on_delete: OnDeleteStrategy::None,
on_delete: None,
on_update: None,
};

let basename = opposite_foreign_key.referenced_table.clone();
Expand Down Expand Up @@ -174,12 +175,21 @@ pub(crate) fn calculate_relation_field(
) -> Result<RelationField, SqlError> {
debug!("Handling foreign key {:?}", foreign_key);

let map_action = |action: ForeignKeyAction| match action {
ForeignKeyAction::NoAction => ReferentialAction::NoAction,
ForeignKeyAction::Restrict => ReferentialAction::Restrict,
ForeignKeyAction::Cascade => ReferentialAction::Cascade,
ForeignKeyAction::SetNull => ReferentialAction::SetNull,
ForeignKeyAction::SetDefault => ReferentialAction::SetDefault,
};

let relation_info = RelationInfo {
name: calculate_relation_name(schema, foreign_key, table)?,
fields: foreign_key.columns.clone(),
to: foreign_key.referenced_table.clone(),
references: foreign_key.referenced_columns.clone(),
on_delete: OnDeleteStrategy::None,
on_delete: Some(map_action(foreign_key.on_delete_action)),
on_update: Some(map_action(foreign_key.on_update_action)),
};

let columns: Vec<&Column> = foreign_key
Expand Down Expand Up @@ -213,7 +223,8 @@ pub(crate) fn calculate_backrelation_field(
to: model.name.clone(),
fields: vec![],
references: vec![],
on_delete: OnDeleteStrategy::None,
on_delete: None,
on_update: None,
};

// unique or id
Expand Down
Expand Up @@ -85,6 +85,10 @@ impl TestApi {
self.tags().contains(Tags::Cockroach)
}

pub fn is_mysql8(&self) -> bool {
self.tags().contains(Tags::Mysql8)
}

#[tracing::instrument(skip(self, data_model_string))]
#[track_caller]
pub async fn re_introspect(&self, data_model_string: &str) -> Result<String> {
Expand Down
Expand Up @@ -19,13 +19,30 @@ async fn a_table_without_uniques_should_ignore(api: &TestApi) -> TestResult {
})
.await?;

let dm = if api.sql_family().is_mysql() {
let dm = if api.sql_family().is_mysql() && !api.is_mysql8() {
indoc! {r#"
/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by the Prisma Client.
model Post {
id Int
user_id Int
User User @relation(fields: [user_id], references: [id])
User User @relation(fields: [user_id], references: [id], onDelete: Restrict, onUpdate: Restrict)
@@index([user_id], name: "user_id")
@@ignore
}
model User {
id Int @id @default(autoincrement())
Post Post[] @ignore
}
"#}
} else if api.sql_family().is_mysql() {
indoc! {r#"
/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by the Prisma Client.
model Post {
id Int
user_id Int
User User @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
@@index([user_id], name: "user_id")
@@ignore
Expand All @@ -42,7 +59,7 @@ async fn a_table_without_uniques_should_ignore(api: &TestApi) -> TestResult {
model Post {
id Int
user_id Int
User User @relation(fields: [user_id], references: [id])
User User @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
@@ignore
}
Expand Down Expand Up @@ -79,7 +96,7 @@ async fn relations_between_ignored_models_should_not_have_field_level_ignores(ap
model Post {
id Unsupported("macaddr") @id
user_id Unsupported("macaddr")
User User @relation(fields: [user_id], references: [id])
User User @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
@@ignore
}
Expand Down Expand Up @@ -274,7 +291,7 @@ async fn a_table_with_unsupported_types_in_a_relation(api: &TestApi) -> TestResu
model Post {
id Int @id @default(autoincrement())
user_ip Unsupported("cidr")
User User @relation(fields: [user_ip], references: [ip])
User User @relation(fields: [user_ip], references: [ip], onDelete: NoAction, onUpdate: NoAction)
}
model User {
Expand Down Expand Up @@ -395,7 +412,7 @@ async fn ignore_on_back_relation_field_if_pointing_to_ignored_model(api: &TestAp
model Post {
id Int
user_ip Int
User User @relation(fields: [user_ip], references: [ip])
User User @relation(fields: [user_ip], references: [ip], onDelete: NoAction, onUpdate: NoAction)
@@ignore
}
Expand Down

0 comments on commit 9fccafd

Please sign in to comment.