test_func.mod 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. MODULE test_func ;
  2. (*
  3. m2SQLITE test: application-defined SQL functions. Covers scalar
  4. registration, every value reader and result writer used here,
  5. error paths, an aggregate with shared state and userdata, and
  6. the auxdata round trip.
  7. *)
  8. FROM SYSTEM IMPORT ADDRESS, ADR;
  9. FROM SQLite IMPORT DbHandle, StmtHandle, ContextHandle, ValueHandle,
  10. SQLiteOk, SQLiteRow, SQLiteDone,
  11. SQLiteInteger, SQLiteText, SQLiteNull,
  12. SQLiteTooBig, SQLiteUtf8, SQLiteDeterministic,
  13. sqlite3_open, sqlite3_close,
  14. sqlite3_prepare_v2, sqlite3_step, sqlite3_finalize,
  15. sqlite3_create_function, sqlite3_create_function_v2,
  16. sqlite3_value_blob, sqlite3_value_double, sqlite3_value_int,
  17. sqlite3_value_int64, sqlite3_value_text, sqlite3_value_bytes,
  18. sqlite3_value_type, sqlite3_value_numeric_type,
  19. sqlite3_result_blob, sqlite3_result_blob64, sqlite3_result_double,
  20. sqlite3_result_error, sqlite3_result_error_code,
  21. sqlite3_result_error_toobig, sqlite3_result_error_nomem,
  22. sqlite3_result_int, sqlite3_result_int64, sqlite3_result_null,
  23. sqlite3_result_text, sqlite3_result_text64, sqlite3_result_value,
  24. sqlite3_result_zeroblob, sqlite3_result_zeroblob64,
  25. sqlite3_aggregate_context, sqlite3_user_data,
  26. sqlite3_context_db_handle, sqlite3_get_auxdata, sqlite3_set_auxdata,
  27. sqlite3_column_int, sqlite3_column_int64, sqlite3_column_double,
  28. sqlite3_column_text,
  29. sqlite3_errmsg;
  30. FROM SQLiteUtils IMPORT CStrToM2, ErrMsg, TransientDestr;
  31. FROM libc IMPORT printf;
  32. TYPE
  33. AddrVec = POINTER TO ARRAY [0..255] OF ADDRESS;
  34. ByteVec = POINTER TO ARRAY [0..1023] OF CHAR;
  35. SumPtr = POINTER TO LONGINT;
  36. CntPtr = POINTER TO INTEGER;
  37. VAR
  38. db: DbHandle; stmt: StmtHandle;
  39. stepCalls: INTEGER; dbBad: BOOLEAN; auxSlot: ADDRESS;
  40. PROCEDURE fail (what: ARRAY OF CHAR);
  41. VAR e: ARRAY [0..255] OF CHAR;
  42. BEGIN
  43. ErrMsg(db, e);
  44. printf("FAIL %s: %s\n", what, e);
  45. HALT(1)
  46. END fail;
  47. PROCEDURE check (rc: INTEGER; what: ARRAY OF CHAR);
  48. BEGIN
  49. IF rc # SQLiteOk THEN fail(what) END
  50. END check;
  51. PROCEDURE ArgAt (argv: ADDRESS; i: INTEGER) : ValueHandle;
  52. VAR vec: AddrVec;
  53. BEGIN
  54. vec := VAL(AddrVec, argv);
  55. RETURN vec^[i]
  56. END ArgAt;
  57. PROCEDURE oneRow (sql: ARRAY OF CHAR; what: ARRAY OF CHAR);
  58. VAR rc: INTEGER;
  59. BEGIN
  60. check(sqlite3_prepare_v2(db, sql, -1, stmt, NIL), what);
  61. rc := sqlite3_step(stmt);
  62. IF rc # SQLiteRow THEN fail(what) END
  63. END oneRow;
  64. PROCEDURE endRow (what: ARRAY OF CHAR);
  65. VAR rc: INTEGER;
  66. BEGIN
  67. rc := sqlite3_step(stmt);
  68. IF rc # SQLiteDone THEN fail(what) END;
  69. check(sqlite3_finalize(stmt), what)
  70. END endRow;
  71. PROCEDURE errCase (sql: ARRAY OF CHAR; tag: INTEGER);
  72. VAR rc: INTEGER;
  73. BEGIN
  74. check(sqlite3_prepare_v2(db, sql, -1, stmt, NIL), "err2 prep");
  75. rc := sqlite3_step(stmt);
  76. printf("errcase %d rc %d\n", tag, rc);
  77. IF rc = SQLiteOk THEN fail("err2 ok") END;
  78. rc := sqlite3_finalize(stmt)
  79. END errCase;
  80. (* dbl(x) = 2*x, errors on missing argument *)
  81. PROCEDURE dbl (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  82. BEGIN
  83. IF argc < 1 THEN
  84. sqlite3_result_error(ctx, "need an argument", -1);
  85. RETURN
  86. END;
  87. sqlite3_result_double(ctx, sqlite3_value_double(ArgAt(argv, 0)) * 2.0)
  88. END dbl;
  89. (* shout(t) uppercases ASCII text *)
  90. PROCEDURE shout (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  91. VAR v: ValueHandle; p: ByteVec; n, i: INTEGER; c: CHAR;
  92. out: ARRAY [0..127] OF CHAR;
  93. BEGIN
  94. IF argc < 1 THEN
  95. sqlite3_result_error(ctx, "need an argument", -1);
  96. RETURN
  97. END;
  98. v := ArgAt(argv, 0);
  99. n := sqlite3_value_bytes(v);
  100. IF n > 127 THEN n := 127 END;
  101. p := VAL(ByteVec, sqlite3_value_text(v));
  102. FOR i := 0 TO n - 1 DO
  103. c := p^[i];
  104. IF (c >= 'a') AND (c <= 'z') THEN c := CHR(ORD(c) - 32) END;
  105. out[i] := c
  106. END;
  107. out[n] := 0C;
  108. sqlite3_result_text(ctx, out, n, TransientDestr())
  109. END shout;
  110. (* incbig(x) = x+1 in 64 bits *)
  111. PROCEDURE incbig (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  112. BEGIN
  113. sqlite3_result_int64(ctx, sqlite3_value_int64(ArgAt(argv, 0)) + 1)
  114. END incbig;
  115. (* echoblob(x) passes bytes through *)
  116. PROCEDURE echoblob (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  117. VAR v: ValueHandle;
  118. BEGIN
  119. v := ArgAt(argv, 0);
  120. sqlite3_result_blob(ctx, sqlite3_value_blob(v),
  121. sqlite3_value_bytes(v), TransientDestr())
  122. END echoblob;
  123. (* typenum / numtype expose the type codes *)
  124. PROCEDURE typenum (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  125. BEGIN
  126. sqlite3_result_int(ctx, sqlite3_value_type(ArgAt(argv, 0)))
  127. END typenum;
  128. PROCEDURE numtype (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  129. BEGIN
  130. sqlite3_result_int(ctx, sqlite3_value_numeric_type(ArgAt(argv, 0)))
  131. END numtype;
  132. (* nullifneg returns NULL or the value itself *)
  133. PROCEDURE nullifneg (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  134. VAR v: ValueHandle;
  135. BEGIN
  136. v := ArgAt(argv, 0);
  137. IF sqlite3_value_int(v) < 0 THEN
  138. sqlite3_result_null(ctx)
  139. ELSE
  140. sqlite3_result_value(ctx, v)
  141. END
  142. END nullifneg;
  143. (* failcode reports an error with a chosen code *)
  144. PROCEDURE failcode (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  145. BEGIN
  146. sqlite3_result_error_code(ctx, SQLiteTooBig);
  147. sqlite3_result_error(ctx, "boom", -1)
  148. END failcode;
  149. PROCEDURE bigerr (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  150. BEGIN
  151. sqlite3_result_error_toobig(ctx)
  152. END bigerr;
  153. PROCEDURE nomem (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  154. BEGIN
  155. sqlite3_result_error_nomem(ctx)
  156. END nomem;
  157. (* fixed-size results *)
  158. PROCEDURE zb8 (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  159. BEGIN
  160. sqlite3_result_zeroblob(ctx, 8)
  161. END zb8;
  162. PROCEDURE zb64 (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  163. VAR rc: INTEGER;
  164. BEGIN
  165. rc := sqlite3_result_zeroblob64(ctx, VAL(LONGCARD, 12));
  166. IF rc # SQLiteOk THEN sqlite3_result_error(ctx, "zb64", -1) END
  167. END zb64;
  168. PROCEDURE b64 (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  169. VAR v: ValueHandle;
  170. BEGIN
  171. v := ArgAt(argv, 0);
  172. sqlite3_result_blob64(ctx, sqlite3_value_blob(v),
  173. VAL(LONGCARD, sqlite3_value_bytes(v)),
  174. TransientDestr())
  175. END b64;
  176. PROCEDURE t64 (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  177. BEGIN
  178. sqlite3_result_text64(ctx, "hi", VAL(LONGCARD, 2),
  179. TransientDestr(), SQLiteUtf8)
  180. END t64;
  181. (* msum aggregate: state in aggregate_context, count via userdata *)
  182. PROCEDURE sumStep (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  183. VAR s: SumPtr; c: CntPtr; v: ValueHandle;
  184. BEGIN
  185. s := VAL(SumPtr, sqlite3_aggregate_context(ctx, 8));
  186. c := VAL(CntPtr, sqlite3_user_data(ctx));
  187. c^ := c^ + 1;
  188. IF sqlite3_context_db_handle(ctx) # db THEN dbBad := TRUE END;
  189. v := ArgAt(argv, 0);
  190. IF sqlite3_value_type(v) # SQLiteNull THEN
  191. s^ := s^ + sqlite3_value_int64(v)
  192. END
  193. END sumStep;
  194. PROCEDURE sumFinal (ctx: ContextHandle);
  195. VAR s: SumPtr;
  196. BEGIN
  197. s := VAL(SumPtr, sqlite3_aggregate_context(ctx, 0));
  198. sqlite3_result_int64(ctx, s^)
  199. END sumFinal;
  200. (* auxdata set/get round trip *)
  201. PROCEDURE auxdemo (ctx: ContextHandle; argc: INTEGER; argv: ADDRESS);
  202. VAR got: ADDRESS;
  203. BEGIN
  204. IF sqlite3_get_auxdata(ctx, 0) = NIL THEN
  205. sqlite3_set_auxdata(ctx, 0, ADR(auxSlot), NIL)
  206. END;
  207. got := sqlite3_get_auxdata(ctx, 0);
  208. IF got # ADR(auxSlot) THEN
  209. sqlite3_result_error(ctx, "auxdata lost", -1);
  210. RETURN
  211. END;
  212. sqlite3_result_int(ctx, sqlite3_value_int(ArgAt(argv, 0)) * 10)
  213. END auxdemo;
  214. VAR
  215. rc, i: INTEGER; li: LONGINT; d: REAL;
  216. buf: ARRAY [0..63] OF CHAR;
  217. BEGIN
  218. stepCalls := 0; dbBad := FALSE; auxSlot := NIL;
  219. check(sqlite3_open(":memory:", db), "open");
  220. check(sqlite3_create_function(db, "dbl", 1, SQLiteUtf8, NIL,
  221. dbl, NIL, NIL), "reg dbl");
  222. check(sqlite3_create_function(db, "dblany", -1, SQLiteUtf8, NIL,
  223. dbl, NIL, NIL), "reg dblany");
  224. check(sqlite3_create_function_v2(db, "shout", 1,
  225. SQLiteUtf8 + SQLiteDeterministic,
  226. NIL, shout, NIL, NIL, NIL),
  227. "reg shout");
  228. check(sqlite3_create_function(db, "incbig", 1, SQLiteUtf8, NIL,
  229. incbig, NIL, NIL), "reg incbig");
  230. check(sqlite3_create_function(db, "echoblob", 1, SQLiteUtf8, NIL,
  231. echoblob, NIL, NIL), "reg echoblob");
  232. check(sqlite3_create_function(db, "typenum", 1, SQLiteUtf8, NIL,
  233. typenum, NIL, NIL), "reg typenum");
  234. check(sqlite3_create_function(db, "numtype", 1, SQLiteUtf8, NIL,
  235. numtype, NIL, NIL), "reg numtype");
  236. check(sqlite3_create_function(db, "nullifneg", 1, SQLiteUtf8, NIL,
  237. nullifneg, NIL, NIL), "reg nullifneg");
  238. check(sqlite3_create_function(db, "failcode", 0, SQLiteUtf8, NIL,
  239. failcode, NIL, NIL), "reg failcode");
  240. check(sqlite3_create_function(db, "bigerr", 0, SQLiteUtf8, NIL,
  241. bigerr, NIL, NIL), "reg bigerr");
  242. check(sqlite3_create_function(db, "nomem", 0, SQLiteUtf8, NIL,
  243. nomem, NIL, NIL), "reg nomem");
  244. check(sqlite3_create_function(db, "zb8", 0, SQLiteUtf8, NIL,
  245. zb8, NIL, NIL), "reg zb8");
  246. check(sqlite3_create_function(db, "zb64", 0, SQLiteUtf8, NIL,
  247. zb64, NIL, NIL), "reg zb64");
  248. check(sqlite3_create_function(db, "b64", 1, SQLiteUtf8, NIL,
  249. b64, NIL, NIL), "reg b64");
  250. check(sqlite3_create_function(db, "t64", 0, SQLiteUtf8, NIL,
  251. t64, NIL, NIL), "reg t64");
  252. check(sqlite3_create_function(db, "msum", 1, SQLiteUtf8,
  253. ADR(stepCalls), NIL,
  254. sumStep, sumFinal), "reg msum");
  255. check(sqlite3_create_function(db, "auxdemo", 1, SQLiteUtf8, NIL,
  256. auxdemo, NIL, NIL), "reg auxdemo");
  257. oneRow("SELECT dbl(21.0);", "dbl prep");
  258. d := sqlite3_column_double(stmt, 0);
  259. printf("dbl %f\n", d);
  260. IF (d < 41.9) OR (d > 42.1) THEN fail("dbl value") END;
  261. endRow("dbl");
  262. oneRow("SELECT shout('hello');", "shout prep");
  263. CStrToM2(sqlite3_column_text(stmt, 0), buf);
  264. printf("shout %s\n", buf);
  265. IF buf[0] # 'H' THEN fail("shout value") END;
  266. endRow("shout");
  267. oneRow("SELECT incbig(9000000000);", "incbig prep");
  268. li := sqlite3_column_int64(stmt, 0);
  269. printf("incbig %ld\n", li);
  270. IF li # VAL(LONGINT, 9000000001) THEN fail("incbig value") END;
  271. endRow("incbig");
  272. oneRow("SELECT echoblob(X'ABCD') = X'ABCD';", "echoblob prep");
  273. IF sqlite3_column_int(stmt, 0) # 1 THEN fail("echoblob value") END;
  274. endRow("echoblob");
  275. oneRow("SELECT typenum(1), typenum('a'), typenum(NULL),"
  276. + " numtype('123'), numtype(1.5);", "types prep");
  277. IF sqlite3_column_int(stmt, 0) # SQLiteInteger THEN fail("t int") END;
  278. IF sqlite3_column_int(stmt, 1) # SQLiteText THEN fail("t text") END;
  279. IF sqlite3_column_int(stmt, 2) # SQLiteNull THEN fail("t null") END;
  280. IF sqlite3_column_int(stmt, 3) # SQLiteInteger THEN fail("nt") END;
  281. IF sqlite3_column_int(stmt, 4) # 2 THEN fail("nt float") END;
  282. printf("types ok\n");
  283. endRow("types");
  284. oneRow("SELECT nullifneg(-5) IS NULL, nullifneg(7);", "null prep");
  285. IF sqlite3_column_int(stmt, 0) # 1 THEN fail("null isnull") END;
  286. IF sqlite3_column_int(stmt, 1) # 7 THEN fail("null passthru") END;
  287. endRow("null");
  288. oneRow("SELECT LENGTH(zb8()), LENGTH(zb64()),"
  289. + " LENGTH(b64(X'0102')), t64();", "zeros prep");
  290. IF sqlite3_column_int(stmt, 0) # 8 THEN fail("zb8") END;
  291. IF sqlite3_column_int(stmt, 1) # 12 THEN fail("zb64") END;
  292. IF sqlite3_column_int(stmt, 2) # 2 THEN fail("b64") END;
  293. CStrToM2(sqlite3_column_text(stmt, 3), buf);
  294. IF buf[0] # 'h' THEN fail("t64") END;
  295. printf("sized results ok\n");
  296. endRow("zeros");
  297. oneRow("SELECT msum(column1) FROM (VALUES (1),(2),(3));",
  298. "msum prep");
  299. li := sqlite3_column_int64(stmt, 0);
  300. printf("msum %ld calls %d\n", li, stepCalls);
  301. IF li # 6 THEN fail("msum value") END;
  302. IF stepCalls # 3 THEN fail("msum userdata") END;
  303. IF dbBad THEN fail("db handle") END;
  304. endRow("msum");
  305. oneRow("SELECT auxdemo(5);", "aux prep");
  306. IF sqlite3_column_int(stmt, 0) # 50 THEN fail("aux value") END;
  307. endRow("aux");
  308. (* error paths: each must fail the step with a message *)
  309. check(sqlite3_prepare_v2(db, "SELECT dblany();", -1, stmt, NIL),
  310. "err prep");
  311. rc := sqlite3_step(stmt);
  312. CStrToM2(sqlite3_errmsg(db), buf);
  313. printf("dblany rc %d err %s\n", rc, buf);
  314. IF rc = SQLiteOk THEN fail("dblany ok") END;
  315. IF buf[0] = 0C THEN fail("dblany msg") END;
  316. rc := sqlite3_finalize(stmt);
  317. IF rc = SQLiteOk THEN fail("dblany fin") END;
  318. FOR i := 0 TO 2 DO
  319. IF i = 0 THEN errCase("SELECT failcode();", i)
  320. ELSIF i = 1 THEN errCase("SELECT bigerr();", i)
  321. ELSE errCase("SELECT nomem();", i)
  322. END
  323. END;
  324. check(sqlite3_close(db), "close");
  325. printf("PASS test_func\n")
  326. END test_func.