From 0b760977102a05edd8f23cbaa9d6dcf042fede43 Mon Sep 17 00:00:00 2001
From: Linnea Gräf <nea@nea.moe>
Date: Wed, 22 Jan 2025 01:34:16 +0100
Subject: refactor: More type safe where clauses

---
 .../src/main/kotlin/moe/nea/ledger/database/Column.kt    | 10 +++++-----
 .../main/kotlin/moe/nea/ledger/database/Constraint.kt    |  2 +-
 .../src/main/kotlin/moe/nea/ledger/database/DBType.kt    | 16 +++++++++++++---
 .../kotlin/moe/nea/ledger/database/InsertStatement.kt    |  4 ++--
 .../src/main/kotlin/moe/nea/ledger/database/Query.kt     |  2 +-
 .../src/main/kotlin/moe/nea/ledger/database/ResultRow.kt |  6 +++---
 .../src/main/kotlin/moe/nea/ledger/database/Table.kt     | 14 +++++++-------
 .../kotlin/moe/nea/ledger/database/UniqueConstraint.kt   |  4 ++--
 .../kotlin/moe/nea/ledger/database/columns/DBDouble.kt   |  2 +-
 .../kotlin/moe/nea/ledger/database/columns/DBEnum.kt     |  2 +-
 .../kotlin/moe/nea/ledger/database/columns/DBInstant.kt  |  2 +-
 .../main/kotlin/moe/nea/ledger/database/columns/DBInt.kt |  2 +-
 .../kotlin/moe/nea/ledger/database/columns/DBString.kt   |  2 +-
 .../kotlin/moe/nea/ledger/database/columns/DBUlid.kt     |  2 +-
 .../kotlin/moe/nea/ledger/database/columns/DBUuid.kt     |  2 +-
 .../kotlin/moe/nea/ledger/database/sql/ANDExpression.kt  |  5 ++++-
 .../main/kotlin/moe/nea/ledger/database/sql/Clause.kt    |  3 +++
 .../kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt  | 16 ++++++++--------
 .../kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt  |  2 +-
 .../kotlin/moe/nea/ledger/database/sql/EqualsClause.kt   |  2 +-
 .../kotlin/moe/nea/ledger/database/sql/IntoSelectable.kt |  2 +-
 .../nea/ledger/database/sql/LessThanEqualsExpression.kt  |  2 +-
 .../moe/nea/ledger/database/sql/LessThanExpression.kt    |  2 +-
 .../kotlin/moe/nea/ledger/database/sql/LikeClause.kt     |  2 +-
 .../main/kotlin/moe/nea/ledger/database/sql/Operand.kt   |  7 ++++++-
 .../kotlin/moe/nea/ledger/database/sql/Selectable.kt     |  8 ++++----
 .../kotlin/moe/nea/ledger/database/sql/StringOperand.kt  |  2 +-
 .../src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt |  4 ++--
 .../src/main/kotlin/moe/nea/ledger/database/Upgrades.kt  |  2 --
 29 files changed, 75 insertions(+), 56 deletions(-)

diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/Column.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/Column.kt
index 33727de..c21a159 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/Column.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/Column.kt
@@ -4,20 +4,20 @@ import moe.nea.ledger.database.sql.IntoSelectable
 import moe.nea.ledger.database.sql.Selectable
 import java.sql.PreparedStatement
 
