SQLiteFeatureShowcase.mod 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. MODULE SQLiteFeatureShowcase;
  2. (* Broad V3 SQLite binding showcase:
  3. diagnostics, prepared statements, parameter metadata, row callbacks,
  4. scalar types, BLOB I/O, statement metadata, UDFs, hooks, backup,
  5. constraint errors and authorizer behavior. *)
  6. FROM SYSTEM IMPORT ADDRESS, ADR;
  7. FROM CStr IMPORT CString, Copy;
  8. FROM SysIO IMPORT Write, WriteLn, WriteInt;
  9. FROM SQLite IMPORT
  10. SQLiteOk, SQLiteRow, SQLiteDone, SQLiteBusy, SQLiteAuth, SQLiteBlob,
  11. SQLiteBlobReadWrite, SQLiteOpenReadWrite, SQLiteOpenCreate,
  12. SQLiteOpenMemory, SQLiteTraceStmt, SQLiteUtf8, SQLiteDeterministic,
  13. SQLiteAuthOk, SQLiteAuthDeny, SQLiteAuthDelete,
  14. SQLiteStmtStatusVmStep,
  15. sqlite3_open_v2, sqlite3_close, sqlite3_close_v2, sqlite3_exec,
  16. sqlite3_prepare_v2, sqlite3_step, sqlite3_reset,
  17. sqlite3_clear_bindings, sqlite3_finalize,
  18. sqlite3_bind_int, sqlite3_bind_null, sqlite3_bind_blob,
  19. sqlite3_bind_zeroblob,
  20. sqlite3_bind_text, sqlite3_bind_parameter_count,
  21. sqlite3_bind_parameter_name, sqlite3_bind_parameter_index,
  22. sqlite3_column_type, sqlite3_column_bytes, sqlite3_column_int,
  23. sqlite3_column_blob,
  24. sqlite3_changes, sqlite3_total_changes,
  25. sqlite3_sql, sqlite3_expanded_sql, sqlite3_stmt_readonly,
  26. sqlite3_stmt_busy, sqlite3_next_stmt, sqlite3_stmt_status,
  27. sqlite3_libversion, sqlite3_libversion_number, sqlite3_sourceid,
  28. sqlite3_threadsafe, sqlite3_get_autocommit, sqlite3_complete,
  29. sqlite3_blob_open, sqlite3_blob_close, sqlite3_blob_read,
  30. sqlite3_blob_write, sqlite3_blob_bytes,
  31. sqlite3_backup_init, sqlite3_backup_step, sqlite3_backup_finish,
  32. sqlite3_backup_remaining, sqlite3_backup_pagecount,
  33. sqlite3_create_function_v2, sqlite3_value_int,
  34. sqlite3_result_int, sqlite3_result_error,
  35. sqlite3_progress_handler, sqlite3_trace_v2,
  36. sqlite3_update_hook, sqlite3_commit_hook, sqlite3_rollback_hook,
  37. sqlite3_set_authorizer, sqlite3_extended_result_codes,
  38. sqlite3_extended_errcode, sqlite3_errmsg, sqlite3_free,
  39. SQLiteArrayAt, SQLiteTransient;
  40. VAR
  41. ExitCode : INTEGER;
  42. db, dst, stmt, s1, s2, blob, bk, prev : ADDRESS;
  43. rc, pages, remaining, progressCalls, traceCalls : INTEGER;
  44. updateCalls, commitCalls, rollbackCalls, authCalls : INTEGER;
  45. ok : BOOLEAN;
  46. bin, out : ARRAY [0..7] OF CHAR;
  47. PROCEDURE Say (s : ARRAY OF CHAR);
  48. VAR
  49. buf : ARRAY [0..255] OF CHAR;
  50. i : INTEGER;
  51. BEGIN
  52. i := 0;
  53. WHILE (i <= HIGH(buf)) AND (i <= HIGH(s)) AND (s[i] # CHR(0)) DO
  54. buf[i] := s[i];
  55. i := i + 1
  56. END;
  57. IF i <= HIGH(buf) THEN buf[i] := CHR(0) END;
  58. IF i > 0 THEN Write(buf) END
  59. END Say;
  60. PROCEDURE SayLn (s : ARRAY OF CHAR);
  61. BEGIN
  62. Say(s); WriteLn
  63. END SayLn;
  64. PROCEDURE SayInt (n : INTEGER);
  65. BEGIN
  66. WriteInt(n)
  67. END SayInt;
  68. PROCEDURE SayCS (s : CString);
  69. VAR
  70. buf : ARRAY [0..255] OF CHAR;
  71. BEGIN
  72. IF s = NIL THEN
  73. Say("NULL")
  74. ELSE
  75. IF Copy(s, buf) > 0 THEN Say(buf) END
  76. END
  77. END SayCS;
  78. PROCEDURE SameText (a, b : ARRAY OF CHAR) : BOOLEAN;
  79. VAR
  80. i : INTEGER;
  81. BEGIN
  82. i := 0;
  83. WHILE (a[i] # CHR(0)) AND (a[i] = b[i]) DO i := i + 1 END;
  84. RETURN (a[i] = CHR(0)) AND (b[i] = CHR(0))
  85. END SameText;
  86. PROCEDURE SameBytes (a, b : ARRAY OF CHAR; n : INTEGER) : BOOLEAN;
  87. VAR
  88. i : INTEGER;
  89. BEGIN
  90. i := 0;
  91. WHILE (i < n) AND (a[i] = b[i]) DO i := i + 1 END;
  92. RETURN i = n
  93. END SameBytes;
  94. PROCEDURE OpenMemory (VAR h : ADDRESS) : BOOLEAN;
  95. BEGIN
  96. h := NIL;
  97. RETURN sqlite3_open_v2(":memory:", h,
  98. SQLiteOpenReadWrite + SQLiteOpenCreate + SQLiteOpenMemory,
  99. NIL) = SQLiteOk
  100. END OpenMemory;
  101. PROCEDURE ExecSQL (sql : ARRAY OF CHAR) : BOOLEAN;
  102. VAR
  103. errmsg : ADDRESS;
  104. good : BOOLEAN;
  105. BEGIN
  106. errmsg := NIL;
  107. good := sqlite3_exec(db, sql, NIL, NIL, errmsg) = SQLiteOk;
  108. IF errmsg # NIL THEN sqlite3_free(errmsg) END;
  109. RETURN good
  110. END ExecSQL;
  111. PROCEDURE QueryInt (h : ADDRESS; sql : ARRAY OF CHAR; want : INTEGER) : BOOLEAN;
  112. VAR
  113. s : ADDRESS;
  114. code, got : INTEGER;
  115. good : BOOLEAN;
  116. BEGIN
  117. s := NIL; got := -777; good := FALSE;
  118. code := sqlite3_prepare_v2(h, sql, -1, s, NIL);
  119. IF (code = SQLiteOk) AND (s # NIL) THEN
  120. IF sqlite3_step(s) = SQLiteRow THEN
  121. got := sqlite3_column_int(s, 0);
  122. IF sqlite3_step(s) = SQLiteDone THEN good := TRUE END
  123. END;
  124. IF sqlite3_finalize(s) # SQLiteOk THEN good := FALSE END
  125. END;
  126. RETURN good AND (got = want)
  127. END QueryInt;
  128. PROCEDURE ExecCallback (sql : ARRAY OF CHAR; cb : ADDRESS) : BOOLEAN;
  129. VAR
  130. errmsg : ADDRESS;
  131. good : BOOLEAN;
  132. BEGIN
  133. errmsg := NIL;
  134. good := sqlite3_exec(db, sql, cb, NIL, errmsg) = SQLiteOk;
  135. IF errmsg # NIL THEN sqlite3_free(errmsg) END;
  136. RETURN good
  137. END ExecCallback;
  138. PROCEDURE Register (name : ARRAY OF CHAR; n : INTEGER;
  139. user, fn : ADDRESS) : BOOLEAN;
  140. BEGIN
  141. RETURN sqlite3_create_function_v2(db, name, n,
  142. SQLiteUtf8 + SQLiteDeterministic, user, fn, NIL, NIL, NIL)
  143. = SQLiteOk
  144. END Register;
  145. PROCEDURE RowPrinter (arg : ADDRESS; argc : INTEGER;
  146. argv, colNames : ADDRESS) : INTEGER;
  147. VAR
  148. i : INTEGER;
  149. s : CString;
  150. buf : ARRAY [0..127] OF CHAR;
  151. BEGIN
  152. FOR i := 0 TO argc - 1 DO
  153. IF i > 0 THEN Say(" | ") END;
  154. s := SQLiteArrayAt(argv, i);
  155. IF s = NIL THEN
  156. Say("NULL")
  157. ELSIF Copy(s, buf) > 0 THEN
  158. Say(buf)
  159. END
  160. END;
  161. WriteLn;
  162. RETURN 0
  163. END RowPrinter;
  164. PROCEDURE AddFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  165. BEGIN
  166. IF argc = 2 THEN
  167. sqlite3_result_int(ctx,
  168. sqlite3_value_int(SQLiteArrayAt(argv, 0))
  169. + sqlite3_value_int(SQLiteArrayAt(argv, 1)))
  170. ELSE
  171. sqlite3_result_error(ctx, "m2_add expects 2 arguments", -1)
  172. END
  173. END AddFn;
  174. PROCEDURE ProgressFn (arg : ADDRESS) : INTEGER;
  175. BEGIN
  176. progressCalls := progressCalls + 1;
  177. RETURN 0
  178. END ProgressFn;
  179. PROCEDURE TraceFn (mask : CARDINAL; ctx, sql, expanded : ADDRESS) : INTEGER;
  180. BEGIN
  181. traceCalls := traceCalls + 1;
  182. RETURN 0
  183. END TraceFn;
  184. PROCEDURE UpdateFn (arg : ADDRESS; op : INTEGER;
  185. dbName, tableName : ADDRESS; rowid : LONGINT);
  186. BEGIN
  187. updateCalls := updateCalls + 1
  188. END UpdateFn;
  189. PROCEDURE CommitFn (arg : ADDRESS) : INTEGER;
  190. BEGIN
  191. commitCalls := commitCalls + 1;
  192. RETURN 0
  193. END CommitFn;
  194. PROCEDURE RollbackFn (arg : ADDRESS);
  195. BEGIN
  196. rollbackCalls := rollbackCalls + 1
  197. END RollbackFn;
  198. PROCEDURE AuthFn (arg : ADDRESS; action : INTEGER;
  199. dbName, tableName, colName, triggerName : ADDRESS) : INTEGER;
  200. BEGIN
  201. authCalls := authCalls + 1;
  202. IF action = SQLiteAuthDelete THEN
  203. RETURN SQLiteAuthDeny
  204. ELSE
  205. RETURN SQLiteAuthOk
  206. END
  207. END AuthFn;
  208. BEGIN
  209. ExitCode := 1;
  210. db := NIL; dst := NIL; stmt := NIL; s1 := NIL; s2 := NIL;
  211. blob := NIL; bk := NIL; prev := NIL;
  212. progressCalls := 0; traceCalls := 0; updateCalls := 0;
  213. commitCalls := 0; rollbackCalls := 0; authCalls := 0;
  214. bin[0] := CHR(0); bin[1] := CHR(1); bin[2] := CHR(2);
  215. bin[3] := CHR(0); bin[4] := CHR(255);
  216. ok := FALSE;
  217. SayLn("SQLite V3 feature showcase");
  218. Say("libversion="); SayCS(sqlite3_libversion()); WriteLn;
  219. Say("version_number="); SayInt(sqlite3_libversion_number()); WriteLn;
  220. Say("threadsafe="); SayInt(sqlite3_threadsafe()); WriteLn;
  221. Say("complete="); SayInt(sqlite3_complete("SELECT 1;")); WriteLn;
  222. IF OpenMemory(db) THEN
  223. ok := sqlite3_extended_result_codes(db, 1) = SQLiteOk
  224. END;
  225. IF ok THEN
  226. ok := ExecSQL("CREATE TABLE people(id INTEGER, name TEXT, data BLOB);")
  227. AND ExecSQL("CREATE TABLE hook(id INTEGER PRIMARY KEY, v TEXT);")
  228. END;
  229. (* Prepared insert with named/anonymous parameter metadata. *)
  230. IF ok THEN
  231. stmt := NIL;
  232. rc := sqlite3_prepare_v2(db,
  233. "INSERT INTO people(name,data) VALUES(?,?);", -1, stmt, NIL);
  234. ok := (rc = SQLiteOk) AND (stmt # NIL)
  235. AND (sqlite3_bind_parameter_count(stmt) = 2)
  236. AND (sqlite3_bind_parameter_name(stmt, 1) = NIL)
  237. AND (sqlite3_bind_parameter_index(stmt, ":name") = 0);
  238. IF ok THEN
  239. Say("params="); SayInt(sqlite3_bind_parameter_count(stmt));
  240. Say(" index(:name)="); SayInt(sqlite3_bind_parameter_index(stmt, ":name"));
  241. WriteLn
  242. END;
  243. IF ok THEN
  244. ok := (sqlite3_bind_text(stmt, 1, "Ada", -1,
  245. SQLiteTransient()) = SQLiteOk)
  246. AND (sqlite3_bind_blob(stmt, 2, ADR(bin[0]), 3,
  247. SQLiteTransient()) = SQLiteOk)
  248. END;
  249. IF ok THEN ok := sqlite3_step(stmt) = SQLiteDone END;
  250. IF ok THEN ok := sqlite3_reset(stmt) = SQLiteOk END;
  251. IF ok THEN ok := sqlite3_clear_bindings(stmt) = SQLiteOk END;
  252. IF ok THEN
  253. ok := (sqlite3_bind_text(stmt, 1, "Grace", -1,
  254. SQLiteTransient()) = SQLiteOk)
  255. AND (sqlite3_bind_null(stmt, 2) = SQLiteOk)
  256. END;
  257. IF ok THEN ok := sqlite3_step(stmt) = SQLiteDone END;
  258. IF ok THEN ok := sqlite3_reset(stmt) = SQLiteOk END;
  259. IF ok THEN ok := sqlite3_clear_bindings(stmt) = SQLiteOk END;
  260. IF ok THEN
  261. ok := (sqlite3_bind_text(stmt, 1, "Blob", -1,
  262. SQLiteTransient()) = SQLiteOk)
  263. AND (sqlite3_bind_zeroblob(stmt, 2, 8) = SQLiteOk)
  264. END;
  265. IF ok THEN ok := sqlite3_step(stmt) = SQLiteDone END;
  266. IF ok THEN
  267. Say("rows inserted, changes="); SayInt(sqlite3_changes(db));
  268. Say(" total="); SayInt(sqlite3_total_changes(db)); WriteLn
  269. END;
  270. IF stmt # NIL THEN
  271. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END;
  272. stmt := NIL
  273. END
  274. END;
  275. (* Row callback output. *)
  276. IF ok THEN
  277. SayLn("people:");
  278. ok := ExecCallback("SELECT rowid,name FROM people ORDER BY rowid;",
  279. ADR(RowPrinter))
  280. END;
  281. (* User-defined scalar function. *)
  282. IF ok THEN
  283. ok := Register("m2_add", 2, NIL, ADR(AddFn))
  284. AND QueryInt(db, "SELECT m2_add(20,22);", 42);
  285. IF ok THEN
  286. Say("m2_add(20,22)=42"); WriteLn
  287. END
  288. END;
  289. (* Statement metadata and expanded SQL. *)
  290. IF ok THEN
  291. stmt := NIL;
  292. rc := sqlite3_prepare_v2(db, "SELECT ?;", -1, stmt, NIL);
  293. ok := (rc = SQLiteOk) AND (stmt # NIL)
  294. AND (sqlite3_bind_int(stmt, 1, 42) = SQLiteOk);
  295. IF ok THEN
  296. Say("sql="); SayCS(sqlite3_sql(stmt));
  297. Say(" readonly="); SayInt(sqlite3_stmt_readonly(stmt));
  298. WriteLn;
  299. prev := sqlite3_expanded_sql(stmt);
  300. Say("expanded="); SayCS(prev); WriteLn;
  301. IF prev # NIL THEN sqlite3_free(prev); prev := NIL END
  302. END;
  303. IF ok THEN ok := sqlite3_step(stmt) = SQLiteRow END;
  304. IF ok THEN
  305. Say("busy_after_row="); SayInt(sqlite3_stmt_busy(stmt));
  306. Say(" vm_steps="); SayInt(sqlite3_stmt_status(stmt,
  307. SQLiteStmtStatusVmStep, 0)); WriteLn
  308. END;
  309. IF ok THEN ok := sqlite3_step(stmt) = SQLiteDone END;
  310. IF stmt # NIL THEN
  311. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END;
  312. stmt := NIL
  313. END
  314. END;
  315. (* Statement iteration. *)
  316. IF ok THEN
  317. s1 := NIL; s2 := NIL;
  318. ok := (sqlite3_prepare_v2(db, "SELECT 1;", -1, s1, NIL) = SQLiteOk)
  319. AND (sqlite3_prepare_v2(db, "SELECT 2;", -1, s2, NIL) = SQLiteOk);
  320. IF ok THEN
  321. rc := 0;
  322. prev := sqlite3_next_stmt(db, NIL);
  323. WHILE prev # NIL DO
  324. rc := rc + 1;
  325. prev := sqlite3_next_stmt(db, prev)
  326. END;
  327. Say("open statements="); SayInt(rc); WriteLn;
  328. ok := rc = 2
  329. END;
  330. IF s1 # NIL THEN
  331. IF sqlite3_finalize(s1) # SQLiteOk THEN ok := FALSE END;
  332. s1 := NIL
  333. END;
  334. IF s2 # NIL THEN
  335. IF sqlite3_finalize(s2) # SQLiteOk THEN ok := FALSE END;
  336. s2 := NIL
  337. END
  338. END;
  339. (* Column BLOB metadata. *)
  340. IF ok THEN
  341. stmt := NIL;
  342. rc := sqlite3_prepare_v2(db,
  343. "SELECT data FROM people WHERE rowid=1;", -1, stmt, NIL);
  344. ok := (rc = SQLiteOk) AND (stmt # NIL)
  345. AND (sqlite3_step(stmt) = SQLiteRow)
  346. AND (sqlite3_column_type(stmt, 0) = SQLiteBlob)
  347. AND (sqlite3_column_bytes(stmt, 0) = 3)
  348. AND (sqlite3_column_blob(stmt, 0) # NIL);
  349. IF ok THEN
  350. Say("column BLOB bytes=3"); WriteLn
  351. END;
  352. IF stmt # NIL THEN
  353. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END;
  354. stmt := NIL
  355. END
  356. END;
  357. (* Incremental BLOB round trip. *)
  358. IF ok THEN
  359. blob := NIL;
  360. rc := sqlite3_blob_open(db, "main", "people", "data", 3,
  361. SQLiteBlobReadWrite, blob);
  362. ok := (rc = SQLiteOk) AND (blob # NIL)
  363. AND (sqlite3_blob_bytes(blob) = 8)
  364. AND (sqlite3_blob_write(blob, ADR(bin[0]), 3, 0) = SQLiteOk)
  365. AND (sqlite3_blob_read(blob, ADR(out[0]), 3, 0) = SQLiteOk)
  366. AND SameBytes(bin, out, 3);
  367. IF ok THEN
  368. Say("incremental BLOB bytes=8 roundtrip=OK"); WriteLn
  369. END;
  370. IF blob # NIL THEN
  371. IF sqlite3_blob_close(blob) # SQLiteOk THEN ok := FALSE END;
  372. blob := NIL
  373. END
  374. END;
  375. (* Autocommit state. *)
  376. IF ok THEN
  377. Say("autocommit="); SayInt(sqlite3_get_autocommit(db));
  378. ok := ExecSQL("BEGIN;");
  379. IF ok THEN
  380. Say(" in_txn="); SayInt(sqlite3_get_autocommit(db));
  381. ok := ExecSQL("COMMIT;")
  382. END;
  383. IF ok THEN
  384. Say(" after_commit="); SayInt(sqlite3_get_autocommit(db)); WriteLn
  385. END
  386. END;
  387. (* Progress and trace hooks. *)
  388. IF ok THEN
  389. progressCalls := 0;
  390. sqlite3_progress_handler(db, 1, ADR(ProgressFn), NIL);
  391. ok := QueryInt(db, "SELECT count(*) FROM people;", 3)
  392. AND (progressCalls > 0);
  393. sqlite3_progress_handler(db, 0, NIL, NIL);
  394. Say("progress calls="); SayInt(progressCalls); WriteLn
  395. END;
  396. IF ok THEN
  397. traceCalls := 0;
  398. ok := sqlite3_trace_v2(db, SQLiteTraceStmt,
  399. ADR(TraceFn), NIL) = SQLiteOk;
  400. IF ok THEN ok := QueryInt(db, "SELECT 1;", 1) AND (traceCalls > 0) END;
  401. IF ok THEN ok := sqlite3_trace_v2(db, 0, NIL, NIL) = SQLiteOk END;
  402. Say("trace calls="); SayInt(traceCalls); WriteLn
  403. END;
  404. (* Update / commit / rollback hooks. *)
  405. IF ok THEN
  406. updateCalls := 0;
  407. prev := sqlite3_update_hook(db, ADR(UpdateFn), NIL);
  408. ok := ExecSQL("INSERT INTO hook(v) VALUES('a');")
  409. AND ExecSQL("UPDATE hook SET v='b' WHERE id=1;")
  410. AND ExecSQL("DELETE FROM hook WHERE id=1;")
  411. AND (updateCalls = 3);
  412. prev := sqlite3_update_hook(db, NIL, NIL);
  413. Say("update events="); SayInt(updateCalls); WriteLn
  414. END;
  415. IF ok THEN
  416. commitCalls := 0; rollbackCalls := 0;
  417. prev := sqlite3_commit_hook(db, ADR(CommitFn), NIL);
  418. sqlite3_rollback_hook(db, ADR(RollbackFn), NIL);
  419. ok := ExecSQL("BEGIN; INSERT INTO hook(v) VALUES('c'); COMMIT;")
  420. AND (commitCalls = 1)
  421. AND ExecSQL("BEGIN; INSERT INTO hook(v) VALUES('d'); ROLLBACK;")
  422. AND (rollbackCalls = 1);
  423. prev := sqlite3_commit_hook(db, NIL, NIL);
  424. sqlite3_rollback_hook(db, NIL, NIL);
  425. Say("commit="); SayInt(commitCalls);
  426. Say(" rollback="); SayInt(rollbackCalls); WriteLn
  427. END;
  428. (* Authorizer denies DELETE. *)
  429. IF ok THEN
  430. authCalls := 0;
  431. ok := sqlite3_set_authorizer(db, ADR(AuthFn), NIL) = SQLiteOk;
  432. IF ok THEN
  433. prev := NIL;
  434. rc := sqlite3_exec(db, "DELETE FROM hook;", NIL, NIL, prev);
  435. IF prev # NIL THEN sqlite3_free(prev); prev := NIL END;
  436. ok := (rc = SQLiteAuth) AND (authCalls > 0);
  437. Say("authorizer delete rc="); SayInt(rc); WriteLn
  438. END;
  439. IF sqlite3_set_authorizer(db, NIL, NIL) # SQLiteOk THEN ok := FALSE END
  440. END;
  441. (* Constraint error and errmsg ownership. *)
  442. IF ok THEN
  443. ok := ExecSQL("CREATE TABLE u(v TEXT UNIQUE);")
  444. AND ExecSQL("INSERT INTO u VALUES('x');");
  445. IF ok THEN
  446. prev := NIL;
  447. rc := sqlite3_exec(db, "INSERT INTO u VALUES('x');", NIL, NIL, prev);
  448. ok := (rc # SQLiteOk) AND (sqlite3_extended_errcode(db) = 2067);
  449. Say("constraint rc="); SayInt(rc);
  450. Say(" msg="); SayCS(prev); WriteLn;
  451. IF prev # NIL THEN sqlite3_free(prev); prev := NIL END
  452. END
  453. END;
  454. (* Online backup. *)
  455. IF ok THEN
  456. ok := OpenMemory(dst);
  457. IF ok THEN
  458. bk := sqlite3_backup_init(dst, "main", db, "main");
  459. ok := bk # NIL;
  460. IF ok THEN
  461. ok := sqlite3_backup_step(bk, -1) = SQLiteDone;
  462. IF ok THEN
  463. pages := sqlite3_backup_pagecount(bk);
  464. remaining := sqlite3_backup_remaining(bk);
  465. ok := (pages > 0) AND (remaining = 0)
  466. END;
  467. IF sqlite3_backup_finish(bk) # SQLiteOk THEN ok := FALSE END;
  468. bk := NIL
  469. END
  470. END;
  471. IF ok THEN
  472. ok := QueryInt(dst, "SELECT count(*) FROM people;", 3);
  473. Say("backup pages="); SayInt(pages);
  474. Say(" remaining="); SayInt(remaining); WriteLn
  475. END
  476. END;
  477. (* Close status and cleanup. *)
  478. IF ok THEN
  479. stmt := NIL;
  480. rc := sqlite3_prepare_v2(db, "SELECT 1;", -1, stmt, NIL);
  481. ok := (rc = SQLiteOk) AND (stmt # NIL);
  482. IF ok THEN
  483. rc := sqlite3_close(db);
  484. Say("close with statement="); SayInt(rc); WriteLn;
  485. ok := rc = SQLiteBusy
  486. END;
  487. IF stmt # NIL THEN
  488. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END;
  489. stmt := NIL
  490. END
  491. END;
  492. IF blob # NIL THEN
  493. IF sqlite3_blob_close(blob) # SQLiteOk THEN ok := FALSE END
  494. END;
  495. IF bk # NIL THEN
  496. IF sqlite3_backup_finish(bk) # SQLiteOk THEN ok := FALSE END
  497. END;
  498. IF dst # NIL THEN
  499. IF sqlite3_close_v2(dst) # SQLiteOk THEN ok := FALSE END
  500. END;
  501. IF db # NIL THEN
  502. IF sqlite3_close(db) # SQLiteOk THEN ok := FALSE END
  503. END;
  504. IF ok THEN
  505. SayLn("done");
  506. ExitCode := 42
  507. ELSE
  508. SayLn("FAIL")
  509. END
  510. END SQLiteFeatureShowcase.