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

refactor: EXPOSED-561 Restructure code in MigrationUtils and SchemaUtils to avoid calling currentDialect.tableColumns(*tables) in MigrationUtils.statementsRequiredForDatabaseMigration twice #2279

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

joc-a
Copy link
Collaborator

@joc-a joc-a commented Oct 17, 2024

Description

Detailed description:

  • What:
    Restructure code in MigrationUtils and SchemaUtils.
  • Why:
    To avoid calling currentDialect.tableColumns(*tables) in MigrationUtils.statementsRequiredForDatabaseMigration twice.
  • How:
    SchemaUtils and MigrationUtils reside in separate modules and need to share non-public functions, so using a common abstract base class with protected functions in the exposed-core module is the most effective and clean solution. For the functions addMissingColumnsStatements and dropUnmappedColumnsStatements, two corresponding private functions with a different signature were created, with the difference being that the new functions expect existingTablesColumns to be passed as a parameter. Since the new private addMissingColumnsStatements function is common to both SchemaUtils and MigrationUtils, it was placed in BaseSchemaUtils, and the same thing was done for the logTimeSpent function.

Type of Change

Please mark the relevant options with an "X":

  • Bug fix
  • New feature
  • Documentation update
  • Code enhancement

Updates/remove existing public API methods:

  • Is breaking change

Affected databases:

  • MariaDB
  • Mysql5
  • Mysql8
  • Oracle
  • Postgres
  • SqlServer
  • H2
  • SQLite

Checklist

  • Unit tests are in place
  • The build is green (including the Detekt check)
  • All public methods affected by my PR has up to date API docs
  • Documentation for my change is up to date

Related Issues

…aUtils` to avoid calling `currentDialect.tableColumns(*tables)` in `MigrationUtils.statementsRequiredForDatabaseMigration` twice

`SchemaUtils` and `MigrationUtils` reside in separate modules and need to share non-public functions, so using a common abstract base class with protected functions in the exposed-core module is the most effective and clean solution.
@joc-a joc-a marked this pull request as ready for review October 17, 2024 14:18
import org.jetbrains.exposed.sql.vendors.H2Dialect
import org.jetbrains.exposed.sql.vendors.MysqlDialect
import org.jetbrains.exposed.sql.vendors.SQLiteDialect
import org.jetbrains.exposed.sql.vendors.currentDialect
import java.io.File

object MigrationUtils {
/** Utility functions that assist with performing database migrations. */
object MigrationUtils : BaseSchemaUtils() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that in terms of OOP (at least from my current understanding of MigrationUtils's purpose) MigrationUtils should not extend SchemaUtils (BaseSchemaUtils) but should use it, because MigrationUtils is more abstract than SchemaUtils (SchemaUtils is responsible for manipulating with single db objects like tables, but MigrationUtils are supposed to move the whole DB from one state to another).

And as I can see MigrationUtils overrides nothing from base class.

@@ -102,16 +105,21 @@ object MigrationUtils {
*/
fun dropUnmappedColumnsStatements(vararg tables: Table, withLogs: Boolean = true): List<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a problem of that particular PR, but this is a public method, and there is only one test that check that function returns empty list. Probably it would be nice to add one more test, that check for non empty result of that function)

Copy link
Collaborator Author

@joc-a joc-a Oct 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That case is tested, but not by directly calling dropUnmappedColumnsStatements, in testDropUnmappedColumns.

@@ -102,16 +105,21 @@ object MigrationUtils {
*/
fun dropUnmappedColumnsStatements(vararg tables: Table, withLogs: Boolean = true): List<String> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And do we have any requests for such functionality? Looks like it's already used in statementsRequiredForDatabaseMigration that should returns All statements for migration. Probably, the method that checks columns of one (or multiple) tables for deleting could be a part of SchemaUtils. But it's discussable, just some thoughts on how it could be.

import org.jetbrains.exposed.sql.vendors.H2Dialect
import org.jetbrains.exposed.sql.vendors.MysqlDialect
import org.jetbrains.exposed.sql.vendors.SQLiteDialect
import org.jetbrains.exposed.sql.vendors.currentDialect
import java.io.File

object MigrationUtils {
/** Utility functions that assist with performing database migrations. */
object MigrationUtils : BaseSchemaUtils() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also not a problem of that PR, and can't say I finally understand how users will use it, but this class has a method generateMigrationScript that accepts the list of tables as an argument.

But if you want to create a script, and has hundreds of tables, it could be painful to collect all of them into collection and pass here.

If I use a migration tool, I'd expect that it should search for table objects automatically. As a variant for future improvements we could add one more method that accepts package, and searches for all the table objects (with reflection) within this package.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is already an improvement that I have on my to-do list.

import java.math.BigDecimal

/** Base class housing shared code between [SchemaUtils] and [MigrationUtils]. */
abstract class BaseSchemaUtils {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is one another idea of how common part could be extracted.

At the current moment the whole SchemaUtils looks a bit overloaded. It has methods to generate DDL, and has methods to execute commands to manipulate DB.

We could try to split it into the way to extract DDL generation part into another class, let's call it for now DDLUtils; and keep inside SchemaUtils responsibility to execute these DDLs on the database.

In this case we will have common part with DDL statements generation, and two consumers of that class. SchemaUtils that directly performs it on the DB, and MigrationUtils that prepare it as a migration scripts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@obabichevjb In that case, how would we make the following function visible only to SchemaUtils and MigrationUtils? We don't want it to be public.

fun addMissingColumnsStatements(vararg tables: Table, existingTablesColumns: Map<Table, List<ColumnMetadata>>, withLogs: Boolean = true): List<String>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants