Skip to content

Commit

Permalink
psl: take @@Schema into account in constraint & enum name clashes
Browse files Browse the repository at this point in the history
  • Loading branch information
tomhoule committed Oct 6, 2022
1 parent ee0282f commit 9491fc1
Show file tree
Hide file tree
Showing 24 changed files with 323 additions and 223 deletions.
5 changes: 5 additions & 0 deletions psl/diagnostics/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ impl DatamodelError {
Self::new(msg, span)
}

pub fn new_duplicate_enum_database_name_error(span: Span) -> DatamodelError {
let msg = "An enum with the same database name is already defined.";
Self::new(msg, span)
}

pub fn new_duplicate_model_database_name_error(
model_database_name: &str,
existing_model_name: &str,
Expand Down
7 changes: 6 additions & 1 deletion psl/parser-database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,12 @@ impl ParserDatabase {
&self.ast
}

/// The total number of models. This is O(1).
/// The total number of enums in the schema. This is O(1).
pub fn enums_count(&self) -> usize {
self.types.enum_attributes.len()
}

/// The total number of models in the schema. This is O(1).
pub fn models_count(&self) -> usize {
self.types.model_attributes.len()
}
Expand Down
17 changes: 10 additions & 7 deletions psl/parser-database/src/walkers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,18 @@ use crate::{ast, ParserDatabase};
/// A generic walker. Only walkers intantiated with a concrete ID type (`I`) are useful.
#[derive(Clone, Copy)]
pub struct Walker<'db, I> {
db: &'db crate::ParserDatabase,
id: I,
/// The parser database being traversed.
pub db: &'db crate::ParserDatabase,
/// The identifier of the focused element.
pub id: I,
}

impl ParserDatabase {
/// Traverse a schema element by id.
pub fn walk<I>(&self, id: I) -> Walker<'_, I> {
Walker { db: self, id }
}

/// Walk all enums in the schema.
pub fn walk_enums(&self) -> impl Iterator<Item = EnumWalker<'_>> {
self.ast()
Expand All @@ -44,11 +51,7 @@ impl ParserDatabase {

/// Find a model by ID.
pub(crate) fn walk_model(&self, model_id: ast::ModelId) -> ModelWalker<'_> {
ModelWalker {
model_id,
db: self,
model_attributes: &self.types.model_attributes[&model_id],
}
self.walk(model_id)
}

/// Find an enum by ID.
Expand Down
10 changes: 2 additions & 8 deletions psl/parser-database/src/walkers/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ impl<'db> EnumWalker<'db> {

/// The values of the enum.
pub fn values(self) -> impl Iterator<Item = EnumValueWalker<'db>> {
(0..self.ast_enum().values.len()).map(move |idx| Walker {
db: self.db,
id: (self.id, idx),
})
(0..self.ast_enum().values.len()).map(move |idx| self.db.walk((self.id, idx)))
}

/// The name of the schema the enum belongs to.
Expand All @@ -62,10 +59,7 @@ impl<'db> EnumWalker<'db> {

impl<'db> EnumValueWalker<'db> {
fn r#enum(self) -> EnumWalker<'db> {
Walker {
db: self.db,
id: self.id.0,
}
self.db.walk(self.id.0)
}

/// The enum documentation
Expand Down
6 changes: 1 addition & 5 deletions psl/parser-database/src/walkers/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,7 @@ impl<'db> IndexWalker<'db> {

/// The model the index is defined on.
pub fn model(self) -> ModelWalker<'db> {
ModelWalker {
model_id: self.model_id,
db: self.db,
model_attributes: &self.db.types.model_attributes[&self.model_id],
}
self.db.walk(self.model_id)
}

/// The field the model was defined on, if any.
Expand Down
78 changes: 33 additions & 45 deletions psl/parser-database/src/walkers/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,18 @@ pub use primary_key::*;

pub(crate) use unique_criteria::*;

use schema_ast::ast::{IndentationType, NewlineType, WithSpan};

use super::{
CompleteInlineRelationWalker, IndexWalker, InlineRelationWalker, RelationFieldWalker, RelationWalker,
ScalarFieldWalker,
};
use crate::{
ast::{self, WithName},
types::ModelAttributes,
ParserDatabase,
};
use std::hash::{Hash, Hasher};
use schema_ast::ast::{IndentationType, NewlineType, WithSpan};

/// A `model` declaration in the Prisma schema.
#[derive(Copy, Clone, Debug)]
pub struct ModelWalker<'db> {
pub(super) model_id: ast::ModelId,
pub(super) db: &'db ParserDatabase,
pub(super) model_attributes: &'db ModelAttributes,
}

impl<'db> PartialEq for ModelWalker<'db> {
fn eq(&self, other: &Self) -> bool {
self.model_id == other.model_id
}
}

impl<'db> Eq for ModelWalker<'db> {}

impl<'db> Hash for ModelWalker<'db> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.model_id.hash(state);
}
}
pub type ModelWalker<'db> = super::Walker<'db, ast::ModelId>;

