|
@@ -0,0 +1,201 @@
|
|
|
|
|
+MODULE SQLiteCapi3c;
|
|
|
|
|
+(* Broad C-API integration test, adapted from upstream
|
|
|
|
|
+ sqlite-master/test/capi3c.test (public domain).
|
|
|
|
|
+
|
|
|
|
|
+ Exercises open/exec/prepare_v2, bind, step, column metadata,
|
|
|
|
|
+ scalar/BLOB column access, changes/rowid, autocommit, errors and
|
|
|
|
|
+ close-with-outstanding-statement behavior. *)
|
|
|
|
|
+
|
|
|
|
|
+FROM SYSTEM IMPORT ADDRESS;
|
|
|
|
|
+FROM CStr IMPORT CString, Copy;
|
|
|
|
|
+FROM SQLite IMPORT
|
|
|
|
|
+ SQLiteOk, SQLiteRow, SQLiteDone, SQLiteBusy,
|
|
|
|
|
+ SQLiteInteger, SQLiteFloat, SQLiteText, SQLiteBlob, SQLiteNull,
|
|
|
|
|
+ SQLiteOpenReadWrite, SQLiteOpenCreate, SQLiteOpenMemory,
|
|
|
|
|
+ sqlite3_open_v2, sqlite3_close, sqlite3_exec,
|
|
|
|
|
+ sqlite3_prepare_v2, sqlite3_step, sqlite3_finalize,
|
|
|
|
|
+ sqlite3_bind_int, sqlite3_bind_text,
|
|
|
|
|
+ sqlite3_column_count, sqlite3_column_name, sqlite3_column_decltype,
|
|
|
|
|
+ sqlite3_column_type, sqlite3_column_int, sqlite3_column_int64,
|
|
|
|
|
+ sqlite3_column_double, sqlite3_column_text, sqlite3_column_blob,
|
|
|
|
|
+ sqlite3_column_bytes,
|
|
|
|
|
+ sqlite3_changes, sqlite3_last_insert_rowid,
|
|
|
|
|
+ sqlite3_errcode, sqlite3_errmsg, sqlite3_get_autocommit,
|
|
|
|
|
+ sqlite3_free, SQLiteTransient;
|
|
|
|
|
+
|
|
|
|
|
+VAR
|
|
|
|
|
+ ExitCode : INTEGER;
|
|
|
|
|
+ db, stmt : ADDRESS;
|
|
|
|
|
+ rc : INTEGER;
|
|
|
|
|
+ ok : BOOLEAN;
|
|
|
|
|
+
|
|
|
|
|
+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;
|
|
|
|
|
+
|
|
|
|
|
+PROCEDURE TextIs (s : CString; want : ARRAY OF CHAR) : BOOLEAN;
|
|
|
|
|
+VAR
|
|
|
|
|
+ buf : ARRAY [0..127] OF CHAR;
|
|
|
|
|
+BEGIN
|
|
|
|
|
+ IF s = NIL THEN RETURN FALSE END;
|
|
|
|
|
+ IF Copy(s, buf) = 0 THEN RETURN FALSE END;
|
|
|
|
|
+ RETURN SameText(buf, want)
|
|
|
|
|
+END TextIs;
|
|
|
|
|
+
|
|
|
|
|
+PROCEDURE ExecSQL (sql : ARRAY OF CHAR) : BOOLEAN;
|
|
|
|
|
+VAR
|
|
|
|
|
+ errmsg : ADDRESS;
|
|
|
|
|
+ good : BOOLEAN;
|
|
|
|
|
+BEGIN
|
|
|
|
|
+ errmsg := NIL;
|
|
|
|
|
+ good := sqlite3_exec(db, sql, NIL, NIL, errmsg) = SQLiteOk;
|
|
|
|
|
+ IF errmsg # NIL THEN sqlite3_free(errmsg) END;
|
|
|
|
|
+ RETURN good
|
|
|
|
|
+END ExecSQL;
|
|
|
|
|
+
|
|
|
|
|
+PROCEDURE Finish (VAR s : ADDRESS) : BOOLEAN;
|
|
|
|
|
+VAR
|
|
|
|
|
+ good : BOOLEAN;
|
|
|
|
|
+BEGIN
|
|
|
|
|
+ good := TRUE;
|
|
|
|
|
+ IF s # NIL THEN
|
|
|
|
|
+ IF sqlite3_finalize(s) # SQLiteOk THEN good := FALSE END;
|
|
|
|
|
+ s := NIL
|
|
|
|
|
+ END;
|
|
|
|
|
+ RETURN good
|
|
|
|
|
+END Finish;
|
|
|
|
|
+
|
|
|
|
|
+BEGIN
|
|
|
|
|
+ ExitCode := 1;
|
|
|
|
|
+ db := NIL; stmt := NIL; ok := FALSE;
|
|
|
|
|
+
|
|
|
|
|
+ IF (sqlite3_open_v2(":memory:", db,
|
|
|
|
|
+ SQLiteOpenReadWrite + SQLiteOpenCreate + SQLiteOpenMemory,
|
|
|
|
|
+ NIL) = SQLiteOk) AND (db # NIL) THEN
|
|
|
|
|
+ ok := ExecSQL("CREATE TABLE t(a VARINT, b BLOB, c VARCHAR(16));")
|
|
|
|
|
+ AND ExecSQL("INSERT INTO t VALUES(1, X'0102', 3);")
|
|
|
|
|
+ AND ExecSQL("INSERT INTO t VALUES('one', 'two', NULL);")
|
|
|
|
|
+ AND ExecSQL("INSERT INTO t VALUES(1.2, 1.3, 1.4);")
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ (* Prepared SELECT and column metadata. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ stmt := NIL;
|
|
|
|
|
+ rc := sqlite3_prepare_v2(db, "SELECT * FROM t;", -1, stmt, NIL);
|
|
|
|
|
+ ok := (rc = SQLiteOk) AND (stmt # NIL)
|
|
|
|
|
+ AND (sqlite3_column_count(stmt) = 3)
|
|
|
|
|
+ AND TextIs(sqlite3_column_name(stmt, 0), "a")
|
|
|
|
|
+ AND TextIs(sqlite3_column_name(stmt, 1), "b")
|
|
|
|
|
+ AND TextIs(sqlite3_column_name(stmt, 2), "c")
|
|
|
|
|
+ AND TextIs(sqlite3_column_decltype(stmt, 0), "VARINT")
|
|
|
|
|
+ AND TextIs(sqlite3_column_decltype(stmt, 1), "BLOB")
|
|
|
|
|
+ AND TextIs(sqlite3_column_decltype(stmt, 2), "VARCHAR(16)")
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ (* Integer + BLOB + integer row. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := sqlite3_step(stmt) = SQLiteRow;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := (sqlite3_column_type(stmt, 0) = SQLiteInteger)
|
|
|
|
|
+ AND (sqlite3_column_int(stmt, 0) = 1)
|
|
|
|
|
+ AND (sqlite3_column_int64(stmt, 0) = 1)
|
|
|
|
|
+ AND (sqlite3_column_double(stmt, 0) = 1.0)
|
|
|
|
|
+ AND (sqlite3_column_type(stmt, 1) = SQLiteBlob)
|
|
|
|
|
+ AND (sqlite3_column_blob(stmt, 1) # NIL)
|
|
|
|
|
+ AND (sqlite3_column_bytes(stmt, 1) = 2)
|
|
|
|
|
+ AND (sqlite3_column_type(stmt, 2) = SQLiteText)
|
|
|
|
|
+ AND (sqlite3_column_int(stmt, 2) = 3)
|
|
|
|
|
+ AND TextIs(sqlite3_column_text(stmt, 2), "3")
|
|
|
|
|
+ END
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ (* Text + text + NULL row. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := sqlite3_step(stmt) = SQLiteRow;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := (sqlite3_column_type(stmt, 0) = SQLiteText)
|
|
|
|
|
+ AND TextIs(sqlite3_column_text(stmt, 0), "one")
|
|
|
|
|
+ AND (sqlite3_column_type(stmt, 1) = SQLiteText)
|
|
|
|
|
+ AND (sqlite3_column_bytes(stmt, 1) = 3)
|
|
|
|
|
+ AND TextIs(sqlite3_column_text(stmt, 1), "two")
|
|
|
|
|
+ AND (sqlite3_column_type(stmt, 2) = SQLiteNull)
|
|
|
|
|
+ AND (sqlite3_column_int(stmt, 2) = 0)
|
|
|
|
|
+ END
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ (* Real + real + real row. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := sqlite3_step(stmt) = SQLiteRow;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := (sqlite3_column_type(stmt, 0) = SQLiteFloat)
|
|
|
|
|
+ AND (sqlite3_column_double(stmt, 0) = 1.2)
|
|
|
|
|
+ AND (sqlite3_column_type(stmt, 1) = SQLiteFloat)
|
|
|
|
|
+ AND (sqlite3_column_double(stmt, 1) = 1.3)
|
|
|
|
|
+ AND (sqlite3_column_type(stmt, 2) = SQLiteText)
|
|
|
|
|
+ AND (sqlite3_column_double(stmt, 2) = 1.4)
|
|
|
|
|
+ AND TextIs(sqlite3_column_text(stmt, 2), "1.4")
|
|
|
|
|
+ END
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ IF ok THEN ok := sqlite3_step(stmt) = SQLiteDone END;
|
|
|
|
|
+ IF NOT Finish(stmt) THEN ok := FALSE END;
|
|
|
|
|
+
|
|
|
|
|
+ (* Bound insert and change counters. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ stmt := NIL;
|
|
|
|
|
+ rc := sqlite3_prepare_v2(db,
|
|
|
|
|
+ "INSERT INTO t(a,c) VALUES(?,?);", -1, stmt, NIL);
|
|
|
|
|
+ ok := (rc = SQLiteOk) AND (stmt # NIL)
|
|
|
|
|
+ AND (sqlite3_bind_int(stmt, 1, 7) = SQLiteOk)
|
|
|
|
|
+ AND (sqlite3_bind_text(stmt, 2, "seven", -1,
|
|
|
|
|
+ SQLiteTransient()) = SQLiteOk);
|
|
|
|
|
+ IF ok THEN ok := sqlite3_step(stmt) = SQLiteDone END;
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := (sqlite3_changes(db) = 1)
|
|
|
|
|
+ AND (sqlite3_last_insert_rowid(db) = 4)
|
|
|
|
|
+ END;
|
|
|
|
|
+ IF NOT Finish(stmt) THEN ok := FALSE END
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ (* Autocommit state. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ ok := (sqlite3_get_autocommit(db) = 1)
|
|
|
|
|
+ AND ExecSQL("BEGIN;")
|
|
|
|
|
+ AND (sqlite3_get_autocommit(db) = 0)
|
|
|
|
|
+ AND ExecSQL("COMMIT;")
|
|
|
|
|
+ AND (sqlite3_get_autocommit(db) = 1)
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ (* A failed prepare leaves an error and a message. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ stmt := NIL;
|
|
|
|
|
+ rc := sqlite3_prepare_v2(db, "SELECT namex FROM sqlite_master;",
|
|
|
|
|
+ -1, stmt, NIL);
|
|
|
|
|
+ ok := (rc # SQLiteOk) AND (stmt = NIL)
|
|
|
|
|
+ AND (sqlite3_errcode(db) # SQLiteOk)
|
|
|
|
|
+ AND (sqlite3_errmsg(db) # NIL);
|
|
|
|
|
+ IF NOT Finish(stmt) THEN ok := FALSE END
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ (* close() must fail while a statement is outstanding. *)
|
|
|
|
|
+ IF ok THEN
|
|
|
|
|
+ stmt := NIL;
|
|
|
|
|
+ rc := sqlite3_prepare_v2(db, "SELECT 1;", -1, stmt, NIL);
|
|
|
|
|
+ ok := (rc = SQLiteOk) AND (stmt # NIL)
|
|
|
|
|
+ AND (sqlite3_close(db) = SQLiteBusy);
|
|
|
|
|
+ IF NOT Finish(stmt) THEN ok := FALSE END
|
|
|
|
|
+ END;
|
|
|
|
|
+ IF db # NIL THEN
|
|
|
|
|
+ IF sqlite3_close(db) # SQLiteOk THEN ok := FALSE END;
|
|
|
|
|
+ db := NIL
|
|
|
|
|
+ END;
|
|
|
|
|
+
|
|
|
|
|
+ IF ok THEN ExitCode := 42 END
|
|
|
|
|
+END SQLiteCapi3c.
|