Skip to content

Commit

Permalink
Add indexes for sql, add mssql support and enforce database type limits
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim203 committed Sep 24, 2024
1 parent aa7a7a3 commit 9324a60
Show file tree
Hide file tree
Showing 21 changed files with 779 additions and 137 deletions.
33 changes: 21 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ currently examples can be found in the tests of the AP module and the tests of t
- support adding every variable of the entity as parameter
- add `upsert` which either inserts the entity if it's not present or updates the already existing entity
- adding migrations
- add null and non-null support
- and plenty more

# Supported types
Expand Down Expand Up @@ -52,7 +53,8 @@ The current codebase is not flexible enough to do these wildly different behavio
but will be supported in the future.

#### any deleteFirst() and any deleteTop*()
Anything with a limit projection in delete currently doesn't work for Oracle Database, PostgreSQL and SQLite.
Anything with a limit projection in delete currently doesn't work for Oracle Database, PostgreSQL, SQLite and SQL Server.
For SQL Server it's TOP instead of LIMIT.
For SQLite this is a flag that can be enabled during compile, but it's disabled by default.
For Oracle and Postgres delete with a limit doesn't exist.
We have to fetch a record first and then delete it (inside a transaction).
Expand All @@ -74,25 +76,32 @@ These are the base type conversions:
| Java Type | SQL type | Reason / remarks |
|-----------|------------------|-----------------------------------------------------------------|
| Boolean | boolean | More platforms support boolean than tinyint / bit |
| Byte | smallint | SQL Server's tinyint is unsigned and PostgresSQL has no tinyint |
| Byte | tinyint | SQL Server's tinyint is unsigned and PostgresSQL has no tinyint |
| Short | smallint | - |
| Char | smallint | Basically the same as short |
| Char | int | Unlike short, char is unsigned |
| Integer | int | - |
| Long | bigint | - |
| Float | real | Some dialects map this as a double precision type |
| Float | real | MySQL and MariaDB map this as a double precision type |
| Double | double precision | - |
| String | varchar | - |
| String | varchar | All dialects support varchar, but it's deprecated on OracleDB |
| Byte[] | varbinary | Most dialects support varbinary |

And these are the exceptions:

| Java Type | Dialect | SQL Type |
|-----------|------------|----------|
| Boolean | SQLite | int |
| Boolean | SQL Server | bit |
| Double | SQL Server | float |
| Byte[] | PostgreSQL | bytea |
| Byte[] | SQLite | varchar |
| Java Type | Dialect | SQL Type |
|-----------|------------|-------------------|
| Boolean | SQLite | int |
| Boolean | SQL Server | bit |
| Byte | PostgreSQL | smallint |
| Byte | SQL Server | smallint |
| Char | MariaDB | smallint unsigned |
| Char | MySQL | smallint unsigned |
| Char | OracleDB | number(5) |
| Double | SQL Server | float |
| String | OracleDB | varchar2 |
| Byte[] | OracleDB | raw |
| Byte[] | PostgreSQL | bytea |
| Byte[] | SQLite | blob |

# Query syntax
Assuming we have the following entity called TestEntity:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.geysermc.databaseutils.processor.info.ColumnInfo;
import org.geysermc.databaseutils.processor.info.EntityInfo;
import org.geysermc.databaseutils.processor.info.IndexInfo;
import org.geysermc.databaseutils.processor.info.IndexInfo.IndexType;
import org.geysermc.databaseutils.processor.type.LimitsEnforcer;
import org.geysermc.databaseutils.processor.util.TypeUtils;

