Skip to content

Commit

Permalink
feat: EXPOSED-296 Add ability to check if a Sequence exists in a data…
Browse files Browse the repository at this point in the history
…base
  • Loading branch information
joc-a committed Apr 22, 2024
1 parent 123bba9 commit 7ca593c
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 0 deletions.
9 changes: 9 additions & 0 deletions exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,7 @@ public final class org/jetbrains/exposed/sql/Sequence {
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Long;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun createStatement ()Ljava/util/List;
public final fun dropStatement ()Ljava/util/List;
public final fun exists ()Z
public final fun getCache ()Ljava/lang/Long;
public final fun getCycle ()Ljava/lang/Boolean;
public final fun getDdl ()Ljava/util/List;
Expand Down Expand Up @@ -3204,6 +3205,7 @@ public abstract class org/jetbrains/exposed/sql/statements/api/ExposedDatabaseMe
public abstract fun getUrl ()Ljava/lang/String;
public abstract fun getVersion ()Ljava/math/BigDecimal;
public abstract fun resetCurrentScheme ()V
public abstract fun sequences ()Ljava/util/List;
public abstract fun tableConstraints (Ljava/util/List;)Ljava/util/Map;
public abstract fun tableNamesByCurrentSchema (Ljava/util/Map;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata;
}
Expand Down Expand Up @@ -3489,6 +3491,8 @@ public abstract interface class org/jetbrains/exposed/sql/vendors/DatabaseDialec
public abstract fun resetSchemaCaches ()V
public abstract fun resolveRefOptionFromJdbc (I)Lorg/jetbrains/exposed/sql/ReferenceOption;
public abstract fun schemaExists (Lorg/jetbrains/exposed/sql/Schema;)Z
public abstract fun sequenceExists (Lorg/jetbrains/exposed/sql/Sequence;)Z
public abstract fun sequences ()Ljava/util/List;
public abstract fun setSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
public abstract fun supportsSelectForUpdate ()Z
public abstract fun tableColumns ([Lorg/jetbrains/exposed/sql/Table;)Ljava/util/Map;
Expand Down Expand Up @@ -3711,6 +3715,7 @@ public class org/jetbrains/exposed/sql/vendors/H2Dialect : org/jetbrains/exposed
public fun listDatabases ()Ljava/lang/String;
public fun modifyColumn (Lorg/jetbrains/exposed/sql/Column;Lorg/jetbrains/exposed/sql/ColumnDiff;)Ljava/util/List;
public fun resolveRefOptionFromJdbc (I)Lorg/jetbrains/exposed/sql/ReferenceOption;
public fun sequences ()Ljava/util/List;
public fun toString ()Ljava/lang/String;
}

Expand Down Expand Up @@ -3796,6 +3801,7 @@ public class org/jetbrains/exposed/sql/vendors/OracleDialect : org/jetbrains/exp
public fun listDatabases ()Ljava/lang/String;
public fun modifyColumn (Lorg/jetbrains/exposed/sql/Column;Lorg/jetbrains/exposed/sql/ColumnDiff;)Ljava/util/List;
public fun resolveRefOptionFromJdbc (I)Lorg/jetbrains/exposed/sql/ReferenceOption;
public fun sequences ()Ljava/util/List;
public fun setSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
}

Expand Down Expand Up @@ -3867,6 +3873,7 @@ public class org/jetbrains/exposed/sql/vendors/SQLServerDialect : org/jetbrains/
public fun isAllowedAsColumnDefault (Lorg/jetbrains/exposed/sql/Expression;)Z
public fun listDatabases ()Ljava/lang/String;
public fun modifyColumn (Lorg/jetbrains/exposed/sql/Column;Lorg/jetbrains/exposed/sql/ColumnDiff;)Ljava/util/List;
public fun sequences ()Ljava/util/List;
public fun setSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
}

