Skip to content

Commit ea3a6a0

Browse files
authored
Merge pull request #324 from BenCodez/codex/shared-sql-backend-init
Extract platform-neutral SQL backend initialization
2 parents 3de4b66 + c91e273 commit ea3a6a0

29 files changed

Lines changed: 4142 additions & 0 deletions

‎AdvancedCore/pom.xml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,12 @@
387387
<version>5.22.0</version>
388388
<scope>test</scope>
389389
</dependency>
390+
<dependency>
391+
<groupId>org.xerial</groupId>
392+
<artifactId>sqlite-jdbc</artifactId>
393+
<version>3.53.4.0</version>
394+
<scope>test</scope>
395+
</dependency>
390396
<dependency>
391397
<groupId>com.nickuc.login</groupId>
392398
<artifactId>api</artifactId>

‎AdvancedCore/src/main/java/com/bencodez/advancedcore/core/user/storage/sql/JdbcSqlUserStorage.java‎

Lines changed: 388 additions & 0 deletions
Large diffs are not rendered by default.

‎AdvancedCore/src/main/java/com/bencodez/advancedcore/core/user/storage/sql/MysqlUserBackend.java‎

Lines changed: 452 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.bencodez.advancedcore.core.user.storage.sql;
2+
3+
public interface SqlBackendLogger {
4+
SqlBackendLogger NO_OP = new SqlBackendLogger() {
5+
@Override public void info(String message) {}
6+
@Override public void warn(String message, Throwable error) {}
7+
};
8+
9+
void info(String message);
10+
11+
void warn(String message, Throwable error);
12+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.bencodez.advancedcore.core.user.storage.sql;
2+
3+
import java.util.List;
4+
import java.util.Objects;
5+
import java.util.UUID;
6+
import java.util.function.Consumer;
7+
8+
import com.bencodez.advancedcore.api.user.UserStorage;
9+
import com.bencodez.advancedcore.core.user.storage.SqlUserStorage;
10+
11+
public interface SqlUserBackend extends AutoCloseable {
12+
/** Materialized compatibility API is intentionally bounded by concrete SQL backends. */
13+
int MAX_MATERIALIZED_USERS = 100_000;
14+
15+
UserStorage storageType();
16+
17+
SqlUserStorage user(UUID uuid);
18+
19+
List<UUID> enumerateUsers();
20+
21+
/**
22+
* Streaming enumeration for large historical user tables. Concrete SQL backends
23+
* override this to avoid first materializing every UUID in heap.
24+
*/
25+
default void forEachUser(Consumer<UUID> consumer) {
26+
Objects.requireNonNull(consumer, "consumer");
27+
enumerateUsers().forEach(consumer);
28+
}
29+
30+
boolean isOpen();
31+
32+
@Override
33+
void close();
34+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.bencodez.advancedcore.core.user.storage.sql;
2+
3+
import java.nio.file.Path;
4+
import java.util.Collection;
5+
import java.util.Objects;
6+
7+
import com.bencodez.advancedcore.api.user.usercache.keys.UserDataKey;
8+
import com.bencodez.simpleapi.sql.mysql.config.MysqlConfig;
9+
10+
/**
11+
* Constructs SQL user backends from explicit platform-neutral inputs. Native
12+
* platform adapters are responsible for translating their configuration and
13+
* data-directory concepts before calling this factory.
14+
*/
15+
public final class SqlUserBackendFactory {
16+
private SqlUserBackendFactory() {
17+
}
18+
19+
public static SqliteUserBackend sqlite(Path dataDirectory, String databaseName, String tableName,
20+
Collection<? extends UserDataKey> keys, SqlBackendLogger logger) {
21+
Objects.requireNonNull(keys, "keys");
22+
return new SqliteUserBackend(dataDirectory, databaseName, tableName, SqlUserSchema.fromKeys(keys), logger);
23+
}
24+
25+
public static MysqlUserBackend mysql(String baseTableName, MysqlConfig config,
26+
Collection<? extends UserDataKey> keys, SqlBackendLogger logger) {
27+
Objects.requireNonNull(keys, "keys");
28+
return new MysqlUserBackend(baseTableName, config, SqlUserSchema.fromKeys(keys), logger);
29+
}
30+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.bencodez.advancedcore.core.user.storage.sql;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.LinkedHashMap;
7+
import java.util.List;
8+
import java.util.Locale;
9+
import java.util.Map;
10+
import java.util.Objects;
11+
12+
import com.bencodez.advancedcore.api.user.usercache.keys.UserDataKey;
13+
import com.bencodez.advancedcore.api.user.usercache.keys.UserDataKeyBoolean;
14+
import com.bencodez.advancedcore.api.user.usercache.keys.UserDataKeyInt;
15+
import com.bencodez.simpleapi.sql.DataType;
16+
17+
public final class SqlUserSchema {
18+
public static final String UUID_COLUMN = "uuid";
19+
20+
public record ColumnDefinition(String name, String sqlType, DataType dataType) {
21+
public ColumnDefinition {
22+
Objects.requireNonNull(name, "name");
23+
Objects.requireNonNull(sqlType, "sqlType");
24+
Objects.requireNonNull(dataType, "dataType");
25+
if (name.isBlank()) throw new IllegalArgumentException("Column name cannot be blank");
26+
if (sqlType.isBlank()) throw new IllegalArgumentException("SQL type cannot be blank");
27+
}
28+
}
29+
30+
private final Map<String, ColumnDefinition> columnsByLowerName;
31+
32+
private SqlUserSchema(Map<String, ColumnDefinition> columnsByLowerName) {
33+
this.columnsByLowerName = Collections.unmodifiableMap(new LinkedHashMap<>(columnsByLowerName));
34+
}
35+
36+
public static Builder builder() { return new Builder(); }
37+
38+
public static SqlUserSchema fromKeys(Collection<? extends UserDataKey> keys) {
39+
Builder builder = builder();
40+
for (UserDataKey key : Objects.requireNonNull(keys, "keys")) {
41+
DataType type = DataType.STRING;
42+
if (key instanceof UserDataKeyInt) type = DataType.INTEGER;
43+
else if (key instanceof UserDataKeyBoolean) type = DataType.BOOLEAN;
44+
builder.column(key.getKey(), key.getColumnType(), type);
45+
}
46+
return builder.build();
47+
}
48+
49+
public List<ColumnDefinition> columns() { return new ArrayList<>(columnsByLowerName.values()); }
50+
51+
public ColumnDefinition column(String name) {
52+
if (name == null) return null;
53+
return columnsByLowerName.get(name.toLowerCase(Locale.ROOT));
54+
}
55+
56+
public boolean contains(String name) { return column(name) != null; }
57+
58+
public static final class Builder {
59+
private final Map<String, ColumnDefinition> columns = new LinkedHashMap<>();
60+
61+
private Builder() {
62+
columns.put(UUID_COLUMN, new ColumnDefinition(UUID_COLUMN, "VARCHAR(37)", DataType.STRING));
63+
}
64+
65+
public Builder column(String name, String sqlType, DataType dataType) {
66+
Objects.requireNonNull(name, "name");
67+
if (UUID_COLUMN.equalsIgnoreCase(name)) {
68+
throw new IllegalArgumentException("Column name 'uuid' is reserved for user identity");
69+
}
70+
String canonical = name.toLowerCase(Locale.ROOT);
71+
ColumnDefinition existing = columns.get(canonical);
72+
if (existing != null) {
73+
throw new IllegalArgumentException("Duplicate SQL column name: " + name + " conflicts with " + existing.name());
74+
}
75+
columns.put(canonical, new ColumnDefinition(name, sqlType, dataType));
76+
return this;
77+
}
78+
79+
public SqlUserSchema build() { return new SqlUserSchema(columns); }
80+
}
81+
}

0 commit comments

Comments
 (0)