Skip to content

Commit

Permalink
fix(linter): consider cardinality of strong entity
Browse files Browse the repository at this point in the history
  • Loading branch information
matias-lg committed Jan 23, 2024
1 parent 487b815 commit a2979ce
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export const checkWeakEntityConstraints = (
if (
/* entity is composite, every role must satisfy the constraints */
(entityInRelationship.isComposite &&
entityInRelationship.childParticipants.every(hasCorrectConstraints)) ||
entityInRelationship.childParticipants.every(
hasCorrectConstraints,
)) ||
/* entity is not composite and has total participation */
(!entityInRelationship.isComposite &&
hasCorrectConstraints(entityInRelationship))
Expand Down
29 changes: 20 additions & 9 deletions src/ERDoc/linter/entity/checkWeakEntityNoPkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ER } from "../../types/parser/ER";
import { WeakEntityHasNoPkeyError } from "../../types/linter/SemanticError";

/**
* Finds weak entities that don't have a pkey when they have other than 1 to 1 participation in some identifying relationship.
* Finds weak entities that don't have a pkey when some strong entity have other than cardinality of "1" in some identifying relationship.
* @param {ER} er - The ER object to lint
* @return {WeakEntityHasNoPkeyError[]} An array of errors for each weak entity without pkey
*/
Expand All @@ -22,16 +22,27 @@ export const checkWeakEntityNoPkey = (er: ER): WeakEntityHasNoPkeyError[] => {
);
if (weakEntityInRelationship === undefined) continue;

let isManyCardinality = false;
if (weakEntityInRelationship.isComposite) {
isManyCardinality = weakEntityInRelationship.childParticipants.some(
(child) => child.cardinality !== "1",
);
} else {
isManyCardinality = weakEntityInRelationship.cardinality !== "1";
let someIsMany = false;

for (const participant of relationship.participantEntities) {
if (participant.entityName === entity.name) continue;

if (participant.isComposite) {
if (
participant.childParticipants.some(
(child) => child.cardinality !== "1",
)
) {
someIsMany = true;
break;
}
} else if (participant.cardinality !== "1") {
someIsMany = true;
break;
}
}

if (isManyCardinality) {
if (someIsMany) {
errors.push({
type: "WEAK_ENTITY_HAS_NO_PKEY",
entityName: entity.name,
Expand Down
3 changes: 1 addition & 2 deletions src/ERDoc/linter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ export const getSemanticErrors = (er: ER): SemanticError[] => {
const cycleErrors = checkEntityExtendsChildEntity(er);
if (cycleErrors.length > 0) {
errors.push(...cycleErrors);
}
else {
} else {
for (const validator of inheritanceValidators) {
errors.push(...validator(er));
}
Expand Down
4 changes: 2 additions & 2 deletions src/app/static/examples/bank.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"erDoc": "entity bank {\n code key\n name\n addr\n}\n\nentity bank_branch depends on has_branches {\n addr\n branch_no pkey\n}\n\nrelation has_branches(bank 1!, bank_branch N!)\n\nentity account {\n acct_no key\n balance\n type\n}\n\nentity loan {\n loan_no key\n amount\n type\n}\n\nrelation accts(Bank_With_Branches 1, account N!)\nrelation loans(Bank_With_Branches 1, loan N!)\n\nentity customer {\n ssn key\n name\n addr\n phone\n}\n\nentity premium_customer extends customer {\n discount\n}\n\nrelation a_c(customer N, account M!)\nrelation l_c(customer N, loan M!)\n\naggregation Bank_With_Branches(has_branches)",
"erDoc": "entity bank {\n code key\n name\n addr\n}\n\nentity bank_branch depends on has_branches {\n addr\n branch_no pkey\n}\n\nrelation has_branches(bank N!, bank_branch 1!)\n\nentity account {\n acct_no key\n balance\n type\n}\n\nentity loan {\n loan_no key\n amount\n type\n}\n\nrelation accts(Bank_With_Branches 1, account N!)\nrelation loans(Bank_With_Branches 1, loan N!)\n\nentity customer {\n ssn key\n name\n addr\n phone\n}\n\nentity premium_customer extends customer {\n discount\n}\n\nrelation a_c(customer N, account M!)\nrelation l_c(customer N, loan M!)\n\naggregation Bank_With_Branches(has_branches)",
"nodes": [
{
"id": "0",
Expand Down Expand Up @@ -347,4 +347,4 @@
"target": "11"
}
]
}
}
4 changes: 2 additions & 2 deletions src/app/static/examples/company.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"erDoc": "entity Employee {\n e_id key\n name\n}\n \nentity Department {\n d_number key\n d_name\n}\n \nrelation Manages(Department: [Management 1!, Research])\n \n \nrelation Works_for(Employee N, Department 1!)\n \nentity Project {\n p_id key\n p_name\n}\n \nrelation Controls(Department 1, Project N!)\n \nrelation Works_on(Employee M, Project N) {\n hours\n}\n \nentity Part {\n p_number key\n p_name\n}\n \nentity Screw extends Part {\n head_style\n}\n \nentity Supplier {\n s_id key\n s_name\n}\n \nrelation Supplies(Project M, Part N!, Supplier P!) {\n Quantity\n}\n \nentity Dependent depends on Dependent_of {\n Dep_name pkey\n Gender\n}\n \nrelation Dependent_of(Employee 1, Dependent N!)",
"erDoc": "entity Employee {\n e_id key\n name\n}\n \nentity Department {\n d_number key\n d_name\n}\n \nrelation Manages(Department: [Management 1!, Research])\n \n \nrelation Works_for(Employee N, Department 1!)\n \nentity Project {\n p_id key\n p_name\n}\n \nrelation Controls(Department 1, Project N!)\n \nrelation Works_on(Employee M, Project N) {\n hours\n}\n \nentity Part {\n p_number key\n p_name\n}\n \nentity Screw extends Part {\n head_style\n}\n \nentity Supplier {\n s_id key\n s_name\n}\n \nrelation Supplies(Project M, Part N!, Supplier P!) {\n Quantity\n}\n \nentity Dependent depends on Dependent_of {\n Dep_name pkey\n Gender\n}\n \nrelation Dependent_of(Employee, Dependent 1!)",
"nodes": [
{
"id": "0",
Expand Down Expand Up @@ -357,4 +357,4 @@
"target": "3"
}
]
}
}
4 changes: 1 addition & 3 deletions test/ERDoc/linter/entity_extends_child_entity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ describe("Linter detects when a child entity extends a child entity", () => {
const errors = checkEntityExtendsChildEntity(cyclicER);
expect(errors.length).toBe(1);
});

});

const wrongER: ER = parse(`entity Dog extends Animal {
Expand Down Expand Up @@ -74,8 +73,7 @@ entity Westie extends Dog {
entity Frenchie extends Dog {
}`
);
}`);

const correctER: ER = parse(`entity Dog extends Mammal {
breed
Expand Down
32 changes: 16 additions & 16 deletions test/ERDoc/linter/weak_entity_has_pkey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@ entity Course {
code key
name
}
entity Evaluation depends on of {
name
date
}
relation of(Course, Evaluation 1!)
relation of(Course 1, Evaluation 1!)
entity Grade depends on eval, belongs_to {
question
value
}
entity Student {
s_id key
name
}
relation eval(Grade N!, Evaluation)
relation belongs_to(Grade N!, Student)`);
relation eval(Grade 1!, Evaluation 1)
relation belongs_to(Grade 1!, Student)`);

