Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add partial index support (Postgres only) #1748

Merged
merged 14 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 =
bog-walk marked this conversation as resolved.
Show resolved Hide resolved
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 =
bog-walk marked this conversation as resolved.
Show resolved Hide resolved
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