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

intro: Handle relation field introspection using the pair setup #3468

Merged
merged 1 commit into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
use crate::{
introspection::introspect,
introspection_helpers::{is_new_migration_table, is_old_migration_table, is_prisma_join_table, is_relay_table},
pair::{EnumPair, ModelPair, Pair},
introspection_map::RelationName,
pair::{EnumPair, ModelPair, Pair, RelationFieldDirection},
warnings, EnumVariantName, IntrospectedName, ModelName, SqlFamilyTrait, SqlIntrospectionResult,
};
use introspection_connector::{IntrospectionContext, IntrospectionResult, Version, Warning};
use psl::{builtin_connectors::*, datamodel_connector::Connector, parser_database::walkers, Configuration};
use psl::{
builtin_connectors::*,
datamodel_connector::Connector,
parser_database::{ast, walkers},
Configuration,
};
use quaint::prelude::SqlFamily;
use sql_schema_describer as sql;
use std::collections::HashMap;
use std::borrow::Cow;

#[derive(Debug, Default)]
pub(crate) struct Warnings {
Expand All @@ -23,6 +29,7 @@ pub(crate) struct Warnings {
pub(crate) reintrospected_id_names: Vec<warnings::Model>,
pub(crate) unsupported_types: Vec<warnings::ModelAndFieldAndType>,
pub(crate) remapped_models: Vec<warnings::Model>,
pub(crate) reintrospected_relations: Vec<warnings::Model>,
}

impl Warnings {
Expand Down Expand Up @@ -104,6 +111,12 @@ impl Warnings {
&mut self.warnings,
);

maybe_warn(
&self.reintrospected_relations,
warnings::warning_relations_added_from_the_previous_data_model,
&mut self.warnings,
);

std::mem::take(&mut self.warnings)
}
}
Expand All @@ -116,12 +129,11 @@ pub(crate) struct InputContext<'a> {
pub(crate) sql_family: SqlFamily,
pub(crate) version: Version,
pub(crate) previous_schema: &'a psl::ValidatedSchema,
pub(crate) introspection_map: &'a crate::introspection_map::IntrospectionMap,
pub(crate) introspection_map: &'a crate::introspection_map::IntrospectionMap<'a>,
}

pub(crate) struct OutputContext<'a> {
pub(crate) rendered_schema: datamodel_renderer::Datamodel<'a>,
pub(crate) target_models: HashMap<sql::TableId, usize>,
pub(crate) warnings: Warnings,
}

Expand Down Expand Up @@ -260,14 +272,134 @@ impl<'a> InputContext<'a> {
// Failing that, potentially sanitize the table name.
.unwrap_or_else(|| ModelName::new_from_sql(self.schema.walk(id).name()))
}

pub(crate) fn forward_inline_relation_field_prisma_name(self, id: sql::ForeignKeyId) -> &'a str {
let existing_relation = self
.existing_inline_relation(id)
.and_then(|relation| relation.as_complete());

match existing_relation {
Some(relation) => relation.referencing_field().name(),
None => &self.inline_relation_name(id).unwrap()[1],
}
}

pub(crate) fn back_inline_relation_field_prisma_name(self, id: sql::ForeignKeyId) -> &'a str {
let existing_relation = self
.existing_inline_relation(id)
.and_then(|relation| relation.as_complete());

match existing_relation {
Some(relation) => relation.referenced_field().name(),
None => &self.inline_relation_name(id).unwrap()[2],
}
}

#[track_caller]
pub(crate) fn forward_m2m_relation_field_prisma_name(self, id: sql::TableId) -> &'a str {
let existing_relation = self.existing_m2m_relation(id);

match existing_relation {
Some(relation) if !relation.is_self_relation() => relation.field_a().name(),
_ => &self.m2m_relation_name(id)[1],
}
}

#[track_caller]
pub(crate) fn back_m2m_relation_field_prisma_name(self, id: sql::TableId) -> &'a str {
let existing_relation = self.existing_m2m_relation(id);

match existing_relation {
Some(relation) if !relation.is_self_relation() => relation.field_b().name(),
_ => &self.m2m_relation_name(id)[2],
}
}

#[track_caller]
pub(crate) fn inline_relation_prisma_name(self, id: sql::ForeignKeyId) -> Cow<'a, str> {
let existing_relation = self
.existing_inline_relation(id)
.and_then(|relation| relation.as_complete());