Expand Down Expand Up @@ -3959,6 +3966,8 @@ public abstract class org/jetbrains/exposed/sql/vendors/VendorDialect : org/jetb
public fun resetSchemaCaches ()V
public fun resolveRefOptionFromJdbc (I)Lorg/jetbrains/exposed/sql/ReferenceOption;
public fun schemaExists (Lorg/jetbrains/exposed/sql/Schema;)Z
public fun sequenceExists (Lorg/jetbrains/exposed/sql/Sequence;)Z
public fun sequences ()Ljava/util/List;
public fun setSchema (Lorg/jetbrains/exposed/sql/Schema;)Ljava/lang/String;
public fun supportsSelectForUpdate ()Z
public fun tableColumns ([Lorg/jetbrains/exposed/sql/Table;)Ljava/util/Map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,9 @@ class Sequence(

return listOf(dropSequenceDDL)
}

/**
* Returns whether this sequence exists in the database.
*/
fun exists(): Boolean = currentDialect.sequenceExists(this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ abstract class ExposedDatabaseMetadata(val database: String) {
/** Returns a map with the [PrimaryKeyMetadata] in each of the specified [tables]. */
abstract fun existingPrimaryKeys(vararg tables: Table): Map<Table, PrimaryKeyMetadata?>

/** Returns a list of the names of all sequences in the database. */
abstract fun sequences(): List<String>

/**
* Returns a map with the [ForeignKeyConstraint] of all the defined columns in each of the specified [tables],
* with the table name used as the key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ interface DatabaseDialect {
/** Checks if the specified schema exists. */
fun schemaExists(schema: Schema): Boolean

/** Returns whether the specified sequence exists. */
fun sequenceExists(sequence: Sequence): Boolean

fun checkTableMapping(table: Table): Boolean = true

/** Returns a map with the column metadata of all the defined columns in each of the specified [tables]. */
Expand All @@ -103,6 +106,9 @@ interface DatabaseDialect {
/** Returns a map with the primary key metadata in each of the specified [tables]. */
fun existingPrimaryKeys(vararg tables: Table): Map<Table, PrimaryKeyMetadata?> = emptyMap()

/** Returns a list of the names of all sequences in the database. */
fun sequences(): List<String>

/** Returns `true` if the dialect supports `SELECT FOR UPDATE` statements, `false` otherwise. */
fun supportsSelectForUpdate(): Boolean

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,16 @@ open class H2Dialect : VendorDialect(dialectName, H2DataTypeProvider, H2Function
}
}

override fun sequences(): List<String> {
val sequences = mutableListOf<String>()
TransactionManager.current().exec("SELECT SEQUENCE_NAME FROM INFORMATION_SCHEMA.SEQUENCES") { rs ->
while (rs.next()) {
sequences.add(rs.getString("SEQUENCE_NAME"))
}
}
return sequences
}

companion object : DialectNameProvider("H2")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,5 +393,15 @@ open class OracleDialect : VendorDialect(dialectName, OracleDataTypeProvider, Or
else -> currentDialect.defaultReferenceOption
}

override fun sequences(): List<String> {
val sequences = mutableListOf<String>()
TransactionManager.current().exec("SELECT SEQUENCE_NAME FROM USER_SEQUENCES") { rs ->
while (rs.next()) {
sequences.add(rs.getString("SEQUENCE_NAME"))
}
}
return sequences
}

companion object : DialectNameProvider("Oracle")
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,16 @@ open class SQLServerDialect : VendorDialect(dialectName, SQLServerDataTypeProvid
// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/like-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15#arguments
override val likePatternSpecialChars = sqlServerLikePatternSpecialChars

override fun sequences(): List<String> {
val sequences = mutableListOf<String>()
TransactionManager.current().exec("SELECT name FROM sys.sequences") { rs ->
while (rs.next()) {
sequences.add(rs.getString("name"))
}
}
return sequences
}

companion object : DialectNameProvider("SQLServer") {
private val sqlServerLikePatternSpecialChars = mapOf('%' to null, '_' to null, '[' to ']')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ abstract class VendorDialect(
return allSchemas.any { it == schema.identifier.inProperCase() }
}

override fun sequenceExists(sequence: Sequence): Boolean {
return sequences().any { it == sequence.identifier.inProperCase() }
}

override fun tableColumns(vararg tables: Table): Map<Table, List<ColumnMetadata>> =
TransactionManager.current().connection.metadata { columns(*tables) }

Expand All @@ -116,6 +120,9 @@ abstract class VendorDialect(
override fun existingPrimaryKeys(vararg tables: Table): Map<Table, PrimaryKeyMetadata?> =
TransactionManager.current().db.metadata { existingPrimaryKeys(*tables) }

override fun sequences(): List<String> =
TransactionManager.current().db.metadata { sequences() }

private val supportsSelectForUpdate: Boolean by lazy {
TransactionManager.current().db.metadata { supportsSelectForUpdate }
}
Expand Down
1 change: 1 addition & 0 deletions exposed-jdbc/api/exposed-jdbc.api
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public final class org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadat
public fun getUrl ()Ljava/lang/String;
public fun getVersion ()Ljava/math/BigDecimal;
public fun resetCurrentScheme ()V
public fun sequences ()Ljava/util/List;
public fun tableConstraints (Ljava/util/List;)Ljava/util/Map;
public fun tableNamesByCurrentSchema (Ljava/util/Map;)Lorg/jetbrains/exposed/sql/vendors/SchemaMetadata;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData)
}
}

@Suppress("MagicNumber")
override fun sequences(): List<String> {
val sequences = mutableListOf<String>()
val rs = metadata.getTables(null, null, null, arrayOf("SEQUENCE"))
while (rs.next()) {
sequences.add(rs.getString(3))
}
return sequences
}

@Synchronized
override fun tableConstraints(tables: List<Table>): Map<String, List<ForeignKeyConstraint>> {
val allTables = SchemaUtils.sortTablesByReferences(tables).associateBy { it.nameInDatabaseCaseUnquoted() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.tests.DatabaseTestsBase
import org.jetbrains.exposed.sql.tests.TestDB
import org.jetbrains.exposed.sql.tests.currentDialectTest
import org.jetbrains.exposed.sql.tests.inProperCase
import org.jetbrains.exposed.sql.tests.shared.assertEquals
import org.jetbrains.exposed.sql.tests.shared.assertTrue
import org.jetbrains.exposed.sql.vendors.currentDialect
import org.junit.Test
import kotlin.test.assertNotNull

Expand Down Expand Up @@ -132,6 +136,68 @@ class SequencesTests : DatabaseTestsBase() {
}
}

@Test
fun testManuallyCreatedSequenceExists() {
withDb {
if (currentDialectTest.supportsCreateSequence) {
try {
SchemaUtils.createSequence(myseq)

assertTrue(myseq.exists())
} finally {
SchemaUtils.dropSequence(myseq)
}
}
}
}

@Test
fun testExistingSequencesForAutoIncrementWithExplicitSequenceName() {
val sequenceName = "id_seq"
val tableWithExplicitSequenceName = object : IdTable<Long>() {
override val id: Column<EntityID<Long>> = long("id").autoIncrement(sequenceName).entityId()
}

withDb {
if (currentDialectTest.supportsSequenceAsGeneratedKeys) {
try {
SchemaUtils.create(tableWithExplicitSequenceName)

val sequences = currentDialectTest.sequences()

assertTrue(sequences.isNotEmpty())
assertTrue(sequences.any { it == sequenceName.inProperCase() })
} finally {
SchemaUtils.drop(tableWithExplicitSequenceName)
}
}
}
}

@Test
fun testExistingSequencesForAutoIncrementWithoutExplicitSequenceName() {
val tableWithoutExplicitSequenceName = object : IdTable<Long>() {
override val id: Column<EntityID<Long>> = long("id").autoIncrement().entityId()
}

withDb { testDb ->
if (currentDialect.needsSequenceToAutoInc) {
try {
SchemaUtils.create(tableWithoutExplicitSequenceName)

val sequences = currentDialectTest.sequences()

assertTrue(sequences.isNotEmpty())

val expected = tableWithoutExplicitSequenceName.id.autoIncColumnType!!.autoincSeq!!
assertTrue(sequences.any { it == if (testDb == TestDB.ORACLE) expected.inProperCase() else expected })
} finally {
SchemaUtils.drop(tableWithoutExplicitSequenceName)
}
}
}
}

private object Developer : Table() {
val id = integer("id")
var name = varchar("name", 25)
Expand Down

0 comments on commit 7ca593c

Please sign in to comment.