Skip to content

Commit

Permalink
feat: Add partial index support (Postgres only) (#1748)
Browse files Browse the repository at this point in the history
* add partial index support (pg only)

* Update exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateIndexTests.kt

Co-authored-by: Jocelyne <[email protected]>

* Update exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt

Co-authored-by: Jocelyne <[email protected]>

* Update exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt

Co-authored-by: Jocelyne <[email protected]>

* Update exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt

Co-authored-by: Jocelyne <[email protected]>

* Update exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt

Co-authored-by: Jocelyne <[email protected]>

* Update exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt

Co-authored-by: Jocelyne <[email protected]>

* Update Table.kt

* Fix typo

* add POSTGRESQLNG to the partial index test

* add javadoc uniqueIndex method

* add apiDump results

---------

Co-authored-by: Aleksandr Shubert <[email protected]>
Co-authored-by: Jocelyne <[email protected]>
  • Loading branch information
3 people authored Jun 14, 2023
1 parent cb216b6 commit 332b216
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 47 deletions.
36 changes: 21 additions & 15 deletions exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -1016,19 +1016,21 @@ public final class org/jetbrains/exposed/sql/InSubQueryOp : org/jetbrains/expose
}

public final class org/jetbrains/exposed/sql/Index : org/jetbrains/exposed/sql/DdlAware {
public fun <init> (Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;Lorg/jetbrains/exposed/sql/Op;)V
public synthetic fun <init> (Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;Lorg/jetbrains/exposed/sql/Op;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/util/List;
public final fun component2 ()Z
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Ljava/lang/String;
public final fun copy (Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Index;
public static synthetic fun copy$default (Lorg/jetbrains/exposed/sql/Index;Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Index;
public final fun component5 ()Lorg/jetbrains/exposed/sql/Op;
public final fun copy (Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;Lorg/jetbrains/exposed/sql/Op;)Lorg/jetbrains/exposed/sql/Index;
public static synthetic fun copy$default (Lorg/jetbrains/exposed/sql/Index;Ljava/util/List;ZLjava/lang/String;Ljava/lang/String;Lorg/jetbrains/exposed/sql/Op;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Index;
public fun createStatement ()Ljava/util/List;
public fun dropStatement ()Ljava/util/List;
public fun equals (Ljava/lang/Object;)Z
public final fun getColumns ()Ljava/util/List;
public final fun getCustomName ()Ljava/lang/String;
public final fun getFilterCondition ()Lorg/jetbrains/exposed/sql/Op;
public final fun getIndexName ()Ljava/lang/String;
public final fun getIndexType ()Ljava/lang/String;
public final fun getTable ()Lorg/jetbrains/exposed/sql/Table;
Expand Down Expand Up @@ -2039,10 +2041,10 @@ public class org/jetbrains/exposed/sql/Table : org/jetbrains/exposed/sql/ColumnS
public fun getPrimaryKey ()Lorg/jetbrains/exposed/sql/Table$PrimaryKey;
public fun getTableName ()Ljava/lang/String;
public fun hashCode ()I
public final fun index (Ljava/lang/String;Z[Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;)V
public final fun index (Ljava/lang/String;Z[Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public final fun index (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;Z)Lorg/jetbrains/exposed/sql/Column;
public final fun index (Z[Lorg/jetbrains/exposed/sql/Column;)V
public static synthetic fun index$default (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Z[Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;ILjava/lang/Object;)V
public static synthetic fun index$default (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;Z[Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun index$default (Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;ZILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
public static synthetic fun index$default (Lorg/jetbrains/exposed/sql/Table;Z[Lorg/jetbrains/exposed/sql/Column;ILjava/lang/Object;)V
public fun innerJoin (Lorg/jetbrains/exposed/sql/ColumnSet;)Lorg/jetbrains/exposed/sql/Join;
Expand Down Expand Up @@ -2085,11 +2087,12 @@ public class org/jetbrains/exposed/sql/Table : org/jetbrains/exposed/sql/ColumnS
public final fun ubyte (Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun uinteger (Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun ulong (Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun uniqueIndex (Ljava/lang/String;[Lorg/jetbrains/exposed/sql/Column;)V
public final fun uniqueIndex (Ljava/lang/String;[Lorg/jetbrains/exposed/sql/Column;Lkotlin/jvm/functions/Function1;)V
public final fun uniqueIndex (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun uniqueIndex ([Lorg/jetbrains/exposed/sql/Column;)V
public static synthetic fun uniqueIndex$default (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;[Lorg/jetbrains/exposed/sql/Column;ILjava/lang/Object;)V
public final fun uniqueIndex ([Lorg/jetbrains/exposed/sql/Column;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun uniqueIndex$default (Lorg/jetbrains/exposed/sql/Table;Ljava/lang/String;[Lorg/jetbrains/exposed/sql/Column;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun uniqueIndex$default (Lorg/jetbrains/exposed/sql/Table;Lorg/jetbrains/exposed/sql/Column;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/exposed/sql/Column;
public static synthetic fun uniqueIndex$default (Lorg/jetbrains/exposed/sql/Table;[Lorg/jetbrains/exposed/sql/Column;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun ushort (Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun uuid (Ljava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
public final fun varchar (Ljava/lang/String;ILjava/lang/String;)Lorg/jetbrains/exposed/sql/Column;
Expand Down Expand Up @@ -2932,7 +2935,7 @@ public abstract interface class org/jetbrains/exposed/sql/vendors/DatabaseDialec
public abstract fun createIndex (Lorg/jetbrains/exposed/sql/Index;)Ljava/lang/String;
public abstract fun createSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
public abstract fun dropDatabase (Ljava/lang/String;)Ljava/lang/String;
public abstract fun dropIndex (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public abstract fun dropIndex (Ljava/lang/String;Ljava/lang/String;ZZ)Ljava/lang/String;
public abstract fun dropSchema (Lorg/jetbrains/exposed/sql/Schema;Z)Ljava/lang/String;
public abstract fun existingIndices ([Lorg/jetbrains/exposed/sql/Table;)Ljava/util/Map;
public abstract fun getDataTypeProvider ()Lorg/jetbrains/exposed/sql/vendors/DataTypeProvider;
Expand Down Expand Up @@ -3202,7 +3205,7 @@ public class org/jetbrains/exposed/sql/vendors/MysqlDialect : org/jetbrains/expo
public static final field Companion Lorg/jetbrains/exposed/sql/vendors/MysqlDialect$Companion;
public fun <init> ()V
public fun createSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
public fun dropIndex (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public fun dropIndex (Ljava/lang/String;Ljava/lang/String;ZZ)Ljava/lang/String;
public fun dropSchema (Lorg/jetbrains/exposed/sql/Schema;Z)Ljava/lang/String;
protected fun fillConstraintCacheForTables (Ljava/util/List;)V
public fun getSupportsCreateSequence ()Z
Expand Down Expand Up @@ -3244,8 +3247,9 @@ public class org/jetbrains/exposed/sql/vendors/PostgreSQLDialect : org/jetbrains
public static final field Companion Lorg/jetbrains/exposed/sql/vendors/PostgreSQLDialect$Companion;
public fun <init> ()V
public fun createDatabase (Ljava/lang/String;)Ljava/lang/String;
protected fun createIndexWithType (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
protected fun createIndexWithType (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public fun dropDatabase (Ljava/lang/String;)Ljava/lang/String;
public fun dropIndex (Ljava/lang/String;Ljava/lang/String;ZZ)Ljava/lang/String;
public fun getRequiresAutoCommitOnCreateDrop ()Z
public fun getSupportsOrderByNullsFirstLast ()Z
public fun isAllowedAsColumnDefault (Lorg/jetbrains/exposed/sql/Expression;)Z
Expand All @@ -3269,7 +3273,7 @@ public class org/jetbrains/exposed/sql/vendors/SQLServerDialect : org/jetbrains/
public static final field Companion Lorg/jetbrains/exposed/sql/vendors/SQLServerDialect$Companion;
public fun <init> ()V
public fun createDatabase (Ljava/lang/String;)Ljava/lang/String;
protected fun createIndexWithType (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
protected fun createIndexWithType (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public fun createSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
public fun dropDatabase (Ljava/lang/String;)Ljava/lang/String;
public fun dropSchema (Lorg/jetbrains/exposed/sql/Schema;Z)Ljava/lang/String;
Expand Down Expand Up @@ -3311,19 +3315,21 @@ public abstract class org/jetbrains/exposed/sql/vendors/VendorDialect : org/jetb
public fun columnConstraints ([Lorg/jetbrains/exposed/sql/Table;)Ljava/util/Map;
public fun createDatabase (Ljava/lang/String;)Ljava/lang/String;
public fun createIndex (Lorg/jetbrains/exposed/sql/Index;)Ljava/lang/String;
protected fun createIndexWithType (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
protected fun createIndexWithType (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public fun createSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
public fun dropDatabase (Ljava/lang/String;)Ljava/lang/String;
public fun dropIndex (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
public fun dropIndex (Ljava/lang/String;Ljava/lang/String;ZZ)Ljava/lang/String;
public fun dropSchema (Lorg/jetbrains/exposed/sql/Schema;Z)Ljava/lang/String;
public fun existingIndices ([Lorg/jetbrains/exposed/sql/Table;)Ljava/util/Map;
protected fun fillConstraintCacheForTables (Ljava/util/List;)V
public final fun filterCondition (Lorg/jetbrains/exposed/sql/Index;)Ljava/lang/String;
public final fun getAllTablesNames ()Ljava/util/List;
protected final fun getColumnConstraintsCache ()Ljava/util/Map;
public fun getDataTypeProvider ()Lorg/jetbrains/exposed/sql/vendors/DataTypeProvider;
public fun getDatabase ()Ljava/lang/String;
public fun getDefaultReferenceOption ()Lorg/jetbrains/exposed/sql/ReferenceOption;
public fun getFunctionProvider ()Lorg/jetbrains/exposed/sql/vendors/FunctionProvider;
protected final fun getIdentifierManager ()Lorg/jetbrains/exposed/sql/statements/api/IdentifierManagerApi;
public fun getLikePatternSpecialChars ()Ljava/util/Map;
public fun getName ()Ljava/lang/String;
public fun getNeedsQuotesWhenSymbolsInNames ()Z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ data class CheckConstraint(
}
}

typealias FilterCondition = (SqlExpressionBuilder.() -> Op<Boolean>)?

/**
* Represents an index.
*/
Expand All @@ -238,7 +240,9 @@ data class Index(
/** Optional custom name for the index. */
val customName: String? = null,
/** Optional custom index type (e.g, BTREE or HASH) */
val indexType: String? = null
val indexType: String? = null,
/** Partial index filter condition */
val filterCondition: Op<Boolean>? = null
) : DdlAware {
/** Table where the index is defined. */
val table: Table
Expand All @@ -263,7 +267,7 @@ data class Index(

override fun createStatement(): List<String> = listOf(currentDialect.createIndex(this))
override fun modifyStatement(): List<String> = dropStatement() + createStatement()
override fun dropStatement(): List<String> = listOf(currentDialect.dropIndex(table.nameInDatabaseCase(), indexName))
override fun dropStatement(): List<String> = listOf(currentDialect.dropIndex(table.nameInDatabaseCase(), indexName, unique, filterCondition != null))

/** Returns `true` if the [other] index has the same columns and uniqueness as this index, but a different name, `false` otherwise */
fun onlyNameDiffer(other: Index): Boolean = indexName != other.indexName && columns == other.columns && unique == other.unique
Expand Down
29 changes: 24 additions & 5 deletions exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,15 @@ class Join(
onColumn != null && otherColumn != null -> {
join(otherTable, joinType, onColumn, otherColumn, additionalConstraint)
}

onColumn != null || otherColumn != null -> {
error("Can't prepare join on $table and $otherTable when only column from a one side provided.")
}

additionalConstraint != null -> {
join(otherTable, joinType, emptyList(), additionalConstraint)
}

else -> {
implicitJoin(otherTable, joinType)
}
Expand Down Expand Up @@ -250,10 +253,12 @@ class Join(
joinType != JoinType.CROSS && fkKeys.isEmpty() -> {
error("Cannot join with $otherTable as there is no matching primary key/foreign key pair and constraint missing")
}

fkKeys.any { it.second.size > 1 } -> {
val references = fkKeys.joinToString(" & ") { "${it.first} -> ${it.second.joinToString()}" }
error("Cannot join with $otherTable as there is multiple primary key <-> foreign key references.\n$references")
}

else -> {
val cond = fkKeys.filter { it.second.size == 1 }.map { it.first to it.second.single() }
join(otherTable, joinType, cond, null)
Expand Down Expand Up @@ -337,6 +342,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
}

internal val tableNameWithoutScheme: String get() = tableName.substringAfter(".")

// Table name may contain quotes, remove those before appending
internal val tableNameWithoutSchemeSanitized: String get() = tableNameWithoutScheme.replace("\"", "").replace("'", "")

Expand Down Expand Up @@ -943,9 +949,16 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
* @param columns Columns that compose the index.
* @param isUnique Whether the index is unique or not.
* @param indexType A custom index type (e.g., "BTREE" or "HASH").
* @param filterCondition Index filtering conditions (also known as "partial index") declaration.
*/
fun index(customIndexName: String? = null, isUnique: Boolean = false, vararg columns: Column<*>, indexType: String? = null) {
_indices.add(Index(columns.toList(), isUnique, customIndexName, indexType = indexType))
fun index(
customIndexName: String? = null,
isUnique: Boolean = false,
vararg columns: Column<*>,
indexType: String? = null,
filterCondition: FilterCondition = null
) {
_indices.add(Index(columns.toList(), isUnique, customIndexName, indexType = indexType, filterCondition?.invoke(SqlExpressionBuilder)))
}

/**
Expand All @@ -962,22 +975,27 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
*
* @param customIndexName Name of the index.
*/
fun <T> Column<T>.uniqueIndex(customIndexName: String? = null): Column<T> = index(customIndexName, true)
fun <T> Column<T>.uniqueIndex(customIndexName: String? = null): Column<T> =
index(customIndexName, true)

/**
* Creates a unique index.
*
* @param columns Columns that compose the index.
* @param filterCondition Index filtering conditions (also known as "partial index") declaration.
*/
fun uniqueIndex(vararg columns: Column<*>): Unit = index(null, true, *columns)
fun uniqueIndex(vararg columns: Column<*>, filterCondition: FilterCondition = null): Unit =
index(null, true, *columns, filterCondition = filterCondition)

/**
* Creates a unique index.
*
* @param customIndexName Name of the index.
* @param columns Columns that compose the index.
* @param filterCondition Index filtering conditions (also known as "partial index") declaration.
*/
fun uniqueIndex(customIndexName: String? = null, vararg columns: Column<*>): Unit = index(customIndexName, true, *columns)
fun uniqueIndex(customIndexName: String? = null, vararg columns: Column<*>, filterCondition: FilterCondition = null): Unit =
index(customIndexName, true, *columns, filterCondition = filterCondition)

/**
* Creates a composite foreign key.
Expand Down Expand Up @@ -1074,6 +1092,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware {
is ColumnType -> {
this.withColumnType(AutoIncColumnType(columnType, idSeqName, "${tableName}_${name}_seq"))
}

else -> error("Unsupported column type for auto-increment $columnType")
}

Expand Down
Loading

0 comments on commit 332b216

Please sign in to comment.