diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/java/de/cccwi/Attribute.java b/src/main/java/de/cccwi/Attribute.java index 3988c4d..4756b5d 100644 --- a/src/main/java/de/cccwi/Attribute.java +++ b/src/main/java/de/cccwi/Attribute.java @@ -3,17 +3,14 @@ package de.cccwi; public record Attribute(String name, Class type) { + public Attribute { + assert name != null; + assert type != null; + } + public Attribute rho(String newName) { + assert newName != null; + return new Attribute(newName, type); } - - @Override - public boolean equals(Object o) { - return o instanceof Attribute other && name.equals(other.name); - } - - @Override - public int hashCode() { - return name.hashCode(); - } } \ No newline at end of file diff --git a/src/main/java/de/cccwi/Database.java b/src/main/java/de/cccwi/Database.java deleted file mode 100644 index f932a1e..0000000 --- a/src/main/java/de/cccwi/Database.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.cccwi; - -import java.util.Map; - -public class Database { - private Map tables; - - public Database(Map tables) { - this.tables = tables; - } - - public Relation executeQuery(String query) { - return null; - } -} diff --git a/src/main/java/de/cccwi/Main.java b/src/main/java/de/cccwi/Main.java index 6c32e6a..d320ed5 100644 --- a/src/main/java/de/cccwi/Main.java +++ b/src/main/java/de/cccwi/Main.java @@ -2,66 +2,83 @@ package de.cccwi; import de.cccwi.conditions.AttributeEquals; import de.cccwi.conditions.GreaterThan; +import de.cccwi.conditions.Not; import de.cccwi.conditions.ValueEquals; import java.util.List; import java.util.UUID; public class Main { - public static void main(String[] args) { + private static Relation createUserTable() { var idColumn = new Attribute("id", String.class); var nameColumn = new Attribute("name", String.class); var ageColumn = new Attribute("age", Integer.class); - var enabledColumn = new Attribute("enabled", Boolean.class); - var userTable = new Relation( - "Users", - List.of(idColumn, nameColumn, ageColumn, enabledColumn), - List.of( - List.of(UUID.randomUUID().toString(), "nitrix", 31, true), - List.of(UUID.randomUUID().toString(), "oxedos", 27, true), - List.of(UUID.randomUUID().toString(), "yubilee", 30, false) - ) - ); - System.out.println("Users"); - System.out.println(userTable); + var enabledColumn = new Attribute("enabled", Boolean.class); + return new Relation("Users", List.of(idColumn, nameColumn, ageColumn, enabledColumn), List.of()) + .insert(List.of(UUID.randomUUID().toString(), "nitrix", 32, true)) + .insert(List.of(UUID.randomUUID().toString(), "oxedos", 32, true)) + .insert(List.of(UUID.randomUUID().toString(), "yubilee", 31, false)); + } + private static Relation createEventTable(Relation userTable) { + var idColumn = new Attribute("id", String.class); var userIdColumn = new Attribute("userId", String.class); var typeColumn = new Attribute("type", String.class); var descriptionColumn = new Attribute("description", String.class); - var eventTable = new Relation( - "Events", - List.of(idColumn, userIdColumn, typeColumn, descriptionColumn), - List.of( - List.of(UUID.randomUUID(), userTable.tuples().getFirst().getFirst(), "login", "user logged in"), - List.of(UUID.randomUUID(), userTable.tuples().getFirst().getFirst(), "logout", "user logged out") - ) - ); + return new Relation("Events", List.of(idColumn, userIdColumn, typeColumn, descriptionColumn), List.of()) + .insert(List.of(UUID.randomUUID().toString(), userTable.tuples().getFirst().getFirst(), "login", "user logged in")) + .insert(List.of(UUID.randomUUID().toString(), userTable.tuples().getFirst().getFirst(), "logout", "user logged out")); + } + + public static void main(String[] args) { + var userTable = createUserTable(); + System.out.println("Users"); + System.out.println(userTable); + + var eventTable = createEventTable(userTable); System.out.println("Events"); System.out.println(eventTable); - System.out.println("SELECT name, id, age AS howOld FROM Users WHERE age > 30"); + System.out.println("SELECT name, id, age AS howOld FROM Users WHERE age > 31"); var resultTable = userTable - .sigma(new GreaterThan(ageColumn, 30)) - .pi(List.of(nameColumn, idColumn, ageColumn)) - .rho(ageColumn.name(), "howOld"); + .sigma(new GreaterThan(new Attribute("age", Integer.class), 31)) + .pi(List.of( + new Attribute("name", String.class), + new Attribute("id", String.class), + new Attribute("age", Integer.class)) + ) + .rho("age", "howOld"); System.out.println(resultTable); - System.out.println("SELECT name AS enabledUsers FROM Users WHERE NOT enabled"); + System.out.println("SELECT name AS disabledUsers FROM Users WHERE enabled != true"); resultTable = userTable - .sigma(new ValueEquals(enabledColumn, false)) - .pi(List.of(nameColumn)) - .rho(nameColumn.name(), "enabledUsers"); + .sigma(new Not(new ValueEquals(new Attribute("enabled", Boolean.class), true))) + .pi(List.of(new Attribute("name", String.class))) + .rho("name", "disabledUsers"); System.out.println(resultTable); System.out.println("SELECT * FROM Users u1 JOIN Users u2 ON u1.id = u2.id"); - var u1 = userTable.rho("u1"); - var u2 = userTable.rho("u2"); - resultTable = u1.join(u2, new AttributeEquals(idColumn.rho("u1.id"), idColumn.rho("u2.id"))); + resultTable = userTable + .rho("u1") + .join( + userTable.rho("u2"), + new AttributeEquals( + new Attribute("u1.id", String.class), + new Attribute("u2.id", String.class) + ) + ); System.out.println(resultTable); System.out.println("SELECT * FROM Users u JOIN Events e ON u.id = e.userId"); - resultTable = userTable.rho("u") - .join(eventTable.rho("e"), new AttributeEquals(idColumn.rho("u.id"), userIdColumn)); + resultTable = userTable + .rho("u") + .join( + eventTable.rho("e"), + new AttributeEquals( + new Attribute("u.id", String.class), + new Attribute("userId", String.class) + ) + ); System.out.println(resultTable); System.out.println("SELECT * FROM Users UNION Users"); diff --git a/src/main/java/de/cccwi/Relation.java b/src/main/java/de/cccwi/Relation.java index d5353df..a616828 100644 --- a/src/main/java/de/cccwi/Relation.java +++ b/src/main/java/de/cccwi/Relation.java @@ -12,7 +12,33 @@ public record Relation( List attributes, List> tuples ) { + + public Relation { + assert name != null; + assert attributes != null && !attributes.isEmpty(); + assert tuples != null; + + for (var tuple : tuples) { + assert tuple.size() == attributes.size(); + for (var i = 0; i < tuple.size(); i++) { + assert attributes.get(i).type().isInstance(tuple.get(i)); + } + } + } + + public Relation insert(List tuple) { + assert tuple != null; + + return new Relation( + name, + attributes, + List.copyOf(Stream.concat(tuples.stream(), Stream.of(tuple)).toList()) + ); + } + public Relation sigma(BiFunction, List, Boolean> condition) { + assert condition != null; + return new Relation( name, List.copyOf(attributes), @@ -21,11 +47,15 @@ public record Relation( } public Relation pi(List newAttributes) { + assert newAttributes != null && !newAttributes.isEmpty(); + // find out the required indices in the correct order var indices = newAttributes.stream() .map(attributes::indexOf) .toList(); + assert indices.stream().noneMatch(idx -> idx == -1); + // filter and reorder the attributes of each row var resultTuples = tuples.stream() .map(t -> indices.stream().map(t::get).toList()) @@ -39,10 +69,15 @@ public record Relation( } public Relation rho(String newRelationName) { + assert newRelationName != null; + return new Relation(newRelationName, List.copyOf(attributes), List.copyOf(tuples)); } public Relation rho(String oldAttributeName, String newAttributeName) { + assert oldAttributeName != null; + assert newAttributeName != null; + return new Relation( name, List.copyOf(attributes.stream() @@ -53,6 +88,7 @@ public record Relation( } public Relation cross(Relation other) { + assert other != null; assert !this.name.equals(other.name); var resultTuples = new ArrayList>(); @@ -70,10 +106,7 @@ public record Relation( } public Relation union(Relation other) { - assert attributes.size() == other.attributes.size(); - for (int i = 0; i < attributes.size(); i++) { - assert attributes.get(i).equals(other.attributes.get(i)); - } + checkAttributesMatch(other); return new Relation( "%sU%s".formatted(name, other.name), @@ -83,10 +116,7 @@ public record Relation( } public Relation unionAll(Relation other) { - assert attributes.size() == other.attributes.size(); - for (int i = 0; i < attributes.size(); i++) { - assert attributes.get(i).equals(other.attributes.get(i)); - } + checkAttributesMatch(other); return new Relation( "%sU%s".formatted(name, other.name), @@ -96,10 +126,7 @@ public record Relation( } public Relation difference(Relation other) { - assert attributes.size() == other.attributes.size(); - for (int i = 0; i < attributes.size(); i++) { - assert attributes.get(i).equals(other.attributes.get(i)); - } + checkAttributesMatch(other); return new Relation( "%s-%s".formatted(name, other.name), @@ -138,7 +165,6 @@ public record Relation( return line.toString(); }; - // Header var sb = new StringBuilder(); sb.append(formatRow.apply(attributes.stream().map(Attribute::name).map(a -> (Object) a).toList())); @@ -187,4 +213,12 @@ public record Relation( return resultAttributes; } + + private void checkAttributesMatch(Relation other) { + assert other != null; + assert attributes.size() == other.attributes.size(); + for (int i = 0; i < attributes.size(); i++) { + assert attributes.get(i).equals(other.attributes.get(i)); + } + } } diff --git a/src/main/java/de/cccwi/conditions/AttributeEquals.java b/src/main/java/de/cccwi/conditions/AttributeEquals.java index 82ea4bf..0d73582 100644 --- a/src/main/java/de/cccwi/conditions/AttributeEquals.java +++ b/src/main/java/de/cccwi/conditions/AttributeEquals.java @@ -10,13 +10,16 @@ public class AttributeEquals implements BiFunction, List private final Attribute rhs; public AttributeEquals(Attribute lhs, Attribute rhs) { + assert lhs != null; + assert rhs != null; assert lhs.type().equals(rhs.type()); + this.lhs = lhs; this.rhs = rhs; } @Override - public Boolean apply(List attributes, List rows) { - return rows.get(attributes.indexOf(lhs)).equals(rows.get(attributes.indexOf(rhs))); + public Boolean apply(List attributes, List tuple) { + return tuple.get(attributes.indexOf(lhs)).equals(tuple.get(attributes.indexOf(rhs))); } } diff --git a/src/main/java/de/cccwi/conditions/GreaterThan.java b/src/main/java/de/cccwi/conditions/GreaterThan.java index ae881ae..141665b 100644 --- a/src/main/java/de/cccwi/conditions/GreaterThan.java +++ b/src/main/java/de/cccwi/conditions/GreaterThan.java @@ -10,14 +10,17 @@ public class GreaterThan implements BiFunction, List, Bo private final Integer value; public GreaterThan(Attribute attribute, Integer value) { + assert attribute != null && attribute.type().equals(Integer.class); + assert value != null; + this.attribute = attribute; this.value = value; } @Override - public Boolean apply(List attributes, List rows) { - var columnIndex = attributes.indexOf(attribute); - assert attributes.get(columnIndex).type().equals(Integer.class); - return ((Integer) rows.get(columnIndex)) > value; + public Boolean apply(List attributes, List tuple) { + var attributeIndex = attributes.indexOf(attribute); + assert attributes.get(attributeIndex).type().equals(Integer.class); + return ((Integer) tuple.get(attributeIndex)) > value; } } diff --git a/src/main/java/de/cccwi/conditions/GreaterThanEquals.java b/src/main/java/de/cccwi/conditions/GreaterThanEquals.java index a8727ca..ba24c0b 100644 --- a/src/main/java/de/cccwi/conditions/GreaterThanEquals.java +++ b/src/main/java/de/cccwi/conditions/GreaterThanEquals.java @@ -10,14 +10,17 @@ public class GreaterThanEquals implements BiFunction, List attributes, List rows) { - var columnIndex = attributes.indexOf(attribute); - assert attributes.get(columnIndex).type().equals(Integer.class); - return ((Integer) rows.get(columnIndex)) >= value; + public Boolean apply(List attributes, List tuple) { + var attributeIndex = attributes.indexOf(attribute); + assert attributes.get(attributeIndex).type().equals(Integer.class); + return ((Integer) tuple.get(attributeIndex)) >= value; } } diff --git a/src/main/java/de/cccwi/conditions/LessThan.java b/src/main/java/de/cccwi/conditions/LessThan.java index 7c085bf..d952d37 100644 --- a/src/main/java/de/cccwi/conditions/LessThan.java +++ b/src/main/java/de/cccwi/conditions/LessThan.java @@ -10,14 +10,17 @@ public class LessThan implements BiFunction, List, Boole private final Integer value; public LessThan(Attribute attribute, Integer value) { + assert attribute != null && attribute.type().equals(Integer.class); + assert value != null; + this.attribute = attribute; this.value = value; } @Override - public Boolean apply(List attributes, List rows) { - var columnIndex = attributes.indexOf(attribute); - assert attributes.get(columnIndex).type().equals(Integer.class); - return ((Integer) rows.get(columnIndex)) < value; + public Boolean apply(List attributes, List tuple) { + var attributeIndex = attributes.indexOf(attribute); + assert attributes.get(attributeIndex).type().equals(Integer.class); + return ((Integer) tuple.get(attributeIndex)) < value; } } diff --git a/src/main/java/de/cccwi/conditions/LessThanEquals.java b/src/main/java/de/cccwi/conditions/LessThanEquals.java index bb82987..a3f0eb2 100644 --- a/src/main/java/de/cccwi/conditions/LessThanEquals.java +++ b/src/main/java/de/cccwi/conditions/LessThanEquals.java @@ -10,14 +10,17 @@ public class LessThanEquals implements BiFunction, List, private final Integer value; public LessThanEquals(Attribute attribute, Integer value) { + assert attribute != null && attribute.type().equals(Integer.class); + assert value != null; + this.attribute = attribute; this.value = value; } @Override - public Boolean apply(List attributes, List rows) { - var columnIndex = attributes.indexOf(attribute); - assert attributes.get(columnIndex).type().equals(Integer.class); - return ((Integer) rows.get(columnIndex)) <= value; + public Boolean apply(List attributes, List tuple) { + var attributeIndex = attributes.indexOf(attribute); + assert attributes.get(attributeIndex).type().equals(Integer.class); + return ((Integer) tuple.get(attributeIndex)) <= value; } } diff --git a/src/main/java/de/cccwi/conditions/Not.java b/src/main/java/de/cccwi/conditions/Not.java index dc5c4c5..cd368f2 100644 --- a/src/main/java/de/cccwi/conditions/Not.java +++ b/src/main/java/de/cccwi/conditions/Not.java @@ -6,14 +6,16 @@ import java.util.List; import java.util.function.BiFunction; public class Not implements BiFunction, List, Boolean> { - private BiFunction, List, Boolean> condition; + private final BiFunction, List, Boolean> condition; public Not(BiFunction, List, Boolean> condition) { + assert condition != null; + this.condition = condition; } @Override - public Boolean apply(List attributes, List objects) { - return !condition.apply(attributes, objects); + public Boolean apply(List attributes, List tuple) { + return !condition.apply(attributes, tuple); } } diff --git a/src/main/java/de/cccwi/conditions/ValueEquals.java b/src/main/java/de/cccwi/conditions/ValueEquals.java index 1b7a095..3e06cab 100644 --- a/src/main/java/de/cccwi/conditions/ValueEquals.java +++ b/src/main/java/de/cccwi/conditions/ValueEquals.java @@ -10,13 +10,16 @@ public class ValueEquals implements BiFunction, List, Bo private final Object rhs; public ValueEquals(Attribute lhs, Object rhs) { + assert lhs != null; + assert rhs != null; assert lhs.type().isInstance(rhs); + this.lhs = lhs; this.rhs = rhs; } @Override - public Boolean apply(List attributes, List rows) { - return rows.get(attributes.indexOf(lhs)).equals(rhs); + public Boolean apply(List attributes, List tuple) { + return tuple.get(attributes.indexOf(lhs)).equals(rhs); } }