Skip to content

Commit

Permalink
Review issues: rename functions (arrayN -> array, arrayNLiteral -> ar…
Browse files Browse the repository at this point in the history
…rayLiteral, arrayNParam -> arrayParam), fix breaking change in ArrayColumnType constructor, remove array2(), array3() functions, update documentation
  • Loading branch information
obabichevjb committed Oct 8, 2024
1 parent cbdec3f commit 5acb4cd
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 72 deletions.
7 changes: 3 additions & 4 deletions documentation-website/Writerside/topics/Breaking-Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
## 0.56.0
* If the `distinct` parameter of `groupConcat()` is set to `true`, when using Oracle or SQL Server, this will now fail early with an
`UnsupportedByDialectException`. Previously, the setting would be ignored and SQL function generation would not include a `DISTINCT` clause.
* `ArrayColumnType` supports multidimensional arrays now and has one more generic parameter.
If the was used directly for one dimensional arrays with parameter `T` like `ArrayColumnType<T>`, now it should
be defined as `ArrayColumnType<T, List<T>>`, for example `ArrayColumnType<Int>` -> `ArrayColumnType<Int, List<Int>>`

* `ArrayColumnType` now supports multidimensional arrays and includes an additional generic parameter.
If it was previously used for one-dimensional arrays with the parameter `T` like `ArrayColumnType<T>`,
it should now be defined as `ArrayColumnType<T, List<T>>`. For instance, `ArrayColumnType<Int>` should now be `ArrayColumnType<Int, List<Int>>`.

## 0.55.0
* The `DeleteStatement` property `table` is now deprecated in favor of `targetsSet`, which holds a `ColumnSet` that may be a `Table` or `Join`.
Expand Down
24 changes: 17 additions & 7 deletions documentation-website/Writerside/topics/Data-Types.topic
Original file line number Diff line number Diff line change
Expand Up @@ -334,18 +334,27 @@

<code-block lang="kotlin">
object Teams : Table("teams") {
val memberIds = array2<UUID>("member_ids")
val memberNames = array3<String>("member_names")
val budgets = array2<Double>("budgets")
val memberIds = array&lt;UUID, List&lt;List&lt;UUID&gt;&gt;&gt;(
"member_ids", dimensions = 2
)
val memberNames = array&lt;String, List&lt;List&lt;List&lt;String&gt;&gt;&gt;&gt;(
"member_names", dimensions = 3
)
}
</code-block>
<p>If more control is needed over the base content type, or if the latter is user-defined or from a non-core
module, the explicit type should be provided to the function:</p>

<code-block lang="kotlin">
object Teams : Table("teams") {
val memberIds = array2<UUID>("member_ids")
val memberNames = array3<String>("member_names", VarCharColumnType(colLength = 32))
val memberIds = array&lt;UUID, List&lt;List&lt;UUID&gt;&gt;&gt;(
"member_ids", dimensions = 2
)
val memberNames = array&lt;String, List&lt;List&lt;List&lt;String&gt;&gt;&gt;&gt;(
"member_names",
VarCharColumnType(colLength = 32),
dimensions = 3
)
}
</code-block>

Expand All @@ -354,8 +363,9 @@
<code-block lang="kotlin">
Teams.insert {
it[memberIds] = List(5) { List(5) { UUID.randomUUID() } }
it[memberNames] = List(3) { List(3) { List(3) { i -> "Member ${'A' + i}" } } }
it[budgets] = listOf(listOf(9999.0, 8888.0))
it[memberNames] = List(3) { List(3) { List(3) {
i -> "Member ${'A' + i}"
} } }
}
</code-block>
</chapter>
Expand Down
10 changes: 6 additions & 4 deletions exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,10 @@ public final class org/jetbrains/exposed/sql/AndOp : org/jetbrains/exposed/sql/C
}

