Explorar el Código

bindings: add SQLite showcase

Eric Streit hace 2 días
padre
commit
f40a0262d4

+ 12 - 1
bindings/sqlite3/README.md

@@ -22,7 +22,14 @@ SQLiteDone
 ```
 
 The smoke tests open an in-memory database, execute SQL, bind
-parameters, read a row and close the handle.
+parameters, read a row and close the handle.  The showcase
+(`showcase/SQLiteShowcase.mod`) exercises the complete current surface
+and prints:
+
+```text
+1: Ada
+2: Grace
+```
 
 ## Build/run
 
@@ -47,6 +54,10 @@ without `libsqlite3-dev`.
 - `VAR db : ADDRESS` / `VAR stmt : ADDRESS` exercise opaque C handles.
 - `sqlite3_column_text` returns `CStr.CString`, converted with
   `CStr.Copy`.
+- `SQLiteTransient()` supplies the SQLITE_TRANSIENT sentinel so SQLite
+  copies bound text; passing `NIL` means SQLITE_STATIC and requires the
+  caller's buffer to remain alive.
 - The prepared-statement test is `SQLitePrepared.mod`.
+- The full showcase is `showcase/SQLiteShowcase.mod`.
 - The full `m2SQLITE` definition remains the source for later API
   coverage; error strings, column types, resets and callbacks are next.

+ 6 - 1
bindings/sqlite3/SQLite.def

@@ -16,7 +16,8 @@ EXPORT UNQUALIFIED
   sqlite3_step,
   sqlite3_column_int,
   sqlite3_column_text,
-  sqlite3_finalize;
+  sqlite3_finalize,
+  SQLiteTransient;
 
 CONST
   SQLiteOk = 0;
@@ -50,4 +51,8 @@ PROCEDURE sqlite3_column_text (stmt : ADDRESS;
                                col : INTEGER) : CString;
 PROCEDURE sqlite3_finalize (stmt : ADDRESS) : INTEGER;
 
+PROCEDURE SQLiteTransient () : ADDRESS;
+  EXTERNAL "m2_sqlite_transient";
+(* The SQLITE_TRANSIENT destructor sentinel: SQLite copies bound text. *)
+
 END SQLite.

+ 4 - 1
bindings/sqlite3/run.sh

@@ -17,6 +17,7 @@ fi
 
 build_and_run() {
   name="$1"
+  src="${2:-tests/$name.mod}"
   out="${TMPDIR:-/tmp}/m2-sqlite-$name"
   "$M2MAKE" -c m2 \
     --m2bin "$ROOT/compiler/M2" \
@@ -24,8 +25,9 @@ build_and_run() {
     --lib "$SQLITE_LIBS" \
     -I "$ROOT/bindings/sqlite3" \
     -I "$ROOT/runtime/syslib" \
+    -I "$ROOT/lib/common" \
     -o "$out" \
-    "tests/$name.mod"
+    "$src"
   set +e
   "$out"
   rc=$?
@@ -36,3 +38,4 @@ build_and_run() {
 
 build_and_run SQLiteSmoke
 build_and_run SQLitePrepared
+build_and_run SQLiteShowcase showcase/SQLiteShowcase.mod

+ 108 - 0
bindings/sqlite3/showcase/SQLiteShowcase.mod

@@ -0,0 +1,108 @@
+MODULE SQLiteShowcase;
+(* Showcase of the current SQLite binding surface:
+   open/exec/close, prepare, bind int/text, step, column int/text,
+   finalize.  Prints the two stored rows. *)
+
+FROM SYSTEM IMPORT ADDRESS;
+FROM CStr IMPORT CString, Copy;
+FROM SysIO IMPORT Write, WriteLn, WriteInt, WriteChar;
+FROM SQLite IMPORT
+  SQLiteOk, SQLiteRow, SQLiteDone,
+  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, SQLiteTransient;
+
+VAR
+  ExitCode : INTEGER;
+  db : ADDRESS;
+
+PROCEDURE ExecSQL (sql : ARRAY OF CHAR) : BOOLEAN;
+BEGIN
+  RETURN sqlite3_exec(db, sql, NIL, NIL, NIL) = SQLiteOk
+END ExecSQL;
+
+PROCEDURE InsertPerson (id : INTEGER; name : ARRAY OF CHAR) : BOOLEAN;
+VAR
+  stmt : ADDRESS;
+  rc : INTEGER;
+  ok : BOOLEAN;
+BEGIN
+  stmt := NIL;
+  rc := sqlite3_prepare_v2(db, "INSERT INTO people VALUES (?, ?);",
+                           -1, stmt, NIL);
+  ok := (rc = SQLiteOk) AND (stmt # NIL);
+  IF ok THEN
+    ok := (sqlite3_bind_int(stmt, 1, id) = SQLiteOk)
+          AND (sqlite3_bind_text(stmt, 2, name, -1,
+                                 SQLiteTransient()) = SQLiteOk)
+  END;
+  IF ok THEN
+    ok := sqlite3_step(stmt) = SQLiteDone
+  END;
+  IF stmt # NIL THEN
+    IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
+  END;
+  RETURN ok
+END InsertPerson;
+
+PROCEDURE ShowPeople () : BOOLEAN;
+VAR
+  stmt : ADDRESS;
+  rc : INTEGER;
+  id : INTEGER;
+  name : ARRAY [0..63] OF CHAR;
+  ctext : CString;
+  n : CARDINAL;
+  ok : BOOLEAN;
+BEGIN
+  stmt := NIL;
+  rc := sqlite3_prepare_v2(db, "SELECT id, name FROM people ORDER BY id;",
+                           -1, stmt, NIL);
+  ok := (rc = SQLiteOk) AND (stmt # NIL);
+  IF ok THEN
+    LOOP
+      rc := sqlite3_step(stmt);
+      IF rc = SQLiteRow THEN
+        id := sqlite3_column_int(stmt, 0);
+        ctext := sqlite3_column_text(stmt, 1);
+        IF ctext = NIL THEN
+          ok := FALSE; EXIT
+        END;
+        n := Copy(ctext, name);
+        WriteInt(id);
+        WriteChar(':');
+        WriteChar(' ');
+        Write(name);
+        WriteLn
+      ELSIF rc = SQLiteDone THEN
+        EXIT
+      ELSE
+        ok := FALSE; EXIT
+      END
+    END
+  END;
+  IF stmt # NIL THEN
+    IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END
+  END;
+  RETURN ok
+END ShowPeople;
+
+BEGIN
+  db := NIL;
+  IF sqlite3_open(":memory:", db) = SQLiteOk THEN
+    IF ExecSQL("CREATE TABLE people(id INTEGER, name TEXT);")
+       AND InsertPerson(1, "Ada")
+       AND InsertPerson(2, "Grace")
+       AND ShowPeople() THEN
+      ExitCode := 42
+    ELSE
+      ExitCode := 1
+    END
+  ELSE
+    ExitCode := 1
+  END;
+  IF db # NIL THEN
+    IF sqlite3_close(db) # SQLiteOk THEN ExitCode := 1 END
+  END
+END SQLiteShowcase.

+ 3 - 2
bindings/sqlite3/tests/SQLitePrepared.mod

@@ -8,7 +8,7 @@ FROM SQLite IMPORT
   sqlite3_open, sqlite3_close,
   sqlite3_prepare_v2, sqlite3_bind_int, sqlite3_bind_text,
   sqlite3_step, sqlite3_column_int, sqlite3_column_text,
-  sqlite3_finalize;
+  sqlite3_finalize, SQLiteTransient;
 
 VAR
   ExitCode : INTEGER;
@@ -33,7 +33,8 @@ BEGIN
           "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
+             AND (sqlite3_bind_text(stmt, 2, "hello", -1,
+                                    SQLiteTransient()) = SQLiteOk) THEN
             rc := sqlite3_step(stmt);
             IF (rc = SQLiteDone) AND (sqlite3_finalize(stmt) = SQLiteOk) THEN
               stmt := NIL;

+ 6 - 0
runtime/syslib/shim.c

@@ -441,6 +441,12 @@ _Bool m2_cbool_not(_Bool b)
     return !b;
 }
 
+/* SQLITE_TRANSIENT, without requiring sqlite3.h in the binding. */
+void *m2_sqlite_transient(void)
+{
+    return (void *)-1;
+}
+
 /* C callback test: call a V3 top-level procedure with C arguments. */
 int m2_c_call_add(void *fn, int a, int b)
 {