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

chore(psl): removing parser DB early bailout #4942

Merged
merged 8 commits into from
Jul 9, 2024
5 changes: 2 additions & 3 deletions prisma-fmt/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn hover(ctx: HoverContext<'_>) -> Option<Hover> {
SchemaPosition::Model(model_id, ModelPosition::Field(field_id, FieldPosition::Type(name))) => {
let initiating_field = &ctx.db.walk((ctx.initiating_file_id, model_id)).field(field_id);

match initiating_field.refine() {
initiating_field.refine().and_then(|field| match field {
walkers::RefinedFieldWalker::Scalar(scalar) => match scalar.scalar_field_type() {
ScalarFieldType::CompositeType(_) => {
let ct = scalar.field_type_as_composite_type().unwrap().ast_composite_type();
Expand All @@ -140,7 +140,6 @@ fn hover(ctx: HoverContext<'_>) -> Option<Hover> {
}
_ => None,
},

walkers::RefinedFieldWalker::Relation(rf) => {
let opposite_model = rf.related_model();
let relation_info = rf.opposite_relation_field().map(|rf| (rf, rf.ast_field()));
Expand All @@ -157,7 +156,7 @@ fn hover(ctx: HoverContext<'_>) -> Option<Hover> {
relation_info,
))
}
}
})
}

SchemaPosition::CompositeType(ct_id, CompositeTypePosition::Field(field_id, FieldPosition::Type(_))) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"contents": {
"kind": "markdown",
"value": "```prisma\nmodel Post {\n\t...\n\tUser User? @relation(name: \"PostToUser\", fields: [userId], references: [id])\n}\n```\n___\none-to-many \n___\n"
"value": "```prisma\nmodel Post {\n\t...\n\tUser User? @relation(name: \"PostToUser\", fields: [userId], references: [id])\n}\n```\n___\none-to-many\n___\n"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
null
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model interm {
id Int @id

forumId Int
forum For<|>um @relation(fields: [forumId], references: [id])
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"contents": {
"kind": "markdown",
"value": "```prisma\nmodel ModelNameA {\n\t...\n\tval ModelNameB @relation(name: \"ModelNameAToModelNameB\", fields: [bId], references: [id])\n}\n```\n___\none-to-many \n___\nThis is doc for A"
"value": "```prisma\nmodel ModelNameA {\n\t...\n\tval ModelNameB @relation(name: \"ModelNameAToModelNameB\", fields: [bId], references: [id])\n}\n```\n___\none-to-many\n___\nThis is doc for A"
}
}
3 changes: 2 additions & 1 deletion prisma-fmt/tests/hover/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ pub(crate) fn test_scenario(scenario_name: &str) {
);

// Prettify the JSON
let result = serde_json::to_string_pretty(&serde_json::from_str::<lsp_types::Hover>(&result).unwrap()).unwrap();
let result =
serde_json::to_string_pretty(&serde_json::from_str::<Option<lsp_types::Hover>>(&result).unwrap()).unwrap();

if *UPDATE_EXPECT {
let mut file = std::fs::File::create(&path).unwrap(); // truncate
Expand Down
3 changes: 2 additions & 1 deletion prisma-fmt/tests/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ scenarios! {
field_from_composite_field_name
field_from_model_field_name
model_from_block_name
model_from_model_type_broken_relations
model_from_model_type_includes_broken_relations
model_from_model_type_on_broken_relations
model_from_view_type
one_to_many_self_relation
value_from_enum_value_name
Expand Down
42 changes: 13 additions & 29 deletions psl/parser-database/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ fn resolve_composite_type_attributes<'db>(
ctx: &mut Context<'db>,
) {
for (field_id, field) in ct.iter_fields() {
let CompositeTypeField { r#type, .. } = ctx.types.composite_type_fields[&(ctid, field_id)];
let CompositeTypeField { r#type, .. } =
if let Some(val) = ctx.types.composite_type_fields.get(&(ctid, field_id)) {
val.clone()
} else {
continue;
};

ctx.visit_attributes((ctid.0, (ctid.1, field_id)));

Expand Down Expand Up @@ -628,15 +633,16 @@ fn common_index_validations(
let mut suggested_fields = Vec::new();

for (_, field_id) in &relation_fields {
let fields = ctx
let Some(rf) = ctx
.types
.range_model_relation_fields(model_id)
.find(|(_, rf)| rf.field_id == *field_id)
.unwrap()
.1
.fields
.iter()
.flatten();
else {
continue;
};

let fields = rf.1.fields.iter().flatten();

for underlying_field in fields {
let ScalarField { model_id, field_id, .. } = ctx.types[*underlying_field];
suggested_fields.push(ctx.asts[model_id][field_id].name());
Expand Down Expand Up @@ -1096,25 +1102,3 @@ fn validate_clustering_setting(ctx: &mut Context<'_>) -> Option<bool> {
ctx.visit_optional_arg("clustered")
.and_then(|sort| coerce::boolean(sort, ctx.diagnostics))
}

/// Create the default values of [`ModelAttributes`] and [`EnumAttributes`] for each model and enum
/// in the AST to ensure [`crate::walkers::ModelWalker`] and [`crate::walkers::EnumWalker`] can
/// access their corresponding entries in the attributes map in the database even in the presence
/// of name and type resolution errors. This is useful for the language tools.
pub(super) fn create_default_attributes(ctx: &mut Context<'_>) {
for ((file_id, top), _) in ctx.iter_tops() {
match top {
ast::TopId::Model(model_id) => {
ctx.types
.model_attributes
.insert((file_id, model_id), ModelAttributes::default());
}
ast::TopId::Enum(enum_id) => {
ctx.types
.enum_attributes
.insert((file_id, enum_id), EnumAttributes::default());
}
_ => (),
}
}
}
26 changes: 0 additions & 26 deletions psl/parser-database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,35 +107,9 @@ impl ParserDatabase {
// First pass: resolve names.
names::resolve_names(&mut ctx);

// Return early on name resolution errors.
if ctx.diagnostics.has_errors() {
attributes::create_default_attributes(&mut ctx);

return ParserDatabase {
asts,
interner,
names,
types,
relations,
};
}

// Second pass: resolve top-level items and field types.
types::resolve_types(&mut ctx);

// Return early on type resolution errors.
if ctx.diagnostics.has_errors() {
attributes::create_default_attributes(&mut ctx);

return ParserDatabase {
asts,
interner,
names,
types,
relations,
};
}

// Third pass: validate model and field attributes. All these
// validations should be _order independent_ and only rely on
// information from previous steps, not from other attributes.
Expand Down
14 changes: 10 additions & 4 deletions psl/parser-database/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ pub(super) fn resolve_types(ctx: &mut Context<'_>) {
}
}

pub enum RefinedFieldVariant {
Relation(RelationFieldId),
Scalar(ScalarFieldId),
Unknown,
}

#[derive(Debug, Default)]
pub(super) struct Types {
pub(super) composite_type_fields: BTreeMap<(crate::CompositeTypeId, ast::FieldId), CompositeTypeField>,
Expand Down Expand Up @@ -92,16 +98,16 @@ impl Types {
.map(move |(idx, rf)| (RelationFieldId((first_relation_field_idx + idx) as u32), rf))
}

pub(super) fn refine_field(&self, id: (crate::ModelId, ast::FieldId)) -> Either<RelationFieldId, ScalarFieldId> {
pub(super) fn refine_field(&self, id: (crate::ModelId, ast::FieldId)) -> RefinedFieldVariant {
self.relation_fields
.binary_search_by_key(&id, |rf| (rf.model_id, rf.field_id))
.map(|idx| Either::Left(RelationFieldId(idx as u32)))
.map(|idx| RefinedFieldVariant::Relation(RelationFieldId(idx as u32)))
.or_else(|_| {
self.scalar_fields
.binary_search_by_key(&id, |sf| (sf.model_id, sf.field_id))
.map(|id| Either::Right(ScalarFieldId(id as u32)))
.map(|id| RefinedFieldVariant::Scalar(ScalarFieldId(id as u32)))
})
.expect("expected field to be either scalar or relation field")
.unwrap_or(RefinedFieldVariant::Unknown)
}

pub(super) fn push_relation_field(&mut self, relation_field: RelationField) -> RelationFieldId {
Expand Down
16 changes: 12 additions & 4 deletions psl/parser-database/src/walkers/field.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{CompositeTypeFieldWalker, ModelWalker, RelationFieldWalker, ScalarFieldWalker, Walker};
use crate::{
types::{RelationField, ScalarField},
types::{RefinedFieldVariant, RelationField, ScalarField},
ScalarType,
};
use schema_ast::ast;
Expand All @@ -25,12 +25,20 @@ impl<'db> FieldWalker<'db> {
}

/// Find out which kind of field this is.
pub fn refine(self) -> RefinedFieldWalker<'db> {
/// Returns `None` if we encounter an unknown field.
pub fn refine(self) -> Option<RefinedFieldWalker<'db>> {
match self.db.types.refine_field(self.id) {
either::Either::Left(id) => RefinedFieldWalker::Relation(self.walk(id)),
either::Either::Right(id) => RefinedFieldWalker::Scalar(self.walk(id)),
RefinedFieldVariant::Relation(id) => Some(RefinedFieldWalker::Relation(self.walk(id))),
RefinedFieldVariant::Scalar(id) => Some(RefinedFieldWalker::Scalar(self.walk(id))),
RefinedFieldVariant::Unknown => None,
}
}

/// Find out which kind of field this is.
/// ! Panics on unknown field, only to be used in query-engine where unknowns should not exist.
pub fn refine_known(self) -> RefinedFieldWalker<'db> {
self.refine().unwrap()
}
}

/// A field that has been identified as scalar field or relation field.
Expand Down
6 changes: 6 additions & 0 deletions psl/psl/tests/attributes/composite_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,12 @@ fn pointing_to_a_non_existing_type() {
16 |  id Int @id @map("_id")
17 |  a C
 | 
error: Error validating model "B": The index definition refers to the relation fields a. Index definitions must reference only scalar fields.
--> schema.prisma:19
 | 
18 | 
19 |  @@index([a.field])
 | 
"#]];

expected.assert_eq(&error);
Expand Down
6 changes: 6 additions & 0 deletions psl/psl/tests/base/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ fn type_aliases_must_error() {
 | 
 1 | type MyString = String @default("B")
 | 
error: Type "MyString" is neither a built-in type, nor refers to another model, composite type, or enum.
--> schema.prisma:5
 | 
 4 |  id Int @id
 5 |  val MyString
 | 
"#]];

expectation.assert_eq(&error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,15 @@ model B {
// 15 |  @@index([other, field])
// 16 |  @@unique([content, rank])
//  | 
// error: Attribute not known: "@id".
// --> schema.prisma:7
//  | 
//  6 | type A {
//  7 |  pk String @id
//  | 
// error: Attribute not known: "@unique".
Druue marked this conversation as resolved.
Show resolved Hide resolved
// --> schema.prisma:8
//  | 
//  7 |  pk String @id
//  8 |  field Int @unique
//  | 
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ model B {
// 11 |  c C[] @relation("foo")
// 12 | }
//  | 
// error: Attribute not known: "@relation".
// --> schema.prisma:11
//  | 
// 10 | type A {
// 11 |  c C[] @relation("foo")
//  | 
4 changes: 2 additions & 2 deletions query-engine/dmmf/src/ast_builders/datamodel_ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,14 @@ fn model_to_dmmf(model: walkers::ModelWalker<'_>) -> Model {
}

fn should_skip_model_field(field: &walkers::FieldWalker<'_>) -> bool {
match field.refine() {
match field.refine_known() {
walkers::RefinedFieldWalker::Scalar(f) => f.is_ignored() || f.is_unsupported(),
walkers::RefinedFieldWalker::Relation(f) => f.is_ignored(),
}
}

fn field_to_dmmf(field: walkers::FieldWalker<'_>) -> Field {
match field.refine() {
match field.refine_known() {
walkers::RefinedFieldWalker::Scalar(sf) => scalar_field_to_dmmf(sf),
walkers::RefinedFieldWalker::Relation(rf) => relation_field_to_dmmf(rf),
}
Expand Down
Loading