forked from SteamWar/SteamWar
@@ -17,121 +17,83 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.steamwar.sql;
|
package de.steamwar.sql
|
||||||
|
|
||||||
import de.steamwar.sql.internal.*;
|
import de.steamwar.sql.internal.useDb
|
||||||
import lombok.AllArgsConstructor;
|
import org.jetbrains.exposed.v1.core.SortOrder
|
||||||
import lombok.Getter;
|
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
|
||||||
|
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
|
||||||
|
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.statements.api.ExposedBlob
|
||||||
|
import org.jetbrains.exposed.v1.dao.CompositeEntity
|
||||||
|
import org.jetbrains.exposed.v1.dao.CompositeEntityClass
|
||||||
|
import org.jetbrains.exposed.v1.javatime.timestamp
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.util.zip.GZIPInputStream
|
||||||
|
|
||||||
import javax.swing.plaf.nimbus.State;
|
object NodeDataTable: CompositeIdTable("NodeData") {
|
||||||
import java.io.*;
|
val nodeId = reference("NodeId", SchematicNodeTable)
|
||||||
import java.sql.PreparedStatement;
|
val createdAt = timestamp("CreatedAt").entityId()
|
||||||
import java.sql.Timestamp;
|
val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
|
||||||
import java.time.Instant;
|
val schemData = blob("SchemData")
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
override val primaryKey = PrimaryKey(nodeId, createdAt)
|
||||||
@Getter
|
|
||||||
public class NodeData {
|
|
||||||
static {
|
|
||||||
new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
|
|
||||||
new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
|
|
||||||
new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
|
|
||||||
|
|
||||||
SqlTypeMapper.ordinalEnumMapper(SchematicFormat.class);
|
init {
|
||||||
|
addIdColumn(nodeId)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final Table<NodeData> table = new Table<>(NodeData.class);
|
class NodeData(id: EntityID<CompositeID>): CompositeEntity(id) {
|
||||||
|
val nodeId by NodeDataTable.nodeId
|
||||||
|
val createdAt by NodeDataTable.createdAt
|
||||||
|
val nodeFormat by NodeDataTable.nodeFormat
|
||||||
|
val schemData by NodeDataTable.schemData
|
||||||
|
|
||||||
private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?)", true);
|
companion object: CompositeEntityClass<NodeData>(NodeDataTable) {
|
||||||
private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ? AND CreatedAt = ?");
|
@JvmStatic
|
||||||
private static final Statement delete = table.delete(Table.PRIMARY);
|
fun getLatest(node: SchematicNode) = useDb {
|
||||||
|
find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.DESC).firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
private static final SelectStatement<NodeData> get = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt ");
|
@JvmStatic
|
||||||
private static final Statement getRevisions = new Statement("SELECT COUNT(DISTINCT CreatedAt) as CNT FROM NodeData WHERE NodeId = ?");
|
fun get(node: SchematicNode) = useDb {
|
||||||
private static final SelectStatement<NodeData> getLatest = new SelectStatement<>(table, "SELECT NodeId, CreatedAt, NodeFormat FROM NodeData WHERE NodeId = ? ORDER BY CreatedAt DESC LIMIT 1");
|
find { (NodeDataTable.nodeId eq node.nodeId) }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList()
|
||||||
|
}
|
||||||
|
|
||||||
public static NodeData getLatest(SchematicNode node) {
|
@JvmStatic
|
||||||
if (node.isDir()) throw new IllegalArgumentException("Node is dir");
|
fun get(node: SchematicNode, revision: Int) = useDb {
|
||||||
return Optional.ofNullable(getLatest.select(node)).orElseGet(() -> new NodeData(node.getId(), Timestamp.from(Instant.now()), SchematicFormat.MCEDIT));
|
find { NodeDataTable.nodeId eq node.nodeId }.orderBy(NodeDataTable.createdAt to SortOrder.ASC).toList().get(revision - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<NodeData> get(SchematicNode node) {
|
@JvmStatic
|
||||||
return get.listSelect(node);
|
fun getRevisions(node: SchematicNode) = useDb {
|
||||||
}
|
count(NodeDataTable.nodeId eq node.nodeId).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
public static NodeData get(SchematicNode node, int revision) {
|
@JvmStatic
|
||||||
return get.listSelect(node).get(revision - 1);
|
fun saveFromStream(node: SchematicNode, blob: InputStream, format: SchematicFormat) {
|
||||||
}
|
NodeDataTable.insertIgnore {
|
||||||
|
it[NodeDataTable.nodeId] = EntityID(node.getId(), SchematicNodeTable)
|
||||||
public static int getRevisions(SchematicNode node) {
|
it[NodeDataTable.nodeFormat] = format
|
||||||
return getRevisions.select(rs -> {
|
it[NodeDataTable.schemData] = ExposedBlob(blob)
|
||||||
if (rs.next()) {
|
|
||||||
return rs.getInt("CNT");
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void saveFromStream(SchematicNode node, InputStream blob, SchematicFormat format) {
|
|
||||||
updateDatabase.update(node.getId(), format, blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Field(keys = {Table.PRIMARY})
|
|
||||||
private final int nodeId;
|
|
||||||
|
|
||||||
@Field(keys = {Table.PRIMARY})
|
|
||||||
private Timestamp createdAt;
|
|
||||||
|
|
||||||
@Field
|
|
||||||
public SchematicFormat nodeFormat;
|
|
||||||
|
|
||||||
public InputStream schemData() throws IOException {
|
|
||||||
return schemData(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputStream schemData(boolean decompress) throws IOException {
|
|
||||||
try {
|
|
||||||
return selSchemData.select(rs -> {
|
|
||||||
rs.next();
|
|
||||||
InputStream schemData = rs.getBinaryStream("SchemData");
|
|
||||||
try {
|
|
||||||
if(rs.wasNull() || schemData.available() == 0) {
|
|
||||||
throw new SecurityException("SchemData is null");
|
|
||||||
}
|
|
||||||
if (decompress) {
|
|
||||||
return new GZIPInputStream(schemData);
|
|
||||||
} else {
|
|
||||||
return schemData;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SecurityException("SchemData is wrong", e);
|
|
||||||
}
|
|
||||||
}, nodeId, createdAt);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
fun schemData(decompress: Boolean) = useDb {
|
||||||
public void saveFromStream(InputStream blob, SchematicFormat newFormat) {
|
schemData.inputStream.let { if(decompress) GZIPInputStream(it) else it }
|
||||||
saveFromStream(SchematicNode.getSchematicNode(nodeId), blob, newFormat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
fun schemData() = schemData(true)
|
||||||
delete.update(nodeId, createdAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
override fun delete() = useDb { super.delete() }
|
||||||
@Getter
|
|
||||||
public enum SchematicFormat {
|
enum class SchematicFormat(val fileEnding: String) {
|
||||||
MCEDIT(".schematic"),
|
MCEDIT(".schematic"),
|
||||||
SPONGE_V2(".schem"),
|
SPONGE_V2(".schem"),
|
||||||
SPONGE_V3(".schem");
|
SPONGE_V3(".schem");
|
||||||
|
|
||||||
public final String fileEnding;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,7 +335,7 @@ class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
|
|||||||
val members: Set<NodeMember> by lazy { NodeMember.getNodeMembers(nodeId) }
|
val members: Set<NodeMember> by lazy { NodeMember.getNodeMembers(nodeId) }
|
||||||
private lateinit var breadcrumbs: String
|
private lateinit var breadcrumbs: String
|
||||||
|
|
||||||
fun getFileEnding(): String = checkDir { NodeData.getLatest(this).nodeFormat.fileEnding }
|
fun getFileEnding(): String = checkDir { NodeData.getLatest(this)!!.nodeFormat.fileEnding }
|
||||||
fun getId() = nodeId
|
fun getId() = nodeId
|
||||||
fun isDir() = nodeType == null
|
fun isDir() = nodeType == null
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,14 @@ import java.util.UUID
|
|||||||
|
|
||||||
object UserConfigTable: CompositeIdTable("UserConfig") {
|
object UserConfigTable: CompositeIdTable("UserConfig") {
|
||||||
val userId = reference("User", SteamwarUserTable)
|
val userId = reference("User", SteamwarUserTable)
|
||||||
val config = varchar("Config", 32)
|
val config = varchar("Config", 32).entityId()
|
||||||
val value = text("Value", eagerLoading = true)
|
val value = text("Value", eagerLoading = true)
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(userId, config)
|
override val primaryKey = PrimaryKey(userId, config)
|
||||||
|
|
||||||
|
init {
|
||||||
|
addIdColumn(userId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserConfig(id: EntityID<CompositeID>): CompositeEntity(id) {
|
class UserConfig(id: EntityID<CompositeID>): CompositeEntity(id) {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.intellij.lang.annotations.Language
|
|||||||
import org.jetbrains.exposed.v1.core.ColumnType
|
import org.jetbrains.exposed.v1.core.ColumnType
|
||||||
import org.jetbrains.exposed.v1.core.Expression
|
import org.jetbrains.exposed.v1.core.Expression
|
||||||
import org.jetbrains.exposed.v1.core.ResultRow
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import org.jetbrains.exposed.v1.core.StdOutSqlLogger
|
||||||
import org.jetbrains.exposed.v1.core.statements.StatementType
|
import org.jetbrains.exposed.v1.core.statements.StatementType
|
||||||
import org.jetbrains.exposed.v1.core.statements.api.RowApi
|
import org.jetbrains.exposed.v1.core.statements.api.RowApi
|
||||||
import org.jetbrains.exposed.v1.dao.IntEntity
|
import org.jetbrains.exposed.v1.dao.IntEntity
|
||||||
@@ -64,7 +65,10 @@ object KotlinDatabase {
|
|||||||
|
|
||||||
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
|
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
|
||||||
KotlinDatabase.ensureConnected()
|
KotlinDatabase.ensureConnected()
|
||||||
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db, statement)
|
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) {
|
||||||
|
addLogger(StdOutSqlLogger)
|
||||||
|
statement()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : IntEntity> IntEntityClass<T>.fromSql(
|
fun <T : IntEntity> IntEntityClass<T>.fromSql(
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import kotlinx.serialization.Serializable
|
|||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
|
import java.io.InputStream
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|||||||
Reference in New Issue
Block a user