Skip to content

Commit

Permalink
feat(schema-engine): CHECK and EXCLUDE constraints stopgap for Po…
Browse files Browse the repository at this point in the history
…stgres (#3860)

* feat(schema-engine): add constraints_query.sql for postgres

* feat(schema-engine): add check and exclusion constraints stopgap support for postgres

* feat(schema-engine): add skeleton for stopgap warnings for check and exclusion constraints in postgres

* feat(schema-engine): emit warnings when finding check and exclusion constraints when introspecting postgres

* chore: remove comments

* chore: remove dbg

* test(schema-engine): add missing defaults in describer tests

* chore: update warning messages so that they make more sense

* feat(schema-engine): updated check and exclusion constraints warning messages

* test(schema-engine): validate introspected schemas for check and exclusion constraints

* feat(schema-engine): introspecting models with check or exclusion constraints now adds a comment to such models

* feat(schema-engine): fix edge case of model having both check and exclusion constraints

* feat(schema-engine): fix prisma/prisma#17515

* chore: remove dead code

* chore: fix conflicts

* fix: avoid duplicated model commments upon reintrospection

* feat(schema-engine): use ModelAndConstraint rather than CheckConstraint and ExclusionConstraint

* feat(schema-engine): replace IndexMap with BTreeMap for leaner flag "upsert" capabilities

* chore: add TODO to answer Tom's review comment

* Exclude constraints are PG only, lighten up the api (#3887)

* Exclude constraints are PG only, lighten up the api

* Split constraint tests into its own file

* Fix describer tests

* feat(schema-engine): apply review comments

---------

Co-authored-by: Julius de Bruijn <julius+github@nauk.io>
  • Loading branch information
jkomyno and Julius de Bruijn committed Apr 19, 2023
1 parent ae591d8 commit 42f41a6
Show file tree
Hide file tree
Showing 18 changed files with 649 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ pub(crate) trait IntrospectionFlavour {
) -> bool {
false
}

fn uses_exclude_constraint(&self, _ctx: &DatamodelCalculatorContext<'_>, _table: TableWalker<'_>) -> bool {
false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ impl super::IntrospectionFlavour for PostgresIntrospectionFlavour {
}

fn generate_warnings(&self, ctx: &DatamodelCalculatorContext<'_>, warnings: &mut Warnings) {
let pg_ext: &PostgresSchemaExt = ctx.sql_schema.downcast_connector_data();

for table in ctx.sql_schema.table_walkers() {
for index in table.indexes() {
for column in index.columns().filter(|c| self.uses_non_default_null_position(ctx, *c)) {
Expand Down Expand Up @@ -46,6 +48,15 @@ impl super::IntrospectionFlavour for PostgresIntrospectionFlavour {
model: ctx.table_prisma_name(table.id).prisma_name().to_string(),
});
}

for constraint in pg_ext.exclude_constraints(table.id) {
let exclusion_constraint = ModelAndConstraint {
model: ctx.table_prisma_name(table.id).prisma_name().to_string(),
constraint: constraint.to_string(),
};

warnings.exclusion_constraints.push(exclusion_constraint);
}
}
}

Expand Down Expand Up @@ -80,4 +91,9 @@ impl super::IntrospectionFlavour for PostgresIntrospectionFlavour {

pg_ext.non_default_null_position(column)
}

fn uses_exclude_constraint(&self, ctx: &DatamodelCalculatorContext<'_>, table: TableWalker<'_>) -> bool {
let pg_ext: &PostgresSchemaExt = ctx.sql_schema.downcast_connector_data();
pg_ext.uses_exclude_constraint(table.id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ impl<'a> ModelPair<'a> {
self.next.has_row_level_security()
}

/// Whether the model has check constraints.
pub(crate) fn adds_check_constraints(self) -> bool {
self.previous.is_none() && self.next.has_check_constraints()
}

/// The names of check constraints for this model.
pub(crate) fn check_constraints(self) -> impl Iterator<Item = &'a str> {
self.next.check_constraints()
}

/// Whether the model has exclusion constraints.
pub(crate) fn adds_exclusion_constraints(self) -> bool {
self.previous.is_none() && self.context.flavour.uses_exclude_constraint(self.context, self.next)
}

/// True, if we add a new model with row level security enabled.
pub(crate) fn adds_row_level_security(self) -> bool {
self.previous.is_none() && self.has_row_level_security()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ fn render_model(model: ModelPair<'_>, sql_family: SqlFamily) -> renderer::Model<
rendered.documentation(docs);
}

if model.adds_check_constraints() {
let docs = "This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/postgres-check-constraints for more info.";

rendered.documentation(docs);
}

if model.adds_exclusion_constraints() {
let docs = "This table contains exclusion constraints and requires additional setup for migrations. Visit https://pris.ly/d/postgres-exclusion-constraints for more info.";

rendered.documentation(docs);
}

if let Some(namespace) = model.namespace() {
rendered.schema(namespace);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ pub(crate) struct Warnings {
pub(crate) non_default_index_null_sort_order: Vec<IndexedColumn>,
/// Warn about using row level security, which is currently unsupported.
pub(crate) row_level_security_tables: Vec<Model>,
/// Warn about check constraints.
pub(crate) check_constraints: Vec<ModelAndConstraint>,
/// Warn about exclusion constraints.
pub(crate) exclusion_constraints: Vec<ModelAndConstraint>,
/// Warn about row level TTL
pub(crate) row_level_ttl: Vec<Model>,
/// Warn about non-default unique deferring setup
Expand Down Expand Up @@ -218,6 +222,14 @@ impl Warnings {
&mut self.warnings,
);

maybe_warn(&self.check_constraints, check_constraints_found, &mut self.warnings);

maybe_warn(
&self.exclusion_constraints,
exclusion_constraints_found,
&mut self.warnings,
);

maybe_warn(&self.row_level_ttl, row_level_ttl_in_tables, &mut self.warnings);
maybe_warn(&self.non_default_deferring, non_default_deferring, &mut self.warnings);
maybe_warn(&self.objects_with_comments, commented_objects, &mut self.warnings);
Expand Down Expand Up @@ -600,6 +612,26 @@ pub(crate) fn row_level_ttl_in_tables(affected: &[Model]) -> Warning {
}
}

pub(crate) fn check_constraints_found(affected: &[ModelAndConstraint]) -> Warning {
let message = "These constraints are not supported by the Prisma Client, because Prisma currently does not fully support check constraints. Read more: https://pris.ly/d/postgres-check-constraints";

Warning {
code: 33,
message: message.into(),
affected: serde_json::to_value(affected).unwrap(),
}
}

pub(crate) fn exclusion_constraints_found(affected: &[ModelAndConstraint]) -> Warning {
let message = "These constraints are not supported by the Prisma Client, because Prisma currently does not fully support exclusion constraints. Read more: https://pris.ly/d/postgres-exclusion-constraints";

Warning {
code: 34,
message: message.into(),
affected: serde_json::to_value(affected).unwrap(),
}
}

pub(crate) fn non_default_deferring(affected: &[ModelAndConstraint]) -> Warning {
let message = "These primary key, foreign key or unique constraints are using non-default deferring in the database, which is not yet fully supported. Read more: https://pris.ly/d/constraint-deferring";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ pub(super) fn generate_warnings(model: ModelPair<'_>, warnings: &mut Warnings) {
})
}

for constraint in model.check_constraints() {
warnings.check_constraints.push(generators::ModelAndConstraint {
model: model.name().to_string(),
constraint: constraint.to_string(),
})
}

for field in model.scalar_fields() {
if let Some(DefaultKind::Prisma1Uuid) = field.default().kind() {
let warn = generators::ModelAndField {
Expand Down

0 comments on commit 42f41a6

Please sign in to comment.