test_blob.mod 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. MODULE test_blob ;
  2. (*
  3. m2SQLITE test: incremental BLOB I/O and the online backup API.
  4. Covers open/read/write/reopen/bytes/close, the read-only write
  5. error, direct backup paging, and the BackupToFile helper with
  6. a temp file that is removed afterwards.
  7. *)
  8. FROM SYSTEM IMPORT ADDRESS, ADR;
  9. FROM SQLite IMPORT DbHandle, StmtHandle, BlobHandle, BackupHandle,
  10. SQLiteOk, SQLiteRow, SQLiteDone, SQLiteReadOnly,
  11. sqlite3_open, sqlite3_close,
  12. sqlite3_prepare_v2, sqlite3_step, sqlite3_finalize,
  13. sqlite3_last_insert_rowid,
  14. sqlite3_column_int,
  15. sqlite3_blob_open, sqlite3_blob_reopen, sqlite3_blob_close,
  16. sqlite3_blob_bytes, sqlite3_blob_read, sqlite3_blob_write,
  17. sqlite3_backup_init, sqlite3_backup_step, sqlite3_backup_finish,
  18. sqlite3_backup_remaining, sqlite3_backup_pagecount;
  19. FROM SQLiteUtils IMPORT ErrMsg, ExecSimple, BackupToFile;
  20. FROM libc IMPORT printf, unlink;
  21. VAR
  22. db, db2: DbHandle; stmt: StmtHandle;
  23. bh, bh2: BlobHandle; bk: BackupHandle;
  24. row1, row2: LONGINT;
  25. wbuf: ARRAY [0..15] OF CHAR;
  26. rbuf: ARRAY [0..255] OF CHAR;
  27. path: ARRAY [0..63] OF CHAR;
  28. i: INTEGER;
  29. PROCEDURE fail (what: ARRAY OF CHAR);
  30. VAR e: ARRAY [0..255] OF CHAR;
  31. BEGIN
  32. ErrMsg(db, e);
  33. printf("FAIL %s: %s\n", what, e);
  34. HALT(1)
  35. END fail;
  36. PROCEDURE check (rc: INTEGER; what: ARRAY OF CHAR);
  37. BEGIN
  38. IF rc # SQLiteOk THEN fail(what) END
  39. END check;
  40. PROCEDURE RemoveFile (name: ARRAY OF CHAR);
  41. VAR buf: ARRAY [0..255] OF CHAR; n: INTEGER; rc: INTEGER;
  42. BEGIN
  43. n := 0;
  44. WHILE (VAL(CARDINAL, n) <= HIGH(buf))
  45. AND (VAL(CARDINAL, n) <= HIGH(name))
  46. AND (name[n] # 0C) DO
  47. buf[n] := name[n];
  48. INC(n)
  49. END;
  50. IF VAL(CARDINAL, n) <= HIGH(buf) THEN buf[n] := 0C END;
  51. rc := unlink(ADR(buf))
  52. END RemoveFile;
  53. PROCEDURE countFiles (h: DbHandle; what: ARRAY OF CHAR) : INTEGER;
  54. VAR st: StmtHandle; n, rc: INTEGER;
  55. BEGIN
  56. rc := sqlite3_prepare_v2(h, "SELECT COUNT(*) FROM files;",
  57. -1, st, NIL);
  58. IF rc # SQLiteOk THEN fail(what) END;
  59. IF sqlite3_step(st) # SQLiteRow THEN fail(what) END;
  60. n := sqlite3_column_int(st, 0);
  61. IF sqlite3_finalize(st) # SQLiteOk THEN fail(what) END;
  62. RETURN n
  63. END countFiles;
  64. BEGIN
  65. FOR i := 0 TO 15 DO
  66. wbuf[i] := CHR(ORD('A') + VAL(CARDINAL, i))
  67. END;
  68. check(sqlite3_open(":memory:", db), "open");
  69. check(ExecSimple(db, "CREATE TABLE files(name TEXT, data BLOB);"),
  70. "create");
  71. check(ExecSimple(db, "INSERT INTO files VALUES('a', zeroblob(256));"),
  72. "seed1");
  73. row1 := sqlite3_last_insert_rowid(db);
  74. check(ExecSimple(db, "INSERT INTO files VALUES('b', zeroblob(16));"),
  75. "seed2");
  76. row2 := sqlite3_last_insert_rowid(db);
  77. (* read-write handle, size check, write, read back *)
  78. check(sqlite3_blob_open(db, "main", "files", "data", row1, 1, bh),
  79. "blob open");
  80. IF sqlite3_blob_bytes(bh) # 256 THEN fail("blob bytes") END;
  81. check(sqlite3_blob_write(bh, ADR(wbuf), 16, 10), "blob write");
  82. check(sqlite3_blob_read(bh, ADR(rbuf), 16, 10), "blob read");
  83. FOR i := 0 TO 15 DO
  84. IF rbuf[i] # wbuf[i] THEN fail("blob roundtrip") END
  85. END;
  86. printf("blob roundtrip ok\n");
  87. (* move the handle to the second row *)
  88. check(sqlite3_blob_reopen(bh, row2), "blob reopen");
  89. IF sqlite3_blob_bytes(bh) # 16 THEN fail("reopen bytes") END;
  90. check(sqlite3_blob_close(bh), "blob close");
  91. (* read-only handle must refuse writes *)
  92. check(sqlite3_blob_open(db, "main", "files", "data", row1, 0, bh2),
  93. "ro open");
  94. IF sqlite3_blob_write(bh2, ADR(wbuf), 1, 0) # SQLiteReadOnly THEN
  95. fail("ro write accepted")
  96. END;
  97. printf("readonly guard ok\n");
  98. (* close relays the handle's pending write error, but the
  99. handle is released regardless *)
  100. i := sqlite3_blob_close(bh2);
  101. printf("ro close rc %d\n", i);
  102. (* direct backup API into a second memory database *)
  103. check(sqlite3_open(":memory:", db2), "open dst");
  104. bk := sqlite3_backup_init(db2, "main", db, "main");
  105. IF bk = NIL THEN fail("backup init") END;
  106. i := sqlite3_backup_step(bk, -1);
  107. printf("backup step %d remaining %d pages %d\n",
  108. i, sqlite3_backup_remaining(bk),
  109. sqlite3_backup_pagecount(bk));
  110. IF i # SQLiteDone THEN fail("backup step") END;
  111. IF sqlite3_backup_remaining(bk) # 0 THEN fail("remaining") END;
  112. IF sqlite3_backup_pagecount(bk) <= 0 THEN fail("pagecount") END;
  113. check(sqlite3_backup_finish(bk), "backup finish");
  114. IF countFiles(db2, "verify") # 2 THEN fail("backup content") END;
  115. printf("backup copy ok\n");
  116. check(sqlite3_close(db2), "close dst");
  117. (* helper to a temp file, verified and removed *)
  118. path[0] := '/'; path[1] := 't'; path[2] := 'm'; path[3] := 'p';
  119. path[4] := '/'; path[5] := 'm'; path[6] := '2'; path[7] := 's';
  120. path[8] := 'q'; path[9] := 'l'; path[10] := 'i'; path[11] := 't';
  121. path[12] := 'e'; path[13] := '_'; path[14] := 'b'; path[15] := 'k';
  122. path[16] := '.'; path[17] := 'd'; path[18] := 'b'; path[19] := 0C;
  123. check(BackupToFile(db, path), "backup file");
  124. check(sqlite3_open(path, db2), "open file");
  125. IF countFiles(db2, "file content") # 2 THEN fail("file rows") END;
  126. check(sqlite3_close(db2), "close file");
  127. RemoveFile(path);
  128. printf("backup file ok\n");
  129. check(sqlite3_close(db), "close");
  130. printf("PASS test_blob\n")
  131. END test_blob.