-- sqlite3.open against a database that exists only inside a .game. Asked to read, Singe opens it -- where it lies, through a read-only SQLite VFS over the packed rows; asked to write, it copies the -- database once to the game's data directory and opens the copy, then and from then on. -- -- It has to be run packed, with no loose copy beside the container -- a loose file shadows the -- packed one and the whole test then proves nothing -- so from a directory holding this folder: -- -- Singe -P packedSql packedSql.game -- rm -rf packedSql -- Singe -w -C 720x480 packedSql.game -- Singe -w -C 720x480 packedSql.game -- -- Each run prints one line: SQLTEST pass=N fail=0 count=C. The count is the rows the writable -- copy holds after this run adds one: 4 the first time, 5 the second, since the copy persists. -- The .game itself is never written. Every failure prints what it wanted first. dofile("Singe/Framework.singe") local sqlite3 = require("sqlite3") local pass = 0 local fail = 0 local function check(what, got, want) if got == want then pass = pass + 1 else fail = fail + 1 debugPrint(string.format("FAIL %s: got %s, wanted %s", what, tostring(got), tostring(want))) end end local function count(db) local n = nil for row in db:nrows("SELECT COUNT(*) AS n FROM levels") do n = row.n end return n end -- Read only: the packed database, read where it lies. On a later run the writable copy exists and -- is what a name resolves to, as io.open's does; either way the first three rows are the packed ones. local reader = sqlite3.open("packedSql/levels.db", sqlite3.OPEN_READONLY) local names = {} check("read-only open", reader ~= nil, true) for row in reader:nrows("SELECT name FROM levels WHERE id <= 3 ORDER BY id") do names[#names + 1] = row.name end check("rows read", table.concat(names, ","), "first,second,third") check("a read-only open refuses a write", reader:exec("INSERT INTO levels VALUES (99, 'nope')") == sqlite3.OK, false) reader:close() -- Writable: copied out to the data directory the first time, and the copy changed. local writer = sqlite3.open("packedSql/levels.db") local before = writer and count(writer) check("writable open", writer ~= nil, true) check("a write", writer:exec("INSERT INTO levels (name) VALUES ('added')"), sqlite3.OK) local after = count(writer) check("the write is there", after, before + 1) writer:close() -- The copy is what the name means now, read only or not. local again = sqlite3.open("packedSql/levels.db", sqlite3.OPEN_READONLY) check("the copy is what a read now opens", count(again), after) again:close() -- What is not there, and the names SQLite keeps for itself. check("a missing packed name does not open", sqlite3.open("packedSql/missing.db", sqlite3.OPEN_READONLY), nil) local memory = sqlite3.open(":memory:") check(":memory: still opens", memory ~= nil, true) check(":memory: still works", memory:exec("CREATE TABLE t (x)"), sqlite3.OK) memory:close() debugPrint(string.format("SQLTEST pass=%d fail=%d count=%d", pass, fail, after or -1)) singeQuit()