diff --git a/documentation-website/Writerside/topics/Breaking-Changes.md b/documentation-website/Writerside/topics/Breaking-Changes.md index 437946f1aa..6b9f3bd657 100644 --- a/documentation-website/Writerside/topics/Breaking-Changes.md +++ b/documentation-website/Writerside/topics/Breaking-Changes.md @@ -5,8 +5,11 @@ * Migration of kotlinx-datetime from version 6 to version 7. The only package affected is `exposed-kotlin-datetime`. `KotlinInstantColumnType`, and `Table.timestamp(name: String)` are parametrized with `kotlin.time.Instant` class now. If you need to use `kotlinx.datetime.Instant` with Exposed, you have to replace usages of `KotlinInstantColumnType` and `Table.timestamp(name: String)` with `XKotlinInstantColumnType` and `Table.xTimestamp(name: String)` respectively, - also the `CurrentTimestamp` constant should be changed with `XCurrentTimestamp`, `CustomTimeStampFunction` with `XCustomTimeStampFunction`. - + also the `CurrentTimestamp` constant should be changed with `XCurrentTimestamp`, `CustomTimeStampFunction` with `XCustomTimeStampFunction`. +* The newly introduced `IStatementBuilder` interface has been renamed and deprecated in favor of `StatementBuilder`, + which contains all the original and unchanged methods. It's associated function `buildStatement()` no longer accepts the + deprecated interface as the receiver of its `body` parameter; the parameter expects the new `StatementBuilder` instead. + The same parameter type change applies to the function `explain()`. ## 1.0.0-beta-4 diff --git a/exposed-core/api/exposed-core.api b/exposed-core/api/exposed-core.api index b9b5744942..0ef0a79a6f 100644 --- a/exposed-core/api/exposed-core.api +++ b/exposed-core/api/exposed-core.api @@ -2938,10 +2938,6 @@ public final class org/jetbrains/exposed/v1/core/statements/IStatementBuilder$De public static synthetic fun upsertReturning$default (Lorg/jetbrains/exposed/v1/core/statements/IStatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Column;Ljava/util/List;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; } -public final class org/jetbrains/exposed/v1/core/statements/IStatementBuilderKt { - public static final fun buildStatement (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; -} - public class org/jetbrains/exposed/v1/core/statements/InsertSelectStatement : org/jetbrains/exposed/v1/core/statements/Statement { public fun (Ljava/util/List;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Z)V public synthetic fun (Ljava/util/List;Lorg/jetbrains/exposed/v1/core/AbstractQuery;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -3088,6 +3084,77 @@ public abstract class org/jetbrains/exposed/v1/core/statements/Statement { public static synthetic fun prepareSQL$default (Lorg/jetbrains/exposed/v1/core/statements/Statement;Lorg/jetbrains/exposed/v1/core/Transaction;ZILjava/lang/Object;)Ljava/lang/String; } +public abstract interface class org/jetbrains/exposed/v1/core/statements/StatementBuilder { + public abstract fun batchInsert (Lorg/jetbrains/exposed/v1/core/Table;ZZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/BatchInsertStatement; + public abstract fun batchReplace (Lorg/jetbrains/exposed/v1/core/Table;ZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/BatchReplaceStatement; + public abstract fun batchUpsert (Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Z[Lorg/jetbrains/exposed/v1/core/Column;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/BatchUpsertStatement; + public abstract fun delete (Lorg/jetbrains/exposed/v1/core/Join;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Table;ZLjava/lang/Integer;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public abstract fun deleteAll (Lorg/jetbrains/exposed/v1/core/Table;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public abstract fun deleteIgnoreWhere (Lorg/jetbrains/exposed/v1/core/Table;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public abstract fun deleteReturning (Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public abstract fun deleteWhere (Lorg/jetbrains/exposed/v1/core/Table;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public abstract fun insert (Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/InsertStatement; + public abstract fun insert (Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;)Lorg/jetbrains/exposed/v1/core/statements/InsertSelectStatement; + public abstract fun insertIgnore (Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/InsertStatement; + public abstract fun insertIgnore (Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;)Lorg/jetbrains/exposed/v1/core/statements/InsertSelectStatement; + public abstract fun insertReturning (Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;ZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public abstract fun mergeFrom (Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/QueryAlias;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/MergeSelectStatement; + public abstract fun mergeFrom (Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/MergeTableStatement; + public abstract fun replace (Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReplaceStatement; + public abstract fun replace (Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;)Lorg/jetbrains/exposed/v1/core/statements/ReplaceSelectStatement; + public abstract fun update (Lorg/jetbrains/exposed/v1/core/Join;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/UpdateStatement; + public abstract fun update (Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/UpdateStatement; + public abstract fun updateReturning (Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public abstract fun upsert (Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Column;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/UpsertStatement; + public abstract fun upsertReturning (Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Column;Ljava/util/List;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; +} + +public final class org/jetbrains/exposed/v1/core/statements/StatementBuilder$DefaultImpls { + public static fun batchInsert (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;ZZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/BatchInsertStatement; + public static synthetic fun batchInsert$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;ZZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/BatchInsertStatement; + public static fun batchReplace (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;ZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/BatchReplaceStatement; + public static synthetic fun batchReplace$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/BatchReplaceStatement; + public static fun batchUpsert (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Z[Lorg/jetbrains/exposed/v1/core/Column;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/BatchUpsertStatement; + public static synthetic fun batchUpsert$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Z[Lorg/jetbrains/exposed/v1/core/Column;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/BatchUpsertStatement; + public static fun delete (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Join;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Table;ZLjava/lang/Integer;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public static synthetic fun delete$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Join;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Table;ZLjava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public static fun deleteAll (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public static fun deleteIgnoreWhere (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public static synthetic fun deleteIgnoreWhere$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public static fun deleteReturning (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public static synthetic fun deleteReturning$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public static fun deleteWhere (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public static synthetic fun deleteWhere$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement; + public static fun insert (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/InsertStatement; + public static fun insert (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;)Lorg/jetbrains/exposed/v1/core/statements/InsertSelectStatement; + public static synthetic fun insert$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/InsertSelectStatement; + public static fun insertIgnore (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/InsertStatement; + public static fun insertIgnore (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;)Lorg/jetbrains/exposed/v1/core/statements/InsertSelectStatement; + public static synthetic fun insertIgnore$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/InsertSelectStatement; + public static fun insertReturning (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;ZLkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public static synthetic fun insertReturning$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public static fun mergeFrom (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/QueryAlias;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/MergeSelectStatement; + public static fun mergeFrom (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/MergeTableStatement; + public static synthetic fun mergeFrom$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/MergeTableStatement; + public static fun replace (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReplaceStatement; + public static fun replace (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;)Lorg/jetbrains/exposed/v1/core/statements/ReplaceSelectStatement; + public static synthetic fun replace$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lorg/jetbrains/exposed/v1/core/AbstractQuery;Ljava/util/List;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/ReplaceSelectStatement; + public static fun update (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Join;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/exposed/v1/core/statements/UpdateStatement; + public static fun update (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/UpdateStatement; + public static synthetic fun update$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Join;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/UpdateStatement; + public static synthetic fun update$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Lkotlin/jvm/functions/Function1;Ljava/lang/Integer;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/UpdateStatement; + public static fun updateReturning (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public static synthetic fun updateReturning$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public static fun upsert (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Column;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/UpsertStatement; + public static synthetic fun upsert$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Column;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/UpsertStatement; + public static fun upsertReturning (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Column;Ljava/util/List;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; + public static synthetic fun upsertReturning$default (Lorg/jetbrains/exposed/v1/core/statements/StatementBuilder;Lorg/jetbrains/exposed/v1/core/Table;[Lorg/jetbrains/exposed/v1/core/Column;Ljava/util/List;Lkotlin/jvm/functions/Function2;Ljava/util/List;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lorg/jetbrains/exposed/v1/core/statements/ReturningStatement; +} + +public final class org/jetbrains/exposed/v1/core/statements/StatementBuilderKt { + public static final fun buildStatement (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +} + public final class org/jetbrains/exposed/v1/core/statements/StatementContext { public fun (Lorg/jetbrains/exposed/v1/core/statements/Statement;Ljava/lang/Iterable;)V public final fun getArgs ()Ljava/lang/Iterable; diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/DeleteStatement.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/DeleteStatement.kt index 7dcb9e0740..2847edd22d 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/DeleteStatement.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/DeleteStatement.kt @@ -90,12 +90,8 @@ open class DeleteStatement( Replace directly with a table extension function: `table.deleteWhere(limit) { op }` OR `table.deleteIgnoreWhere(limit) { op }` - Or pass the expected statement to an instance of Executable: - For JDBC: - `DeleteBlockingExecutable(buildStatement { table.deleteWhere(limit, { op }) }).execute(transaction) ?: 0` - - FOR R2DBC: - `DeleteSuspendExecutable(buildStatement { table.deleteWhere(limit, { op }) }).execute(transaction) ?: 0` + Or convert the expected statement to an instance of Executable: + `buildStatement { table.deleteWhere(limit, { op }) }.toExecutable().execute(transaction) ?: 0` """, level = DeprecationLevel.ERROR ) @@ -105,13 +101,8 @@ open class DeleteStatement( @Deprecated( message = """ Statement execution has been removed from exposed-core. - Replace directly with a table extension function or pass the expected statement to an instance of Executable: - - For JDBC: - `DeleteBlockingExecutable(buildStatement { table.deleteAll() }).execute(transaction) ?: 0` - - FOR R2DBC: - `DeleteSuspendExecutable(buildStatement { table.deleteAll() }).execute(transaction) ?: 0` + Replace directly with a table extension function or convert the expected statement to an instance of Executable: + `buildStatement { table.deleteAll() }.toExecutable().execute(transaction) ?: 0` """, level = DeprecationLevel.ERROR ) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/IStatementBuilder.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/IStatementBuilder.kt index 11a5e63a9a..6fd3b7cf1b 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/IStatementBuilder.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/IStatementBuilder.kt @@ -6,6 +6,11 @@ import org.jetbrains.exposed.v1.core.vendors.currentDialect /** Represents all the DSL methods available when building SQL statements. */ @Suppress("TooManyFunctions") +@Deprecated( + message = "This interface has been renamed and will be removed in future releases.", + replaceWith = ReplaceWith("StatementBuilder", "org.jetbrains.exposed.v1.core.statements.StatementBuilder"), + level = DeprecationLevel.WARNING +) interface IStatementBuilder { /** * Represents the SQL statement that deletes only rows in a table that match the provided [op]. @@ -121,9 +126,9 @@ interface IStatementBuilder { fun T.insert( selectQuery: AbstractQuery<*>, columns: List>? = null - ): org.jetbrains.exposed.v1.core.statements.InsertSelectStatement { + ): InsertSelectStatement { val columnsToReplace = columns ?: this.columns.filter { it.isValidIfAutoIncrement() } - return org.jetbrains.exposed.v1.core.statements.InsertSelectStatement(columnsToReplace, selectQuery, false) + return InsertSelectStatement(columnsToReplace, selectQuery, false) } /** @@ -140,9 +145,9 @@ interface IStatementBuilder { fun T.insertIgnore( selectQuery: AbstractQuery<*>, columns: List>? = null - ): org.jetbrains.exposed.v1.core.statements.InsertSelectStatement { + ): InsertSelectStatement { val columnsToReplace = columns ?: this.columns.filter { it.isValidIfAutoIncrement() } - return org.jetbrains.exposed.v1.core.statements.InsertSelectStatement(columnsToReplace, selectQuery, true) + return InsertSelectStatement(columnsToReplace, selectQuery, true) } /** @@ -429,12 +434,3 @@ interface IStatementBuilder { private fun Column<*>.isValidIfAutoIncrement(): Boolean = !columnType.isAutoInc || autoIncColumnType?.nextValExpression != null } - -/** Builder object for creating SQL statements. Made it private to avoid imports clash */ -@Suppress("ForbiddenComment") -// TODO: StatementBuilder -> StatementBuilderImpl, and IStatementBuilder -> StatementBuilder -private object StatementBuilder : IStatementBuilder - -// TODO: add documentation for building statements without execution, like in the old DSL -@Suppress("ForbiddenComment", "AnnotationSpacing") -fun buildStatement(body: IStatementBuilder.() -> S): S = body(StatementBuilder) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/StatementBuilder.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/StatementBuilder.kt new file mode 100644 index 0000000000..cbfbc09168 --- /dev/null +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/statements/StatementBuilder.kt @@ -0,0 +1,449 @@ +package org.jetbrains.exposed.v1.core.statements + +import org.jetbrains.exposed.v1.core.* +import org.jetbrains.exposed.v1.core.vendors.SQLServerDialect +import org.jetbrains.exposed.v1.core.vendors.currentDialect + +/** Represents all the DSL methods available when building SQL statements. */ +@Suppress("TooManyFunctions") +interface StatementBuilder { + /** + * Represents the SQL statement that deletes only rows in a table that match the provided [op]. + * + * @param limit Maximum number of rows to delete. + * @param op Condition that determines which rows to delete. + * @return A [DeleteStatement] that can be executed. + */ + fun T.deleteWhere( + limit: Int? = null, + op: T.(ISqlExpressionBuilder) -> Op + ): DeleteStatement { + return DeleteStatement(this, op(SqlExpressionBuilder), false, limit, emptyList()) + } + + /** + * Represents the SQL statement that deletes only rows in a table that match the provided [op], while ignoring any + * possible errors that occur during the process. + * + * **Note:** `DELETE IGNORE` is not supported by all vendors. Please check the documentation. + * + * @param limit Maximum number of rows to delete. + * @param op Condition that determines which rows to delete. + * @return A [DeleteStatement] that can be executed. + */ + fun T.deleteIgnoreWhere( + limit: Int? = null, + op: T.(ISqlExpressionBuilder) -> Op + ): DeleteStatement { + return DeleteStatement(this, op(SqlExpressionBuilder), true, limit, emptyList()) + } + + /** + * Represents the SQL statement that deletes all rows in a table. + * + * @return A [DeleteStatement] that can be executed. + */ + fun T.deleteAll(): DeleteStatement = DeleteStatement(this) + + /** + * Represents the SQL statement that deletes rows in a table and returns specified data from the deleted rows. + * + * @param returning Columns and expressions to include in the returned data. This defaults to all columns in the table. + * @param where Condition that determines which rows to delete. If left as `null`, all rows in the table will be deleted. + * @return A [ReturningStatement] that can be executed once iterated over. + */ + fun T.deleteReturning( + returning: List> = columns, + where: (SqlExpressionBuilder.() -> Op)? = null + ): ReturningStatement { + val delete = DeleteStatement(this, where?.let { SqlExpressionBuilder.it() }, false, null) + return ReturningStatement(this, returning, delete) + } + + /** + * Represents the SQL statement that deletes rows from a table in a join relation. + * + * @param targetTable The specific table from this join relation to delete rows from. + * @param targetTables (Optional) Other tables from this join relation to delete rows from. + * **Note** Targeting multiple tables for deletion is not supported by all vendors. Please check the documentation. + * @param ignore Whether to ignore any possible errors that occur when deleting rows. + * **Note** [ignore] is not supported by all vendors. Please check the documentation. + * @param limit Maximum number of rows to delete. + * **Note** [limit] is not supported by all vendors. Please check the documentation. + * @param where Condition that determines which rows to delete. If left as `null`, all rows will be deleted. + * @return A [DeleteStatement] that can be executed. + */ + fun Join.delete( + targetTable: Table, + vararg targetTables: Table, + ignore: Boolean = false, + limit: Int? = null, + where: (SqlExpressionBuilder.() -> Op)? = null + ): DeleteStatement { + val targets = listOf(targetTable) + targetTables + return DeleteStatement(this, where?.let { SqlExpressionBuilder.it() }, ignore, limit, targets) + } + + /** + * Represents the SQL statement that inserts a new row into a table. + * + * @return Am [InsertStatement] that can be executed. + */ + fun T.insert( + body: T.(InsertStatement) -> Unit + ): InsertStatement { + return InsertStatement(this).apply { body(this) } + } + + /** + * Represents the SQL statement that inserts a new row into a table, while ignoring any possible errors that occur + * during the process. + * + * For example, if the new row would violate a unique constraint, its insertion would be ignored. + * **Note:** `INSERT IGNORE` is not supported by all vendors. Please check the documentation. + * + * @return An [InsertStatement] that can be executed. + */ + fun T.insertIgnore( + body: T.(UpdateBuilder<*>) -> Unit + ): InsertStatement { + return InsertStatement(this, true).apply { body(this) } + } + + /** + * Represents the SQL statement that uses data retrieved from a [selectQuery] to insert new rows into a table. + * + * @param selectQuery Source `SELECT` query that provides the values to insert. + * @param columns Columns to insert the values into. This defaults to all columns in the table that are not + * auto-increment columns without a valid sequence to generate new values. + * @return An [InsertSelectStatement] that can be executed. + */ + fun T.insert( + selectQuery: AbstractQuery<*>, + columns: List>? = null + ): InsertSelectStatement { + val columnsToReplace = columns ?: this.columns.filter { it.isValidIfAutoIncrement() } + return InsertSelectStatement(columnsToReplace, selectQuery, false) + } + + /** + * Represents the SQL statement that uses data retrieved from a [selectQuery] to insert new rows into a table, + * while ignoring any possible errors that occur during the process. + * + * **Note:** `INSERT IGNORE` is not supported by all vendors. Please check the documentation. + * + * @param selectQuery Source `SELECT` query that provides the values to insert. + * @param columns Columns to insert the values into. This defaults to all columns in the table that are not + * auto-increment columns without a valid sequence to generate new values. + * @return An [InsertSelectStatement] that can be executed. + */ + fun T.insertIgnore( + selectQuery: AbstractQuery<*>, + columns: List>? = null + ): InsertSelectStatement { + val columnsToReplace = columns ?: this.columns.filter { it.isValidIfAutoIncrement() } + return InsertSelectStatement(columnsToReplace, selectQuery, true) + } + + /** + * Represents the SQL statement that inserts new rows into a table and returns specified data from the inserted rows. + * + * @param returning Columns and expressions to include in the returned data. This defaults to all columns in the table. + * @param ignoreErrors Whether to ignore any possible errors that occur during the process. + * Note `INSERT IGNORE` is not supported by all vendors. Please check the documentation. + * @return A [ReturningStatement] that can be executed once iterated over. + */ + fun T.insertReturning( + returning: List> = columns, + ignoreErrors: Boolean = false, + body: T.(InsertStatement) -> Unit + ): ReturningStatement { + val insert = InsertStatement(this, ignoreErrors) + body(insert) + return ReturningStatement(this, returning, insert) + } + + /** + * Represents the SQL statement that batch inserts new rows into a table. + * + * @param ignoreErrors Whether to ignore errors or not. + * **Note** [ignoreErrors] is not supported by all vendors. Please check the documentation. + * @param shouldReturnGeneratedValues Specifies whether newly generated values (for example, auto-incremented IDs) + * should be returned. + * @return A [BatchInsertStatement] that can be executed. + */ + fun T.batchInsert( + ignoreErrors: Boolean = false, + shouldReturnGeneratedValues: Boolean = true, + body: BatchInsertStatement.(E) -> Unit + ): BatchInsertStatement { + return if (currentDialect is SQLServerDialect && autoIncColumn != null) { + SQLServerBatchInsertStatement(this, ignoreErrors, shouldReturnGeneratedValues) + } else { + BatchInsertStatement(this, ignoreErrors, shouldReturnGeneratedValues) + } + } + + /** + * Represents the SQL statement that either inserts a new row into a table, or, if insertion would violate a unique constraint, + * first deletes the existing row before inserting a new row. + * + * **Note:** This operation is not supported by all vendors, please check the documentation. + * + * @return A [ReplaceStatement] that can be executed. + */ + fun T.replace( + body: T.(UpdateBuilder<*>) -> Unit + ): ReplaceStatement { + return ReplaceStatement(this).apply { body(this) } + } + + /** + * Represents the SQL statement that uses data retrieved from a [selectQuery] to either insert a new row into a table, + * or, if insertion would violate a unique constraint, first delete the existing row before inserting a new row. + * + * **Note:** This operation is not supported by all vendors, please check the documentation. + * + * @param selectQuery Source `SELECT` query that provides the values to insert. + * @param columns Columns to either insert values into or delete values from then insert into. This defaults to all + * columns in the table that are not auto-increment columns without a valid sequence to generate new values. + * @return A [ReplaceSelectStatement] that can be executed. + */ + fun T.replace( + selectQuery: AbstractQuery<*>, + columns: List>? = null + ): ReplaceSelectStatement { + val columnsToReplace = columns ?: this.columns.filter { it.isValidIfAutoIncrement() } + return ReplaceSelectStatement(columnsToReplace, selectQuery) + } + + /** + * Represents the SQL statement that either batch inserts new rows into a table, or, if insertions violate unique constraints, + * first deletes the existing rows before inserting new rows. + * + * **Note:** This operation is not supported by all vendors, please check the documentation. + * + * @param shouldReturnGeneratedValues Specifies whether newly generated values (for example, auto-incremented IDs) + * should be returned. + * @return A [BatchReplaceStatement] that can be executed. + */ + fun T.batchReplace( + shouldReturnGeneratedValues: Boolean = true, + body: BatchReplaceStatement.(E) -> Unit + ): BatchReplaceStatement { + return BatchReplaceStatement(this, shouldReturnGeneratedValues) + } + + /** + * Represents the SQL statement that updates rows of a table. + * + * @param where Condition that determines which rows to update. If left `null`, all columns will be updated. + * @param limit Maximum number of rows to update. + * @return An [UpdateStatement] that can be executed. + */ + fun T.update( + where: (SqlExpressionBuilder.() -> Op)? = null, + limit: Int? = null, + body: T.(UpdateStatement) -> Unit + ): UpdateStatement { + return UpdateStatement(this, limit, where?.let { SqlExpressionBuilder.it() }).apply { body(this) } + } + + /** + * Represents the SQL statement that updates rows of a join relation. + * + * @param where Condition that determines which rows to update. If left `null`, all columns will be updated. + * @param limit Maximum number of rows to update. + * @return An [UpdateStatement] that can be executed. + */ + fun Join.update( + where: (SqlExpressionBuilder.() -> Op)? = null, + limit: Int? = null, + body: (UpdateStatement) -> Unit + ): UpdateStatement { + return UpdateStatement(this, limit, where?.let { SqlExpressionBuilder.it() }).apply(body) + } + + /** + * Represents the SQL statement that updates rows of a table and returns specified data from the updated rows. + * + * @param returning Columns and expressions to include in the returned data. This defaults to all columns in the table. + * @param where Condition that determines which rows to update. If left `null`, all columns will be updated. + * @return A [ReturningStatement] that can be executed once iterated over. + */ + fun T.updateReturning( + returning: List> = columns, + where: (SqlExpressionBuilder.() -> Op)? = null, + body: T.(UpdateStatement) -> Unit + ): ReturningStatement { + val update = UpdateStatement(this, null, where?.let { SqlExpressionBuilder.it() }) + body(update) + return ReturningStatement(this, returning, update) + } + + /** + * Represents the SQL statement that either inserts a new row into a table, or updates the existing row if insertion would violate a unique constraint. + * + * **Note:** Vendors that do not support this operation directly implement the standard MERGE USING command. + * + * @param keys (optional) Columns to include in the condition that determines a unique constraint match. + * If no columns are provided, primary keys will be used. If the table does not have any primary keys, the first unique index will be attempted. + * @param onUpdate Lambda block with an [UpdateStatement] as its argument, allowing values to be assigned to the UPDATE clause. + * To specify manually that the insert value should be used when updating a column, for example within an expression + * or function, invoke `insertValue()` with the desired column as the function argument. + * If left `null`, all columns will be updated with the values provided for the insert. + * @param onUpdateExclude List of specific columns to exclude from updating. + * If left `null`, all columns will be updated with the values provided for the insert. + * @param where Condition that determines which rows to update, if a unique violation is found. + * @return An [UpsertStatement] that can be executed. + */ + fun T.upsert( + vararg keys: Column<*>, + onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null, + onUpdateExclude: List>? = null, + where: (UpsertSqlExpressionBuilder.() -> Op)? = null, + body: T.(UpsertStatement) -> Unit + ): UpsertStatement { + return UpsertStatement( + table = this, + keys = keys, + onUpdateExclude = onUpdateExclude, + where = where?.let { UpsertSqlExpressionBuilder.it() } + ).apply { + onUpdate?.let { storeUpdateValues(it) } + body(this) + } + } + + /** + * Represents the SQL statement that either inserts a new row into a table, or updates the existing row if insertion would + * violate a unique constraint, and also returns specified data from the modified rows. + * + * @param keys (optional) Columns to include in the condition that determines a unique constraint match. If no columns are + * provided, primary keys will be used. If the table does not have any primary keys, the first unique index will be attempted. + * @param returning Columns and expressions to include in the returned data. This defaults to all columns in the table. + * @param onUpdate Lambda block with an [UpdateStatement] as its argument, allowing values to be assigned to the UPDATE clause. + * To specify manually that the insert value should be used when updating a column, for example within an expression + * or function, invoke `insertValue()` with the desired column as the function argument. + * If left null, all columns will be updated with the values provided for the insert. + * @param onUpdateExclude List of specific columns to exclude from updating. + * If left null, all columns will be updated with the values provided for the insert. + * @param where Condition that determines which rows to update, if a unique violation is found. + * @return A [ReturningStatement] that can be executed once iterated over. + */ + fun T.upsertReturning( + vararg keys: Column<*>, + returning: List> = columns, + onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null, + onUpdateExclude: List>? = null, + where: (SqlExpressionBuilder.() -> Op)? = null, + body: T.(UpsertStatement) -> Unit + ): ReturningStatement { + val upsert = UpsertStatement( + table = this, + keys = keys, + onUpdateExclude = onUpdateExclude, + where = where?.let { SqlExpressionBuilder.it() } + ) + onUpdate?.let { upsert.storeUpdateValues(it) } + body(upsert) + return ReturningStatement(this, returning, upsert) + } + + /** + * Represents the SQL statement that either batch inserts new rows into a table, or updates the existing rows if insertions violate unique constraints. + * + * @param keys (optional) Columns to include in the condition that determines a unique constraint match. If no columns are provided, + * primary keys will be used. If the table does not have any primary keys, the first unique index will be attempted. + * @param onUpdate Lambda block with an [UpdateStatement] as its argument, allowing values to be assigned to the UPDATE clause. + * To specify manually that the insert value should be used when updating a column, for example within an expression + * or function, invoke `insertValue()` with the desired column as the function argument. + * If left null, all columns will be updated with the values provided for the insert. + * @param onUpdateExclude List of specific columns to exclude from updating. + * If left null, all columns will be updated with the values provided for the insert. + * @param where Condition that determines which rows to update, if a unique violation is found. + * @param shouldReturnGeneratedValues Specifies whether newly generated values (for example, auto-incremented IDs) + * should be returned. + * @return A [BatchUpsertStatement] that can be executed. + */ + @Suppress("LongParameterList") + fun T.batchUpsert( + onUpdateList: List, Any?>>? = null, + onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null, + onUpdateExclude: List>? = null, + where: (UpsertSqlExpressionBuilder.() -> Op)? = null, + shouldReturnGeneratedValues: Boolean = true, + vararg keys: Column<*>, + body: BatchUpsertStatement.(E) -> Unit + ): BatchUpsertStatement { + return BatchUpsertStatement( + table = this, + keys = keys, + onUpdateExclude = onUpdateExclude, + where = where?.let { UpsertSqlExpressionBuilder.it() }, + shouldReturnGeneratedValues = shouldReturnGeneratedValues + ).apply { + onUpdate?.let { storeUpdateValues(it) } + ?: onUpdateList?.let { updateValues.putAll(it) } + } + } + + /** + * Represents the SQL statement that inserts, updates, or deletes records in a target table based on + * a comparison with a source table. + * + * @param source An instance of the source table. + * @param on A lambda function with [SqlExpressionBuilder] as its receiver that should return an `Op` condition. + * This condition is used to match records between the source and target tables. + * @param body A lambda where [MergeTableStatement] can be configured with specific actions to perform + * when records are matched or not matched. + * @return A [MergeTableStatement] that can be executed. + */ + fun D.mergeFrom( + source: S, + on: (SqlExpressionBuilder.() -> Op)? = null, + body: MergeTableStatement.() -> Unit + ): MergeTableStatement { + return MergeTableStatement(this, source, on = on?.invoke(SqlExpressionBuilder)).apply(body) + } + + /** + * Represents the SQL statement that inserts, updates, or deletes records in a target table based on + * a comparison with a select query source. + * + * @param selectQuery The aliased query for a complex subquery to be used as the source. + * @param on A lambda with a receiver of type [SqlExpressionBuilder] that returns an `Op` condition. + * This condition is used to match records between the source query and the target table. + * @param body A lambda where [MergeSelectStatement] can be configured with specific actions to perform + * when records are matched or not matched. + * @return A [MergeSelectStatement] which represents the MERGE operation with the configured actions. + */ + fun T.mergeFrom( + selectQuery: QueryAlias, + on: SqlExpressionBuilder.() -> Op, + body: MergeSelectStatement.() -> Unit + ): MergeSelectStatement { + return MergeSelectStatement(this, selectQuery, SqlExpressionBuilder.on()).apply(body) + } + + private fun Column<*>.isValidIfAutoIncrement(): Boolean = + !columnType.isAutoInc || autoIncColumnType?.nextValExpression != null +} + +/** Builder object for creating SQL statements. Made it private to avoid imports clash */ +private object StatementBuilderImpl : StatementBuilder + +/** + * Builder block for generating instances representing SQl statements, without the statement being sent to the database + * for execution. + * + * ```kotlin + * val insertTaskStatement = buildStatement { + * Tasks.insert { + * it[title] = "Follow Exposed tutorial" + * it[isComplete] = false + * } + * } + * ``` + */ +fun buildStatement(body: StatementBuilder.() -> S): S = body(StatementBuilderImpl) diff --git a/exposed-jdbc/api/exposed-jdbc.api b/exposed-jdbc/api/exposed-jdbc.api index 561d31147b..c986d38253 100644 --- a/exposed-jdbc/api/exposed-jdbc.api +++ b/exposed-jdbc/api/exposed-jdbc.api @@ -465,6 +465,10 @@ public final class org/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable$D public static fun prepared (Lorg/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable;Lorg/jetbrains/exposed/v1/jdbc/JdbcTransaction;Ljava/lang/String;)Lorg/jetbrains/exposed/v1/jdbc/statements/api/JdbcPreparedStatementApi; } +public final class org/jetbrains/exposed/v1/jdbc/statements/BlockingExecutableKt { + public static final fun toExecutable (Lorg/jetbrains/exposed/v1/core/statements/Statement;)Lorg/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable; +} + public class org/jetbrains/exposed/v1/jdbc/statements/DeleteBlockingExecutable : org/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable { public fun (Lorg/jetbrains/exposed/v1/core/statements/DeleteStatement;)V public fun execute (Lorg/jetbrains/exposed/v1/jdbc/JdbcTransaction;)Ljava/lang/Integer; diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/ExplainBlockingExecutable.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/ExplainBlockingExecutable.kt index 6b12adf8eb..cf84dff14f 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/ExplainBlockingExecutable.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/ExplainBlockingExecutable.kt @@ -2,8 +2,8 @@ package org.jetbrains.exposed.v1.jdbc import org.jetbrains.exposed.v1.core.ExplainQuery import org.jetbrains.exposed.v1.core.ExplainResultRow -import org.jetbrains.exposed.v1.core.statements.IStatementBuilder import org.jetbrains.exposed.v1.core.statements.Statement +import org.jetbrains.exposed.v1.core.statements.StatementBuilder import org.jetbrains.exposed.v1.core.statements.api.ResultApi import org.jetbrains.exposed.v1.core.statements.buildStatement import org.jetbrains.exposed.v1.jdbc.statements.BlockingExecutable @@ -56,7 +56,7 @@ open class ExplainBlockingExecutable( fun JdbcTransaction.explain( analyze: Boolean = false, options: String? = null, - body: IStatementBuilder.() -> Statement<*> + body: StatementBuilder.() -> Statement<*> ): ExplainBlockingExecutable { val stmt = ExplainQuery(analyze, options, buildStatement(body)) return ExplainBlockingExecutable(stmt) diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable.kt index 263ee01b4d..4efaff0033 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/statements/BlockingExecutable.kt @@ -1,8 +1,7 @@ package org.jetbrains.exposed.v1.jdbc.statements import org.jetbrains.exposed.v1.core.InternalApi -import org.jetbrains.exposed.v1.core.statements.Statement -import org.jetbrains.exposed.v1.core.statements.StatementContext +import org.jetbrains.exposed.v1.core.statements.* import org.jetbrains.exposed.v1.exceptions.ExposedSQLException import org.jetbrains.exposed.v1.jdbc.JdbcTransaction import org.jetbrains.exposed.v1.jdbc.statements.api.JdbcPreparedStatementApi @@ -79,6 +78,43 @@ interface BlockingExecutable> { } } +/** + * Returns the associated [BlockingExecutable] that accepts this [Statement] type as an argument, + * allowing the provided statement to be then sent to the database for execution. + * + *```kotlin + * transaction { + * val insertTaskStatement = buildStatement { + * Tasks.insert { + * it[title] = "Follow Exposed tutorial" + * it[isComplete] = false + * } + * } + * + * exec(insertTask.toExecutable()) + * } + * ``` + * + * @throws IllegalStateException If the invoking statement does not have a corresponding built-in executable. + */ +fun > S.toExecutable(): BlockingExecutable { + @Suppress("UNCHECKED_CAST") + return when (this) { + is BatchUpsertStatement -> BatchUpsertBlockingExecutable(this) + is UpsertStatement<*> -> UpsertBlockingExecutable(this as UpsertStatement) + is SQLServerBatchInsertStatement -> SQLServerBatchInsertBlockingExecutable(this) + is BatchInsertStatement -> BatchInsertBlockingExecutable(this) + is InsertStatement<*> -> InsertBlockingExecutable(this as InsertStatement) + is BatchUpdateStatement -> BatchUpdateBlockingExecutable(this) + is UpdateStatement -> UpdateBlockingExecutable(this) + is DeleteStatement -> DeleteBlockingExecutable(this) + is InsertSelectStatement -> InsertSelectBlockingExecutable(this) + is MergeStatement -> MergeBlockingExecutable(this) + is ReturningStatement -> ReturningBlockingExecutable(this) + else -> error("An executable could not be associated with ${this::class.qualifiedName}. Pass this statement to a custom executable instance directly.") + } as BlockingExecutable +} + @OptIn(InternalApi::class) internal fun > BlockingExecutable.executeIn( transaction: JdbcTransaction diff --git a/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/TransactionExecTests.kt b/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/TransactionExecTests.kt index baaea75881..8209dbc1f4 100644 --- a/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/TransactionExecTests.kt +++ b/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/TransactionExecTests.kt @@ -1,16 +1,24 @@ package org.jetbrains.exposed.v1.r2dbc.sql.tests.shared +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.single import kotlinx.coroutines.flow.singleOrNull import kotlinx.coroutines.flow.toList import org.jetbrains.exposed.v1.core.InternalApi +import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.eq import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.core.autoIncColumnType import org.jetbrains.exposed.v1.core.statements.StatementType +import org.jetbrains.exposed.v1.core.statements.buildStatement +import org.jetbrains.exposed.v1.core.upperCase import org.jetbrains.exposed.v1.core.vendors.inProperCase import org.jetbrains.exposed.v1.r2dbc.R2dbcTransaction import org.jetbrains.exposed.v1.r2dbc.batchInsert import org.jetbrains.exposed.v1.r2dbc.insert +import org.jetbrains.exposed.v1.r2dbc.select +import org.jetbrains.exposed.v1.r2dbc.selectAll +import org.jetbrains.exposed.v1.r2dbc.sql.tests.shared.dml.withCitiesAndUsers +import org.jetbrains.exposed.v1.r2dbc.statements.toExecutable import org.jetbrains.exposed.v1.r2dbc.tests.R2dbcDatabaseTestsBase import org.jetbrains.exposed.v1.r2dbc.tests.TestDB import org.jetbrains.exposed.v1.r2dbc.tests.getInt @@ -166,4 +174,49 @@ class TransactionExecTests : R2dbcDatabaseTestsBase() { assertNull(nullTransformResult) } } + + @Test + fun testExecWithBuildStatement() { + withCitiesAndUsers { cities, users, userData -> + val initialCityCount = cities.selectAll().count() + val initialUserDataCount = userData.selectAll().count() + + val newCity = "Amsterdam" + val insertCity = buildStatement { + cities.insert { + it[name] = newCity + } + } + val upsertCity = buildStatement { + cities.upsert(onUpdate = { it[cities.name] = cities.name.upperCase() }) { + it[id] = initialCityCount.toInt() + 1 + it[name] = newCity + } + } + val newName = "Alexey" + val userFilter = users.id eq "alex" + val updateUser = buildStatement { + users.update({ userFilter }) { + it[users.name] = newName + } + } + val deleteAllUserData = buildStatement { userData.deleteAll() } + + insertCity.toExecutable().execute(this) + assertEquals(initialCityCount + 1, cities.selectAll().count()) + + exec(upsertCity.toExecutable()) + assertEquals(initialCityCount + 1, cities.selectAll().count()) + val updatedCity = cities.selectAll().where { cities.id eq (initialCityCount.toInt() + 1) }.single() + assertEquals(newCity.uppercase(), updatedCity[cities.name]) + + updateUser.toExecutable().execute(this) + val updatedUserName = users.select(users.name).where { userFilter }.first() + assertEquals(newName, updatedUserName[users.name]) + + val rowsDeleted = exec(deleteAllUserData.toExecutable()) + assertEquals(initialUserDataCount, rowsDeleted?.toLong()) + assertEquals(0, userData.selectAll().count()) + } + } } diff --git a/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/dml/ExplainTests.kt b/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/dml/ExplainTests.kt index 39cac8a515..0be7dfdd39 100644 --- a/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/dml/ExplainTests.kt +++ b/exposed-r2dbc-tests/src/test/kotlin/org/jetbrains/exposed/v1/r2dbc/sql/tests/shared/dml/ExplainTests.kt @@ -7,8 +7,8 @@ import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.core.intParam import org.jetbrains.exposed.v1.core.or -import org.jetbrains.exposed.v1.core.statements.IStatementBuilder import org.jetbrains.exposed.v1.core.statements.Statement +import org.jetbrains.exposed.v1.core.statements.StatementBuilder import org.jetbrains.exposed.v1.core.vendors.H2Dialect import org.jetbrains.exposed.v1.core.vendors.MysqlDialect import org.jetbrains.exposed.v1.r2dbc.* @@ -54,7 +54,7 @@ class ExplainTests : R2dbcDatabaseTestsBase() { var explainCount = 0 val cityName = "City A" - suspend fun R2dbcTransaction.explainAndIncrement(body: IStatementBuilder.() -> Statement<*>) = explain(body = body).also { + suspend fun R2dbcTransaction.explainAndIncrement(body: StatementBuilder.() -> Statement<*>) = explain(body = body).also { it.toList() // as with select queries, explain is only executed when iterated over explainCount++ } diff --git a/exposed-r2dbc/api/exposed-r2dbc.api b/exposed-r2dbc/api/exposed-r2dbc.api index 96c91df405..164d512315 100644 --- a/exposed-r2dbc/api/exposed-r2dbc.api +++ b/exposed-r2dbc/api/exposed-r2dbc.api @@ -758,6 +758,10 @@ public final class org/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable$D public static fun prepared (Lorg/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable;Lorg/jetbrains/exposed/v1/r2dbc/R2dbcTransaction;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public final class org/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutableKt { + public static final fun toExecutable (Lorg/jetbrains/exposed/v1/core/statements/Statement;)Lorg/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable; +} + public class org/jetbrains/exposed/v1/r2dbc/statements/UpdateSuspendExecutable : org/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable { public fun (Lorg/jetbrains/exposed/v1/core/statements/UpdateStatement;)V public fun execute (Lorg/jetbrains/exposed/v1/r2dbc/R2dbcTransaction;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/ExplainSuspendExecutable.kt b/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/ExplainSuspendExecutable.kt index a10b44a708..9d171daa61 100644 --- a/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/ExplainSuspendExecutable.kt +++ b/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/ExplainSuspendExecutable.kt @@ -5,8 +5,8 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector import org.jetbrains.exposed.v1.core.ExplainQuery import org.jetbrains.exposed.v1.core.ExplainResultRow -import org.jetbrains.exposed.v1.core.statements.IStatementBuilder import org.jetbrains.exposed.v1.core.statements.Statement +import org.jetbrains.exposed.v1.core.statements.StatementBuilder import org.jetbrains.exposed.v1.core.statements.api.ResultApi import org.jetbrains.exposed.v1.core.statements.buildStatement import org.jetbrains.exposed.v1.r2dbc.statements.SuspendExecutable @@ -57,7 +57,7 @@ open class ExplainSuspendExecutable( fun R2dbcTransaction.explain( analyze: Boolean = false, options: String? = null, - body: IStatementBuilder.() -> Statement<*> + body: StatementBuilder.() -> Statement<*> ): ExplainSuspendExecutable { val stmt = ExplainQuery(analyze, options, buildStatement(body)) return ExplainSuspendExecutable(stmt) diff --git a/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/Queries.kt b/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/Queries.kt index a80fbe73d5..f3e679ef12 100644 --- a/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/Queries.kt +++ b/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/Queries.kt @@ -291,7 +291,7 @@ suspend fun > T.insertIgnoreAndGetId( suspend fun T.insert( selectQuery: AbstractQuery<*>, columns: List>? = null -): org.jetbrains.exposed.v1.core.statements.InsertSelectStatement { +): InsertSelectStatement { val stmt = buildStatement { insert(selectQuery, columns) } return InsertSelectSuspendExecutable(stmt).apply { execute(TransactionManager.current()) }.statement } diff --git a/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable.kt b/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable.kt index 621ff2e1b7..edf7e771d4 100644 --- a/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable.kt +++ b/exposed-r2dbc/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/statements/SuspendExecutable.kt @@ -2,8 +2,7 @@ package org.jetbrains.exposed.v1.r2dbc.statements import io.r2dbc.spi.R2dbcException import org.jetbrains.exposed.v1.core.InternalApi -import org.jetbrains.exposed.v1.core.statements.Statement -import org.jetbrains.exposed.v1.core.statements.StatementContext +import org.jetbrains.exposed.v1.core.statements.* import org.jetbrains.exposed.v1.r2dbc.ExposedR2dbcException import org.jetbrains.exposed.v1.r2dbc.R2dbcTransaction import org.jetbrains.exposed.v1.r2dbc.statements.api.R2dbcPreparedStatementApi @@ -79,6 +78,43 @@ interface SuspendExecutable> { } } +/** + * Returns the associated [SuspendExecutable] that accepts this [Statement] type as an argument, + * allowing the provided statement to be then sent to the database for execution. + * + *```kotlin + * suspendTransaction { + * val insertTaskStatement = buildStatement { + * Tasks.insert { + * it[title] = "Follow Exposed tutorial" + * it[isComplete] = false + * } + * } + * + * exec(insertTask.toExecutable()) + * } + * ``` + * + * @throws IllegalStateException If the invoking statement does not have a corresponding built-in executable. + */ +fun > S.toExecutable(): SuspendExecutable { + @Suppress("UNCHECKED_CAST") + return when (this) { + is BatchUpsertStatement -> BatchUpsertSuspendExecutable(this) + is UpsertStatement<*> -> UpsertSuspendExecutable(this as UpsertStatement) + is SQLServerBatchInsertStatement -> SQLServerBatchInsertSuspendExecutable(this) + is BatchInsertStatement -> BatchInsertSuspendExecutable(this) + is InsertStatement<*> -> InsertSuspendExecutable(this as InsertStatement) + is BatchUpdateStatement -> BatchUpdateSuspendExecutable(this) + is UpdateStatement -> UpdateSuspendExecutable(this) + is DeleteStatement -> DeleteSuspendExecutable(this) + is InsertSelectStatement -> InsertSelectSuspendExecutable(this) + is MergeStatement -> MergeSuspendExecutable(this) + is ReturningStatement -> ReturningSuspendExecutable(this) + else -> error("An executable could not be associated with ${this::class.qualifiedName}. Pass this statement to a custom executable instance directly.") + } as SuspendExecutable +} + @OptIn(InternalApi::class) internal suspend fun > SuspendExecutable.executeIn( transaction: R2dbcTransaction diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/TransactionExecTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/TransactionExecTests.kt index 50e1040355..888640e48f 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/TransactionExecTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/TransactionExecTests.kt @@ -1,15 +1,20 @@ package org.jetbrains.exposed.v1.tests.shared import org.jetbrains.exposed.v1.core.InternalApi +import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.eq import org.jetbrains.exposed.v1.core.Table import org.jetbrains.exposed.v1.core.autoIncColumnType import org.jetbrains.exposed.v1.core.statements.StatementType +import org.jetbrains.exposed.v1.core.statements.buildStatement +import org.jetbrains.exposed.v1.core.upperCase import org.jetbrains.exposed.v1.core.vendors.inProperCase import org.jetbrains.exposed.v1.jdbc.* +import org.jetbrains.exposed.v1.jdbc.statements.toExecutable import org.jetbrains.exposed.v1.jdbc.transactions.TransactionManager import org.jetbrains.exposed.v1.jdbc.transactions.transaction import org.jetbrains.exposed.v1.tests.DatabaseTestsBase import org.jetbrains.exposed.v1.tests.TestDB +import org.jetbrains.exposed.v1.tests.shared.dml.withCitiesAndUsers import org.junit.Assume import org.junit.Test import kotlin.test.assertNotNull @@ -175,4 +180,51 @@ class TransactionExecTests : DatabaseTestsBase() { assertNull(nullTransformResult) } } + + @Test + fun testExecWithBuildStatement() { + withCitiesAndUsers( + exclude = TestDB.ALL_H2_V1 + ) { cities, users, userData -> + val initialCityCount = cities.selectAll().count() + val initialUserDataCount = userData.selectAll().count() + + val newCity = "Amsterdam" + val insertCity = buildStatement { + cities.insert { + it[name] = newCity + } + } + val upsertCity = buildStatement { + cities.upsert(onUpdate = { it[cities.name] = cities.name.upperCase() }) { + it[id] = initialCityCount.toInt() + 1 + it[name] = newCity + } + } + val newName = "Alexey" + val userFilter = users.id eq "alex" + val updateUser = buildStatement { + users.update({ userFilter }) { + it[users.name] = newName + } + } + val deleteAllUserData = buildStatement { userData.deleteAll() } + + insertCity.toExecutable().execute(this) + assertEquals(initialCityCount + 1, cities.selectAll().count()) + + exec(upsertCity.toExecutable()) + assertEquals(initialCityCount + 1, cities.selectAll().count()) + val updatedCity = cities.selectAll().where { cities.id eq (initialCityCount.toInt() + 1) }.single() + assertEquals(newCity.uppercase(), updatedCity[cities.name]) + + updateUser.toExecutable().execute(this) + val updatedUserName = users.select(users.name).where { userFilter }.first() + assertEquals(newName, updatedUserName[users.name]) + + val rowsDeleted = exec(deleteAllUserData.toExecutable()) + assertEquals(initialUserDataCount, rowsDeleted?.toLong()) + assertEquals(0, userData.selectAll().count()) + } + } } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/dml/ExplainTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/dml/ExplainTests.kt index 95a6906d48..166f0e365e 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/dml/ExplainTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/v1/tests/shared/dml/ExplainTests.kt @@ -5,8 +5,8 @@ import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.core.intParam import org.jetbrains.exposed.v1.core.or -import org.jetbrains.exposed.v1.core.statements.IStatementBuilder import org.jetbrains.exposed.v1.core.statements.Statement +import org.jetbrains.exposed.v1.core.statements.StatementBuilder import org.jetbrains.exposed.v1.core.vendors.H2Dialect import org.jetbrains.exposed.v1.core.vendors.MysqlDialect import org.jetbrains.exposed.v1.core.vendors.SQLiteDialect @@ -55,7 +55,7 @@ class ExplainTests : DatabaseTestsBase() { var explainCount = 0 val cityName = "City A" - fun JdbcTransaction.explainAndIncrement(body: IStatementBuilder.() -> Statement<*>) = explain(body = body).also { + fun JdbcTransaction.explainAndIncrement(body: StatementBuilder.() -> Statement<*>) = explain(body = body).also { it.toList() // as with select queries, explain is only executed when iterated over explainCount++ }