impl<'db> ModelWalker<'db> {
/// The name of the model.
Expand Down Expand Up @@ -76,17 +54,17 @@ impl<'db> ModelWalker<'db> {

/// The ID of the model in the db
pub fn model_id(self) -> ast::ModelId {
self.model_id
self.id
}

/// The AST node.
pub fn ast_model(self) -> &'db ast::Model {
&self.db.ast[self.model_id]
&self.db.ast[self.id]
}

/// The parsed attributes.
pub(crate) fn attributes(self) -> &'db ModelAttributes {
self.model_attributes
&self.db.types.model_attributes[&self.id]
}

/// Model has the @@ignore attribute.
Expand All @@ -97,18 +75,18 @@ impl<'db> ModelWalker<'db> {
/// The name of the database table the model points to.
#[allow(clippy::unnecessary_lazy_evaluations)] // respectfully disagree
pub fn database_name(self) -> &'db str {
self.model_attributes
self.attributes()
.mapped_name
.map(|id| &self.db[id])
.unwrap_or_else(|| self.db.ast[self.model_id].name())
.unwrap_or_else(|| self.db.ast[self.id].name())
}

/// Get the database name of the scalar field.
pub fn get_field_database_name(self, field_id: ast::FieldId) -> &'db str {
self.db.types.scalar_fields[&(self.model_id, field_id)]
self.db.types.scalar_fields[&(self.id, field_id)]
.mapped_name
.map(|id| &self.db[id])
.unwrap_or_else(|| self.db.ast[self.model_id][field_id].name())
.unwrap_or_else(|| self.db.ast[self.id][field_id].name())
}

