SQLiteAggregate.mod 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. MODULE SQLiteAggregate;
  2. (* Aggregate functions, auxdata and the remaining UDF value/result APIs. *)
  3. FROM SYSTEM IMPORT ADDRESS, ADR;
  4. FROM CStr IMPORT CString, Copy;
  5. FROM SQLite IMPORT
  6. DbHandle, StmtHandle, ContextHandle, ValueHandle,
  7. SQLiteOk, SQLiteRow, SQLiteDone, SQLiteNull,
  8. SQLiteInteger, SQLiteText, SQLiteTooBig, SQLiteNoMem,
  9. SQLiteOpenReadWrite, SQLiteOpenCreate, SQLiteOpenMemory, SQLiteUtf8,
  10. sqlite3_open_v2, sqlite3_close, sqlite3_exec,
  11. sqlite3_prepare_v2, sqlite3_step, sqlite3_column_int,
  12. sqlite3_column_text, sqlite3_finalize,
  13. sqlite3_create_function_v2,
  14. sqlite3_value_type, sqlite3_value_int64, sqlite3_value_numeric_type,
  15. sqlite3_value_dup, sqlite3_value_free,
  16. sqlite3_aggregate_context, sqlite3_get_auxdata, sqlite3_set_auxdata,
  17. sqlite3_result_int, sqlite3_result_int64, sqlite3_result_null,
  18. sqlite3_result_error, sqlite3_result_value, sqlite3_result_zeroblob,
  19. sqlite3_result_zeroblob64,
  20. sqlite3_result_text64, sqlite3_result_blob64,
  21. sqlite3_result_error_toobig, sqlite3_result_error_nomem,
  22. sqlite3_free, SQLiteArrayAt, SQLiteTransient;
  23. TYPE
  24. SumPtr = POINTER TO LONGINT;
  25. VAR
  26. ExitCode : INTEGER;
  27. db : DbHandle;
  28. stmt : StmtHandle;
  29. rc : INTEGER;
  30. ok : BOOLEAN;
  31. bin, auxMarker : ARRAY [0..7] OF CHAR;
  32. PROCEDURE SameText (a, b : ARRAY OF CHAR) : BOOLEAN;
  33. VAR
  34. i : INTEGER;
  35. BEGIN
  36. i := 0;
  37. WHILE (a[i] # CHR(0)) AND (a[i] = b[i]) DO i := i + 1 END;
  38. RETURN (a[i] = CHR(0)) AND (b[i] = CHR(0))
  39. END SameText;
  40. PROCEDURE ExecSQL (sql : ARRAY OF CHAR) : BOOLEAN;
  41. VAR
  42. errmsg : ADDRESS;
  43. good : BOOLEAN;
  44. BEGIN
  45. errmsg := NIL;
  46. good := sqlite3_exec(db, sql, NIL, NIL, errmsg) = SQLiteOk;
  47. IF errmsg # NIL THEN sqlite3_free(errmsg) END;
  48. RETURN good
  49. END ExecSQL;
  50. PROCEDURE QueryInt (sql : ARRAY OF CHAR; want : INTEGER) : BOOLEAN;
  51. VAR
  52. s : StmtHandle;
  53. code, got : INTEGER;
  54. good : BOOLEAN;
  55. BEGIN
  56. s := NIL; got := -777; good := FALSE;
  57. code := sqlite3_prepare_v2(db, sql, -1, s, NIL);
  58. IF (code = SQLiteOk) AND (s # NIL) THEN
  59. IF sqlite3_step(s) = SQLiteRow THEN
  60. got := sqlite3_column_int(s, 0);
  61. IF sqlite3_step(s) = SQLiteDone THEN good := TRUE END
  62. END;
  63. IF sqlite3_finalize(s) # SQLiteOk THEN good := FALSE END
  64. END;
  65. RETURN good AND (got = want)
  66. END QueryInt;
  67. PROCEDURE QueryText (sql : ARRAY OF CHAR; want : ARRAY OF CHAR) : BOOLEAN;
  68. VAR
  69. s : StmtHandle;
  70. code : INTEGER;
  71. p : CString;
  72. buf : ARRAY [0..63] OF CHAR;
  73. good : BOOLEAN;
  74. BEGIN
  75. s := NIL; good := FALSE;
  76. code := sqlite3_prepare_v2(db, sql, -1, s, NIL);
  77. IF (code = SQLiteOk) AND (s # NIL) THEN
  78. IF sqlite3_step(s) = SQLiteRow THEN
  79. p := sqlite3_column_text(s, 0);
  80. IF (p # NIL) AND (Copy(p, buf) > 0) THEN
  81. good := SameText(buf, want)
  82. END;
  83. IF sqlite3_step(s) # SQLiteDone THEN good := FALSE END
  84. END;
  85. IF sqlite3_finalize(s) # SQLiteOk THEN good := FALSE END
  86. END;
  87. RETURN good
  88. END QueryText;
  89. PROCEDURE QueryError (sql : ARRAY OF CHAR; want : INTEGER) : BOOLEAN;
  90. VAR
  91. s : StmtHandle;
  92. code : INTEGER;
  93. good : BOOLEAN;
  94. BEGIN
  95. s := NIL; good := FALSE;
  96. code := sqlite3_prepare_v2(db, sql, -1, s, NIL);
  97. IF (code = SQLiteOk) AND (s # NIL) THEN
  98. good := sqlite3_step(s) = want;
  99. rc := sqlite3_finalize(s)
  100. END;
  101. RETURN good
  102. END QueryError;
  103. PROCEDURE Register (name : ARRAY OF CHAR; n : INTEGER;
  104. xFunc, xStep, xFinal : ADDRESS) : BOOLEAN;
  105. BEGIN
  106. RETURN sqlite3_create_function_v2(db, name, n, SQLiteUtf8,
  107. NIL, xFunc, xStep, xFinal, NIL) = SQLiteOk
  108. END Register;
  109. PROCEDURE SumStep (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  110. VAR
  111. p : SumPtr;
  112. v : ValueHandle;
  113. BEGIN
  114. p := VAL(SumPtr, sqlite3_aggregate_context(ctx, 8));
  115. IF p # NIL THEN
  116. v := SQLiteArrayAt(argv, 0);
  117. IF sqlite3_value_type(v) # SQLiteNull THEN
  118. p^ := p^ + sqlite3_value_int64(v)
  119. END
  120. END
  121. END SumStep;
  122. PROCEDURE SumFinal (ctx : ContextHandle);
  123. VAR
  124. p : SumPtr;
  125. BEGIN
  126. p := VAL(SumPtr, sqlite3_aggregate_context(ctx, 0));
  127. IF p = NIL THEN
  128. sqlite3_result_null(ctx)
  129. ELSE
  130. sqlite3_result_int64(ctx, p^)
  131. END
  132. END SumFinal;
  133. PROCEDURE AuxFn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  134. VAR
  135. p, q : ADDRESS;
  136. BEGIN
  137. p := ADR(auxMarker[0]);
  138. sqlite3_set_auxdata(ctx, 0, p, NIL);
  139. q := sqlite3_get_auxdata(ctx, 0);
  140. IF q = p THEN
  141. sqlite3_result_int(ctx, 1)
  142. ELSE
  143. sqlite3_result_int(ctx, 0)
  144. END
  145. END AuxFn;
  146. PROCEDURE DupFn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  147. VAR
  148. v, d : ValueHandle;
  149. BEGIN
  150. IF argc = 1 THEN
  151. v := SQLiteArrayAt(argv, 0);
  152. d := sqlite3_value_dup(v);
  153. IF d = NIL THEN
  154. sqlite3_result_error(ctx, "dup failed", -1)
  155. ELSE
  156. sqlite3_result_value(ctx, d);
  157. sqlite3_value_free(d)
  158. END
  159. ELSE
  160. sqlite3_result_error(ctx, "m2_dup expects 1 argument", -1)
  161. END
  162. END DupFn;
  163. PROCEDURE NumTypeFn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  164. BEGIN
  165. IF argc = 1 THEN
  166. sqlite3_result_int(ctx,
  167. sqlite3_value_numeric_type(SQLiteArrayAt(argv, 0)))
  168. ELSE
  169. sqlite3_result_error(ctx, "m2_numtype expects 1 argument", -1)
  170. END
  171. END NumTypeFn;
  172. PROCEDURE Zero32Fn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  173. BEGIN
  174. sqlite3_result_zeroblob(ctx, 4)
  175. END Zero32Fn;
  176. PROCEDURE Zero64Fn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  177. VAR
  178. code : INTEGER;
  179. BEGIN
  180. code := sqlite3_result_zeroblob64(ctx, 4);
  181. IF code # SQLiteOk THEN
  182. sqlite3_result_error(ctx, "zeroblob64 failed", -1)
  183. END
  184. END Zero64Fn;
  185. PROCEDURE Text64Fn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  186. BEGIN
  187. sqlite3_result_text64(ctx, "hi", 2, SQLiteTransient(), SQLiteUtf8)
  188. END Text64Fn;
  189. PROCEDURE Blob64Fn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  190. BEGIN
  191. sqlite3_result_blob64(ctx, ADR(bin[0]), 3, SQLiteTransient())
  192. END Blob64Fn;
  193. PROCEDURE TooBigFn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  194. BEGIN
  195. sqlite3_result_error_toobig(ctx)
  196. END TooBigFn;
  197. PROCEDURE NoMemFn (ctx : ContextHandle; argc : INTEGER; argv : ADDRESS);
  198. BEGIN
  199. sqlite3_result_error_nomem(ctx)
  200. END NoMemFn;
  201. BEGIN
  202. ExitCode := 1;
  203. db := NIL; stmt := NIL; ok := FALSE;
  204. bin[0] := CHR(0); bin[1] := CHR(1); bin[2] := CHR(2);
  205. auxMarker[0] := 'A';
  206. IF (sqlite3_open_v2(":memory:", db,
  207. SQLiteOpenReadWrite + SQLiteOpenCreate + SQLiteOpenMemory,
  208. NIL) = SQLiteOk) AND (db # NIL)
  209. AND ExecSQL("CREATE TABLE nums(x INTEGER);")
  210. AND ExecSQL("INSERT INTO nums VALUES(1);")
  211. AND ExecSQL("INSERT INTO nums VALUES(2);")
  212. AND ExecSQL("INSERT INTO nums VALUES(3);") THEN
  213. ok := TRUE
  214. END;
  215. IF ok THEN
  216. ok := Register("m2_sum", 1, NIL, ADR(SumStep), ADR(SumFinal))
  217. AND QueryInt("SELECT m2_sum(x) FROM nums;", 6)
  218. END;
  219. IF ok THEN
  220. ok := Register("m2_aux", 0, ADR(AuxFn), NIL, NIL)
  221. AND QueryInt("SELECT m2_aux();", 1)
  222. END;
  223. IF ok THEN
  224. ok := Register("m2_dup", 1, ADR(DupFn), NIL, NIL)
  225. AND QueryText("SELECT m2_dup('abc');", "abc")
  226. END;
  227. IF ok THEN
  228. ok := Register("m2_numtype", 1, ADR(NumTypeFn), NIL, NIL)
  229. AND QueryInt("SELECT m2_numtype('42');", SQLiteInteger)
  230. END;
  231. IF ok THEN
  232. ok := Register("m2_zero32", 0, ADR(Zero32Fn), NIL, NIL)
  233. AND QueryText("SELECT hex(m2_zero32());", "00000000")
  234. END;
  235. IF ok THEN
  236. ok := Register("m2_zero64", 0, ADR(Zero64Fn), NIL, NIL)
  237. AND QueryText("SELECT hex(m2_zero64());", "00000000")
  238. END;
  239. IF ok THEN
  240. ok := Register("m2_text64", 0, ADR(Text64Fn), NIL, NIL)
  241. AND QueryText("SELECT m2_text64();", "hi")
  242. END;
  243. IF ok THEN
  244. ok := Register("m2_blob64", 0, ADR(Blob64Fn), NIL, NIL)
  245. AND QueryText("SELECT hex(m2_blob64());", "000102")
  246. END;
  247. IF ok THEN
  248. ok := Register("m2_toobig", 0, ADR(TooBigFn), NIL, NIL)
  249. AND QueryError("SELECT m2_toobig();", SQLiteTooBig)
  250. END;
  251. IF ok THEN
  252. ok := Register("m2_nomem", 0, ADR(NoMemFn), NIL, NIL)
  253. AND QueryError("SELECT m2_nomem();", SQLiteNoMem)
  254. END;
  255. IF db # NIL THEN
  256. IF sqlite3_close(db) # SQLiteOk THEN ok := FALSE END
  257. END;
  258. IF ok THEN ExitCode := 42 END
  259. END SQLiteAggregate.