test: cover mergePrintsHowManyRowsWereInsertedUpdatedAndDeleted
This commit is contained in:
1 parent
3adb4b1eda
commit
e18d5cdc16
1 file changed
+196
@@ -26,6 +26,22 @@ class ReplTest {
|
||||
assertTrue(output.indexOf("grace") < output.indexOf("linus"), "grace should be listed before linus")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mergePrintsHowManyRowsWereInsertedUpdatedAndDeleted() {
|
||||
val script = """
|
||||
CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER);
|
||||
CREATE TABLE staging (id INTEGER, amount INTEGER);
|
||||
INSERT INTO accounts VALUES (1, 100), (2, 200);
|
||||
INSERT INTO staging VALUES (1, 10), (2, -999), (3, 5);
|
||||
MERGE INTO accounts USING staging ON accounts.id = staging.id
|
||||
WHEN MATCHED AND accounts.balance + staging.amount < 0 THEN DELETE
|
||||
WHEN MATCHED THEN UPDATE SET balance = accounts.balance + staging.amount
|
||||
WHEN NOT MATCHED THEN INSERT (id, balance) VALUES (staging.id, staging.amount);
|
||||
""".trimIndent()
|
||||
val output = runScript(script)
|
||||
assertContains(output, "1 row inserted, 1 row updated, 1 row deleted")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun metaTablesListsCreatedTables() {
|
||||
val output = runScript("CREATE TABLE alpha (id INTEGER);\nCREATE TABLE beta (id INTEGER);\n.tables\n")
|
||||
@@ -41,6 +57,26 @@ class ReplTest {
|
||||
assertContains(output, "name TEXT NOT NULL")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun metaSchemaListsUniqueIndexesOnTheTable() {
|
||||
val output = runScript(
|
||||
"CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT NOT NULL);\n" +
|
||||
"CREATE UNIQUE INDEX idx_email ON users (email);\n" +
|
||||
".schema users\n"
|
||||
)
|
||||
assertContains(output, "CREATE UNIQUE INDEX idx_email ON users (email);")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun metaSchemaListsCompositeIndexesOnTheTable() {
|
||||
val output = runScript(
|
||||
"CREATE TABLE orders (id INTEGER PRIMARY KEY, region TEXT, seq INTEGER);\n" +
|
||||
"CREATE INDEX idx_region_seq ON orders (region, seq);\n" +
|
||||
".schema orders\n"
|
||||
)
|
||||
assertContains(output, "CREATE INDEX idx_region_seq ON orders (region, seq);")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseErrorIsReportedAndReplContinues() {
|
||||
val output = runScript("SELCT bad;\nCREATE TABLE t (a INTEGER);\n.tables\n")
|
||||
@@ -54,4 +90,164 @@ class ReplTest {
|
||||
assertContains(output, "Table seen created")
|
||||
assertTrue(!output.contains("unseen"), "statements after .exit must not run")
|
||||
}
|
||||
|
||||
/**
|
||||
* The REPL buffers input line by line and (before this was fixed) treated ANY line ending in
|
||||
* `;` as "statement complete" - a trigger body's inner `INSERT ... ;` would have tripped that
|
||||
* check and been parsed as an incomplete CREATE TRIGGER, well before the closing `END;` was
|
||||
* even typed. Feeding the trigger definition across several `readLine()` calls (rather than as
|
||||
* one line) is the point of this test.
|
||||
*/
|
||||
@Test
|
||||
fun multiLineCreateTriggerIsNotExecutedPrematurelyOnAnInnerSemicolon() {
|
||||
val script = """
|
||||
CREATE TABLE orders (id INTEGER PRIMARY KEY, amount INTEGER);
|
||||
CREATE TABLE audit (order_id INTEGER);
|
||||
CREATE TRIGGER log_insert
|
||||
AFTER INSERT ON orders
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO audit VALUES (NEW.id);
|
||||
END;
|
||||
INSERT INTO orders VALUES (1, 100);
|
||||
SELECT order_id FROM audit;
|
||||
""".trimIndent()
|
||||
val output = runScript(script)
|
||||
assertTrue(!output.contains("Error:"), output)
|
||||
assertContains(output, "Trigger log_insert created")
|
||||
assertContains(output, "1 row")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun metaTriggersListsCreatedTriggers() {
|
||||
val output = runScript(
|
||||
"CREATE TABLE t (id INTEGER);\n" +
|
||||
"CREATE TRIGGER log AFTER INSERT ON t FOR EACH ROW BEGIN DELETE FROM t WHERE 1 = 0; END;\n" +
|
||||
".triggers\n"
|
||||
)
|
||||
assertContains(output, "log")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun metaSequencesListsCreatedSequencesAndReportsEmpty() {
|
||||
val empty = runScript(".sequences\n")
|
||||
assertContains(empty, "no sequences")
|
||||
val output = runScript("CREATE SEQUENCE seq;\n.sequences\n")
|
||||
assertContains(output, "Sequence seq created")
|
||||
assertContains(output, "seq")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createAlterAndDropSequencePrintTheirOwnConfirmations() {
|
||||
val output = runScript(
|
||||
"""
|
||||
CREATE SEQUENCE seq;
|
||||
ALTER SEQUENCE seq RESTART WITH 5;
|
||||
DROP SEQUENCE seq;
|
||||
""".trimIndent()
|
||||
)
|
||||
assertContains(output, "Sequence seq created")
|
||||
assertContains(output, "Sequence seq altered")
|
||||
assertContains(output, "Sequence seq dropped")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nextvalDrivenAutoIncrementWorksEndToEndThroughTheRepl() {
|
||||
val script = """
|
||||
CREATE SEQUENCE order_ids;
|
||||
CREATE TABLE orders (id INTEGER PRIMARY KEY DEFAULT NEXTVAL('order_ids'), item TEXT);
|
||||
INSERT INTO orders (item) VALUES ('a'), ('b');
|
||||
SELECT id, item FROM orders ORDER BY id;
|
||||
""".trimIndent()
|
||||
val output = runScript(script)
|
||||
assertTrue(!output.contains("Error:"), output)
|
||||
assertContains(output, "2 rows inserted")
|
||||
assertContains(output, "2 rows")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun insertReturningPrintsTheAffectedRowsAsATableAfterTheInsertConfirmation() {
|
||||
val script = """
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
|
||||
INSERT INTO users VALUES (1, 'ada') RETURNING id, name;
|
||||
""".trimIndent()
|
||||
val output = runScript(script)
|
||||
assertContains(output, "1 row inserted")
|
||||
assertTrue(
|
||||
output.indexOf("1 row inserted") < output.indexOf("ada"),
|
||||
"the RETURNING table should be printed after the insert confirmation"
|
||||
)
|
||||
assertContains(output, "ada")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateAndDeleteReturningAlsoPrintTheirRowsAfterTheirOwnConfirmation() {
|
||||
val script = """
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
|
||||
INSERT INTO users VALUES (1, 'ada');
|
||||
UPDATE users SET name = 'grace' WHERE id = 1 RETURNING name;
|
||||
DELETE FROM users WHERE id = 1 RETURNING id;
|
||||
""".trimIndent()
|
||||
val output = runScript(script)
|
||||
assertContains(output, "1 row updated")
|
||||
assertContains(output, "grace")
|
||||
assertContains(output, "1 row deleted")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mergeReturningPrintsTheAffectedRowsAsATableAfterTheMergeConfirmation() {
|
||||
val script = """
|
||||
CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER);
|
||||
CREATE TABLE staging (id INTEGER, amount INTEGER);
|
||||
INSERT INTO accounts VALUES (1, 100);
|
||||
INSERT INTO staging VALUES (1, 10), (2, 5);
|
||||
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 id, MERGE_ACTION() AS kind;
|
||||
""".trimIndent()
|
||||
val output = runScript(script)
|
||||
assertContains(output, "1 row inserted, 1 row updated, 0 rows deleted")
|
||||
assertTrue(
|
||||
output.indexOf("1 row inserted, 1 row updated, 0 rows deleted") < output.indexOf("UPDATE"),
|
||||
"the RETURNING table should be printed after the merge confirmation"
|
||||
)
|
||||
assertContains(output, "UPDATE")
|
||||
assertContains(output, "INSERT")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun prepareExecuteAndDeallocatePrintTheirOwnConfirmations() {
|
||||
val script = """
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
|
||||
INSERT INTO users VALUES (1, 'ada');
|
||||
PREPARE byId AS SELECT name FROM users WHERE id = $1;
|
||||
EXECUTE byId(1);
|
||||
DEALLOCATE byId;
|
||||
""".trimIndent()
|
||||
val output = runScript(script)
|
||||
assertContains(output, "Prepared statement byId created")
|
||||
assertContains(output, "ada")
|
||||
assertContains(output, "Prepared statement byId deallocated")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun metaPreparedListsAndThenForgetsAPreparedStatementName() {
|
||||
val before = runScript(
|
||||
"""
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
|
||||
PREPARE byId AS SELECT name FROM users WHERE id = $1;
|
||||
.prepared
|
||||
""".trimIndent()
|
||||
)
|
||||
assertContains(before, "byId")
|
||||
|
||||
val after = runScript(
|
||||
"""
|
||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
|
||||
.prepared
|
||||
""".trimIndent()
|
||||
)
|
||||
assertContains(after, "no prepared statements")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user