| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- MODULE SQLiteCallback;
- (* Callback support:
- sqlite3_exec row callbacks, scalar SQL functions and the
- sqlite3_value_* / sqlite3_result_* families. *)
- FROM SYSTEM IMPORT ADDRESS, ADR;
- FROM CStr IMPORT CString, Copy;
- FROM SQLite IMPORT
- SQLiteOk, SQLiteRow, SQLiteDone, SQLiteAbort,
- SQLiteInteger, SQLiteFloat, SQLiteText, SQLiteBlob, SQLiteNull,
- SQLiteUtf8, SQLiteDeterministic,
- SQLiteOpenReadWrite, SQLiteOpenCreate, SQLiteOpenMemory,
- sqlite3_open_v2, sqlite3_exec, sqlite3_close_v2,
- sqlite3_prepare_v2, sqlite3_step,
- sqlite3_column_int, sqlite3_column_int64, sqlite3_column_double,
- sqlite3_column_text, sqlite3_finalize,
- sqlite3_errmsg, sqlite3_free,
- sqlite3_create_function_v2,
- sqlite3_value_type, sqlite3_value_int, sqlite3_value_int64,
- sqlite3_value_double, sqlite3_value_text, sqlite3_value_blob,
- sqlite3_value_bytes,
- sqlite3_result_null, sqlite3_result_int, sqlite3_result_int64,
- sqlite3_result_double, sqlite3_result_text, sqlite3_result_blob,
- sqlite3_result_error, sqlite3_result_error_code,
- sqlite3_user_data, sqlite3_context_db_handle,
- SQLiteArrayAt, SQLiteTransient;
- VAR
- ExitCode : INTEGER;
- db : ADDRESS;
- cbRows, rc : INTEGER;
- cbBad : BOOLEAN;
- errmsg : ADDRESS;
- ok : BOOLEAN;
- bin : ARRAY [0..7] OF CHAR;
- PROCEDURE SameText (a, b : ARRAY OF CHAR) : BOOLEAN;
- VAR
- i : INTEGER;
- BEGIN
- i := 0;
- WHILE (a[i] # CHR(0)) AND (a[i] = b[i]) DO
- i := i + 1
- END;
- RETURN (a[i] = CHR(0)) AND (b[i] = CHR(0))
- END SameText;
- (* sqlite3_exec row callback. *)
- PROCEDURE RowCb (arg : ADDRESS; argc : INTEGER;
- argv, colNames : ADDRESS) : INTEGER;
- VAR
- value, name : CString;
- vbuf, nbuf : ARRAY [0..7] OF CHAR;
- BEGIN
- IF (argc = 1) AND (arg = ADR(cbRows)) THEN
- value := SQLiteArrayAt(argv, 0);
- name := SQLiteArrayAt(colNames, 0);
- IF (value # NIL) AND (name # NIL)
- AND (Copy(value, vbuf) > 0) AND (Copy(name, nbuf) > 0) THEN
- IF ((vbuf[0] = '1') OR (vbuf[0] = '2')) AND (nbuf[0] = 'n') THEN
- cbRows := cbRows + 1
- ELSE
- cbBad := TRUE
- END
- ELSE
- cbBad := TRUE
- END
- ELSE
- cbBad := TRUE
- END;
- RETURN 0
- END RowCb;
- (* Scalar SQL function callbacks. *)
- PROCEDURE AddFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- BEGIN
- IF argc = 2 THEN
- sqlite3_result_int(ctx,
- sqlite3_value_int(SQLiteArrayAt(argv, 0))
- + sqlite3_value_int(SQLiteArrayAt(argv, 1)))
- ELSE
- sqlite3_result_error(ctx, "m2_add expects 2 arguments", -1)
- END
- END AddFn;
- PROCEDURE MixFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- VAR
- v : ADDRESS;
- s : CString;
- buf : ARRAY [0..7] OF CHAR;
- n : CARDINAL;
- good : BOOLEAN;
- BEGIN
- good := FALSE;
- IF argc = 4 THEN
- v := SQLiteArrayAt(argv, 0);
- good := (sqlite3_value_type(v) = SQLiteInteger)
- AND (sqlite3_value_int(v) = 7)
- AND (sqlite3_value_int64(v) = 7);
- v := SQLiteArrayAt(argv, 1);
- good := good AND (sqlite3_value_type(v) = SQLiteFloat)
- AND (sqlite3_value_double(v) = 8.5);
- v := SQLiteArrayAt(argv, 2);
- s := sqlite3_value_text(v);
- n := Copy(s, buf);
- good := good AND (sqlite3_value_type(v) = SQLiteText)
- AND (sqlite3_value_bytes(v) = 2)
- AND (n = 2) AND (buf[0] = 'h') AND (buf[1] = 'i');
- v := SQLiteArrayAt(argv, 3);
- good := good AND (sqlite3_value_type(v) = SQLiteNull)
- END;
- IF good THEN
- sqlite3_result_int(ctx, 42)
- ELSE
- sqlite3_result_error(ctx, "m2_mix bad arguments", -1)
- END
- END MixFn;
- PROCEDURE EchoFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- VAR
- v : ADDRESS;
- s : CString;
- buf : ARRAY [0..63] OF CHAR;
- n : CARDINAL;
- BEGIN
- IF argc = 1 THEN
- v := SQLiteArrayAt(argv, 0);
- IF sqlite3_value_type(v) = SQLiteText THEN
- s := sqlite3_value_text(v);
- n := Copy(s, buf);
- sqlite3_result_text(ctx, buf, INTEGER(n), SQLiteTransient())
- ELSE
- sqlite3_result_null(ctx)
- END
- ELSE
- sqlite3_result_error(ctx, "m2_echo expects 1 argument", -1)
- END
- END EchoFn;
- PROCEDURE NullFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- BEGIN
- sqlite3_result_null(ctx)
- END NullFn;
- PROCEDURE Int64Fn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- BEGIN
- IF argc = 1 THEN
- sqlite3_result_int64(ctx,
- sqlite3_value_int64(SQLiteArrayAt(argv, 0)))
- ELSE
- sqlite3_result_error(ctx, "m2_int64 expects 1 argument", -1)
- END
- END Int64Fn;
- PROCEDURE DoubleFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- BEGIN
- IF argc = 1 THEN
- sqlite3_result_double(ctx,
- sqlite3_value_double(SQLiteArrayAt(argv, 0)))
- ELSE
- sqlite3_result_error(ctx, "m2_double expects 1 argument", -1)
- END
- END DoubleFn;
- PROCEDURE BlobFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- BEGIN
- sqlite3_result_blob(ctx, ADR(bin[0]), 3, SQLiteTransient())
- END BlobFn;
- PROCEDURE BlobLenFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- VAR
- v, p : ADDRESS;
- BEGIN
- IF argc = 1 THEN
- v := SQLiteArrayAt(argv, 0);
- p := sqlite3_value_blob(v);
- IF (sqlite3_value_type(v) = SQLiteBlob) AND (p # NIL) THEN
- sqlite3_result_int(ctx, sqlite3_value_bytes(v))
- ELSE
- sqlite3_result_error(ctx, "m2_blob_len expects a BLOB", -1)
- END
- ELSE
- sqlite3_result_error(ctx, "m2_blob_len expects 1 argument", -1)
- END
- END BlobLenFn;
- PROCEDURE DataFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- BEGIN
- IF (sqlite3_user_data(ctx) = ADR(db))
- AND (sqlite3_context_db_handle(ctx) = db) THEN
- sqlite3_result_int(ctx, 1)
- ELSE
- sqlite3_result_int(ctx, 0)
- END
- END DataFn;
- PROCEDURE FailFn (ctx : ADDRESS; argc : INTEGER; argv : ADDRESS);
- BEGIN
- sqlite3_result_error(ctx, "m2_fail", -1);
- sqlite3_result_error_code(ctx, SQLiteAbort)
- END FailFn;
- PROCEDURE QueryInt (sql : ARRAY OF CHAR; want : INTEGER) : BOOLEAN;
- VAR
- stmt : ADDRESS;
- rc, got : INTEGER;
- ok : BOOLEAN;
- BEGIN
- stmt := NIL; got := -777; ok := FALSE;
- rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
- IF (rc = SQLiteOk) AND (stmt # NIL) THEN
- IF sqlite3_step(stmt) = SQLiteRow THEN
- got := sqlite3_column_int(stmt, 0);
- IF sqlite3_step(stmt) = SQLiteDone THEN ok := TRUE END
- END;
- IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
- END;
- RETURN ok AND (got = want)
- END QueryInt;
- PROCEDURE QueryInt64 (sql : ARRAY OF CHAR; want : LONGINT) : BOOLEAN;
- VAR
- stmt : ADDRESS;
- rc : INTEGER;
- got : LONGINT;
- ok : BOOLEAN;
- BEGIN
- stmt := NIL; got := -777; ok := FALSE;
- rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
- IF (rc = SQLiteOk) AND (stmt # NIL) THEN
- IF sqlite3_step(stmt) = SQLiteRow THEN
- got := sqlite3_column_int64(stmt, 0);
- IF sqlite3_step(stmt) = SQLiteDone THEN ok := TRUE END
- END;
- IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
- END;
- RETURN ok AND (got = want)
- END QueryInt64;
- PROCEDURE QueryDouble (sql : ARRAY OF CHAR; want : REAL) : BOOLEAN;
- VAR
- stmt : ADDRESS;
- rc : INTEGER;
- got : REAL;
- ok : BOOLEAN;
- BEGIN
- stmt := NIL; got := -777.0; ok := FALSE;
- rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
- IF (rc = SQLiteOk) AND (stmt # NIL) THEN
- IF sqlite3_step(stmt) = SQLiteRow THEN
- got := sqlite3_column_double(stmt, 0);
- IF sqlite3_step(stmt) = SQLiteDone THEN ok := TRUE END
- END;
- IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
- END;
- RETURN ok AND (got = want)
- END QueryDouble;
- PROCEDURE QueryText (sql : ARRAY OF CHAR; want : ARRAY OF CHAR) : BOOLEAN;
- VAR
- stmt : ADDRESS;
- rc : INTEGER;
- s : CString;
- buf : ARRAY [0..63] OF CHAR;
- ok : BOOLEAN;
- BEGIN
- stmt := NIL; ok := FALSE;
- rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
- IF (rc = SQLiteOk) AND (stmt # NIL) THEN
- IF sqlite3_step(stmt) = SQLiteRow THEN
- s := sqlite3_column_text(stmt, 0);
- IF (s # NIL) AND (Copy(s, buf) > 0) THEN
- ok := SameText(buf, want)
- END;
- IF sqlite3_step(stmt) # SQLiteDone THEN ok := FALSE END
- END;
- IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
- END;
- RETURN ok
- END QueryText;
- PROCEDURE QueryFails (sql : ARRAY OF CHAR) : BOOLEAN;
- VAR
- stmt : ADDRESS;
- rc : INTEGER;
- s : CString;
- buf : ARRAY [0..63] OF CHAR;
- ok : BOOLEAN;
- BEGIN
- stmt := NIL; ok := FALSE;
- rc := sqlite3_prepare_v2(db, sql, -1, stmt, NIL);
- IF (rc = SQLiteOk) AND (stmt # NIL) THEN
- IF sqlite3_step(stmt) = SQLiteAbort THEN
- s := sqlite3_errmsg(db);
- IF (s # NIL) AND (Copy(s, buf) > 0)
- AND SameText(buf, "m2_fail") THEN
- ok := TRUE
- END
- END;
- rc := sqlite3_finalize(stmt)
- END;
- RETURN ok
- END QueryFails;
- PROCEDURE Register (name : ARRAY OF CHAR;
- nArgs : INTEGER;
- userData, fn : ADDRESS) : BOOLEAN;
- BEGIN
- RETURN sqlite3_create_function_v2(db, name, nArgs,
- SQLiteUtf8 + SQLiteDeterministic, userData, fn, NIL, NIL, NIL)
- = SQLiteOk
- END Register;
- BEGIN
- ExitCode := 1;
- db := NIL; cbRows := 0; cbBad := FALSE;
- bin[0] := CHR(0); bin[1] := CHR(1); bin[2] := CHR(2);
- bin[3] := CHR(0); bin[4] := CHR(255);
- IF (sqlite3_open_v2(":memory:", db,
- SQLiteOpenReadWrite + SQLiteOpenCreate + SQLiteOpenMemory,
- NIL) = SQLiteOk) AND (db # NIL) THEN
- IF Register("m2_add", 2, NIL, ADR(AddFn))
- AND Register("m2_mix", 4, NIL, ADR(MixFn))
- AND Register("m2_echo", 1, NIL, ADR(EchoFn))
- AND Register("m2_null", 0, NIL, ADR(NullFn))
- AND Register("m2_int64", 1, NIL, ADR(Int64Fn))
- AND Register("m2_double", 1, NIL, ADR(DoubleFn))
- AND Register("m2_blob", 0, NIL, ADR(BlobFn))
- AND Register("m2_blob_len", 1, NIL, ADR(BlobLenFn))
- AND Register("m2_hasdata", 0, ADR(db), ADR(DataFn))
- AND Register("m2_fail", 0, NIL, ADR(FailFn)) THEN
- ok := TRUE
- ELSE
- ok := FALSE
- END
- END;
- IF ok THEN ok := QueryInt("SELECT m2_add(20, 22);", 42) END;
- IF ok THEN
- ok := QueryInt("SELECT m2_mix(7, 8.5, 'hi', NULL);", 42)
- END;
- IF ok THEN ok := QueryText("SELECT m2_echo('hello');", "hello") END;
- IF ok THEN ok := QueryInt("SELECT m2_echo(NULL) IS NULL;", 1) END;
- IF ok THEN ok := QueryInt("SELECT m2_null() IS NULL;", 1) END;
- IF ok THEN ok := QueryInt64("SELECT m2_int64(42);", 42) END;
- IF ok THEN ok := QueryDouble("SELECT m2_double(3.5);", 3.5) END;
- IF ok THEN ok := QueryText("SELECT hex(m2_blob());", "000102") END;
- IF ok THEN
- ok := QueryInt("SELECT m2_blob_len(zeroblob(3));", 3)
- END;
- IF ok THEN ok := QueryInt("SELECT m2_hasdata();", 1) END;
- IF ok THEN ok := QueryFails("SELECT m2_fail();") END;
- IF ok THEN
- cbRows := 0; cbBad := FALSE; errmsg := NIL;
- rc := sqlite3_exec(db, "SELECT 1 AS n UNION ALL SELECT 2;",
- ADR(RowCb), ADR(cbRows), errmsg);
- ok := (rc = SQLiteOk) AND (NOT cbBad) AND (cbRows = 2);
- IF errmsg # NIL THEN
- sqlite3_free(errmsg);
- errmsg := NIL
- END
- END;
- IF db # NIL THEN
- IF sqlite3_close_v2(db) # SQLiteOk THEN ok := FALSE END
- END;
- IF ok THEN ExitCode := 42 END
- END SQLiteCallback.
|