test: cover rollingBackATransactionUndoesEveryEffectOfAMerge
This commit is contained in:
1 parent
1798f78918
commit
e0058b8af2
1 file changed
+147
@@ -310,3 +310,150 @@ class MergeTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun rollingBackATransactionUndoesEveryEffectOfAMerge() {
|
||||||
|
setUpAccountsAndStaging()
|
||||||
|
run("INSERT INTO accounts VALUES (1, 100)")
|
||||||
|
run("INSERT INTO staging VALUES (1, 50), (2, 20)")
|
||||||
|
run("BEGIN")
|
||||||
|
exec(
|
||||||
|
"MERGE INTO accounts USING staging ON accounts.id = staging.id " +
|
||||||
|
"WHEN MATCHED THEN UPDATE SET balance = accounts.balance + staging.amount " +
|
||||||
|
"WHEN NOT MATCHED THEN INSERT (id, balance) VALUES (staging.id, staging.amount)"
|
||||||
|
)
|
||||||
|
run("ROLLBACK")
|
||||||
|
assertEquals(listOf(listOf(int(1), int(100))), select("SELECT id, balance FROM accounts").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun anAliasedTargetAndSourceCanBeReferencedByTheirAlias() {
|
||||||
|
setUpAccountsAndStaging()
|
||||||
|
run("INSERT INTO accounts VALUES (1, 100)")
|
||||||
|
run("INSERT INTO staging VALUES (1, 25)")
|
||||||
|
exec(
|
||||||
|
"MERGE INTO accounts AS acc USING staging AS s ON acc.id = s.id " +
|
||||||
|
"WHEN MATCHED THEN UPDATE SET balance = acc.balance + s.amount"
|
||||||
|
)
|
||||||
|
assertEquals(listOf(listOf(int(125))), select("SELECT balance FROM accounts").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mergeRequiresAtLeastOneWhenClause() {
|
||||||
|
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
|
||||||
|
run("CREATE TABLE s (id INTEGER)")
|
||||||
|
assertFailsWith<KestrelException> { run("MERGE INTO t USING s ON t.id = s.id") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mergingIntoAnUnknownTableFails() {
|
||||||
|
run("CREATE TABLE s (id INTEGER)")
|
||||||
|
assertFailsWith<KestrelException> {
|
||||||
|
run("MERGE INTO nope USING s ON nope.id = s.id WHEN MATCHED THEN DELETE")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun returningStarProjectsEveryColumnOfEveryActionedRow() {
|
||||||
|
setUpAccountsAndStaging()
|
||||||
|
run("INSERT INTO accounts VALUES (1, 100)")
|
||||||
|
run("INSERT INTO staging VALUES (1, 10), (2, 5)")
|
||||||
|
val result = exec(
|
||||||
|
"MERGE INTO accounts USING staging ON accounts.id = staging.id " +
|
||||||
|
"WHEN MATCHED THEN UPDATE SET balance = accounts.balance + staging.amount " +
|
||||||
|
"WHEN NOT MATCHED THEN INSERT (id, balance) VALUES (staging.id, staging.amount) " +
|
||||||
|
"RETURNING *"
|
||||||
|
) as MergeResult
|
||||||
|
assertEquals(listOf("id", "balance"), result.returning!!.columns)
|
||||||
|
assertEquals(
|
||||||
|
listOf(listOf(int(1), int(110)), listOf(int(2), int(5))),
|
||||||
|
result.returning!!.rows
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun returningEvaluatesExpressionsAgainstTheAffectedRow() {
|
||||||
|
setUpAccountsAndStaging()
|
||||||
|
run("INSERT INTO accounts VALUES (1, 100)")
|
||||||
|
run("INSERT INTO staging VALUES (1, 10)")
|
||||||
|
val result = exec(
|
||||||
|
"MERGE INTO accounts USING staging ON accounts.id = staging.id " +
|
||||||
|
"WHEN MATCHED THEN UPDATE SET balance = accounts.balance + staging.amount " +
|
||||||
|
"RETURNING balance * 2 AS doubled"
|
||||||
|
) as MergeResult
|
||||||
|
assertEquals(listOf("doubled"), result.returning!!.columns)
|
||||||
|
assertEquals(listOf(listOf(int(220))), result.returning!!.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun returningIncludesThePreDeletionImageOfADeletedRow() {
|
||||||
|
setUpAccountsAndStaging()
|
||||||
|
run("INSERT INTO accounts VALUES (1, 100)")
|
||||||
|
run("INSERT INTO staging VALUES (1, 0)")
|
||||||
|
val result = exec(
|
||||||
|
"MERGE INTO accounts USING staging ON accounts.id = staging.id WHEN MATCHED THEN DELETE RETURNING id, balance"
|
||||||
|
) as MergeResult
|
||||||
|
assertEquals(listOf(listOf(int(1), int(100))), result.returning!!.rows)
|
||||||
|
assertEquals(emptyList(), select("SELECT * FROM accounts").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mergeActionLabelsEachReturnedRowByWhichClauseFired() {
|
||||||
|
run("CREATE TABLE t (id INTEGER PRIMARY KEY, label TEXT)")
|
||||||
|
run("CREATE TABLE s (id INTEGER, label TEXT)")
|
||||||
|
run("INSERT INTO t VALUES (1, 'old'), (2, 'gone')")
|
||||||
|
run("INSERT INTO s VALUES (1, 'new'), (2, 'new'), (3, 'fresh')")
|
||||||
|
val result = exec(
|
||||||
|
"MERGE INTO t USING s ON t.id = s.id " +
|
||||||
|
"WHEN MATCHED AND s.id = 2 THEN DELETE " +
|
||||||
|
"WHEN MATCHED THEN UPDATE SET label = s.label " +
|
||||||
|
"WHEN NOT MATCHED THEN INSERT (id, label) VALUES (s.id, s.label) " +
|
||||||
|
"RETURNING id, MERGE_ACTION() AS kind"
|
||||||
|
) as MergeResult
|
||||||
|
assertEquals(
|
||||||
|
setOf(listOf(int(1), text("UPDATE")), listOf(int(2), text("DELETE")), listOf(int(3), text("INSERT"))),
|
||||||
|
result.returning!!.rows.toSet()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun noRowMatchingAnyClauseReturnsAnEmptyReturningResultNotANullOne() {
|
||||||
|
setUpAccountsAndStaging()
|
||||||
|
run("INSERT INTO accounts VALUES (1, 100)")
|
||||||
|
run("INSERT INTO staging VALUES (1, 5)")
|
||||||
|
val result = exec(
|
||||||
|
"MERGE INTO accounts USING staging ON accounts.id = staging.id " +
|
||||||
|
"WHEN MATCHED AND staging.amount > 1000 THEN UPDATE SET balance = 0 " +
|
||||||
|
"RETURNING *"
|
||||||
|
) as MergeResult
|
||||||
|
assertEquals(emptyList(), result.returning!!.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mergeActionIsRejectedOutsideOfAMergesOwnReturningClause() {
|
||||||
|
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
|
||||||
|
run("INSERT INTO t VALUES (1)")
|
||||||
|
val error = assertFailsWith<KestrelException> { select("SELECT MERGE_ACTION() FROM t") }
|
||||||
|
assertTrue(error.message!!.contains("MERGE_ACTION"), error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mergeActionInAPlainInsertReturningIsRejectedOnceARowActuallyReturns() {
|
||||||
|
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("INSERT INTO t VALUES (1) RETURNING MERGE_ACTION()")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("MERGE_ACTION"), error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun returningColumnValidationRunsUpFrontEvenWhenNoSourceRowEverReachesAMatchingClause() {
|
||||||
|
setUpAccountsAndStaging()
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run(
|
||||||
|
"MERGE INTO accounts USING staging ON accounts.id = staging.id " +
|
||||||
|
"WHEN MATCHED THEN DELETE RETURNING nope"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("no such column"), error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user