public final class org/jetbrains/exposed/sql/ArrayColumnType : org/jetbrains/exposed/sql/ColumnType {
public fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;ILjava/util/List;)V
public synthetic fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;ILjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;Ljava/lang/Integer;)V
public synthetic fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;Ljava/util/List;I)V
public synthetic fun <init> (Lorg/jetbrains/exposed/sql/ColumnType;Ljava/util/List;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getDelegate ()Lorg/jetbrains/exposed/sql/ColumnType;
public final fun getDelegateType ()Ljava/lang/String;
public final fun getDimensions ()I
Expand Down Expand Up @@ -2464,9 +2466,9 @@ public class org/jetbrains/exposed/sql/Table : org/jetbrains/exposed/sql/ColumnS
public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun array (Ljava/lang/String;Lorg/jetbrains/exposed/sql/ColumnType;Ljava/lang/Integer;)Lorg/jetbrains/exposed/sql/Column;
public final fun array (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Lorg/jetbrains/exposed/sql/ColumnType;Ljava/util/List;I)Lorg/jetbrains/exposed/sql/Column;
public static synthetic fun array$default (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Lorg/jetbrains/exposed/sql/ColumnType;Ljava/lang/Integer;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
public final fun arrayN (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Lorg/jetbrains/exposed/sql/ColumnType;ILjava/util/List;)Lorg/jetbrains/exposed/sql/Column;
public static synthetic fun arrayN$default (Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Lorg/jetbrains/exposed/sql/ColumnType;ILjava/util/List;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
public static synthetic fun array$default (Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Lorg/jetbrains/exposed/sql/ColumnType;Ljava/util/List;IILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
public final fun autoGenerate (Lorg/jetbrains/exposed/sql/Column;)Lorg/jetbrains/exposed/sql/Column;
public final fun autoIncrement (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun autoIncrement (Lorg/jetbrains/exposed/sql/Column;Lorg/jetbrains/exposed/sql/Sequence;)Lorg/jetbrains/exposed/sql/Column;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1240,13 +1240,14 @@ class CustomEnumerationColumnType<T : Enum<T>>(
* @property maximumCardinality The maximum cardinality (number of allowed elements) for each dimension of the array.
*
* **Note:** The maximum cardinality is considered for each dimension, but it is ignored by the PostgreSQL database.
* Validation is performed on the client side.
*/
class ArrayColumnType<T, R : List<Any?>>(
val delegate: ColumnType<T & Any>,
val dimensions: Int,
val maximumCardinality: List<Int>? = null
val maximumCardinality: List<Int>? = null,
val dimensions: Int = 1
) : ColumnType<R>() {
constructor(delegate: ColumnType<T & Any>, maximumCardinality: Int? = null) : this(delegate, maximumCardinality?.let { listOf(it) })

val delegateType: String
get() = delegate.sqlType().substringBefore('(')

Expand Down
12 changes: 6 additions & 6 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Op.kt
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ fun decimalLiteral(value: BigDecimal): LiteralOp<BigDecimal> = LiteralOp(Decimal
* @throws IllegalStateException If no column type mapping is found and a [delegateType] is not provided.
*/
inline fun <reified T : Any> arrayLiteral(value: List<T>, delegateType: ColumnType<T>? = null): LiteralOp<List<T>> =
arrayNLiteral(value, delegateType, dimensions = 1)
arrayLiteral(value, 1, delegateType)

/**
* Returns the specified [value] as an array literal, with elements parsed by the [delegateType] if provided.
Expand All @@ -702,9 +702,9 @@ inline fun <reified T : Any> arrayLiteral(value: List<T>, delegateType: ColumnTy
*
* @throws IllegalStateException If no column type mapping is found and a [delegateType] is not provided.
*/
inline fun <reified T : Any, R : List<Any>> arrayNLiteral(value: R, delegateType: ColumnType<T>? = null, dimensions: Int): LiteralOp<R> {
inline fun <reified T : Any, R : List<Any>> arrayLiteral(value: R, dimensions: Int, delegateType: ColumnType<T>? = null): LiteralOp<R> {
@OptIn(InternalApi::class)
return LiteralOp(ArrayColumnType(delegateType ?: resolveColumnType(T::class), dimensions), value)
return LiteralOp(ArrayColumnType(delegateType ?: resolveColumnType(T::class), dimensions = dimensions), value)
}

// Query Parameters
Expand Down Expand Up @@ -792,7 +792,7 @@ fun blobParam(value: ExposedBlob, useObjectIdentifier: Boolean = false): Express
* @throws IllegalStateException If no column type mapping is found and a [delegateType] is not provided.
*/
inline fun <reified T : Any> arrayParam(value: List<T>, delegateType: ColumnType<T>? = null): Expression<List<T>> =
arrayNParam(value, delegateType, dimensions = 1)
arrayParam(value, 1, delegateType)

/**
* Returns the specified [value] as an array query parameter, with elements parsed by the [delegateType] if provided.
Expand All @@ -806,9 +806,9 @@ inline fun <reified T : Any> arrayParam(value: List<T>, delegateType: ColumnType
*
* @throws IllegalStateException If no column type mapping is found and a [delegateType] is not provided.
*/
inline fun <reified T : Any, R : List<Any>> arrayNParam(value: R, delegateType: ColumnType<T>? = null, dimensions: Int): Expression<R> {
inline fun <reified T : Any, R : List<Any>> arrayParam(value: R, dimensions: Int, delegateType: ColumnType<T>? = null): Expression<R> {
@OptIn(InternalApi::class)
return QueryParameter(value, ArrayColumnType(delegateType ?: resolveColumnType(T::class), dimensions))
return QueryParameter(value, ArrayColumnType(delegateType ?: resolveColumnType(T::class), dimensions = dimensions))
}

// Misc.
Expand Down
52 changes: 8 additions & 44 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* when using the PostgreSQL dialect is allowed, but this value will be ignored by the database.
*/
fun <E> array(name: String, columnType: ColumnType<E & Any>, maximumCardinality: Int? = null): Column<List<E>> =
arrayN<E, List<E>>(name, columnType, dimensions = 1, maximumCardinality = maximumCardinality?.let { listOf(it) })
array<E, List<E>>(name, columnType, dimensions = 1, maximumCardinality = maximumCardinality?.let { listOf(it) })

/**
* Creates an array column, with the specified [name], for storing elements of a `List`.
Expand All @@ -925,41 +925,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* @throws IllegalStateException If no column type mapping is found.
*/
inline fun <reified E : Any> array(name: String, maximumCardinality: Int? = null): Column<List<E>> =
arrayN<E, List<E>>(name, dimensions = 1, maximumCardinality?.let { listOf(it) })

/**
* Creates a 3-dimensional array column, with the specified [name], for storing elements of a nested `List`.
*
* **Note:** This column type is only supported by PostgreSQL dialect.
*
* @param name Name of the column.
* @param maximumCardinality The maximum cardinality (number of allowed elements) for each dimension in the array.
*
* **Note:** Providing an array size limit when using the PostgreSQL dialect is allowed, but this value will be ignored by the database.
* The whole validation is performed on the client side.
*
* @return A column instance that represents a 3-dimensional list of elements of type [T].
* @throws IllegalStateException If no column type mapping is found.
*/
inline fun <reified T : Any> Table.array3(name: String, maximumCardinality: List<Int>? = null): Column<List<List<List<T>>>> =
arrayN<T, List<List<List<T>>>>(name, dimensions = 3, maximumCardinality)

/**
* Creates a 2-dimensional array column, with the specified [name], for storing elements of a nested `List`.
*
* **Note:** This column type is only supported by PostgreSQL dialect.
*
* @param name Name of the column.
* @param maximumCardinality The maximum cardinality (number of allowed elements) for each dimension in the array.
*
* **Note:** Providing an array size limit when using the PostgreSQL dialect is allowed, but this value will be ignored by the database.
* The whole validation is performed on the client side.
*
* @return A column instance that represents a 2-dimensional list of elements of type [T].
* @throws IllegalStateException If no column type mapping is found.
*/
inline fun <reified T : Any> Table.array2(name: String, maximumCardinality: List<Int>? = null): Column<List<List<T>>> =
arrayN<T, List<List<T>>>(name, dimensions = 2, maximumCardinality)
array<E, List<E>>(name, maximumCardinality?.let { listOf(it) }, dimensions = 1)

/**
* Creates a multi-dimensional array column, with the specified [name], for storing elements of a nested `List`.
Expand All @@ -968,19 +934,18 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* **Note:** This column type is only supported by PostgreSQL dialect.
*
* @param name Name of the column.
* @param dimensions The number of dimensions of the array.
* @param maximumCardinality The maximum cardinality (number of allowed elements) for each dimension in the array.
* @param dimensions The number of dimensions of the array.
*
* **Note:** Providing an array size limit when using the PostgreSQL dialect is allowed, but this value will be ignored by the database.
* The whole validation is performed on the client side.
*
* @return A column instance that represents a multi-dimensional list of elements of type [T].
* @throws IllegalArgumentException If [dimensions] is less than or equal to 1.
* @throws IllegalStateException If no column type mapping is found.
*/
inline fun <reified T : Any, R : List<Any>> Table.arrayN(name: String, dimensions: Int, maximumCardinality: List<Int>? = null): Column<R> {
inline fun <reified T : Any, R : List<Any>> Table.array(name: String, maximumCardinality: List<Int>? = null, dimensions: Int): Column<R> {
@OptIn(InternalApi::class)
return arrayN(name, resolveColumnType(T::class), dimensions, maximumCardinality)
return array(name, resolveColumnType(T::class), maximumCardinality, dimensions)
}

/**
Expand All @@ -990,18 +955,17 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* **Note:** This column type is only supported by PostgreSQL dialect.
*
* @param name Name of the column.
* @param dimensions The number of dimensions of the array.
* @param maximumCardinality The maximum cardinality (number of allowed elements) for each dimension in the array.
* @param dimensions The number of dimensions of the array.
*
* **Note:** Providing an array size limit when using the PostgreSQL dialect is allowed, but this value will be ignored by the database.
* The whole validation is performed on the client side.
*
* @return A column instance that represents a multi-dimensional list of elements of type [E].
* @throws IllegalArgumentException If [dimensions] is less than or equal to 1.
* @throws IllegalStateException If no column type mapping is found.
*/
fun <E, R : List<Any?>> Table.arrayN(name: String, columnType: ColumnType<E & Any>, dimensions: Int, maximumCardinality: List<Int>? = null): Column<R> =
registerColumn(name, ArrayColumnType(columnType, dimensions, maximumCardinality))
fun <E, R : List<Any?>> Table.array(name: String, columnType: ColumnType<E & Any>, maximumCardinality: List<Int>? = null, dimensions: Int): Column<R> =
registerColumn(name, ArrayColumnType(columnType, maximumCardinality, dimensions))

// Auto-generated values

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class AllAnyFromArrayOp<T : Any>(
private val delegateType: ColumnType<T>
) : AllAnyFromBaseOp<T, List<T>>(isAny, array) {
override fun QueryBuilder.registerSubSearchArgument(subSearch: List<T>) {
registerArgument(ArrayColumnType<T, List<T>>(delegateType, dimensions = 1), subSearch)
registerArgument(ArrayColumnType<T, List<T>>(delegateType), subSearch)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import org.jetbrains.exposed.sql.tests.shared.assertEquals
import org.junit.Test
import kotlin.test.assertNull

private inline fun <reified T : Any> Table.array3(name: String, maximumCardinality: List<Int>? = null): Column<List<List<List<T>>>> =
array<T, List<List<List<T>>>>(name, maximumCardinality, dimensions = 3)

private inline fun <reified T : Any> Table.array2(name: String, maximumCardinality: List<Int>? = null): Column<List<List<T>>> =
array<T, List<List<T>>>(name, maximumCardinality, dimensions = 2)

class MultiArrayColumnTypeTests : DatabaseTestsBase() {

private val multiArrayTypeUnsupportedDb = TestDB.ALL - TestDB.ALL_POSTGRES.toSet()
Expand Down Expand Up @@ -57,7 +63,7 @@ class MultiArrayColumnTypeTests : DatabaseTestsBase() {
@Test
fun test5xMultiArray() {
val tester = object : IntIdTable("test_table") {
val multiArray = arrayN<String, List<List<List<List<List<String>>>>>>("multi_array", 5)
val multiArray = array<String, List<List<List<List<List<String>>>>>>("multi_array", dimensions = 5)
}

withTables(excludeSettings = multiArrayTypeUnsupportedDb, tester) {
Expand Down Expand Up @@ -140,7 +146,7 @@ class MultiArrayColumnTypeTests : DatabaseTestsBase() {
val list = listOf(listOf(1, 2), listOf(3, 4))

tester.insert {
it[multiArray] = arrayNLiteral<Int, List<List<Int>>>(list, dimensions = 2)
it[multiArray] = arrayLiteral<Int, List<List<Int>>>(list, dimensions = 2)
}

val value = tester.selectAll().first()[tester.multiArray]
Expand All @@ -158,7 +164,7 @@ class MultiArrayColumnTypeTests : DatabaseTestsBase() {
val list = listOf(listOf(1, 2), listOf(3, 4))

tester.insert {
it[multiArray] = arrayNParam<Int, List<List<Int>>>(list, dimensions = 2)
it[multiArray] = arrayParam<Int, List<List<Int>>>(list, dimensions = 2)
}

val value = tester.selectAll().first()[tester.multiArray]
Expand Down

0 comments on commit 5acb4cd

Please sign in to comment.