SQLiteCallback.mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. MODULE SQLiteCallback;
  2. (* Callback support:
  3. sqlite3_exec row callbacks, scalar SQL functions and the
  4. sqlite3_value_* / sqlite3_result_* families. *)
  5. FROM SYSTEM IMPORT ADDRESS, ADR;
  6. FROM CStr IMPORT CString, Copy;
  7. FROM SQLite IMPORT
  8. SQLiteOk, SQLiteRow, SQLiteDone, SQLiteAbort,
  9. SQLiteInteger, SQLiteFloat, SQLiteText, SQLiteBlob, SQLiteNull,
  10. SQLiteUtf8, SQLiteDeterministic,
  11. SQLiteOpenReadWrite, SQLiteOpenCreate, SQLiteOpenMemory,
  12. sqlite3_open_v2, sqlite3_exec, sqlite3_close_v2,
  13. sqlite3_prepare_v2, sqlite3_step,
  14. sqlite3_column_int, sqlite3_column_int64, sqlite3_column_double,
  15. sqlite3_column_text, sqlite3_finalize,
  16. sqlite3_errmsg, sqlite3_free,
  17. sqlite3_create_function_v2,
  18. sqlite3_value_type, sqlite3_value_int, sqlite3_value_int64,
  19. sqlite3_value_double, sqlite3_value_text, sqlite3_value_blob,
  20. sqlite3_value_bytes,
  21. sqlite3_result_null, sqlite3_result_int, sqlite3_result_int64,
  22. sqlite3_result_double, sqlite3_result_text, sqlite3_result_blob,
  23. sqlite3_result_error, sqlite3_result_error_code,
  24. sqlite3_user_data, sqlite3_context_db_handle,
  25. SQLiteArrayAt, SQLiteTransient;
  26. VAR
  27. ExitCode : INTEGER;
  28. db : ADDRESS;
  29. cbRows, rc : INTEGER;
  30. cbBad : BOOLEAN;
  31. errmsg : ADDRESS;
  32. ok : BOOLEAN;
  33. bin : ARRAY [0..7] OF CHAR;
  34. PROCEDURE SameText (a, b : ARRAY OF CHAR) : BOOLEAN;
  35. VAR
  36. i : INTEGER;
  37. BEGIN
  38. i := 0;
  39. WHILE (a[i] # CHR(0)) AND (a[i] = b[i]) DO
  40. i := i + 1
  41. END;
  42. RETURN (a[i] = CHR(0)) AND (b[i] = CHR(0))
  43. END SameText;
  44. (* sqlite3_exec row callback. *)
  45. PROCEDURE RowCb (arg : ADDRESS; argc : INTEGER;
  46. argv, colNames : ADDRESS) : INTEGER;
  47. VAR
  48. value, name : CString;
  49. vbuf, nbuf : ARRAY [0..7] OF CHAR;
  50. BEGIN
  51. IF (argc = 1) AND (arg = ADR(cbRows)) THEN
  52. value := SQLiteArrayAt(argv, 0);
  53. name := SQLiteArrayAt(colNames, 0);
  54. IF (value # NIL) AND (name # NIL)
  55. AND (Copy(value, vbuf) > 0) AND (Copy(name, nbuf) > 0) THEN
  56. IF ((vbuf[0] = '1') OR (vbuf[0] = '2')) AND (nbuf[0] = 'n') THEN
  57. cbRows := cbRows + 1
  58. ELSE
  59. cbBad := TRUE
  60. END
  61. ELSE
  62. cbBad := TRUE
  63. END
  64. ELSE
  65. cbBad := TRUE
  66. END;
  67. RETURN 0
  68. END RowCb;
  69. (* Scalar SQL function callbacks. *)
  70. PROCEDURE AddFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  71. BEGIN
  72. IF argc = 2 THEN
  73. sqlite3_result_int(ctx,
  74. sqlite3_value_int(SQLiteArrayAt(argv, 0))
  75. + sqlite3_value_int(SQLiteArrayAt(argv, 1)))
  76. ELSE
  77. sqlite3_result_error(ctx, "m2_add expects 2 arguments", -1)
  78. END
  79. END AddFn;
  80. PROCEDURE MixFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  81. VAR
  82. v : ADDRESS;
  83. s : CString;
  84. buf : ARRAY [0..7] OF CHAR;
  85. n : CARDINAL;
  86. good : BOOLEAN;
  87. BEGIN
  88. good := FALSE;
  89. IF argc = 4 THEN
  90. v := SQLiteArrayAt(argv, 0);
  91. good := (sqlite3_value_type(v) = SQLiteInteger)
  92. AND (sqlite3_value_int(v) = 7)
  93. AND (sqlite3_value_int64(v) = 7);
  94. v := SQLiteArrayAt(argv, 1);
  95. good := good AND (sqlite3_value_type(v) = SQLiteFloat)
  96. AND (sqlite3_value_double(v) = 8.5);
  97. v := SQLiteArrayAt(argv, 2);
  98. s := sqlite3_value_text(v);
  99. n := Copy(s, buf);
  100. good := good AND (sqlite3_value_type(v) = SQLiteText)
  101. AND (sqlite3_value_bytes(v) = 2)
  102. AND (n = 2) AND (buf[0] = 'h') AND (buf[1] = 'i');
  103. v := SQLiteArrayAt(argv, 3);
  104. good := good AND (sqlite3_value_type(v) = SQLiteNull)
  105. END;
  106. IF good THEN
  107. sqlite3_result_int(ctx, 42)
  108. ELSE
  109. sqlite3_result_error(ctx, "m2_mix bad arguments", -1)
  110. END
  111. END MixFn;
  112. PROCEDURE EchoFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  113. VAR
  114. v : ADDRESS;
  115. s : CString;
  116. buf : ARRAY [0..63] OF CHAR;
  117. n : CARDINAL;
  118. BEGIN
  119. IF argc = 1 THEN
  120. v := SQLiteArrayAt(argv, 0);
  121. IF sqlite3_value_type(v) = SQLiteText THEN
  122. s := sqlite3_value_text(v);
  123. n := Copy(s, buf);
  124. sqlite3_result_text(ctx, buf, INTEGER(n), SQLiteTransient())
  125. ELSE
  126. sqlite3_result_null(ctx)
  127. END
  128. ELSE
  129. sqlite3_result_error(ctx, "m2_echo expects 1 argument", -1)
  130. END
  131. END EchoFn;
  132. PROCEDURE NullFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  133. BEGIN
  134. sqlite3_result_null(ctx)
  135. END NullFn;
  136. PROCEDURE Int64Fn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  137. BEGIN
  138. IF argc = 1 THEN
  139. sqlite3_result_int64(ctx,
  140. sqlite3_value_int64(SQLiteArrayAt(argv, 0)))
  141. ELSE
  142. sqlite3_result_error(ctx, "m2_int64 expects 1 argument", -1)
  143. END
  144. END Int64Fn;
  145. PROCEDURE DoubleFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  146. BEGIN
  147. IF argc = 1 THEN
  148. sqlite3_result_double(ctx,
  149. sqlite3_value_double(SQLiteArrayAt(argv, 0)))
  150. ELSE
  151. sqlite3_result_error(ctx, "m2_double expects 1 argument", -1)
  152. END
  153. END DoubleFn;
  154. PROCEDURE BlobFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  155. BEGIN
  156. sqlite3_result_blob(ctx, ADR(bin[0]), 3, SQLiteTransient())
  157. END BlobFn;
  158. PROCEDURE BlobLenFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  159. VAR
  160. v, p : ADDRESS;
  161. BEGIN
  162. IF argc = 1 THEN
  163. v := SQLiteArrayAt(argv, 0);
  164. p := sqlite3_value_blob(v);
  165. IF (sqlite3_value_type(v) = SQLiteBlob) AND (p # NIL) THEN
  166. sqlite3_result_int(ctx, sqlite3_value_bytes(v))
  167. ELSE
  168. sqlite3_result_error(ctx, "m2_blob_len expects a BLOB", -1)
  169. END
  170. ELSE
  171. sqlite3_result_error(ctx, "m2_blob_len expects 1 argument", -1)
  172. END
  173. END BlobLenFn;
  174. PROCEDURE DataFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  175. BEGIN
  176. IF (sqlite3_user_data(ctx) = ADR(db))
  177. AND (sqlite3_context_db_handle(ctx) = db) THEN
  178. sqlite3_result_int(ctx, 1)
  179. ELSE
  180. sqlite3_result_int(ctx, 0)
  181. END
  182. END DataFn;
  183. PROCEDURE FailFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
  184. BEGIN
  185. sqlite3_result_error(ctx, "m2_fail", -1);
  186. sqlite3_result_error_code(ctx, SQLiteAbort)
  187. END FailFn;
  188. PROCEDURE QueryInt (sql : ARRAY OF CHAR; want : INTEGER) : BOOLEAN;
  189. VAR
  190. stmt : ADDRESS;
  191. rc, got : INTEGER;
  192. ok : BOOLEAN;
  193. BEGIN
  194. stmt := NIL; got := -777; ok := FALSE;
  195. rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
  196. IF (rc = SQLiteOk) AND (stmt # NIL) THEN
  197. IF sqlite3_step(stmt) = SQLiteRow THEN
  198. got := sqlite3_column_int(stmt, 0);
  199. IF sqlite3_step(stmt) = SQLiteDone THEN ok := TRUE END
  200. END;
  201. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
  202. END;
  203. RETURN ok AND (got = want)
  204. END QueryInt;
  205. PROCEDURE QueryInt64 (sql : ARRAY OF CHAR; want : LONGINT) : BOOLEAN;
  206. VAR
  207. stmt : ADDRESS;
  208. rc : INTEGER;
  209. got : LONGINT;
  210. ok : BOOLEAN;
  211. BEGIN
  212. stmt := NIL; got := -777; ok := FALSE;
  213. rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
  214. IF (rc = SQLiteOk) AND (stmt # NIL) THEN
  215. IF sqlite3_step(stmt) = SQLiteRow THEN
  216. got := sqlite3_column_int64(stmt, 0);
  217. IF sqlite3_step(stmt) = SQLiteDone THEN ok := TRUE END
  218. END;
  219. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
  220. END;
  221. RETURN ok AND (got = want)
  222. END QueryInt64;
  223. PROCEDURE QueryDouble (sql : ARRAY OF CHAR; want : REAL) : BOOLEAN;
  224. VAR
  225. stmt : ADDRESS;
  226. rc : INTEGER;
  227. got : REAL;
  228. ok : BOOLEAN;
  229. BEGIN
  230. stmt := NIL; got := -777.0; ok := FALSE;
  231. rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
  232. IF (rc = SQLiteOk) AND (stmt # NIL) THEN
  233. IF sqlite3_step(stmt) = SQLiteRow THEN
  234. got := sqlite3_column_double(stmt, 0);
  235. IF sqlite3_step(stmt) = SQLiteDone THEN ok := TRUE END
  236. END;
  237. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
  238. END;
  239. RETURN ok AND (got = want)
  240. END QueryDouble;
  241. PROCEDURE QueryText (sql : ARRAY OF CHAR; want : ARRAY OF CHAR) : BOOLEAN;
  242. VAR
  243. stmt : ADDRESS;
  244. rc : INTEGER;
  245. s : CString;
  246. buf : ARRAY [0..63] OF CHAR;
  247. ok : BOOLEAN;
  248. BEGIN
  249. stmt := NIL; ok := FALSE;
  250. rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
  251. IF (rc = SQLiteOk) AND (stmt # NIL) THEN
  252. IF sqlite3_step(stmt) = SQLiteRow THEN
  253. s := sqlite3_column_text(stmt, 0);
  254. IF (s # NIL) AND (Copy(s, buf) > 0) THEN
  255. ok := SameText(buf, want)
  256. END;
  257. IF sqlite3_step(stmt) # SQLiteDone THEN ok := FALSE END
  258. END;
  259. IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
  260. END;
  261. RETURN ok
  262. END QueryText;
  263. PROCEDURE QueryFails (sql : ARRAY OF CHAR) : BOOLEAN;
  264. VAR
  265. stmt : ADDRESS;
  266. rc : INTEGER;
  267. s : CString;
  268. buf : ARRAY [0..63] OF CHAR;
  269. ok : BOOLEAN;
  270. BEGIN
  271. stmt := NIL; ok := FALSE;
  272. rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
  273. IF (rc = SQLiteOk) AND (stmt # NIL) THEN
  274. IF sqlite3_step(stmt) = SQLiteAbort THEN
  275. s := sqlite3_errmsg(db);
  276. IF (s # NIL) AND (Copy(s, buf) > 0)
  277. AND SameText(buf, "m2_fail") THEN
  278. ok := TRUE
  279. END
  280. END;
  281. rc := sqlite3_finalize(stmt)
  282. END;
  283. RETURN ok
  284. END QueryFails;
  285. PROCEDURE Register (name : ARRAY OF CHAR;
  286. nArgs : INTEGER;
  287. userData, fn : ADDRESS) : BOOLEAN;
  288. BEGIN
  289. RETURN sqlite3_create_function_v2(db, name, nArgs,
  290. SQLiteUtf8 + SQLiteDeterministic, userData, fn, NIL, NIL, NIL)
  291. = SQLiteOk
  292. END Register;
  293. BEGIN
  294. ExitCode := 1;
  295. db := NIL; cbRows := 0; cbBad := FALSE;
  296. bin[0] := CHR(0); bin[1] := CHR(1); bin[2] := CHR(2);
  297. bin[3] := CHR(0); bin[4] := CHR(255);
  298. IF (sqlite3_open_v2(":memory:", db,
  299. SQLiteOpenReadWrite + SQLiteOpenCreate + SQLiteOpenMemory,
  300. NIL) = SQLiteOk) AND (db # NIL) THEN
  301. IF Register("m2_add", 2, NIL, ADR(AddFn))
  302. AND Register("m2_mix", 4, NIL, ADR(MixFn))
  303. AND Register("m2_echo", 1, NIL, ADR(EchoFn))
  304. AND Register("m2_null", 0, NIL, ADR(NullFn))
  305. AND Register("m2_int64", 1, NIL, ADR(Int64Fn))
  306. AND Register("m2_double", 1, NIL, ADR(DoubleFn))
  307. AND Register("m2_blob", 0, NIL, ADR(BlobFn))
  308. AND Register("m2_blob_len", 1, NIL, ADR(BlobLenFn))
  309. AND Register("m2_hasdata", 0, ADR(db), ADR(DataFn))
  310. AND Register("m2_fail", 0, NIL, ADR(FailFn)) THEN
  311. ok := TRUE
  312. ELSE
  313. ok := FALSE
  314. END
  315. END;
  316. IF ok THEN ok := QueryInt("SELECT m2_add(20, 22);", 42) END;
  317. IF ok THEN
  318. ok := QueryInt("SELECT m2_mix(7, 8.5, 'hi', NULL);", 42)
  319. END;
  320. IF ok THEN ok := QueryText("SELECT m2_echo('hello');", "hello") END;
  321. IF ok THEN ok := QueryInt("SELECT m2_echo(NULL) IS NULL;", 1) END;
  322. IF ok THEN ok := QueryInt("SELECT m2_null() IS NULL;", 1) END;
  323. IF ok THEN ok := QueryInt64("SELECT m2_int64(42);", 42) END;
  324. IF ok THEN ok := QueryDouble("SELECT m2_double(3.5);", 3.5) END;
  325. IF ok THEN ok := QueryText("SELECT hex(m2_blob());", "000102") END;
  326. IF ok THEN
  327. ok := QueryInt("SELECT m2_blob_len(zeroblob(3));", 3)
  328. END;
  329. IF ok THEN ok := QueryInt("SELECT m2_hasdata();", 1) END;
  330. IF ok THEN ok := QueryFails("SELECT m2_fail();") END;
  331. IF ok THEN
  332. cbRows := 0; cbBad := FALSE; errmsg := NIL;
  333. rc := sqlite3_exec(db, "SELECT 1 AS n UNION ALL SELECT 2;",
  334. ADR(RowCb), ADR(cbRows), errmsg);
  335. ok := (rc = SQLiteOk) AND (NOT cbBad) AND (cbRows = 2);
  336. IF errmsg # NIL THEN
  337. sqlite3_free(errmsg);
  338. errmsg := NIL
  339. END
  340. END;
  341. IF db # NIL THEN
  342. IF sqlite3_close_v2(db) # SQLiteOk THEN ok := FALSE END
  343. END;
  344. IF ok THEN ExitCode := 42 END
  345. END SQLiteCallback.