/// Get the database names of the constrained scalar fields.
Expand All @@ -131,8 +109,8 @@ impl<'db> ModelWalker<'db> {

/// The primary key of the model, if defined.
pub fn primary_key(self) -> Option<PrimaryKeyWalker<'db>> {
self.model_attributes.primary_key.as_ref().map(|pk| PrimaryKeyWalker {
model_id: self.model_id,
self.attributes().primary_key.as_ref().map(|pk| PrimaryKeyWalker {
model_id: self.id,
attribute: pk,
db: self.db,
})
Expand All @@ -142,10 +120,10 @@ impl<'db> ModelWalker<'db> {
#[track_caller]
pub(crate) fn scalar_field(self, field_id: ast::FieldId) -> ScalarFieldWalker<'db> {
ScalarFieldWalker {
model_id: self.model_id,
model_id: self.id,
field_id,
db: self.db,
scalar_field: &self.db.types.scalar_fields[&(self.model_id, field_id)],
scalar_field: &self.db.types.scalar_fields[&(self.id, field_id)],
}
}

Expand All @@ -154,7 +132,7 @@ impl<'db> ModelWalker<'db> {
let db = self.db;
db.types
.scalar_fields
.range((self.model_id, ast::FieldId::MIN)..=(self.model_id, ast::FieldId::MAX))
.range((self.id, ast::FieldId::MIN)..=(self.id, ast::FieldId::MAX))
.map(move |((model_id, field_id), scalar_field)| ScalarFieldWalker {
model_id: *model_id,
field_id: *field_id,
Expand All @@ -166,11 +144,11 @@ impl<'db> ModelWalker<'db> {
/// All unique criterias of the model; consisting of the primary key and
/// unique indexes, if set.
pub fn unique_criterias(self) -> impl Iterator<Item = UniqueCriteriaWalker<'db>> {
let model_id = self.model_id;
let model_id = self.id;
let db = self.db;

let from_pk = self
.model_attributes
.attributes()
.primary_key
.iter()
.map(move |pk| UniqueCriteriaWalker {
Expand All @@ -194,10 +172,10 @@ impl<'db> ModelWalker<'db> {
/// Iterate all the indexes in the model in the order they were
/// defined.
pub fn indexes(self) -> impl Iterator<Item = IndexWalker<'db>> {
let model_id = self.model_id;
let model_id = self.id;
let db = self.db;

self.model_attributes
self.attributes()
.ast_indexes
.iter()
.map(move |(index, index_attribute)| IndexWalker {
Expand All @@ -210,7 +188,7 @@ impl<'db> ModelWalker<'db> {

/// All (concrete) relation fields of the model.
pub fn relation_fields(self) -> impl Iterator<Item = RelationFieldWalker<'db>> {
let model_id = self.model_id;
let model_id = self.id;
let db = self.db;

self.db
Expand All @@ -232,18 +210,18 @@ impl<'db> ModelWalker<'db> {
/// If the field does not exist.
pub fn relation_field(self, field_id: ast::FieldId) -> RelationFieldWalker<'db> {
RelationFieldWalker {
model_id: self.model_id,
model_id: self.id,
field_id,
db: self.db,
relation_field: &self.db.types.relation_fields[&(self.model_id, field_id)],
relation_field: &self.db.types.relation_fields[&(self.id, field_id)],
}
}

/// All relations that start from this model.
pub fn relations_from(self) -> impl Iterator<Item = RelationWalker<'db>> {
self.db
.relations
.from_model(self.model_id)
.from_model(self.id)
.map(move |relation_id| RelationWalker {
id: relation_id,
db: self.db,
Expand All @@ -254,7 +232,7 @@ impl<'db> ModelWalker<'db> {
pub fn relations_to(self) -> impl Iterator<Item = RelationWalker<'db>> {
self.db
.relations
.to_model(self.model_id)
.to_model(self.id)
.map(move |relation_id| RelationWalker {
id: relation_id,
db: self.db,
Expand Down Expand Up @@ -326,4 +304,14 @@ impl<'db> ModelWalker<'db> {
pub fn schema(self) -> Option<(&'db str, ast::Span)> {
self.attributes().schema.map(|(id, span)| (&self.db[id], span))
}

/// The name of the schema the model belongs to.
///
/// ```ignore
/// @@schema("public")
/// ^^^^^^^^
/// ```
pub fn schema_name(self) -> Option<&'db str> {
self.schema().map(|(name, _)| name)
}
}
6 changes: 1 addition & 5 deletions psl/parser-database/src/walkers/model/primary_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,7 @@ impl<'db> PrimaryKeyWalker<'db> {

/// The model the id is deined on.
pub fn model(self) -> ModelWalker<'db> {
ModelWalker {
db: self.db,
model_attributes: &self.db.types.model_attributes[&self.model_id],
model_id: self.model_id,
}
self.db.walk(self.model_id)
}

/// The `name` argument of the id attribute. The client name.
Expand Down
4 changes: 2 additions & 2 deletions psl/parser-database/src/walkers/relation/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ impl<'db> InlineRelationWalker<'db> {
match (self.forward_relation_field(), self.back_relation_field()) {
(Some(field_a), Some(field_b)) => {
let walker = CompleteInlineRelationWalker {
side_a: (self.referencing_model().model_id, field_a.field_id),
side_b: (self.referenced_model().model_id, field_b.field_id),
side_a: (self.referencing_model().id, field_a.field_id),
side_b: (self.referenced_model().id, field_b.field_id),
db: self.0.db,
};

Expand Down
16 changes: 4 additions & 12 deletions psl/parser-database/src/walkers/relation/inline/complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,12 @@ pub struct CompleteInlineRelationWalker<'db> {
impl<'db> CompleteInlineRelationWalker<'db> {
/// The model that defines the relation fields and actions.
pub fn referencing_model(self) -> ModelWalker<'db> {
ModelWalker {
model_id: self.side_a.0,
db: self.db,
model_attributes: &self.db.types.model_attributes[&self.side_a.0],
}
self.db.walk(self.side_a.0)
}

/// The implicit relation side.
pub fn referenced_model(self) -> ModelWalker<'db> {
ModelWalker {
model_id: self.side_b.0,
db: self.db,
model_attributes: &self.db.types.model_attributes[&self.side_b.0],
}
self.db.walk(self.side_b.0)
}

pub fn referencing_field(self) -> RelationFieldWalker<'db> {
Expand All @@ -55,7 +47,7 @@ impl<'db> CompleteInlineRelationWalker<'db> {
/// The scalar fields defining the relation on the referenced model.
pub fn referenced_fields(self) -> impl ExactSizeIterator<Item = ScalarFieldWalker<'db>> + 'db {
let f = move |field_id: &ast::FieldId| {
let model_id = self.referenced_model().model_id;
let model_id = self.referenced_model().id;

ScalarFieldWalker {
model_id,
Expand All @@ -74,7 +66,7 @@ impl<'db> CompleteInlineRelationWalker<'db> {
/// The scalar fields on the defining the relation on the referencing model.
pub fn referencing_fields(self) -> impl ExactSizeIterator<Item = ScalarFieldWalker<'db>> + 'db {
let f = move |field_id: &ast::FieldId| {
let model_id = self.referencing_model().model_id;
let model_id = self.referencing_model().id;

ScalarFieldWalker {
model_id,
Expand Down
14 changes: 2 additions & 12 deletions psl/parser-database/src/walkers/relation_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,7 @@ impl<'db> RelationFieldWalker<'db> {

/// The model containing the field.
pub fn model(self) -> ModelWalker<'db> {
ModelWalker {
model_id: self.model_id,
db: self.db,
model_attributes: &self.db.types.model_attributes[&self.model_id],
}
self.db.walk(self.model_id)
}

/// The `@relation` attribute in the field AST.
Expand All @@ -115,13 +111,7 @@ impl<'db> RelationFieldWalker<'db> {

/// The model referenced by the relation.
pub fn related_model(self) -> ModelWalker<'db> {
let model_id = self.relation_field.referenced_model;

ModelWalker {
model_id,
db: self.db,
model_attributes: &self.db.types.model_attributes[&model_id],
}
self.db.walk(self.relation_field.referenced_model)
}

/// The fields in the `@relation(references: ...)` argument.
Expand Down

0 comments on commit 9491fc1

Please sign in to comment.