You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
4.1 KiB
111 lines
4.1 KiB
-- Exercise of the sqlite stdlib module.
|
|
|
|
local function assert_eq(actual, expected, msg)
|
|
if actual ~= expected then
|
|
error(string.format("%s: expected %s, got %s",
|
|
msg or "assertion failed", tostring(expected), tostring(actual)), 2)
|
|
end
|
|
end
|
|
|
|
print("=== connect (in-memory) ===")
|
|
local con = sqlite.connect(":memory:")
|
|
|
|
print("=== CREATE TABLE / INSERT ===")
|
|
con:execute([[
|
|
CREATE TABLE people (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT,
|
|
age INTEGER,
|
|
score REAL,
|
|
vip INTEGER,
|
|
notes TEXT
|
|
)
|
|
]])
|
|
|
|
-- Positional params, every supported type.
|
|
local changed = con:execute(
|
|
"INSERT INTO people (name, age, score, vip, notes) VALUES (?, ?, ?, ?, ?)",
|
|
{"Alice", 30, 9.5, true, "first"}
|
|
)
|
|
assert_eq(changed, 1, "execute returns rows changed")
|
|
assert_eq(con:changes(), 1, "changes() after insert")
|
|
local first_id = con:lastInsertRowid()
|
|
assert_eq(first_id, 1, "lastInsertRowid")
|
|
|
|
-- Named params (mix of :name in SQL).
|
|
con:execute(
|
|
"INSERT INTO people (name, age, score, vip, notes) VALUES (:name, :age, :score, :vip, :notes)",
|
|
{name = "Bob", age = 42, score = 7.25, vip = false, notes = utils.NULL}
|
|
)
|
|
assert_eq(con:lastInsertRowid(), 2, "lastInsertRowid after second insert")
|
|
|
|
print("=== SELECT (query) ===")
|
|
local rows = con:query("SELECT * FROM people ORDER BY id")
|
|
assert_eq(#rows, 2, "query returns 2 rows")
|
|
assert_eq(rows[1].name, "Alice", "row 1 name")
|
|
assert_eq(rows[1].age, 30, "row 1 age (integer round-trip)")
|
|
assert_eq(rows[1].score, 9.5, "row 1 score (real round-trip)")
|
|
assert_eq(rows[1].vip, 1, "row 1 vip (boolean stored as 1)")
|
|
assert_eq(rows[1].notes, "first", "row 1 notes (text round-trip)")
|
|
-- A SQL NULL comes back as nil (absent key).
|
|
assert_eq(rows[2].notes, nil, "row 2 notes is nil (NULL round-trip)")
|
|
|
|
print("=== queryOne ===")
|
|
local bob = con:queryOne("SELECT * FROM people WHERE name = ?", {"Bob"})
|
|
assert(bob ~= nil, "queryOne found Bob")
|
|
assert_eq(bob.age, 42, "queryOne Bob age")
|
|
|
|
local missing = con:queryOne("SELECT * FROM people WHERE name = ?", {"Nobody"})
|
|
assert_eq(missing, nil, "queryOne returns nil for no match")
|
|
|
|
print("=== UPDATE / DELETE ===")
|
|
local n = con:execute("UPDATE people SET age = age + 1 WHERE name = :who", {who = "Alice"})
|
|
assert_eq(n, 1, "update affected 1 row")
|
|
assert_eq(con:queryOne("SELECT age FROM people WHERE name = ?", {"Alice"}).age, 31, "age incremented")
|
|
|
|
n = con:execute("DELETE FROM people WHERE name = ?", {"Bob"})
|
|
assert_eq(n, 1, "delete affected 1 row")
|
|
assert_eq(#con:query("SELECT * FROM people"), 1, "one row left after delete")
|
|
|
|
print("=== transaction(fn) — commit ===")
|
|
con:transaction(function()
|
|
con:execute("INSERT INTO people (name, age) VALUES (?, ?)", {"Carol", 25})
|
|
con:execute("INSERT INTO people (name, age) VALUES (?, ?)", {"Dave", 28})
|
|
end)
|
|
assert_eq(#con:query("SELECT * FROM people"), 3, "two rows committed")
|
|
|
|
print("=== transaction(fn) — rollback on error ===")
|
|
local ok, err = pcall(function()
|
|
con:transaction(function()
|
|
con:execute("INSERT INTO people (name, age) VALUES (?, ?)", {"Eve", 99})
|
|
error("boom")
|
|
end)
|
|
end)
|
|
assert_eq(ok, false, "transaction propagated the error")
|
|
assert(tostring(err):find("boom"), "original error message preserved")
|
|
assert_eq(#con:query("SELECT * FROM people"), 3, "failed transaction rolled back")
|
|
|
|
print("=== manual begin / commit ===")
|
|
con:begin()
|
|
con:execute("INSERT INTO people (name, age) VALUES (?, ?)", {"Frank", 50})
|
|
con:commit()
|
|
assert_eq(#con:query("SELECT * FROM people"), 4, "manual commit persisted")
|
|
|
|
print("=== manual begin / rollback ===")
|
|
con:begin()
|
|
con:execute("INSERT INTO people (name, age) VALUES (?, ?)", {"Grace", 60})
|
|
con:rollback()
|
|
assert_eq(#con:query("SELECT * FROM people"), 4, "manual rollback discarded")
|
|
|
|
print("=== mixed param table is rejected ===")
|
|
local mixed_ok = pcall(function()
|
|
con:query("SELECT 1 WHERE 1 = ?", {1, key = "x"})
|
|
end)
|
|
assert_eq(mixed_ok, false, "mixed positional/named params error")
|
|
|
|
print("=== close ===")
|
|
con:close()
|
|
local closed_ok = pcall(function() con:query("SELECT 1") end)
|
|
assert_eq(closed_ok, false, "use after close errors")
|
|
|
|
print("\nAll sqlite checks passed.")
|
|
|