test: cover viewDefinitionSurvivesReopenAfterCheckpointAndStaysLiveOv...
This commit is contained in:
1 parent
d882371038
commit
9c87f22cbb
1 file changed
+173
@@ -199,4 +199,177 @@ class PersistenceTest {
|
|||||||
assertContains(output.toString(), "1")
|
assertContains(output.toString(), "1")
|
||||||
assertContains(output.toString(), "2")
|
assertContains(output.toString(), "2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun viewDefinitionSurvivesReopenAfterCheckpointAndStaysLiveOverNewData() {
|
||||||
|
val dir = tempDir()
|
||||||
|
runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER);
|
||||||
|
INSERT INTO users VALUES (1, 'ada', 36), (2, 'linus', 54);
|
||||||
|
CREATE VIEW adults AS SELECT id, name FROM users WHERE age >= 40;
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
val afterCheckpoint = runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
SELECT name FROM adults ORDER BY id ASC;
|
||||||
|
INSERT INTO users VALUES (3, 'grace', 85);
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
assertContains(afterCheckpoint, "linus")
|
||||||
|
assertTrue(!afterCheckpoint.contains("ada"), "view predicate must exclude rows under the age threshold")
|
||||||
|
|
||||||
|
val afterInsert = runScript(dir, "SELECT name FROM adults ORDER BY id ASC;\n.views\n.exit\n")
|
||||||
|
assertContains(afterInsert, "grace")
|
||||||
|
assertContains(afterInsert, "adults")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropViewSurvivesReopenWithoutACheckpoint() {
|
||||||
|
val dir = tempDir()
|
||||||
|
val persistence = Persistence(dir)
|
||||||
|
persistence.appendWal(
|
||||||
|
listOf(
|
||||||
|
"CREATE TABLE t (id INTEGER);",
|
||||||
|
"CREATE VIEW v AS SELECT id FROM t;",
|
||||||
|
"DROP VIEW v;"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val output = StringBuilder()
|
||||||
|
Repl(output, output, Persistence(dir)).run(".views\n.exit\n".reader().buffered())
|
||||||
|
assertContains(output.toString(), "no views")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updatesUsingScalarFunctionsAndPredicatesSurviveReopen() {
|
||||||
|
val dir = tempDir()
|
||||||
|
runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, bio TEXT);
|
||||||
|
INSERT INTO users VALUES (1, 'ada', 'mathematician'), (2, 'linus', NULL), (3, 'grace', 'admiral');
|
||||||
|
UPDATE users SET name = UPPER(name) WHERE bio IS NOT NULL;
|
||||||
|
DELETE FROM users WHERE name IN ('linus') OR bio LIKE '%admiral';
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
val reopened = runScript(dir, "SELECT name FROM users ORDER BY id ASC;\n.exit\n")
|
||||||
|
assertContains(reopened, "ADA")
|
||||||
|
assertTrue(!reopened.contains("linus"), "row matching NOT IN predicate must stay deleted")
|
||||||
|
assertTrue(!reopened.contains("grace") && !reopened.contains("GRACE"), "row matching LIKE predicate must stay deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decimalAndDateColumnsSurviveReopenAfterCheckpoint() {
|
||||||
|
val dir = tempDir()
|
||||||
|
runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
CREATE TABLE orders (id INTEGER PRIMARY KEY, amount DECIMAL(10, 2), placed DATE);
|
||||||
|
INSERT INTO orders VALUES (1, 19.9, CAST('2024-03-01' AS DATE));
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
val reopened = runScript(dir, "SELECT amount, placed FROM orders;\n.exit\n")
|
||||||
|
assertContains(reopened, "19.90")
|
||||||
|
assertContains(reopened, "2024-03-01")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun decimalAndDateColumnsSurviveReopenWithoutACheckpointViaWalReplay() {
|
||||||
|
val dir = tempDir()
|
||||||
|
val persistence = Persistence(dir)
|
||||||
|
persistence.appendWal(
|
||||||
|
listOf(
|
||||||
|
"CREATE TABLE orders (id INTEGER PRIMARY KEY, amount DECIMAL(10, 2), placed DATE);",
|
||||||
|
"INSERT INTO orders VALUES (1, CAST('42.5' AS DECIMAL(10, 2)), CAST('2024-03-01' AS DATE));"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val output = StringBuilder()
|
||||||
|
Repl(output, output, Persistence(dir)).run("SELECT amount, placed FROM orders;\n.exit\n".reader().buffered())
|
||||||
|
assertContains(output.toString(), "42.50")
|
||||||
|
assertContains(output.toString(), "2024-03-01")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun foreignKeyAndCheckConstraintsSurviveReopenAfterCheckpoint() {
|
||||||
|
val dir = tempDir()
|
||||||
|
runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
CREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT);
|
||||||
|
CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id) ON DELETE CASCADE, age INTEGER CHECK (age > 0));
|
||||||
|
INSERT INTO departments VALUES (1, 'eng');
|
||||||
|
INSERT INTO employees VALUES (1, 1, 30);
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
val afterConstraintChecks = runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
INSERT INTO employees VALUES (2, 99, 30);
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
assertContains(afterConstraintChecks, "Error")
|
||||||
|
assertContains(afterConstraintChecks, "FOREIGN KEY")
|
||||||
|
|
||||||
|
val afterCascade = runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
DELETE FROM departments WHERE id = 1;
|
||||||
|
SELECT * FROM employees;
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
assertContains(afterCascade, "0 rows")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun uniqueConstraintsSurviveReopenAfterCheckpoint() {
|
||||||
|
val dir = tempDir()
|
||||||
|
runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT UNIQUE, a INTEGER, b INTEGER, UNIQUE (a, b));
|
||||||
|
INSERT INTO users VALUES (1, 'ada@example.com', 1, 1);
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
val afterReopen = runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
INSERT INTO users VALUES (2, 'ada@example.com', 2, 2);
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
assertContains(afterReopen, "Error")
|
||||||
|
assertContains(afterReopen, "UNIQUE constraint")
|
||||||
|
|
||||||
|
val afterCompositeViolation = runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
INSERT INTO users VALUES (3, 'grace@example.com', 1, 1);
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
assertContains(afterCompositeViolation, "Error")
|
||||||
|
assertContains(afterCompositeViolation, "UNIQUE constraint")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun uniqueIndexSurvivesReopenAfterCheckpointAndKeepsEnforcingAndAccelerating() {
|
||||||
|
val dir = tempDir()
|
||||||
|
runScript(
|
||||||
|
dir,
|
||||||
|
"""
|
||||||
|
CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT);
|
||||||
|
INSERT INTO users VALUES (1, 'ada@example.com');
|
||||||
|
CREATE UNIQUE INDEX idx_email ON users (email);
|
||||||
|
.exit
|
||||||
|
""".trimIndent()
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user