final class EntityManager {
Expand All @@ -41,6 +43,9 @@ Collection<EntityInfo> processedEntities() {
}

EntityInfo processEntity(TypeMirror typeMirror) {
// todo technically all those meta annotation classes can be moved a separate module you only need during
// compile time, instead of including the annotation definitions in runtime as well

var type = MoreTypes.asTypeElement(typeMirror);

var cached = entityInfoByClassName.get(type.getQualifiedName());
Expand All @@ -64,7 +69,7 @@ EntityInfo processEntity(TypeMirror typeMirror) {
var columns = new ArrayList<ColumnInfo>();

Arrays.stream(type.getAnnotationsByType(Index.class))
.map(index -> new IndexInfo(index.name(), index.columns(), index.unique()))
.map(index -> new IndexInfo(index.name(), index.columns(), index.unique(), index.direction()))
.forEach(indexes::add);

for (Element element : type.getEnclosedElements()) {
Expand All @@ -84,7 +89,7 @@ EntityInfo processEntity(TypeMirror typeMirror) {
continue;
}

columns.add(new ColumnInfo(field.getSimpleName(), typeUtils.toBoxedTypeElement(field.asType())));
columns.add(new ColumnInfo(field.getSimpleName(), typeUtils.toBoxedTypeElement(field.asType()), field));

if (hasAnnotation(field, Key.class)) {
keys.add(field.getSimpleName());
Expand Down Expand Up @@ -119,13 +124,16 @@ EntityInfo processEntity(TypeMirror typeMirror) {
}

if (!keys.isEmpty()) {
indexes.add(new IndexInfo("", keys.toArray(new CharSequence[0]), true));
indexes.add(new IndexInfo("", keys.toArray(new CharSequence[0]), IndexType.PRIMARY));
} else {
// todo just make every column a key
throw new IllegalStateException("Expected entity to have at least one field marked as key");
}

var entityInfo = new EntityInfo(tableName, type, columns, indexes, keys);

new LimitsEnforcer(entityInfo).enforce();

entityInfoByClassName.put(type.getQualifiedName(), entityInfo);
return entityInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -206,21 +208,8 @@ private void error(Element cause, String message, Object... arguments) {
}

private void error(Element cause, Throwable exception) {
// trimming down the exception until the first trace of ourselves.
// This would be either a test class or our RepositoryProcessor.
// This makes the exception much easier to read
int lastOwnTrace = 0;
StringBuilder stackTrace = new StringBuilder(exception.toString()).append('\n');
for (StackTraceElement traceElement : exception.getStackTrace()) {
stackTrace.append(traceElement.toString()).append('\n');
if (traceElement.getClassName().startsWith("org.geysermc.databaseutils")) {
lastOwnTrace = stackTrace.length() - 1;
}
}

if (lastOwnTrace != stackTrace.length() - 1) {
stackTrace.delete(lastOwnTrace, stackTrace.length());
}
this.messager.printMessage(Diagnostic.Kind.ERROR, stackTrace, cause);
var writer = new StringWriter();
exception.printStackTrace(new PrintWriter(writer));
this.messager.printMessage(Diagnostic.Kind.ERROR, writer.toString(), cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,31 @@
*/
package org.geysermc.databaseutils.processor.info;

import java.lang.annotation.Annotation;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import org.geysermc.databaseutils.meta.Length;

public record ColumnInfo(Name name, TypeElement type) {
public record ColumnInfo(Name name, TypeElement type, VariableElement variable) {
public TypeMirror asType() {
return type.asType();
}

public Name typeName() {
return type.getQualifiedName();
}

public <T extends Annotation> T annotation(Class<T> annotationClass) {
return variable.getAnnotation(annotationClass);
}

/**
* Returns the max length as provided by {@link Length}, or -1 if no limit is provided
*/
public int maxLength() {
var length = annotation(Length.class);
return length != null ? length.max() : -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ public ColumnInfo columnFor(CharSequence columnName) {
return null;
}

public List<ColumnInfo> columnsFor(CharSequence[] columnNames) {
var columns = new ArrayList<ColumnInfo>();
for (CharSequence columnName : columnNames) {
var column = columnFor(columnName);
if (column == null) {
throw new IllegalArgumentException("Column " + columnName + " not found");
}
columns.add(column);
}
return columns;
}

public TypeMirror asType() {
return type.asType();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,22 @@

import org.geysermc.databaseutils.meta.Index;

public record IndexInfo(String name, CharSequence[] columns, boolean unique, Index.IndexDirection direction) {
public IndexInfo(String name, CharSequence[] columns, boolean unique) {
this(name, columns, unique, Index.IndexDirection.ASCENDING);
public record IndexInfo(String name, CharSequence[] columns, IndexType type, Index.IndexDirection direction) {
public IndexInfo(String name, CharSequence[] columns, IndexType type) {
this(name, columns, type, Index.IndexDirection.ASCENDING);
}

public IndexInfo(String name, CharSequence[] columns, boolean unique, Index.IndexDirection direction) {
this(name, columns, unique ? IndexType.UNIQUE : IndexType.NORMAL, direction);
}

public boolean unique() {
return type == IndexType.UNIQUE || type == IndexType.PRIMARY;
}

public enum IndexType {
PRIMARY,
UNIQUE,
NORMAL
}
}
Loading

0 comments on commit 9324a60

Please sign in to comment.