ソースを参照

bindings: add SQLite prepared statements

Eric Streit 2 日 前
親
コミット
ae7ce54516

+ 18 - 7
bindings/sqlite3/README.md

@@ -9,11 +9,20 @@ Current surface:
 sqlite3_open
 sqlite3_exec
 sqlite3_close
+sqlite3_prepare_v2
+sqlite3_bind_int
+sqlite3_bind_text
+sqlite3_step
+sqlite3_column_int
+sqlite3_column_text
+sqlite3_finalize
 SQLiteOk
+SQLiteRow
+SQLiteDone
 ```
 
-The smoke test opens an in-memory database, creates a table, inserts a
-row, and closes the handle.
+The smoke tests open an in-memory database, execute SQL, bind
+parameters, read a row and close the handle.
 
 ## Build/run
 
@@ -33,9 +42,11 @@ without `libsqlite3-dev`.
 
 ## Notes
 
-- `sqlite3_open` and `sqlite3_exec` exercise the C string ABI.
-- `VAR db : ADDRESS` exercises opaque C handles.
-- The smoke test uses no callback; callback bindings come after the
-  generic SQLite callback ABI is designed.
+- `sqlite3_open`, `sqlite3_exec`, `prepare` and `bind_text` exercise the
+  C string ABI.
+- `VAR db : ADDRESS` / `VAR stmt : ADDRESS` exercise opaque C handles.
+- `sqlite3_column_text` returns `CStr.CString`, converted with
+  `CStr.Copy`.
+- The prepared-statement test is `SQLitePrepared.mod`.
 - The full `m2SQLITE` definition remains the source for later API
-  coverage.
+  coverage; error strings, column types, resets and callbacks are next.

+ 32 - 2
bindings/sqlite3/SQLite.def

@@ -3,15 +3,25 @@ DEFINITION MODULE FOR "C" SQLite;
    and close it.  The full binding stays in m2SQLITE for later porting. *)
 
 FROM SYSTEM IMPORT ADDRESS;
+FROM CStr IMPORT CString;
 
 EXPORT UNQUALIFIED
-  SQLiteOk,
+  SQLiteOk, SQLiteRow, SQLiteDone,
   sqlite3_open,
   sqlite3_exec,
-  sqlite3_close;
+  sqlite3_close,
+  sqlite3_prepare_v2,
+  sqlite3_bind_int,
+  sqlite3_bind_text,
+  sqlite3_step,
+  sqlite3_column_int,
+  sqlite3_column_text,
+  sqlite3_finalize;
 
 CONST
   SQLiteOk = 0;
+  SQLiteRow = 100;
+  SQLiteDone = 101;
 
 PROCEDURE sqlite3_open (filename : ARRAY OF CHAR;
                         VAR db : ADDRESS) : INTEGER;
@@ -20,4 +30,24 @@ PROCEDURE sqlite3_exec (db : ADDRESS;
                         callback, arg, errmsg : ADDRESS) : INTEGER;
 PROCEDURE sqlite3_close (db : ADDRESS) : INTEGER;
 
+PROCEDURE sqlite3_prepare_v2 (db : ADDRESS;
+                              sql : ARRAY OF CHAR;
+                              nBytes : INTEGER;
+                              VAR stmt : ADDRESS;
+                              tail : ADDRESS) : INTEGER;
+PROCEDURE sqlite3_bind_int (stmt : ADDRESS;
+                            idx : INTEGER;
+                            value : INTEGER) : INTEGER;
+PROCEDURE sqlite3_bind_text (stmt : ADDRESS;
+                             idx : INTEGER;
+                             value : ARRAY OF CHAR;
+                             nBytes : INTEGER;
+                             destructor : ADDRESS) : INTEGER;
+PROCEDURE sqlite3_step (stmt : ADDRESS) : INTEGER;
+PROCEDURE sqlite3_column_int (stmt : ADDRESS;
+                              col : INTEGER) : INTEGER;
+PROCEDURE sqlite3_column_text (stmt : ADDRESS;
+                               col : INTEGER) : CString;
+PROCEDURE sqlite3_finalize (stmt : ADDRESS) : INTEGER;
+
 END SQLite.

+ 23 - 15
bindings/sqlite3/run.sh

@@ -1,5 +1,5 @@
 #!/bin/sh
-# Minimal SQLite binding smoke test.
+# Minimal SQLite binding smoke tests.
 set -e
 cd "$(dirname "$0")"
 ROOT="$(cd ../.. && pwd)"
@@ -14,17 +14,25 @@ if [ -z "$SQLITE_LIBS" ]; then
   # versioned shared object directly.
   SQLITE_LIBS="/usr/lib/x86_64-linux-gnu/libsqlite3.so.0"
 fi
-OUT="${TMPDIR:-/tmp}/m2-sqlite-smoke"
-"$M2MAKE" -c m2 \
-  --m2bin "$ROOT/compiler/M2" \
-  --shim "$ROOT/runtime/syslib/shim.c" \
-  --lib "$SQLITE_LIBS" \
-  -I "$ROOT/bindings/sqlite3" \
-  -o "$OUT" \
-  tests/SQLiteSmoke.mod
-set +e
-"$OUT"
-rc=$?
-set -e
-if [ "$rc" = 42 ]; then exit 0; fi
-exit 1
+
+build_and_run() {
+  name="$1"
+  out="${TMPDIR:-/tmp}/m2-sqlite-$name"
+  "$M2MAKE" -c m2 \
+    --m2bin "$ROOT/compiler/M2" \
+    --shim "$ROOT/runtime/syslib/shim.c" \
+    --lib "$SQLITE_LIBS" \
+    -I "$ROOT/bindings/sqlite3" \
+    -I "$ROOT/runtime/syslib" \
+    -o "$out" \
+    "tests/$name.mod"
+  set +e
+  "$out"
+  rc=$?
+  set -e
+  if [ "$rc" = 42 ]; then return 0; fi
+  return 1
+}
+
+build_and_run SQLiteSmoke
+build_and_run SQLitePrepared

+ 72 - 0
bindings/sqlite3/tests/SQLitePrepared.mod

@@ -0,0 +1,72 @@
+MODULE SQLitePrepared;
+(* Prepared statements, bound texts and a result read. *)
+
+FROM SYSTEM IMPORT ADDRESS;
+FROM CStr IMPORT CString, Copy;
+FROM SQLite IMPORT
+  SQLiteOk, SQLiteRow, SQLiteDone,
+  sqlite3_open, sqlite3_close,
+  sqlite3_prepare_v2, sqlite3_bind_int, sqlite3_bind_text,
+  sqlite3_step, sqlite3_column_int, sqlite3_column_text,
+  sqlite3_finalize;
+
+VAR
+  ExitCode : INTEGER;
+  db, stmt : ADDRESS;
+  ctext : CString;
+  buf : ARRAY [0..63] OF CHAR;
+  rc : INTEGER;
+  ok : BOOLEAN;
+
+BEGIN
+  db := NIL; stmt := NIL;
+  ok := FALSE;
+  rc := sqlite3_open(":memory:", db);
+  IF (rc = SQLiteOk) AND (db # NIL) THEN
+    rc := sqlite3_prepare_v2(db,
+      "CREATE TABLE t(x INTEGER, s TEXT);", -1, stmt, NIL);
+    IF (rc = SQLiteOk) AND (stmt # NIL) THEN
+      rc := sqlite3_step(stmt);
+      IF (rc = SQLiteDone) AND (sqlite3_finalize(stmt) = SQLiteOk) THEN
+        stmt := NIL;
+        rc := sqlite3_prepare_v2(db,
+          "INSERT INTO t VALUES (?, ?);", -1, stmt, NIL);
+        IF (rc = SQLiteOk) AND (stmt # NIL) THEN
+          IF (sqlite3_bind_int(stmt, 1, 42) = SQLiteOk)
+             AND (sqlite3_bind_text(stmt, 2, "hello", -1, NIL) = SQLiteOk) THEN
+            rc := sqlite3_step(stmt);
+            IF (rc = SQLiteDone) AND (sqlite3_finalize(stmt) = SQLiteOk) THEN
+              stmt := NIL;
+              rc := sqlite3_prepare_v2(db,
+                "SELECT x, s FROM t;", -1, stmt, NIL);
+              IF (rc = SQLiteOk) AND (stmt # NIL) THEN
+                rc := sqlite3_step(stmt);
+                IF rc = SQLiteRow THEN
+                  ctext := sqlite3_column_text(stmt, 1);
+                  IF (sqlite3_column_int(stmt, 0) = 42)
+                     AND (ctext # NIL) THEN
+                    IF Copy(ctext, buf) = 5 THEN
+                      ok := (buf[0] = 'h') AND (buf[4] = 'o')
+                            AND (buf[5] = CHR(0))
+                    END
+                  END;
+                  IF (rc = SQLiteRow) AND (sqlite3_step(stmt) = SQLiteDone) THEN
+                    ok := ok AND (sqlite3_finalize(stmt) = SQLiteOk);
+                    stmt := NIL
+                  END
+                END
+              END
+            END
+          END
+        END
+      END
+    END
+  END;
+  IF stmt # NIL THEN
+    IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
+  END;
+  IF db # NIL THEN
+    IF sqlite3_close(db) # SQLiteOk THEN ok := FALSE END
+  END;
+  IF ok THEN ExitCode := 42 ELSE ExitCode := 1 END
+END SQLitePrepared.