|
|
@@ -0,0 +1,287 @@
|
|
|
+MODULE showcase_all ;
|
|
|
+
|
|
|
+(*
|
|
|
+ m2SQLITE showcase: exercises every procedure of the SQLite
|
|
|
+ binding plus all SQLiteUtils helpers end to end.
|
|
|
+
|
|
|
+ 1. library info: version, source id, threadsafe, errstr, complete
|
|
|
+ 2. open, autocommit, busy timeout, extended codes, limit, open_v2
|
|
|
+ 3. exec with raw errmsg capture, ExecWithErr failure, errcode paths
|
|
|
+ 4. prepare_v2 tail capture, prepare_v3 flags, sql text, parameter ids
|
|
|
+ 5. all bind kinds, step, changes counters, rowid
|
|
|
+ 6. all column kinds, metadata, busy/reset/rebind, transactions
|
|
|
+ 7. expanded sql, zeroblob, static destructor, close_v2, close
|
|
|
+*)
|
|
|
+
|
|
|
+FROM SYSTEM IMPORT ADDRESS, ADR;
|
|
|
+FROM SQLite IMPORT DbHandle, StmtHandle,
|
|
|
+ SQLiteOk, SQLiteRow, SQLiteDone,
|
|
|
+ SQLiteInteger, SQLiteFloat, SQLiteText, SQLiteBlob, SQLiteNull,
|
|
|
+ SQLiteOpenReadWrite, SQLiteOpenCreate,
|
|
|
+ SQLitePreparePersistent, SQLiteLimitVariableNumber,
|
|
|
+ sqlite3_libversion, sqlite3_libversion_number, sqlite3_sourceid,
|
|
|
+ sqlite3_threadsafe,
|
|
|
+ sqlite3_open, sqlite3_open_v2, sqlite3_close, sqlite3_close_v2,
|
|
|
+ sqlite3_exec,
|
|
|
+ sqlite3_errcode, sqlite3_extended_errcode, sqlite3_errmsg,
|
|
|
+ sqlite3_errstr, sqlite3_error_offset, sqlite3_free,
|
|
|
+ sqlite3_complete, sqlite3_busy_timeout, sqlite3_get_autocommit,
|
|
|
+ sqlite3_changes, sqlite3_total_changes, sqlite3_last_insert_rowid,
|
|
|
+ sqlite3_extended_result_codes, sqlite3_limit,
|
|
|
+ sqlite3_prepare_v2, sqlite3_prepare_v3,
|
|
|
+ sqlite3_step, sqlite3_finalize, sqlite3_reset, sqlite3_clear_bindings,
|
|
|
+ sqlite3_data_count, sqlite3_column_count,
|
|
|
+ sqlite3_sql, sqlite3_expanded_sql,
|
|
|
+ sqlite3_stmt_readonly, sqlite3_stmt_busy,
|
|
|
+ sqlite3_bind_blob, sqlite3_bind_zeroblob,
|
|
|
+ sqlite3_bind_double, sqlite3_bind_int, sqlite3_bind_int64,
|
|
|
+ sqlite3_bind_null, sqlite3_bind_text,
|
|
|
+ sqlite3_bind_parameter_count, sqlite3_bind_parameter_name,
|
|
|
+ sqlite3_bind_parameter_index,
|
|
|
+ sqlite3_column_blob, sqlite3_column_double, sqlite3_column_int,
|
|
|
+ sqlite3_column_int64, sqlite3_column_text, sqlite3_column_bytes,
|
|
|
+ sqlite3_column_type, sqlite3_column_name, sqlite3_column_decltype;
|
|
|
+FROM SQLiteUtils IMPORT CStrToM2, ErrMsg, ErrStr, LibVersionStr,
|
|
|
+ StaticDestr, TransientDestr,
|
|
|
+ BindTextCopy, BindBlobCopy, ExecSimple, ExecWithErr;
|
|
|
+FROM libc IMPORT printf, strncpy;
|
|
|
+
|
|
|
+VAR
|
|
|
+ db, db2: DbHandle;
|
|
|
+ stmt: StmtHandle;
|
|
|
+ rc, i, ncols, ndata, busy, ro: INTEGER;
|
|
|
+ li: LONGINT;
|
|
|
+ d: REAL;
|
|
|
+ tp: INTEGER;
|
|
|
+ tail, msg, p, xsql: ADDRESS;
|
|
|
+ buf, buf2: ARRAY [0..127] OF CHAR;
|
|
|
+ blobIn: ARRAY [0..3] OF CHAR;
|
|
|
+ staticBlob: ARRAY [0..3] OF CHAR;
|
|
|
+ out: ARRAY [0..15] OF CHAR;
|
|
|
+ showInt, showFloat, showText, showBlob, showNull: INTEGER;
|
|
|
+
|
|
|
+PROCEDURE fail (what: ARRAY OF CHAR);
|
|
|
+VAR e: ARRAY [0..255] OF CHAR;
|
|
|
+BEGIN
|
|
|
+ ErrMsg(db, e);
|
|
|
+ printf("FAIL %s: %s\n", what, e);
|
|
|
+ HALT(1)
|
|
|
+END fail;
|
|
|
+
|
|
|
+PROCEDURE check (rc: INTEGER; what: ARRAY OF CHAR);
|
|
|
+BEGIN
|
|
|
+ IF rc # SQLiteOk THEN fail(what) END
|
|
|
+END check;
|
|
|
+
|
|
|
+PROCEDURE section (n: INTEGER; title: ARRAY OF CHAR);
|
|
|
+BEGIN
|
|
|
+ printf("--- [%d] %s ---\n", n, title)
|
|
|
+END section;
|
|
|
+
|
|
|
+BEGIN
|
|
|
+ staticBlob[0] := 'W'; staticBlob[1] := 'X';
|
|
|
+ staticBlob[2] := 'Y'; staticBlob[3] := 'Z';
|
|
|
+ blobIn[0] := 'A'; blobIn[1] := 'B';
|
|
|
+ blobIn[2] := 'C'; blobIn[3] := 'D';
|
|
|
+
|
|
|
+ (* 1. library info *)
|
|
|
+ section(1, "library info");
|
|
|
+ LibVersionStr(buf);
|
|
|
+ printf("version %s number %d\n", buf, sqlite3_libversion_number());
|
|
|
+ IF sqlite3_libversion_number() <= 0 THEN HALT(1) END;
|
|
|
+ CStrToM2(sqlite3_libversion(), buf);
|
|
|
+ CStrToM2(sqlite3_sourceid(), buf2);
|
|
|
+ printf("sourceid %s\n", buf2);
|
|
|
+ printf("threadsafe %d\n", sqlite3_threadsafe());
|
|
|
+ ErrStr(SQLiteOk, buf);
|
|
|
+ printf("errstr ok %s\n", buf);
|
|
|
+ CStrToM2(sqlite3_errstr(SQLiteOk), buf);
|
|
|
+ printf("raw errstr %s\n", buf);
|
|
|
+ IF sqlite3_complete("SELECT 1;") = 0 THEN
|
|
|
+ printf("FAIL complete full\n"); HALT(1)
|
|
|
+ END;
|
|
|
+ IF sqlite3_complete("SELECT") # 0 THEN
|
|
|
+ printf("FAIL complete partial\n"); HALT(1)
|
|
|
+ END;
|
|
|
+ printf("complete ok\n");
|
|
|
+
|
|
|
+ (* 2. open and connection settings *)
|
|
|
+ section(2, "open and settings");
|
|
|
+ check(sqlite3_open(":memory:", db), "open");
|
|
|
+ printf("autocommit %d\n", sqlite3_get_autocommit(db));
|
|
|
+ check(sqlite3_busy_timeout(db, 500), "busy timeout");
|
|
|
+ check(sqlite3_extended_result_codes(db, 1), "ext codes");
|
|
|
+ i := sqlite3_limit(db, SQLiteLimitVariableNumber, -1);
|
|
|
+ printf("limit variables %d\n", i);
|
|
|
+ IF i <= 0 THEN printf("FAIL limit\n"); HALT(1) END;
|
|
|
+ check(sqlite3_open_v2(":memory:", db2,
|
|
|
+ SQLiteOpenReadWrite + SQLiteOpenCreate,
|
|
|
+ NIL), "open_v2");
|
|
|
+
|
|
|
+ (* 3. exec, including raw errmsg capture and failure paths *)
|
|
|
+ section(3, "exec and errors");
|
|
|
+ check(ExecSimple(db, "CREATE TABLE demo(id INTEGER PRIMARY KEY,"
|
|
|
+ + " small INTEGER, big INTEGER,"
|
|
|
+ + " score REAL, label TEXT,"
|
|
|
+ + " payload BLOB, code TEXT);"),
|
|
|
+ "create");
|
|
|
+ check(ExecSimple(db, "INSERT INTO demo(small) VALUES(1);"), "seed");
|
|
|
+ printf("changes %d total %d rowid %ld\n",
|
|
|
+ sqlite3_changes(db), sqlite3_total_changes(db),
|
|
|
+ sqlite3_last_insert_rowid(db));
|
|
|
+ msg := NIL;
|
|
|
+ rc := sqlite3_exec(db, "INSERT INTO demo(small) VALUES(2);",
|
|
|
+ NIL, NIL, ADR(msg));
|
|
|
+ IF rc # SQLiteOk THEN fail("raw exec") END;
|
|
|
+ IF msg # NIL THEN sqlite3_free(msg) END;
|
|
|
+ rc := ExecWithErr(db, "INSERT INTO bogus(x) VALUES(1);", buf);
|
|
|
+ printf("expected failure rc %d err %s\n", rc, buf);
|
|
|
+ IF rc = SQLiteOk THEN printf("FAIL bad sql ok\n"); HALT(1) END;
|
|
|
+ printf("errcode %d ext %d offset %d\n",
|
|
|
+ sqlite3_errcode(db), sqlite3_extended_errcode(db),
|
|
|
+ sqlite3_error_offset(db));
|
|
|
+ CStrToM2(sqlite3_errmsg(db), buf);
|
|
|
+ printf("raw errmsg %s\n", buf);
|
|
|
+
|
|
|
+ (* 4. prepare variants, tail, sql text, parameters *)
|
|
|
+ section(4, "prepare and parameters");
|
|
|
+ tail := NIL;
|
|
|
+ check(sqlite3_prepare_v2(db, "SELECT small FROM demo; SELECT 99;",
|
|
|
+ -1, stmt, ADR(tail)), "prep tail");
|
|
|
+ IF tail = NIL THEN printf("FAIL tail nil\n"); HALT(1) END;
|
|
|
+ CStrToM2(tail, buf);
|
|
|
+ printf("tail %s\n", buf);
|
|
|
+ CStrToM2(sqlite3_sql(stmt), buf);
|
|
|
+ printf("sql %s\n", buf);
|
|
|
+ check(sqlite3_finalize(stmt), "fin tail");
|
|
|
+ check(sqlite3_prepare_v3(db, "SELECT :a, ?, :b;",
|
|
|
+ -1, SQLitePreparePersistent,
|
|
|
+ stmt, NIL), "prep v3");
|
|
|
+ printf("param count %d\n", sqlite3_bind_parameter_count(stmt));
|
|
|
+ IF sqlite3_bind_parameter_count(stmt) # 3 THEN HALT(1) END;
|
|
|
+ CStrToM2(sqlite3_bind_parameter_name(stmt, 1), buf);
|
|
|
+ printf("param1 %s\n", buf);
|
|
|
+ IF sqlite3_bind_parameter_name(stmt, 2) # NIL THEN
|
|
|
+ printf("FAIL anon param named\n"); HALT(1)
|
|
|
+ END;
|
|
|
+ i := sqlite3_bind_parameter_index(stmt, ":b");
|
|
|
+ printf("param :b index %d\n", i);
|
|
|
+ IF i # 3 THEN HALT(1) END;
|
|
|
+ ro := sqlite3_stmt_readonly(stmt);
|
|
|
+ printf("readonly select %d\n", ro);
|
|
|
+ IF ro = 0 THEN HALT(1) END;
|
|
|
+ check(sqlite3_finalize(stmt), "fin v3");
|
|
|
+ check(sqlite3_prepare_v2(db, "INSERT INTO demo(small) VALUES(1);",
|
|
|
+ -1, stmt, NIL), "prep ins");
|
|
|
+ IF sqlite3_stmt_readonly(stmt) # 0 THEN
|
|
|
+ printf("FAIL readonly insert\n"); HALT(1)
|
|
|
+ END;
|
|
|
+ check(sqlite3_finalize(stmt), "fin ins");
|
|
|
+
|
|
|
+ (* 5. all bind kinds *)
|
|
|
+ section(5, "binds and step");
|
|
|
+ check(sqlite3_prepare_v2(db, "DELETE FROM demo;", -1, stmt, NIL),
|
|
|
+ "prep delete");
|
|
|
+ IF sqlite3_step(stmt) # SQLiteDone THEN HALT(1) END;
|
|
|
+ check(sqlite3_finalize(stmt), "fin delete");
|
|
|
+ check(sqlite3_prepare_v2(db, "INSERT INTO demo"
|
|
|
+ + "(small,big,score,label,payload,code)"
|
|
|
+ + " VALUES(?,?,?,?,?,?);",
|
|
|
+ -1, stmt, NIL), "prep full insert");
|
|
|
+ check(sqlite3_bind_int(stmt, 1, 42), "bind int");
|
|
|
+ check(sqlite3_bind_int64(stmt, 2, VAL(LONGINT, 9000000001)),
|
|
|
+ "bind int64");
|
|
|
+ check(sqlite3_bind_double(stmt, 3, 2.5), "bind double");
|
|
|
+ check(BindTextCopy(stmt, 4, "hello"), "bind text copy");
|
|
|
+ check(BindBlobCopy(stmt, 5, ADR(blobIn), 4), "bind blob copy");
|
|
|
+ check(sqlite3_bind_null(stmt, 6), "bind null");
|
|
|
+ IF sqlite3_step(stmt) # SQLiteDone THEN fail("step insert") END;
|
|
|
+ printf("insert changes %d rowid %ld\n",
|
|
|
+ sqlite3_changes(db), sqlite3_last_insert_rowid(db));
|
|
|
+ xsql := sqlite3_expanded_sql(stmt);
|
|
|
+ CStrToM2(xsql, buf);
|
|
|
+ printf("expanded %s\n", buf);
|
|
|
+ IF xsql # NIL THEN sqlite3_free(xsql) END;
|
|
|
+ check(sqlite3_finalize(stmt), "fin full insert");
|
|
|
+
|
|
|
+ (* 6. all column kinds, metadata, reuse, transactions *)
|
|
|
+ section(6, "columns and reuse");
|
|
|
+ check(sqlite3_prepare_v2(db, "SELECT small,big,score,label,"
|
|
|
+ + "payload,code FROM demo;",
|
|
|
+ -1, stmt, NIL), "prep select");
|
|
|
+ rc := sqlite3_step(stmt);
|
|
|
+ IF rc # SQLiteRow THEN fail("step row") END;
|
|
|
+ ncols := sqlite3_column_count(stmt);
|
|
|
+ ndata := sqlite3_data_count(stmt);
|
|
|
+ printf("cols %d data %d\n", ncols, ndata);
|
|
|
+ IF (ncols # 6) OR (ndata # 6) THEN HALT(1) END;
|
|
|
+ busy := sqlite3_stmt_busy(stmt);
|
|
|
+ printf("busy %d\n", busy);
|
|
|
+ IF busy = 0 THEN HALT(1) END;
|
|
|
+ i := sqlite3_column_int(stmt, 0);
|
|
|
+ li := sqlite3_column_int64(stmt, 1);
|
|
|
+ d := sqlite3_column_double(stmt, 2);
|
|
|
+ CStrToM2(sqlite3_column_text(stmt, 3), buf);
|
|
|
+ ncols := sqlite3_column_bytes(stmt, 3);
|
|
|
+ p := sqlite3_column_blob(stmt, 4);
|
|
|
+ tp := sqlite3_column_type(stmt, 4);
|
|
|
+ printf("row %d %ld %f %s bytes %d blobtype %d\n",
|
|
|
+ i, li, d, buf, ncols, tp);
|
|
|
+ IF (i # 42) OR (li # VAL(LONGINT, 9000000001)) THEN HALT(1) END;
|
|
|
+ IF ncols # 5 THEN HALT(1) END;
|
|
|
+ IF (p = NIL) OR (sqlite3_column_bytes(stmt, 4) # 4) THEN HALT(1) END;
|
|
|
+ strncpy(ADR(out), p, 4);
|
|
|
+ out[4] := 0C;
|
|
|
+ printf("blob %s\n", out);
|
|
|
+ IF sqlite3_column_type(stmt, 5) # SQLiteNull THEN HALT(1) END;
|
|
|
+ CStrToM2(sqlite3_column_name(stmt, 0), buf);
|
|
|
+ CStrToM2(sqlite3_column_decltype(stmt, 0), buf2);
|
|
|
+ printf("col0 %s decl %s\n", buf, buf2);
|
|
|
+ showInt := SQLiteInteger; showFloat := SQLiteFloat;
|
|
|
+ showText := SQLiteText; showBlob := SQLiteBlob; showNull := SQLiteNull;
|
|
|
+ printf("type ids int %d float %d text %d blob %d null %d\n",
|
|
|
+ showInt, showFloat, showText, showBlob, showNull);
|
|
|
+ check(sqlite3_reset(stmt), "reset");
|
|
|
+ IF sqlite3_stmt_busy(stmt) # 0 THEN
|
|
|
+ printf("FAIL busy after reset\n"); HALT(1)
|
|
|
+ END;
|
|
|
+ check(sqlite3_clear_bindings(stmt), "clear");
|
|
|
+ check(sqlite3_finalize(stmt), "fin select");
|
|
|
+
|
|
|
+ (* statement reuse with raw transient and static destructors *)
|
|
|
+ check(sqlite3_prepare_v2(db, "INSERT INTO demo(label,payload)"
|
|
|
+ + " VALUES(?,?);",
|
|
|
+ -1, stmt, NIL), "prep reuse");
|
|
|
+ check(sqlite3_bind_text(stmt, 1, "raw", -1, TransientDestr()),
|
|
|
+ "bind raw transient");
|
|
|
+ check(sqlite3_bind_blob(stmt, 2, ADR(staticBlob), 4, StaticDestr()),
|
|
|
+ "bind static");
|
|
|
+ IF sqlite3_step(stmt) # SQLiteDone THEN HALT(1) END;
|
|
|
+ check(sqlite3_reset(stmt), "reset reuse");
|
|
|
+ check(sqlite3_clear_bindings(stmt), "clear reuse");
|
|
|
+ check(BindTextCopy(stmt, 1, "second"), "rebind text");
|
|
|
+ check(sqlite3_bind_zeroblob(stmt, 2, 16), "bind zeroblob");
|
|
|
+ IF sqlite3_step(stmt) # SQLiteDone THEN HALT(1) END;
|
|
|
+ check(sqlite3_finalize(stmt), "fin reuse");
|
|
|
+ check(sqlite3_prepare_v2(db, "SELECT COUNT(*),"
|
|
|
+ + " SUM(LENGTH(payload)) FROM demo;",
|
|
|
+ -1, stmt, NIL), "prep agg");
|
|
|
+ IF sqlite3_step(stmt) # SQLiteRow THEN HALT(1) END;
|
|
|
+ printf("rows %d blobs %d\n",
|
|
|
+ sqlite3_column_int(stmt, 0), sqlite3_column_int(stmt, 1));
|
|
|
+ IF sqlite3_column_int(stmt, 0) # 3 THEN HALT(1) END;
|
|
|
+ check(sqlite3_finalize(stmt), "fin agg");
|
|
|
+
|
|
|
+ check(ExecSimple(db, "BEGIN;"), "begin");
|
|
|
+ IF sqlite3_get_autocommit(db) # 0 THEN HALT(1) END;
|
|
|
+ check(ExecSimple(db, "INSERT INTO demo(small) VALUES(7);"), "txn ins");
|
|
|
+ check(ExecSimple(db, "COMMIT;"), "commit");
|
|
|
+ IF sqlite3_get_autocommit(db) = 0 THEN HALT(1) END;
|
|
|
+ printf("total changes %d\n", sqlite3_total_changes(db));
|
|
|
+
|
|
|
+ (* 7. close paths *)
|
|
|
+ section(7, "close");
|
|
|
+ check(sqlite3_close_v2(db2), "close_v2");
|
|
|
+ check(sqlite3_close(db), "close");
|
|
|
+ printf("PASS showcase_all\n")
|
|
|
+END showcase_all.
|