const noPkeyER: ER = parse(`
entity Course {
Expand All @@ -37,7 +37,7 @@ const noPkeyER: ER = parse(`
as_no
}
relation From(Assignment N!, Course)
relation From(Assignment 1!, Course)
`);

const withPkeyEr: ER = parse(`
Expand All @@ -49,7 +49,7 @@ const withPkeyEr: ER = parse(`
as_no pkey
}
relation From(Assignment N!, Course)
relation From(Assignment 1!, Course)
`);

const oneToOneEr: ER = parse(`
Expand All @@ -61,7 +61,7 @@ const oneToOneEr: ER = parse(`
as_no
}
relation From(Assignment 1!, Course)
relation From(Assignment 1!, Course 1)
`);

const multipleWrongEr: ER = parse(`
Expand All @@ -72,7 +72,7 @@ const multipleWrongEr: ER = parse(`
as_no
}
relation From(Assignment N!, Course)
relation From(Assignment 1!, Course)
entity Course2 {
code key
Expand All @@ -82,7 +82,7 @@ const multipleWrongEr: ER = parse(`
as_no
}
relation From2(Assignment2 N!, Course2)
relation From2(Assignment2 1!, Course2)
entity Course3 {
Expand All @@ -93,11 +93,11 @@ const multipleWrongEr: ER = parse(`
as_no
}
relation From3(Assignment3 N!, Course3)
relation From3(Assignment3 1!, Course3)
`);

describe("Linter detects that a weak entity must have a pkey in a one to many relationship", () => {
it("Finds an error when a weak entity doesn't have a pkey and doesn't have 1to1 constraint", () => {
it("Finds an error when a weak entity doesn't have a pkey and a strong entity is 'many'", () => {
const errors = checkWeakEntityNoPkey(noPkeyER);
expect(errors[0].type).toBe("WEAK_ENTITY_HAS_NO_PKEY");
expect(errors.length).toBe(1);
Expand All @@ -109,7 +109,7 @@ describe("Linter detects that a weak entity must have a pkey in a one to many re
expect(errors.length).toBe(0);
});

it("Finds no error when a weak entity has no pkey but is 1to1", () => {
it("Finds no error when a weak entity has no pkey but strong entity has cardinality of 1", () => {
const errors = checkWeakEntityNoPkey(oneToOneEr);
expect(errors.length).toBe(0);
});
Expand Down
10 changes: 0 additions & 10 deletions test/ERDoc/linter/weak_entity_wrong_constraints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,25 +61,15 @@ describe("Linter detects that a weak entity must have total participation in its
expect(errors.length).toBe(2);
});


it("Returns 1 error when a weak entity has cardinality of 1, but not total participation", () => {
const errors = checkWeakEntityConstraints(onlyCardinalityOneER);
expect(errors.length).toBe(1);
});


it("Returns 1 errors when a weak entity has total participation, but not a cardinality of 1", () => {
const errors = checkWeakEntityConstraints(onlyTotalParticipationER);
expect(errors.length).toBe(1);
});








});

const implicitWrongER: ER = parse(`entity Sun depends on BelongsTo {
Expand Down

0 comments on commit a2979ce

Please sign in to comment.