-class Column<T> @Deprecated("Use Table.column instead") constructor(
+class Column<T, Raw> @Deprecated("Use Table.column instead") constructor(
 	val table: Table,
 	val name: String,
-	val type: DBType<T>
+	val type: DBType<T, Raw>
 ) : IntoSelectable<T> {
-	override fun asSelectable() = object : Selectable<T> {
+	override fun asSelectable() = object : Selectable<T, Raw> {
 		override fun asSql(): String {
 			return qualifiedSqlName
 		}
 
-		override val dbType: DBType<T>
+		override val dbType: DBType<T, Raw>
 			get() = this@Column.type
 
-		override fun guessColumn(): Column<T>? {
+		override fun guessColumn(): Column<T, Raw> {
 			return this@Column
 		}
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/Constraint.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/Constraint.kt
index 9f7c9ef..729c6b8 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/Constraint.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/Constraint.kt
@@ -1,6 +1,6 @@
 package moe.nea.ledger.database
 
 interface Constraint {
-	val affectedColumns: Collection<Column<*>>
+	val affectedColumns: Collection<Column<*, *>>
 	fun asSQL(): String
 }
\ No newline at end of file
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/DBType.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/DBType.kt
index 86ff544..622aff3 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/DBType.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/DBType.kt
@@ -1,9 +1,19 @@
 package moe.nea.ledger.database
 
+import moe.nea.ledger.database.sql.ClauseBuilder
 import java.sql.PreparedStatement
 import java.sql.ResultSet
 
-interface DBType<T> {
+
+interface DBType<
+		/**
+		 * Mapped type of this db type. Represents the Java type this db type accepts for saving to the database.
+		 */
+		T,
+		/**
+		 * Phantom marker type representing how this db type is presented to the actual DB. Is used by APIs such as [ClauseBuilder] to allow for rough typechecking.
+		 */
+		RawType> {
 	val dbType: String
 
 	fun get(result: ResultSet, index: Int): T
@@ -12,8 +22,8 @@ interface DBType<T> {
 	fun <R> mapped(
 		from: (R) -> T,
 		to: (T) -> R,
-	): DBType<R> {
-		return object : DBType<R> {
+	): DBType<R, RawType> {
+		return object : DBType<R, RawType> {
 			override fun getName(): String {
 				return "Mapped(${this@DBType.getName()})"
 			}
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/InsertStatement.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/InsertStatement.kt
index 7871ba8..25bef22 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/InsertStatement.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/InsertStatement.kt
@@ -1,7 +1,7 @@
 package moe.nea.ledger.database
 
-class InsertStatement(val properties: MutableMap<Column<*>, Any>) {
-	operator fun <T : Any> set(key: Column<T>, value: T) {
+class InsertStatement(val properties: MutableMap<Column<*, *>, Any>) {
+	operator fun <T : Any> set(key: Column<T, *>, value: T) {
 		properties[key] = value
 	}
 }
\ No newline at end of file
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/Query.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/Query.kt
index e58eef4..a23c878 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/Query.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/Query.kt
@@ -12,7 +12,7 @@ import java.sql.Connection
 
 class Query(
 	val connection: Connection,
-	val selectedColumns: MutableList<Selectable<*>>,
+	val selectedColumns: MutableList<Selectable<*, *>>,
 	var table: Table,
 	var limit: UInt? = null,
 	var skip: UInt? = null,
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/ResultRow.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/ResultRow.kt
index 6715f27..7b57abd 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/ResultRow.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/ResultRow.kt
@@ -2,19 +2,19 @@ package moe.nea.ledger.database
 
 import moe.nea.ledger.database.sql.Selectable
 
-class ResultRow(val selectableValues: Map<Selectable<*>, *>) {
+class ResultRow(val selectableValues: Map<Selectable<*, *>, *>) {
 	val columnValues = selectableValues.mapNotNull {
 		val col = it.key.guessColumn() ?: return@mapNotNull null
 		col to it.value
 	}.toMap()
 
-	operator fun <T> get(column: Column<T>): T {
+	operator fun <T> get(column: Column<T, *>): T {
 		val value = columnValues[column]
 			?: error("Invalid column ${column.name}. Only ${columnValues.keys.joinToString { it.name }} are available.")
 		return value as T
 	}
 
-	operator fun <T> get(column: Selectable<T>): T {
+	operator fun <T> get(column: Selectable<T, *>): T {
 		val value = selectableValues[column]
 			?: error("Invalid selectable ${column}. Only ${selectableValues.keys} are available.")
 		return value as T
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/Table.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/Table.kt
index 61dc8f0..a462813 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/Table.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/Table.kt
@@ -4,15 +4,15 @@ import java.sql.Connection
 
 abstract class Table(val name: String) {
 	val sqlName get() = "`$name`"
-	protected val _mutable_columns: MutableList<Column<*>> = mutableListOf()
+	protected val _mutable_columns: MutableList<Column<*, *>> = mutableListOf()
 	protected val _mutable_constraints: MutableList<Constraint> = mutableListOf()
-	val columns: List<Column<*>> get() = _mutable_columns
+	val columns: List<Column<*, *>> get() = _mutable_columns
 	val constraints get() = _mutable_constraints
-	protected fun unique(vararg columns: Column<*>) {
+	protected fun unique(vararg columns: Column<*, *>) {
 		_mutable_constraints.add(UniqueConstraint(columns.toList()))
 	}
 
-	protected fun <T> column(name: String, type: DBType<T>): Column<T> {
+	protected fun <T, R> column(name: String, type: DBType<T, R>): Column<T, R> {
 		@Suppress("DEPRECATION") val column = Column(this, name, type)
 		_mutable_columns.add(column)
 		return column
@@ -39,7 +39,7 @@ abstract class Table(val name: String) {
 
 	fun createIfNotExists(
 		connection: Connection,
-		filteredColumns: List<Column<*>> = columns
+		filteredColumns: List<Column<*, *>> = columns
 	) {
 		val properties = mutableListOf<String>()
 		for (column in filteredColumns) {
@@ -57,7 +57,7 @@ abstract class Table(val name: String) {
 
 	fun alterTableAddColumns(
 		connection: Connection,
-		newColumns: List<Column<*>>
+		newColumns: List<Column<*, *>>
 	) {
 		for (column in newColumns) {
 			connection.prepareAndLog("ALTER TABLE $sqlName ADD ${column.sqlName} ${column.type.dbType}")
@@ -88,7 +88,7 @@ abstract class Table(val name: String) {
 		val statement =
 			connection.prepareAndLog("INSERT OR ${onConflict.asSql()} INTO $sqlName ($columnNames) VALUES ($valueNames)")
 		for ((index, column) in columns.withIndex()) {
-			(column as Column<Any>).type.set(statement, index + 1, insert.properties[column]!!)
+			(column as Column<Any, *>).type.set(statement, index + 1, insert.properties[column]!!)
 		}
 		statement.execute()
 	}
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/UniqueConstraint.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/UniqueConstraint.kt
index 32e9f79..31ef06c 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/UniqueConstraint.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/UniqueConstraint.kt
@@ -1,11 +1,11 @@
 package moe.nea.ledger.database
 
-class UniqueConstraint(val columns: List<Column<*>>) : Constraint {
+class UniqueConstraint(val columns: List<Column<*, *>>) : Constraint {
 	init {
 		require(columns.isNotEmpty())
 	}
 
-	override val affectedColumns: Collection<Column<*>>
+	override val affectedColumns: Collection<Column<*, *>>
 		get() = columns
 
 	override fun asSQL(): String {
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBDouble.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBDouble.kt
index 6828308..9840df2 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBDouble.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBDouble.kt
@@ -4,7 +4,7 @@ import moe.nea.ledger.database.DBType
 import java.sql.PreparedStatement
 import java.sql.ResultSet
 
-object DBDouble : DBType<Double> {
+object DBDouble : DBType<Double, Double> {
 	override val dbType: String
 		get() = "DOUBLE"
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBEnum.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBEnum.kt
index 3cce8bc..78ac578 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBEnum.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBEnum.kt
@@ -6,7 +6,7 @@ import java.sql.ResultSet
 
 class DBEnum<T : Enum<T>>(
 	val type: Class<T>,
-) : DBType<T> {
+) : DBType<T, String> {
 	companion object {
 		inline operator fun <reified T : Enum<T>> invoke(): DBEnum<T> {
 			return DBEnum(T::class.java)
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInstant.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInstant.kt
index a9f36ef..2cf1882 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInstant.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInstant.kt
@@ -5,7 +5,7 @@ import java.sql.PreparedStatement
 import java.sql.ResultSet
 import java.time.Instant
 
-object DBInstant : DBType<Instant> {
+object DBInstant : DBType<Instant, Long> {
 	override val dbType: String
 		get() = "INTEGER"
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInt.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInt.kt
index 9cf2aa0..b5406e1 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInt.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBInt.kt
@@ -4,7 +4,7 @@ import moe.nea.ledger.database.DBType
 import java.sql.PreparedStatement
 import java.sql.ResultSet
 
-object DBInt : DBType<Long> {
+object DBInt : DBType<Long, Long> {
 	override val dbType: String
 		get() = "INTEGER"
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBString.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBString.kt
index 3994b7d..4406627 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBString.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBString.kt
@@ -4,7 +4,7 @@ import moe.nea.ledger.database.DBType
 import java.sql.PreparedStatement
 import java.sql.ResultSet
 
-object DBString : DBType<String> {
+object DBString : DBType<String, String> {
 	override val dbType: String
 		get() = "TEXT"
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUlid.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUlid.kt
index 33db65f..1fcc9d8 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUlid.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUlid.kt
@@ -5,7 +5,7 @@ import moe.nea.ledger.database.DBType
 import java.sql.PreparedStatement
 import java.sql.ResultSet
 
-object DBUlid : DBType<ULIDWrapper> {
+object DBUlid : DBType<ULIDWrapper, String> {
 	override val dbType: String
 		get() = "TEXT"
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUuid.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUuid.kt
index ede385f..eaea440 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUuid.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/columns/DBUuid.kt
@@ -6,7 +6,7 @@ import java.sql.PreparedStatement
 import java.sql.ResultSet
 import java.util.UUID
 
-object DBUuid : DBType<UUID> {
+object DBUuid : DBType<UUID, String> {
 	override val dbType: String
 		get() = "TEXT"
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ANDExpression.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ANDExpression.kt
index 6ea1b64..43d5a53 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ANDExpression.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ANDExpression.kt
@@ -10,7 +10,10 @@ data class ANDExpression(
 	}
 
 	override fun asSql(): String {
-		return (elements + SQLQueryComponent.standalone("TRUE")).joinToString(" AND ", "(", ")") { it.asSql() }
+		elements.singleOrNull()?.let {
+			return "(" + it.asSql() + ")"
+		}
+		return elements.joinToString(" AND ", "(", ")") { it.asSql() }
 	}
 
 	override fun appendToStatement(stmt: PreparedStatement, startIndex: Int): Int {
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Clause.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Clause.kt
index f2bf193..205e566 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Clause.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Clause.kt
@@ -1,5 +1,8 @@
 package moe.nea.ledger.database.sql
 
+/**
+ * Directly constructing [clauses][Clause] is discouraged. Instead [Clause.invoke] should be used.
+ */
 interface Clause : BooleanExpression {
 	companion object {
 		operator fun <T> invoke(builder: ClauseBuilder.() -> T): T {
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt
index bd16b58..f4fc478 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ClauseBuilder.kt
@@ -3,15 +3,15 @@ package moe.nea.ledger.database.sql
 import moe.nea.ledger.database.Column
 
 class ClauseBuilder {
-	fun <T> column(column: Column<T>): Operand<T> = ColumnOperand(column)
+	fun <T, R> column(column: Column<T, R>): Operand<T, R> = ColumnOperand(column)
 	fun string(string: String): StringOperand = StringOperand(string)
-	infix fun Operand<*>.eq(operand: Operand<*>): Clause = EqualsClause(this, operand)
-	infix fun Operand<*>.like(op: StringOperand): Clause = LikeClause(this, op)
-	infix fun Operand<*>.like(op: String): Clause = LikeClause(this, string(op))
-	infix fun Operand<*>.lt(op: Operand<*>): BooleanExpression = LessThanExpression(this, op)
-	infix fun Operand<*>.le(op: Operand<*>): BooleanExpression = LessThanEqualsExpression(this, op)
-	infix fun Operand<*>.gt(op: Operand<*>): BooleanExpression = op lt this
-	infix fun Operand<*>.ge(op: Operand<*>): BooleanExpression = op le this
+	infix fun <T> Operand<*, T>.eq(operand: Operand<*, T>): Clause = EqualsClause(this, operand)
+	infix fun Operand<*, String>.like(op: StringOperand): Clause = LikeClause(this, op)
+	infix fun Operand<*, String>.like(op: String): Clause = LikeClause(this, string(op))
+	infix fun <T> Operand<*, T>.lt(op: Operand<*, T>): BooleanExpression = LessThanExpression(this, op)
+	infix fun <T> Operand<*, T>.le(op: Operand<*, T>): BooleanExpression = LessThanEqualsExpression(this, op)
+	infix fun <T> Operand<*, T>.gt(op: Operand<*, T>): BooleanExpression = op lt this
+	infix fun <T> Operand<*, T>.ge(op: Operand<*, T>): BooleanExpression = op le this
 	infix fun BooleanExpression.and(clause: BooleanExpression): BooleanExpression = ANDExpression(listOf(this, clause))
 	infix fun BooleanExpression.or(clause: BooleanExpression): BooleanExpression = ORExpression(listOf(this, clause))
 }
\ No newline at end of file
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt
index 9150aa7..d06d834 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/ColumnOperand.kt
@@ -3,7 +3,7 @@ package moe.nea.ledger.database.sql
 import moe.nea.ledger.database.Column
 import java.sql.PreparedStatement
 
-data class ColumnOperand<T>(val column: Column<T>) : Operand<T> {
+data class ColumnOperand<T, Raw>(val column: Column<T, Raw>) : Operand<T, Raw> {
 	override fun asSql(): String {
 		return column.qualifiedSqlName
 	}
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/EqualsClause.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/EqualsClause.kt
index c6c482a..cfe72ef 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/EqualsClause.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/EqualsClause.kt
@@ -2,7 +2,7 @@ package moe.nea.ledger.database.sql
 
 import java.sql.PreparedStatement
 
-data class EqualsClause(val left: Operand<*>, val right: Operand<*>) : Clause { // TODO: typecheck this somehow
+data class EqualsClause(val left: Operand<*, *>, val right: Operand<*, *>) : Clause {
 	override fun asSql(): String {
 		return left.asSql() + " = " + right.asSql()
 	}
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/IntoSelectable.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/IntoSelectable.kt
index 0068f6b..3775387 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/IntoSelectable.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/IntoSelectable.kt
@@ -1,5 +1,5 @@
 package moe.nea.ledger.database.sql
 
 interface IntoSelectable<T> {
-	fun asSelectable(): Selectable<T>
+	fun asSelectable(): Selectable<T, *>
 }
\ No newline at end of file
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanEqualsExpression.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanEqualsExpression.kt
index db7aa17..4820c97 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanEqualsExpression.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanEqualsExpression.kt
@@ -2,7 +2,7 @@ package moe.nea.ledger.database.sql
 
 import java.sql.PreparedStatement
 
-class LessThanEqualsExpression(val lhs: Operand<*>, val rhs: Operand<*>) :
+class LessThanEqualsExpression(val lhs: Operand<*, *>, val rhs: Operand<*, *>) :
 	BooleanExpression {
 	override fun asSql(): String {
 		return "${lhs.asSql()} <= ${rhs.asSql()}"
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanExpression.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanExpression.kt
index c36a1e8..4609ac1 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanExpression.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LessThanExpression.kt
@@ -2,7 +2,7 @@ package moe.nea.ledger.database.sql
 
 import java.sql.PreparedStatement
 
-class LessThanExpression(val lhs: Operand<*>, val rhs: Operand<*>) :
+class LessThanExpression(val lhs: Operand<*, *>, val rhs: Operand<*, *>) :
 	BooleanExpression {
 	override fun asSql(): String {
 		return "${lhs.asSql()} < ${rhs.asSql()}"
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LikeClause.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LikeClause.kt
index f84c5cc..1122329 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LikeClause.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/LikeClause.kt
@@ -2,7 +2,7 @@ package moe.nea.ledger.database.sql
 
 import java.sql.PreparedStatement
 
-data class LikeClause<T>(val left: Operand<T>, val right: StringOperand) : Clause {
+data class LikeClause<T>(val left: Operand<T, String>, val right: StringOperand) : Clause {
 	//TODO: check type safety with this one
 	override fun asSql(): String {
 		return "(" + left.asSql() + " LIKE " + right.asSql() + ")"
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Operand.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Operand.kt
index 9937414..b085103 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Operand.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Operand.kt
@@ -1,5 +1,10 @@
 package moe.nea.ledger.database.sql
 
-interface Operand<T> : SQLQueryComponent {
+interface Operand<T,
+		/**
+		 * The db sided type (or a rough equivalence).
+		 * @see moe.nea.ledger.database.DBType Raw type parameter
+		 */
+		Raw> : SQLQueryComponent {
 
 }
\ No newline at end of file
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Selectable.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Selectable.kt
index a95b66b..8241a9d 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Selectable.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/Selectable.kt
@@ -6,12 +6,12 @@ import moe.nea.ledger.database.DBType
 /**
  * Something that can be selected. Like a column, or an expression thereof
  */
-interface Selectable<T> : SQLQueryComponent, IntoSelectable<T> {
-	override fun asSelectable(): Selectable<T> {
+interface Selectable<T, Raw> : SQLQueryComponent, IntoSelectable<T> {
+	override fun asSelectable(): Selectable<T, Raw> {
 		return this
 	}
 
-	val dbType: DBType<T>
-	fun guessColumn(): Column<T>?
+	val dbType: DBType<T, Raw>
+	fun guessColumn(): Column<T, *>?
 }
 
diff --git a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt
index 24f4e78..e7ede6a 100644
--- a/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt
+++ b/database/core/src/main/kotlin/moe/nea/ledger/database/sql/StringOperand.kt
@@ -2,7 +2,7 @@ package moe.nea.ledger.database.sql
 
 import java.sql.PreparedStatement
 
-data class StringOperand(val value: String) : Operand<String> {
+data class StringOperand(val value: String) : Operand<String, String> {
 	override fun asSql(): String {
 		return "?"
 	}
diff --git a/database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt b/database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt
index 7d1782a..9739978 100644
--- a/database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt
+++ b/database/impl/src/main/kotlin/moe/nea/ledger/database/DBUpgrade.kt
@@ -37,14 +37,14 @@ interface DBUpgrade {
 			return upgrades.groupBy { it.toVersion }
 		}
 
-		fun createTable(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade {
+		fun createTable(to: Long, table: Table, vararg columns: Column<*, *>): DBUpgrade {
 			require(columns.all { it in table.columns })
 			return of("Create table ${table}", to) {
 				table.createIfNotExists(it, columns.toList())
 			}
 		}
 
-		fun addColumns(to: Long, table: Table, vararg columns: Column<*>): DBUpgrade {
+		fun addColumns(to: Long, table: Table, vararg columns: Column<*, *>): DBUpgrade {
 			return of("Add columns to table $table", to) {
 				table.alterTableAddColumns(it, columns.toList())
 			}
diff --git a/database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt b/database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt
index e83abe7..76dfb5d 100644
--- a/database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt
+++ b/database/impl/src/main/kotlin/moe/nea/ledger/database/Upgrades.kt
@@ -15,6 +15,4 @@ class Upgrades {
 			DBItemEntry.itemId, DBItemEntry.size, DBItemEntry.mode, DBItemEntry.transactionId
 		))
 	}
-
-
 }
\ No newline at end of file
-- 
cgit