showcase_all.mod 17 KB

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