commit 867e5e209788c22d5ea33917cced85d2c3cf0ad7 Author: nitrix Date: Sun Jul 13 01:46:42 2025 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7bc07ec --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Environment-dependent path to Maven home directory +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..cb90d2e --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,9 @@ + + + + nitrix + oxedos + yubilee + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9c6181f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..76a2ee7 --- /dev/null +++ b/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + de.cccwi + chaosdb + 1.0-SNAPSHOT + + + 24 + 24 + UTF-8 + + + \ No newline at end of file diff --git a/src/main/java/de/cccwi/Attribute.java b/src/main/java/de/cccwi/Attribute.java new file mode 100644 index 0000000..3988c4d --- /dev/null +++ b/src/main/java/de/cccwi/Attribute.java @@ -0,0 +1,19 @@ +package de.cccwi; + + +public record Attribute(String name, Class type) { + + public Attribute rho(String newName) { + 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 new file mode 100644 index 0000000..f932a1e --- /dev/null +++ b/src/main/java/de/cccwi/Database.java @@ -0,0 +1,15 @@ +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 new file mode 100644 index 0000000..6c32e6a --- /dev/null +++ b/src/main/java/de/cccwi/Main.java @@ -0,0 +1,79 @@ +package de.cccwi; + +import de.cccwi.conditions.AttributeEquals; +import de.cccwi.conditions.GreaterThan; +import de.cccwi.conditions.ValueEquals; + +import java.util.List; +import java.util.UUID; + +public class Main { + public static void main(String[] args) { + 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 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") + ) + ); + System.out.println("Events"); + System.out.println(eventTable); + + System.out.println("SELECT name, id, age AS howOld FROM Users WHERE age > 30"); + var resultTable = userTable + .sigma(new GreaterThan(ageColumn, 30)) + .pi(List.of(nameColumn, idColumn, ageColumn)) + .rho(ageColumn.name(), "howOld"); + System.out.println(resultTable); + + System.out.println("SELECT name AS enabledUsers FROM Users WHERE NOT enabled"); + resultTable = userTable + .sigma(new ValueEquals(enabledColumn, false)) + .pi(List.of(nameColumn)) + .rho(nameColumn.name(), "enabledUsers"); + 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"))); + 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)); + System.out.println(resultTable); + + System.out.println("SELECT * FROM Users UNION Users"); + resultTable = userTable.union(userTable); + System.out.println(resultTable); + + System.out.println("SELECT * FROM Users UNION ALL Users"); + resultTable = userTable.unionAll(userTable); + System.out.println(resultTable); + + System.out.println("SELECT * FROM Users - Users"); + resultTable = userTable.difference(userTable); + System.out.println(resultTable); + } +} \ No newline at end of file diff --git a/src/main/java/de/cccwi/Relation.java b/src/main/java/de/cccwi/Relation.java new file mode 100644 index 0000000..d5353df --- /dev/null +++ b/src/main/java/de/cccwi/Relation.java @@ -0,0 +1,190 @@ +package de.cccwi; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public record Relation( + String name, + List attributes, + List> tuples +) { + public Relation sigma(BiFunction, List, Boolean> condition) { + return new Relation( + name, + List.copyOf(attributes), + List.copyOf(tuples.stream().filter(t -> condition.apply(attributes, t)).toList()) + ); + } + + public Relation pi(List newAttributes) { + // find out the required indices in the correct order + var indices = newAttributes.stream() + .map(attributes::indexOf) + .toList(); + + // filter and reorder the attributes of each row + var resultTuples = tuples.stream() + .map(t -> indices.stream().map(t::get).toList()) + .toList(); + + return new Relation( + name, + List.copyOf(newAttributes), + List.copyOf(resultTuples) + ); + } + + public Relation rho(String newRelationName) { + return new Relation(newRelationName, List.copyOf(attributes), List.copyOf(tuples)); + } + + public Relation rho(String oldAttributeName, String newAttributeName) { + return new Relation( + name, + List.copyOf(attributes.stream() + .map(a -> a.name().equals(oldAttributeName) ? a.rho(newAttributeName) : a) + .toList()), + List.copyOf(tuples) + ); + } + + public Relation cross(Relation other) { + assert !this.name.equals(other.name); + + var resultTuples = new ArrayList>(); + for (var thisTuple : this.tuples) { + for (var otherTuple : other.tuples) { + resultTuples.add(Stream.concat(thisTuple.stream(), otherTuple.stream()).toList()); + } + } + + return new Relation( + "%sX%s".formatted(name, other.name), + List.copyOf(mergeAttributes(other)), + List.copyOf(resultTuples) + ); + } + + 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)); + } + + return new Relation( + "%sU%s".formatted(name, other.name), + List.copyOf(attributes), + List.copyOf(Stream.concat(tuples.stream(), other.tuples.stream()).distinct().toList()) + ); + } + + 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)); + } + + return new Relation( + "%sU%s".formatted(name, other.name), + List.copyOf(attributes), + List.copyOf(Stream.concat(tuples.stream(), other.tuples.stream()).toList()) + ); + } + + 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)); + } + + return new Relation( + "%s-%s".formatted(name, other.name), + List.copyOf(attributes), + List.copyOf(tuples.stream() + .filter(t -> other.tuples().stream().noneMatch(tt -> tt.equals(t))) + .toList()) + ); + } + + public Relation join(Relation other, BiFunction, List, Boolean> condition) { + return cross(other).sigma(condition); + } + + @Override + public String toString() { + int colCount = attributes.size(); + + var columnWidths = new ArrayList(); + for (int col = 0; col < colCount; col++) { + int maxWidth = attributes.get(col).name().length(); + for (var row : tuples) { + String cell = String.valueOf(row.get(col)); + maxWidth = Math.max(maxWidth, cell.length()); + } + columnWidths.add(maxWidth); + } + + Function, String> formatRow = row -> { + StringBuilder line = new StringBuilder(); + for (int col = 0; col < colCount; col++) { + String cell = String.valueOf(row.get(col)); + line.append(String.format(" %-" + columnWidths.get(col) + "s ", cell)); + if (col < colCount - 1) line.append("|"); + } + return line.toString(); + }; + + + // Header + var sb = new StringBuilder(); + sb.append(formatRow.apply(attributes.stream().map(Attribute::name).map(a -> (Object) a).toList())); + sb.append(System.lineSeparator()); + + // Trenner + for (int col = 0; col < colCount; col++) { + sb.append("─".repeat(columnWidths.get(col) + 2)); + if (col < colCount - 1) sb.append("┼"); + } + sb.append(System.lineSeparator()); + + // Daten + for (var row : tuples) { + sb.append(formatRow.apply(row)); + sb.append(System.lineSeparator()); + } + + return sb.toString(); + } + + private List mergeAttributes(Relation other) { + var thisAttributeName = this.attributes.stream() + .map(Attribute::name) + .collect(Collectors.toSet()); + var otherAttributeNames = other.attributes.stream() + .map(Attribute::name) + .collect(Collectors.toSet()); + + var resultAttributes = new ArrayList(); + for (var thisAttribute : this.attributes) { + if (otherAttributeNames.contains(thisAttribute.name())) { + resultAttributes.add(thisAttribute.rho("%s.%s".formatted(this.name, thisAttribute.name()))); + } else { + resultAttributes.add(thisAttribute); + } + } + + for (var otherAttribute : other.attributes) { + if (thisAttributeName.contains(otherAttribute.name())) { + resultAttributes.add(otherAttribute.rho("%s.%s".formatted(other.name, otherAttribute.name()))); + } else { + resultAttributes.add(otherAttribute); + } + } + + return resultAttributes; + } +} diff --git a/src/main/java/de/cccwi/conditions/AttributeEquals.java b/src/main/java/de/cccwi/conditions/AttributeEquals.java new file mode 100644 index 0000000..82ea4bf --- /dev/null +++ b/src/main/java/de/cccwi/conditions/AttributeEquals.java @@ -0,0 +1,22 @@ +package de.cccwi.conditions; + +import de.cccwi.Attribute; + +import java.util.List; +import java.util.function.BiFunction; + +public class AttributeEquals implements BiFunction, List, Boolean> { + private final Attribute lhs; + private final Attribute rhs; + + public AttributeEquals(Attribute lhs, Attribute rhs) { + 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))); + } +} diff --git a/src/main/java/de/cccwi/conditions/GreaterThan.java b/src/main/java/de/cccwi/conditions/GreaterThan.java new file mode 100644 index 0000000..ae881ae --- /dev/null +++ b/src/main/java/de/cccwi/conditions/GreaterThan.java @@ -0,0 +1,23 @@ +package de.cccwi.conditions; + +import de.cccwi.Attribute; + +import java.util.List; +import java.util.function.BiFunction; + +public class GreaterThan implements BiFunction, List, Boolean> { + private final Attribute attribute; + private final Integer value; + + public GreaterThan(Attribute attribute, Integer value) { + 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; + } +} diff --git a/src/main/java/de/cccwi/conditions/GreaterThanEquals.java b/src/main/java/de/cccwi/conditions/GreaterThanEquals.java new file mode 100644 index 0000000..a8727ca --- /dev/null +++ b/src/main/java/de/cccwi/conditions/GreaterThanEquals.java @@ -0,0 +1,23 @@ +package de.cccwi.conditions; + +import de.cccwi.Attribute; + +import java.util.List; +import java.util.function.BiFunction; + +public class GreaterThanEquals implements BiFunction, List, Boolean> { + private final Attribute attribute; + private final Integer value; + + public GreaterThanEquals(Attribute attribute, Integer value) { + 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; + } +} diff --git a/src/main/java/de/cccwi/conditions/LessThan.java b/src/main/java/de/cccwi/conditions/LessThan.java new file mode 100644 index 0000000..7c085bf --- /dev/null +++ b/src/main/java/de/cccwi/conditions/LessThan.java @@ -0,0 +1,23 @@ +package de.cccwi.conditions; + +import de.cccwi.Attribute; + +import java.util.List; +import java.util.function.BiFunction; + +public class LessThan implements BiFunction, List, Boolean> { + private final Attribute attribute; + private final Integer value; + + public LessThan(Attribute attribute, Integer value) { + 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; + } +} diff --git a/src/main/java/de/cccwi/conditions/LessThanEquals.java b/src/main/java/de/cccwi/conditions/LessThanEquals.java new file mode 100644 index 0000000..bb82987 --- /dev/null +++ b/src/main/java/de/cccwi/conditions/LessThanEquals.java @@ -0,0 +1,23 @@ +package de.cccwi.conditions; + +import de.cccwi.Attribute; + +import java.util.List; +import java.util.function.BiFunction; + +public class LessThanEquals implements BiFunction, List, Boolean> { + private final Attribute attribute; + private final Integer value; + + public LessThanEquals(Attribute attribute, Integer value) { + 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; + } +} diff --git a/src/main/java/de/cccwi/conditions/Not.java b/src/main/java/de/cccwi/conditions/Not.java new file mode 100644 index 0000000..dc5c4c5 --- /dev/null +++ b/src/main/java/de/cccwi/conditions/Not.java @@ -0,0 +1,19 @@ +package de.cccwi.conditions; + +import de.cccwi.Attribute; + +import java.util.List; +import java.util.function.BiFunction; + +public class Not implements BiFunction, List, Boolean> { + private BiFunction, List, Boolean> condition; + + public Not(BiFunction, List, Boolean> condition) { + this.condition = condition; + } + + @Override + public Boolean apply(List attributes, List objects) { + return !condition.apply(attributes, objects); + } +} diff --git a/src/main/java/de/cccwi/conditions/ValueEquals.java b/src/main/java/de/cccwi/conditions/ValueEquals.java new file mode 100644 index 0000000..1b7a095 --- /dev/null +++ b/src/main/java/de/cccwi/conditions/ValueEquals.java @@ -0,0 +1,22 @@ +package de.cccwi.conditions; + +import de.cccwi.Attribute; + +import java.util.List; +import java.util.function.BiFunction; + +public class ValueEquals implements BiFunction, List, Boolean> { + private final Attribute lhs; + private final Object rhs; + + public ValueEquals(Attribute lhs, Object rhs) { + 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); + } +}