| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- MODULE test_blob ;
- (*
- m2SQLITE test: incremental BLOB I/O and the online backup API.
- Covers open/read/write/reopen/bytes/close, the read-only write
- error, direct backup paging, and the BackupToFile helper with
- a temp file that is removed afterwards.
- *)
- FROM SYSTEM IMPORT ADDRESS, ADR;
- FROM SQLite IMPORT DbHandle, StmtHandle, BlobHandle, BackupHandle,
- SQLiteOk, SQLiteRow, SQLiteDone, SQLiteReadOnly,
- sqlite3_open, sqlite3_close,
- sqlite3_prepare_v2, sqlite3_step, sqlite3_finalize,
- sqlite3_last_insert_rowid,
- sqlite3_column_int,
- sqlite3_blob_open, sqlite3_blob_reopen, sqlite3_blob_close,
- sqlite3_blob_bytes, sqlite3_blob_read, sqlite3_blob_write,
- sqlite3_backup_init, sqlite3_backup_step, sqlite3_backup_finish,
- sqlite3_backup_remaining, sqlite3_backup_pagecount;
- FROM SQLiteUtils IMPORT ErrMsg, ExecSimple, BackupToFile;
- FROM libc IMPORT printf, unlink;
- VAR
- db, db2: DbHandle; stmt: StmtHandle;
- bh, bh2: BlobHandle; bk: BackupHandle;
- row1, row2: LONGINT;
- wbuf: ARRAY [0..15] OF CHAR;
- rbuf: ARRAY [0..255] OF CHAR;
- path: ARRAY [0..63] OF CHAR;
- i: INTEGER;
- PROCEDURE fail (what: ARRAY OF CHAR);
- VAR e: ARRAY [0..255] OF CHAR;
- BEGIN
- ErrMsg(db, e);
- printf("FAIL %s: %s\n", what, e);
- HALT(1)
- END fail;
- PROCEDURE check (rc: INTEGER; what: ARRAY OF CHAR);
- BEGIN
- IF rc # SQLiteOk THEN fail(what) END
- END check;
- PROCEDURE RemoveFile (name: ARRAY OF CHAR);
- VAR buf: ARRAY [0..255] OF CHAR; n: INTEGER; rc: INTEGER;
- BEGIN
- n := 0;
- WHILE (VAL(CARDINAL, n) <= HIGH(buf))
- AND (VAL(CARDINAL, n) <= HIGH(name))
- AND (name[n] # 0C) DO
- buf[n] := name[n];
- INC(n)
- END;
- IF VAL(CARDINAL, n) <= HIGH(buf) THEN buf[n] := 0C END;
- rc := unlink(ADR(buf))
- END RemoveFile;
- PROCEDURE countFiles (h: DbHandle; what: ARRAY OF CHAR) : INTEGER;
- VAR st: StmtHandle; n, rc: INTEGER;
- BEGIN
- rc := sqlite3_prepare_v2(h, "SELECT COUNT(*) FROM files;",
- -1, st, NIL);
- IF rc # SQLiteOk THEN fail(what) END;
- IF sqlite3_step(st) # SQLiteRow THEN fail(what) END;
- n := sqlite3_column_int(st, 0);
- IF sqlite3_finalize(st) # SQLiteOk THEN fail(what) END;
- RETURN n
- END countFiles;
- BEGIN
- FOR i := 0 TO 15 DO
- wbuf[i] := CHR(ORD('A') + VAL(CARDINAL, i))
- END;
- check(sqlite3_open(":memory:", db), "open");
- check(ExecSimple(db, "CREATE TABLE files(name TEXT, data BLOB);"),
- "create");
- check(ExecSimple(db, "INSERT INTO files VALUES('a', zeroblob(256));"),
- "seed1");
- row1 := sqlite3_last_insert_rowid(db);
- check(ExecSimple(db, "INSERT INTO files VALUES('b', zeroblob(16));"),
- "seed2");
- row2 := sqlite3_last_insert_rowid(db);
- (* read-write handle, size check, write, read back *)
- check(sqlite3_blob_open(db, "main", "files", "data", row1, 1, bh),
- "blob open");
- IF sqlite3_blob_bytes(bh) # 256 THEN fail("blob bytes") END;
- check(sqlite3_blob_write(bh, ADR(wbuf), 16, 10), "blob write");
- check(sqlite3_blob_read(bh, ADR(rbuf), 16, 10), "blob read");
- FOR i := 0 TO 15 DO
- IF rbuf[i] # wbuf[i] THEN fail("blob roundtrip") END
- END;
- printf("blob roundtrip ok\n");
- (* move the handle to the second row *)
- check(sqlite3_blob_reopen(bh, row2), "blob reopen");
- IF sqlite3_blob_bytes(bh) # 16 THEN fail("reopen bytes") END;
- check(sqlite3_blob_close(bh), "blob close");
- (* read-only handle must refuse writes *)
- check(sqlite3_blob_open(db, "main", "files", "data", row1, 0, bh2),
- "ro open");
- IF sqlite3_blob_write(bh2, ADR(wbuf), 1, 0) # SQLiteReadOnly THEN
- fail("ro write accepted")
- END;
- printf("readonly guard ok\n");
- (* close relays the handle's pending write error, but the
- handle is released regardless *)
- i := sqlite3_blob_close(bh2);
- printf("ro close rc %d\n", i);
- (* direct backup API into a second memory database *)
- check(sqlite3_open(":memory:", db2), "open dst");
- bk := sqlite3_backup_init(db2, "main", db, "main");
- IF bk = NIL THEN fail("backup init") END;
- i := sqlite3_backup_step(bk, -1);
- printf("backup step %d remaining %d pages %d\n",
- i, sqlite3_backup_remaining(bk),
- sqlite3_backup_pagecount(bk));
- IF i # SQLiteDone THEN fail("backup step") END;
- IF sqlite3_backup_remaining(bk) # 0 THEN fail("remaining") END;
- IF sqlite3_backup_pagecount(bk) <= 0 THEN fail("pagecount") END;
- check(sqlite3_backup_finish(bk), "backup finish");
- IF countFiles(db2, "verify") # 2 THEN fail("backup content") END;
- printf("backup copy ok\n");
- check(sqlite3_close(db2), "close dst");
- (* helper to a temp file, verified and removed *)
- path[0] := '/'; path[1] := 't'; path[2] := 'm'; path[3] := 'p';
- path[4] := '/'; path[5] := 'm'; path[6] := '2'; path[7] := 's';
- path[8] := 'q'; path[9] := 'l'; path[10] := 'i'; path[11] := 't';
- path[12] := 'e'; path[13] := '_'; path[14] := 'b'; path[15] := 'k';
- path[16] := '.'; path[17] := 'd'; path[18] := 'b'; path[19] := 0C;
- check(BackupToFile(db, path), "backup file");
- check(sqlite3_open(path, db2), "open file");
- IF countFiles(db2, "file content") # 2 THEN fail("file rows") END;
- check(sqlite3_close(db2), "close file");
- RemoveFile(path);
- printf("backup file ok\n");
- check(sqlite3_close(db), "close");
- printf("PASS test_blob\n")
- END test_blob.
|