showcase_all.mod 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. MODULE showcase_all ;
  2. (*
  3. m2SQLITE showcase: exercises every procedure of the SQLite
  4. binding plus all SQLiteUtils helpers end to end.
  5. 1. library info: version, source id, threadsafe, errstr, complete
  6. 2. open, autocommit, busy timeout, extended codes, limit, open_v2
  7. 3. exec with raw errmsg capture, ExecWithErr failure, errcode paths
  8. 4. prepare_v2 tail capture, prepare_v3 flags, sql text, parameter ids
  9. 5. all bind kinds, step, changes counters, rowid
  10. 6. all column kinds, metadata, busy/reset/rebind, transactions
  11. 7. expanded sql, zeroblob, static destructor, close_v2
  12. 8. custom functions: scalar tax plus aggregate total
  13. 9. blob read/write plus file backup
  14. 10. close
  15. *)
  16. FROM SYSTEM IMPORT ADDRESS, ADR;
  17. FROM SQLite IMPORT DbHandle, StmtHandle, ContextHandle, ValueHandle,
  18. BlobHandle,
  19. SQLiteOk, SQLiteRow, SQLiteDone,
  20. SQLiteInteger, SQLiteFloat, SQLiteText, SQLiteBlob, SQLiteNull,
  21. SQLiteOpenReadWrite, SQLiteOpenCreate,
  22. SQLitePreparePersistent, SQLiteLimitVariableNumber,
  23. SQLiteUtf8, SQLiteDeterministic,
  24. sqlite3_libversion, sqlite3_libversion_number, sqlite3_sourceid,
  25. sqlite3_threadsafe,
  26. sqlite3_open, sqlite3_open_v2, sqlite3_close, sqlite3_close_v2,
  27. sqlite3_exec,
  28. sqlite3_errcode, sqlite3_extended_errcode, sqlite3_errmsg,
  29. sqlite3_errstr, sqlite3_error_offset, sqlite3_free,
  30. sqlite3_complete, sqlite3_busy_timeout, sqlite3_get_autocommit,
  31. sqlite3_changes, sqlite3_total_changes, sqlite3_last_insert_rowid,
  32. sqlite3_extended_result_codes, sqlite3_limit,
  33. sqlite3_prepare_v2, sqlite3_prepare_v3,
  34. sqlite3_step, sqlite3_finalize, sqlite3_reset, sqlite3_clear_bindings,
  35. sqlite3_data_count, sqlite3_column_count,
  36. sqlite3_sql, sqlite3_expanded_sql,
  37. sqlite3_stmt_readonly, sqlite3_stmt_busy,
  38. sqlite3_bind_blob, sqlite3_bind_zeroblob,
  39. sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64,
  40. sqlite3_bind_null, sqlite3_bind_text,
  41. sqlite3_bind_parameter_count, sqlite3_bind_parameter_name,
  42. sqlite3_bind_parameter_index,
  43. sqlite3_column_blob, sqlite3_column_double, sqlite3_column_int,
  44. sqlite3_column_int64, sqlite3_column_text, sqlite3_column_bytes,
  45. sqlite3_column_type, sqlite3_column_name, sqlite3_column_decltype,
  46. sqlite3_create_function,
  47. sqlite3_value_int64, sqlite3_value_double,
  48. sqlite3_result_double, sqlite3_result_int64,
  49. sqlite3_aggregate_context,
  50. sqlite3_blob_open, sqlite3_blob_close,
  51. sqlite3_blob_bytes, sqlite3_blob_read, sqlite3_blob_write;
  52. FROM SQLiteUtils IMPORT CStrToM2, ErrMsg, ErrStr, LibVersionStr,
  53. StaticDestr, TransientDestr,
  54. BindTextCopy, BindBlobCopy, ExecSimple, ExecWithErr, BackupToFile;
  55. FROM libc IMPORT printf, strncpy, unlink;
  56. TYPE
  57. ArgVec = POINTER TO ARRAY [0..255] OF ADDRESS;
  58. TotalPtr = POINTER TO LONGINT;
  59. VAR
  60. db, db2, db3: DbHandle;
  61. stmt: StmtHandle;
  62. bh: BlobHandle;
  63. rc, i, ncols, ndata, busy, ro: INTEGER;
  64. li: LONGINT;
  65. d: REAL;
  66. tp: INTEGER;
  67. tail, msg, p, xsql: ADDRESS;
  68. buf, buf2: ARRAY [0..127] OF CHAR;
  69. blobIn: ARRAY [0..3] OF CHAR;
  70. staticBlob: ARRAY [0..3] OF CHAR;
  71. out: ARRAY [0..15] OF CHAR;
  72. wpat: ARRAY [0..15] OF CHAR;
  73. rback: ARRAY [0..63] OF CHAR;
  74. showInt, showFloat, showText, showBlob, showNull: INTEGER;
  75. PROCEDURE RemoveFile (path: ARRAY OF CHAR);
  76. VAR fbuf: ARRAY [0..255] OF CHAR; n: INTEGER; urc: INTEGER;
  77. BEGIN
  78. n := 0;
  79. WHILE (VAL(CARDINAL, n) <= HIGH(fbuf))
  80. AND (VAL(CARDINAL, n) <= HIGH(path))
  81. AND (path[n] # 0C) DO
  82. fbuf[n] := path[n];
  83. INC(n)
  84. END;
  85. IF VAL(CARDINAL, n) <= HIGH(fbuf) THEN fbuf[n] := 0C END;
  86. urc := unlink(ADR(fbuf))
  87. END RemoveFile;
  88. PROCEDURE fail (what: ARRAY OF CHAR);
  89. VAR e: ARRAY [0..255] OF CHAR;
  90. BEGIN
  91. ErrMsg(db, e);
  92. printf("FAIL %s: %s\n", what, e);
  93. HALT(1)
  94. END fail;
  95. PROCEDURE check (rc: INTEGER; what: ARRAY OF CHAR);
  96. BEGIN
  97. IF rc # SQLiteOk THEN fail(what) END
  98. END check;
  99. PROCEDURE section (n: INTEGER; title: ARRAY OF CHAR);
  100. BEGIN
  101. printf("--- [%d] %s ---\n", n, title)
  102. END section;
  103. PROCEDURE ArgAt (argv: ADDRESS; i: INTEGER) : ValueHandle;
  104. VAR vec: ArgVec;
  105. BEGIN
  106. vec := VAL(ArgVec, argv);
  107. RETURN vec^[i]
  108. END ArgAt;
  109. PROCEDURE tax (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  110. BEGIN
  111. sqlite3_result_double(ctx,
  112. sqlite3_value_double(ArgAt(argv, 0)) * 1.2)
  113. END tax;
  114. PROCEDURE totalStep (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  115. VAR s: TotalPtr;
  116. BEGIN
  117. s := VAL(TotalPtr, sqlite3_aggregate_context(ctx, 8));
  118. s^ := s^ + sqlite3_value_int64(ArgAt(argv, 0))
  119. END totalStep;
  120. PROCEDURE totalFinal (ctx: ContextHandle);
  121. VAR s: TotalPtr;
  122. BEGIN
  123. s := VAL(TotalPtr, sqlite3_aggregate_context(ctx, 0));
  124. sqlite3_result_int64(ctx, s^)
  125. END totalFinal;
  126. BEGIN
  127. staticBlob[0] := 'W'; staticBlob[1] := 'X';
  128. staticBlob[2] := 'Y'; staticBlob[3] := 'Z';
  129. blobIn[0] := 'A'; blobIn[1] := 'B';
  130. blobIn[2] := 'C'; blobIn[3] := 'D';
  131. (* 1. library info *)
  132. section(1, "library info");
  133. LibVersionStr(buf);
  134. printf("version %s number %d\n", buf, sqlite3_libversion_number());
  135. IF sqlite3_libversion_number() <= 0 THEN HALT(1) END;
  136. CStrToM2(sqlite3_libversion(), buf);
  137. CStrToM2(sqlite3_sourceid(), buf2);
  138. printf("sourceid %s\n", buf2);
  139. printf("threadsafe %d\n", sqlite3_threadsafe());
  140. ErrStr(SQLiteOk, buf);
  141. printf("errstr ok %s\n", buf);
  142. CStrToM2(sqlite3_errstr(SQLiteOk), buf);
  143. printf("raw errstr %s\n", buf);
  144. IF sqlite3_complete("SELECT 1;") = 0 THEN
  145. printf("FAIL complete full\n"); HALT(1)
  146. END;
  147. IF sqlite3_complete("SELECT") # 0 THEN
  148. printf("FAIL complete partial\n"); HALT(1)
  149. END;
  150. printf("complete ok\n");
  151. (* 2. open and connection settings *)
  152. section(2, "open and settings");
  153. check(sqlite3_open(":memory:", db), "open");
  154. printf("autocommit %d\n", sqlite3_get_autocommit(db));
  155. check(sqlite3_busy_timeout(db, 500), "busy timeout");
  156. check(sqlite3_extended_result_codes(db, 1), "ext codes");
  157. i := sqlite3_limit(db, SQLiteLimitVariableNumber, -1);
  158. printf("limit variables %d\n", i);
  159. IF i <= 0 THEN printf("FAIL limit\n"); HALT(1) END;
  160. check(sqlite3_open_v2(":memory:", db2,
  161. SQLiteOpenReadWrite + SQLiteOpenCreate,
  162. NIL), "open_v2");
  163. (* 3. exec, including raw errmsg capture and failure paths *)
  164. section(3, "exec and errors");
  165. check(ExecSimple(db, "CREATE TABLE demo(id INTEGER PRIMARY KEY,"
  166. + " small INTEGER, big INTEGER,"
  167. + " score REAL, label TEXT,"
  168. + " payload BLOB, code TEXT);"),
  169. "create");
  170. check(ExecSimple(db, "INSERT INTO demo(small) VALUES(1);"), "seed");
  171. printf("changes %d total %d rowid %ld\n",
  172. sqlite3_changes(db), sqlite3_total_changes(db),
  173. sqlite3_last_insert_rowid(db));
  174. msg := NIL;
  175. rc := sqlite3_exec(db, "INSERT INTO demo(small) VALUES(2);",
  176. NIL, NIL, ADR(msg));
  177. IF rc # SQLiteOk THEN fail("raw exec") END;
  178. IF msg # NIL THEN sqlite3_free(msg) END;
  179. rc := ExecWithErr(db, "INSERT INTO bogus(x) VALUES(1);", buf);
  180. printf("expected failure rc %d err %s\n", rc, buf);
  181. IF rc = SQLiteOk THEN printf("FAIL bad sql ok\n"); HALT(1) END;
  182. printf("errcode %d ext %d offset %d\n",
  183. sqlite3_errcode(db), sqlite3_extended_errcode(db),
  184. sqlite3_error_offset(db));
  185. CStrToM2(sqlite3_errmsg(db), buf);
  186. printf("raw errmsg %s\n", buf);
  187. (* 4. prepare variants, tail, sql text, parameters *)
  188. section(4, "prepare and parameters");
  189. tail := NIL;
  190. check(sqlite3_prepare_v2(db, "SELECT small FROM demo; SELECT 99;",
  191. -1, stmt, ADR(tail)), "prep tail");
  192. IF tail = NIL THEN printf("FAIL tail nil\n"); HALT(1) END;
  193. CStrToM2(tail, buf);
  194. printf("tail %s\n", buf);
  195. CStrToM2(sqlite3_sql(stmt), buf);
  196. printf("sql %s\n", buf);
  197. check(sqlite3_finalize(stmt), "fin tail");
  198. check(sqlite3_prepare_v3(db, "SELECT :a, ?, :b;",
  199. -1, SQLitePreparePersistent,
  200. stmt, NIL), "prep v3");
  201. printf("param count %d\n", sqlite3_bind_parameter_count(stmt));
  202. IF sqlite3_bind_parameter_count(stmt) # 3 THEN HALT(1) END;
  203. CStrToM2(sqlite3_bind_parameter_name(stmt, 1), buf);
  204. printf("param1 %s\n", buf);
  205. IF sqlite3_bind_parameter_name(stmt, 2) # NIL THEN
  206. printf("FAIL anon param named\n"); HALT(1)
  207. END;
  208. i := sqlite3_bind_parameter_index(stmt, ":b");
  209. printf("param :b index %d\n", i);
  210. IF i # 3 THEN HALT(1) END;
  211. ro := sqlite3_stmt_readonly(stmt);
  212. printf("readonly select %d\n", ro);
  213. IF ro = 0 THEN HALT(1) END;
  214. check(sqlite3_finalize(stmt), "fin v3");
  215. check(sqlite3_prepare_v2(db, "INSERT INTO demo(small) VALUES(1);",
  216. -1, stmt, NIL), "prep ins");
  217. IF sqlite3_stmt_readonly(stmt) # 0 THEN
  218. printf("FAIL readonly insert\n"); HALT(1)
  219. END;
  220. check(sqlite3_finalize(stmt), "fin ins");
  221. (* 5. all bind kinds *)
  222. section(5, "binds and step");
  223. check(sqlite3_prepare_v2(db, "DELETE FROM demo;", -1, stmt, NIL),
  224. "prep delete");
  225. IF sqlite3_step(stmt) # SQLiteDone THEN HALT(1) END;
  226. check(sqlite3_finalize(stmt), "fin delete");
  227. check(sqlite3_prepare_v2(db, "INSERT INTO demo"
  228. + "(small,big,score,label,payload,code)"
  229. + " VALUES(?,?,?,?,?,?);",
  230. -1, stmt, NIL), "prep full insert");
  231. check(sqlite3_bind_int(stmt, 1, 42), "bind int");
  232. check(sqlite3_bind_int64(stmt, 2, VAL(LONGINT, 9000000001)),
  233. "bind int64");
  234. check(sqlite3_bind_double(stmt, 3, 2.5), "bind double");
  235. check(BindTextCopy(stmt, 4, "hello"), "bind text copy");
  236. check(BindBlobCopy(stmt, 5, ADR(blobIn), 4), "bind blob copy");
  237. check(sqlite3_bind_null(stmt, 6), "bind null");
  238. IF sqlite3_step(stmt) # SQLiteDone THEN fail("step insert") END;
  239. printf("insert changes %d rowid %ld\n",
  240. sqlite3_changes(db), sqlite3_last_insert_rowid(db));
  241. xsql := sqlite3_expanded_sql(stmt);
  242. CStrToM2(xsql, buf);
  243. printf("expanded %s\n", buf);
  244. IF xsql # NIL THEN sqlite3_free(xsql) END;
  245. check(sqlite3_finalize(stmt), "fin full insert");
  246. (* 6. all column kinds, metadata, reuse, transactions *)
  247. section(6, "columns and reuse");
  248. check(sqlite3_prepare_v2(db, "SELECT small,big,score,label,"
  249. + "payload,code FROM demo;",
  250. -1, stmt, NIL), "prep select");
  251. rc := sqlite3_step(stmt);
  252. IF rc # SQLiteRow THEN fail("step row") END;
  253. ncols := sqlite3_column_count(stmt);
  254. ndata := sqlite3_data_count(stmt);
  255. printf("cols %d data %d\n", ncols, ndata);
  256. IF (ncols # 6) OR (ndata # 6) THEN HALT(1) END;
  257. busy := sqlite3_stmt_busy(stmt);
  258. printf("busy %d\n", busy);
  259. IF busy = 0 THEN HALT(1) END;
  260. i := sqlite3_column_int(stmt, 0);
  261. li := sqlite3_column_int64(stmt, 1);
  262. d := sqlite3_column_double(stmt, 2);
  263. CStrToM2(sqlite3_column_text(stmt, 3), buf);
  264. ncols := sqlite3_column_bytes(stmt, 3);
  265. p := sqlite3_column_blob(stmt, 4);
  266. tp := sqlite3_column_type(stmt, 4);
  267. printf("row %d %ld %f %s bytes %d blobtype %d\n",
  268. i, li, d, buf, ncols, tp);
  269. IF (i # 42) OR (li # VAL(LONGINT, 9000000001)) THEN HALT(1) END;
  270. IF ncols # 5 THEN HALT(1) END;
  271. IF (p = NIL) OR (sqlite3_column_bytes(stmt, 4) # 4) THEN HALT(1) END;
  272. strncpy(ADR(out), p, 4);
  273. out[4] := 0C;
  274. printf("blob %s\n", out);
  275. IF sqlite3_column_type(stmt, 5) # SQLiteNull THEN HALT(1) END;
  276. CStrToM2(sqlite3_column_name(stmt, 0), buf);
  277. CStrToM2(sqlite3_column_decltype(stmt, 0), buf2);
  278. printf("col0 %s decl %s\n", buf, buf2);
  279. showInt := SQLiteInteger; showFloat := SQLiteFloat;
  280. showText := SQLiteText; showBlob := SQLiteBlob; showNull := SQLiteNull;
  281. printf("type ids int %d float %d text %d blob %d null %d\n",
  282. showInt, showFloat, showText, showBlob, showNull);
  283. check(sqlite3_reset(stmt), "reset");
  284. IF sqlite3_stmt_busy(stmt) # 0 THEN
  285. printf("FAIL busy after reset\n"); HALT(1)
  286. END;
  287. check(sqlite3_clear_bindings(stmt), "clear");
  288. check(sqlite3_finalize(stmt), "fin select");
  289. (* statement reuse with raw transient and static destructors *)
  290. check(sqlite3_prepare_v2(db, "INSERT INTO demo(label,payload)"
  291. + " VALUES(?,?);",
  292. -1, stmt, NIL), "prep reuse");
  293. check(sqlite3_bind_text(stmt, 1, "raw", -1, TransientDestr()),
  294. "bind raw transient");
  295. check(sqlite3_bind_blob(stmt, 2, ADR(staticBlob), 4, StaticDestr()),
  296. "bind static");
  297. IF sqlite3_step(stmt) # SQLiteDone THEN HALT(1) END;
  298. check(sqlite3_reset(stmt), "reset reuse");
  299. check(sqlite3_clear_bindings(stmt), "clear reuse");
  300. check(BindTextCopy(stmt, 1, "second"), "rebind text");
  301. check(sqlite3_bind_zeroblob(stmt, 2, 16), "bind zeroblob");
  302. IF sqlite3_step(stmt) # SQLiteDone THEN HALT(1) END;
  303. check(sqlite3_finalize(stmt), "fin reuse");
  304. check(sqlite3_prepare_v2(db, "SELECT COUNT(*),"
  305. + " SUM(LENGTH(payload)) FROM demo;",
  306. -1, stmt, NIL), "prep agg");
  307. IF sqlite3_step(stmt) # SQLiteRow THEN HALT(1) END;
  308. printf("rows %d blobs %d\n",
  309. sqlite3_column_int(stmt, 0), sqlite3_column_int(stmt, 1));
  310. IF sqlite3_column_int(stmt, 0) # 3 THEN HALT(1) END;
  311. check(sqlite3_finalize(stmt), "fin agg");
  312. check(ExecSimple(db, "BEGIN;"), "begin");
  313. IF sqlite3_get_autocommit(db) # 0 THEN HALT(1) END;
  314. check(ExecSimple(db, "INSERT INTO demo(small) VALUES(7);"), "txn ins");
  315. check(ExecSimple(db, "COMMIT;"), "commit");
  316. IF sqlite3_get_autocommit(db) = 0 THEN HALT(1) END;
  317. printf("total changes %d\n", sqlite3_total_changes(db));
  318. (* 8. custom functions *)
  319. section(8, "custom functions");
  320. check(sqlite3_create_function(db, "m2tax", 1,
  321. SQLiteUtf8 + SQLiteDeterministic,
  322. NIL, tax, NIL, NIL), "reg tax");
  323. check(sqlite3_create_function(db, "m2total", 1, SQLiteUtf8, NIL,
  324. NIL, totalStep, totalFinal),
  325. "reg total");
  326. check(ExecSimple(db, "CREATE TABLE fx(price REAL);"), "fx create");
  327. check(ExecSimple(db, "INSERT INTO fx VALUES(10.0),(20.0),(30.0);"),
  328. "fx fill");
  329. check(sqlite3_prepare_v2(db, "SELECT m2tax(100.0), m2total(price)"
  330. + " FROM fx;",
  331. -1, stmt, NIL), "prep fx");
  332. rc := sqlite3_step(stmt);
  333. IF rc # SQLiteRow THEN fail("fx step") END;
  334. d := sqlite3_column_double(stmt, 0);
  335. li := sqlite3_column_int64(stmt, 1);
  336. printf("taxed %f totalled %ld\n", d, li);
  337. IF (d < 119.9) OR (d > 120.1) THEN fail("fx tax") END;
  338. IF li # 60 THEN fail("fx total") END;
  339. check(sqlite3_finalize(stmt), "fin fx");
  340. (* 10. blob I/O and backup *)
  341. section(10, "blob and backup");
  342. check(ExecSimple(db, "CREATE TABLE raw(data BLOB);"), "raw create");
  343. check(ExecSimple(db, "INSERT INTO raw VALUES(zeroblob(64));"),
  344. "raw seed");
  345. check(sqlite3_blob_open(db, "main", "raw", "data",
  346. sqlite3_last_insert_rowid(db), 1, bh),
  347. "raw open");
  348. printf("blob size %d\n", sqlite3_blob_bytes(bh));
  349. IF sqlite3_blob_bytes(bh) # 64 THEN fail("raw size") END;
  350. FOR i := 0 TO 15 DO
  351. wpat[i] := CHR(ORD('0') + VAL(CARDINAL, i MOD 10))
  352. END;
  353. check(sqlite3_blob_write(bh, ADR(wpat), 16, 0), "raw write");
  354. check(sqlite3_blob_read(bh, ADR(rback), 16, 0), "raw read");
  355. FOR i := 0 TO 15 DO
  356. IF rback[i] # wpat[i] THEN fail("raw roundtrip") END
  357. END;
  358. printf("blob ok\n");
  359. check(sqlite3_blob_close(bh), "raw close");
  360. check(BackupToFile(db, "/tmp/m2sqlite_showcase_backup.db"),
  361. "show backup");
  362. check(sqlite3_open("/tmp/m2sqlite_showcase_backup.db", db3),
  363. "open backup");
  364. check(sqlite3_prepare_v2(db3, "SELECT COUNT(*) FROM raw;",
  365. -1, stmt, NIL), "prep backup");
  366. IF sqlite3_step(stmt) # SQLiteRow THEN fail("step backup") END;
  367. printf("backup rows %d\n", sqlite3_column_int(stmt, 0));
  368. IF sqlite3_column_int(stmt, 0) # 1 THEN fail("backup count") END;
  369. check(sqlite3_finalize(stmt), "fin backup");
  370. check(sqlite3_close(db3), "close backup");
  371. RemoveFile("/tmp/m2sqlite_showcase_backup.db");
  372. (* 11. close paths *)
  373. section(11, "close");
  374. check(sqlite3_close_v2(db2), "close_v2");
  375. check(sqlite3_close(db), "close");
  376. printf("PASS showcase_all\n")
  377. END showcase_all.