-
Notifications
You must be signed in to change notification settings - Fork 695
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
fix! EXPOSED-458 Stop sending default and null values in insert state… #2295
base: main
Are you sure you want to change the base?
fix! EXPOSED-458 Stop sending default and null values in insert state… #2295
Conversation
@@ -109,24 +108,16 @@ class UpsertTests : DatabaseTestsBase() { | |||
|
|||
withTables(excludeSettings = TestDB.ALL_H2_V1, tester) { testDb -> | |||
val primaryKeyValues = Pair("User A", "Key A") | |||
if (testDb == TestDB.ORACLE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oracle does not throw an error here, because for the case of autogenerated onUpdate
part the key columns are filtered (filtered for Oracle only)
// `amount` must be passed explicitly now due to usage of that column inside the custom onUpdate statement | ||
// There is an option to call `tester.amount.defaultValueFun?.let { it() }!!`, | ||
// it looks ugly but prevents regression on changes in default value | ||
it[amount] = 25 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was one of my biggest concern on upsert statement changes. The main problem is that that column amount
is used inside onUpdate
section (and here this section is not autogenerated). Before the changes in the PR it was working, because amount
with default value was propagated automatically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a large problem, as long as this change is well and clearly documented.
The amount
being intentionally excluded here is only contingent on the fact that it is actually being set in the insert statement. I don't believe that a user will intentionally use insertValue()
in the onUpdate
arg unless they either explicitly want to reference an insert value that they are manually setting anyway or they are aware that Exposed actually includes the column in the statement and are taking advantage of this.
Either way, choosing to use insertValue()
means accepting the syntax contract, which states that the referenced column must be included in the insert clause.
So they may be surprised, but I doubt it would be considered unfair or illogical. And if they still want the original behavior, I believe it would still be achievable by defining:
integer("amount").default(25).clientDefault { 25 }
.
@@ -366,7 +366,7 @@ class CreateMissingTablesAndColumnsTests : DatabaseTestsBase() { | |||
actual.forEach { exec(it) } | |||
} else { | |||
SchemaUtils.drop(whiteSpaceTable) | |||
SchemaUtils.create(whiteSpaceTable) | |||
SchemaUtils.create(emptyTable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was the error in the test, but it didn't occured before because database side defaults were not used.
val acceptableDefaults = mutableListOf("CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP()", "NOW()", "CURRENT_TIMESTAMP(6)", "NOW(6)") | ||
return e.toString().trim() in acceptableDefaults && isFractionDateTimeSupported() | ||
|
||
// This check is quite optimistic, it will not allow to create a varchar columns with "CURRENT_DATE" default value for example | ||
// Comparing to the previous variant with white list of functions the new variant does not reject valid values, | ||
// it could be checked on the test UpsertTests::testUpsertWithColumnExpressions() | ||
return e.toString().trim() !in notAcceptableDefaults |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure I'm understanding correctly:
- Whenever any invalid column default value is used (for example, CURRENT_DATE with MySQL v5 & no client default), then the behavior is that Exposed just logs an error and defines the column with SQL NULL in the CREATE ddl instead. So tests (like the datetime ones below) used to pass, before all these changes, because the insert statements included the current date. And now, the same ddl with NULL marker will be defined, but the insert statement will not include these columns, so a date would never be inserted unless done manually.
This will be a fairly specific breaking change and I'd recommend mend highlighting it whenever the breaking changes docs is updated.
I would also suggest updating the log message that is output in Column.descriptionDdl()
to reflect the new behavior more explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I'm looking at the math functions test below.
I see that the new behavior (for MySQL v5) will be that any invalid default values (other than current_date) will be included in the ddl, leading to a syntax exception, instead of logging an error and marking as NULL.
It might be an edge case, given the older version, but is the plan to include any new api that could avoid this?
Something like a clientDefaultExpression()
, so that the DDL is not affected, but Exposed could automatically set the specified database function on every insert?
// `amount` must be passed explicitly now due to usage of that column inside the custom onUpdate statement | ||
// There is an option to call `tester.amount.defaultValueFun?.let { it() }!!`, | ||
// it looks ugly but prevents regression on changes in default value | ||
it[amount] = 25 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a large problem, as long as this change is well and clearly documented.
The amount
being intentionally excluded here is only contingent on the fact that it is actually being set in the insert statement. I don't believe that a user will intentionally use insertValue()
in the onUpdate
arg unless they either explicitly want to reference an insert value that they are manually setting anyway or they are aware that Exposed actually includes the column in the statement and are taking advantage of this.
Either way, choosing to use insertValue()
means accepting the syntax contract, which states that the referenced column must be included in the insert clause.
So they may be surprised, but I doubt it would be considered unfair or illogical. And if they still want the original behavior, I believe it would still be achievable by defining:
integer("amount").default(25).clientDefault { 25 }
.
With this PR insert, upsert and replace SQL commands will not propogate default values that could be created on the database by default.
The main change is in the logic of how
arguments
of statements are calculated. Instead of taking all the defaults only client defaults are taken now. The methodvaluesAndDefaults()
that was used for creating arguments was deprecated, because it's open and could be used by other people. Also were added some bug fixes for default values.Type of Change
Updates/remove existing public API methods:
Affected databases:
Related Issues
EXPOSED-458 Stop sending default and null values in insert statements