match existing_relation {
Some(relation) => match relation.referenced_field().relation_name() {
walkers::RelationName::Explicit(name) => Cow::Borrowed(name),
walkers::RelationName::Generated(_) => Cow::Borrowed(""),
},
None => Cow::Borrowed(&self.inline_relation_name(id).unwrap()[0]),
}
}

#[track_caller]
pub(crate) fn m2m_relation_prisma_name(self, id: sql::TableId) -> Cow<'a, str> {
let existing_relation = self.existing_m2m_relation(id);

match existing_relation {
Some(relation) => match relation.relation_name() {
walkers::RelationName::Explicit(name) => Cow::Borrowed(name),
walkers::RelationName::Generated(name) => Cow::Owned(name),
},
None => Cow::Borrowed(&self.m2m_relation_name(id)[0]),
}
}

pub(crate) fn inline_relation_name(self, id: sql::ForeignKeyId) -> Option<&'a RelationName<'a>> {
self.introspection_map.relation_names.inline_relation_name(id)
}

#[track_caller]
pub(crate) fn m2m_relation_name(self, id: sql::TableId) -> &'a RelationName<'a> {
self.introspection_map.relation_names.m2m_relation_name(id)
}

pub(crate) fn table_missing_for_model(self, id: &ast::ModelId) -> bool {
self.introspection_map.missing_tables_for_previous_models.contains(id)
}

pub(crate) fn inline_relations_for_table(
self,
table_id_filter: sql::TableId,
) -> impl Iterator<Item = (RelationFieldDirection, sql::ForeignKeyWalker<'a>)> {
self.introspection_map
.inline_relation_positions
.iter()
.filter(move |(table_id, _, _)| *table_id == table_id_filter)
.filter(move |(_, fk_id, _)| self.inline_relation_name(*fk_id).is_some())
.map(|(_, fk_id, direction)| {
let foreign_key = sql::Walker {
id: *fk_id,
schema: self.schema,
};

(*direction, foreign_key)
})
}

pub(crate) fn m2m_relations_for_table(
self,
table_id_filter: sql::TableId,
) -> impl Iterator<Item = (RelationFieldDirection, sql::ForeignKeyWalker<'a>)> {
self.introspection_map
.m2m_relation_positions
.iter()
.filter(move |(table_id, _, _)| *table_id == table_id_filter)
.map(|(_, fk_id, direction)| {
let next = sql::Walker {
id: *fk_id,
schema: self.schema,
};

(*direction, next)
})
}
}

/// Calculate a data model from a database schema.
pub fn calculate_datamodel(
schema: &sql::SqlSchema,
ctx: &IntrospectionContext,
) -> SqlIntrospectionResult<IntrospectionResult> {
let introspection_map = crate::introspection_map::IntrospectionMap::new(schema, ctx.previous_schema());
let introspection_map = Default::default();

let mut input = InputContext {
version: Version::NonPrisma,
Expand All @@ -279,9 +411,11 @@ pub fn calculate_datamodel(
introspection_map: &introspection_map,
};

let introspection_map = crate::introspection_map::IntrospectionMap::new(input);
input.introspection_map = &introspection_map;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we are creating the introspection map twice in this function. Maybe we should make IntrospectionMap::new()` into something that mutates the input instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input takes a reference... That's a bit of an issue. Let's see that later.


let mut output = OutputContext {
rendered_schema: datamodel_renderer::Datamodel::default(),
target_models: HashMap::default(),
warnings: Warnings::new(),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
pub(crate) mod inline_relations;

mod configuration;
mod enums;
mod indexes;
mod m2m_relations;
mod models;
mod postgres;
mod prisma_relation_mode;
mod relation_names;
mod relation_field;
mod scalar_field;

use crate::calculate_datamodel::{InputContext, OutputContext};
Expand All @@ -20,16 +16,7 @@ pub(crate) fn introspect<'a>(
enums::render(input, output);
models::render(input, output);

if input.foreign_keys_enabled() {
let relation_names = relation_names::introspect(input);

inline_relations::render(&relation_names, input, output);
m2m_relations::render(&relation_names, input, output);
} else {
prisma_relation_mode::render(input, output);
}

let rendered = if input.render_config {
let psl_string = if input.render_config {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

format!(
"{}\n{}",
configuration::render(input.config, input.schema),
Expand All @@ -39,5 +26,8 @@ pub(crate) fn introspect<'a>(
output.rendered_schema.to_string()
};

Ok((psl::reformat(&rendered, 2).unwrap(), output.rendered_schema.is_empty()))
Ok((
psl::reformat(&psl_string, 2).unwrap(),
output.rendered_schema.is_empty(),
))
}