| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- MODULE SQLiteHooks;
- (* Connection hooks:
- busy handler/timeout, progress, trace_v2, update/commit/rollback,
- authorizer and sqlite3_db_handle. *)
- FROM SYSTEM IMPORT ADDRESS, ADR;
- FROM CStr IMPORT CString, Copy;
- FROM SQLite IMPORT
- SQLiteOk, SQLiteRow, SQLiteDone, SQLiteAuth,
- SQLiteOpenReadWrite, SQLiteOpenCreate, SQLiteOpenMemory,
- SQLiteOpInsert, SQLiteOpUpdate, SQLiteOpDelete,
- SQLiteAuthOk, SQLiteAuthDeny, SQLiteAuthDelete,
- SQLiteTraceStmt,
- sqlite3_open_v2, sqlite3_exec, sqlite3_close_v2,
- sqlite3_prepare_v2, sqlite3_step, sqlite3_column_int,
- sqlite3_finalize, sqlite3_free,
- sqlite3_busy_timeout, sqlite3_busy_handler, sqlite3_interrupt,
- sqlite3_progress_handler, sqlite3_trace_v2,
- sqlite3_update_hook, sqlite3_commit_hook, sqlite3_rollback_hook,
- sqlite3_set_authorizer, sqlite3_db_handle;
- VAR
- ExitCode : INTEGER;
- db, stmt, prev : ADDRESS;
- rc : INTEGER;
- ok : BOOLEAN;
- progressCalls, traceCalls, updateCalls : INTEGER;
- commitCalls, rollbackCalls, authCalls : INTEGER;
- lastOp : INTEGER;
- lastRowid : LONGINT;
- hookBad : BOOLEAN;
- tableBuf : ARRAY [0..31] OF CHAR;
- 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 QueryInt (sql : ARRAY OF CHAR; want : INTEGER) : BOOLEAN;
- VAR
- s : ADDRESS;
- code, got : INTEGER;
- good : BOOLEAN;
- BEGIN
- s := NIL; got := -777; good := FALSE;
- code := sqlite3_prepare_v2(db, sql, -1, s, NIL);
- IF (code = SQLiteOk) AND (s # NIL) THEN
- IF sqlite3_step(s) = SQLiteRow THEN
- got := sqlite3_column_int(s, 0);
- IF sqlite3_step(s) = SQLiteDone THEN good := TRUE END
- END;
- IF sqlite3_finalize(s) # SQLiteOk THEN good := FALSE END
- END;
- RETURN good AND (got = want)
- END QueryInt;
- PROCEDURE BusyFn (arg : ADDRESS; count : INTEGER) : INTEGER;
- BEGIN
- RETURN 0
- END BusyFn;
- PROCEDURE ProgressFn (arg : ADDRESS) : INTEGER;
- BEGIN
- progressCalls := progressCalls + 1;
- RETURN 0
- END ProgressFn;
- PROCEDURE TraceFn (mask : CARDINAL; ctx, sql, expanded : ADDRESS) : INTEGER;
- BEGIN
- traceCalls := traceCalls + 1;
- RETURN 0
- END TraceFn;
- PROCEDURE UpdateFn (arg : ADDRESS; op : INTEGER;
- dbName, tableName : ADDRESS; rowid : LONGINT);
- VAR
- s : CString;
- BEGIN
- updateCalls := updateCalls + 1;
- lastOp := op;
- lastRowid := rowid;
- s := tableName;
- IF (s = NIL) OR (Copy(s, tableBuf) = 0) OR (tableBuf[0] # 'h') THEN
- hookBad := TRUE
- END
- END UpdateFn;
- PROCEDURE CommitFn (arg : ADDRESS) : INTEGER;
- BEGIN
- commitCalls := commitCalls + 1;
- RETURN 0
- END CommitFn;
- PROCEDURE RollbackFn (arg : ADDRESS);
- BEGIN
- rollbackCalls := rollbackCalls + 1
- END RollbackFn;
- PROCEDURE AuthFn (arg : ADDRESS; action : INTEGER;
- dbName, tableName, colName, triggerName : ADDRESS) : INTEGER;
- BEGIN
- authCalls := authCalls + 1;
- IF action = SQLiteAuthDelete THEN
- RETURN SQLiteAuthDeny
- ELSE
- RETURN SQLiteAuthOk
- END
- END AuthFn;
- BEGIN
- ExitCode := 1;
- db := NIL; stmt := NIL; prev := NIL;
- progressCalls := 0; traceCalls := 0; updateCalls := 0;
- commitCalls := 0; rollbackCalls := 0; authCalls := 0;
- lastOp := 0; lastRowid := 0; hookBad := FALSE;
- ok := FALSE;
- IF (sqlite3_open_v2(":memory:", db,
- SQLiteOpenReadWrite + SQLiteOpenCreate + SQLiteOpenMemory,
- NIL) = SQLiteOk) AND (db # NIL) THEN
- IF ExecSQL("CREATE TABLE h(id INTEGER PRIMARY KEY, v TEXT);") THEN
- ok := TRUE
- END;
- IF ok THEN
- ok := (sqlite3_busy_timeout(db, 100) = SQLiteOk)
- AND (sqlite3_busy_handler(db, ADR(BusyFn), NIL) = SQLiteOk)
- AND (sqlite3_busy_handler(db, NIL, NIL) = SQLiteOk)
- END;
- (* sqlite3_db_handle returns the owning connection. *)
- IF ok THEN
- stmt := NIL;
- rc := sqlite3_prepare_v2(db, "SELECT 1;", -1, stmt, NIL);
- ok := (rc = SQLiteOk) AND (stmt # NIL)
- AND (sqlite3_db_handle(stmt) = db);
- IF stmt # NIL THEN
- IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END;
- stmt := NIL
- END
- END;
- (* Progress handler runs during a recursive query. *)
- IF ok THEN
- progressCalls := 0;
- sqlite3_progress_handler(db, 1, ADR(ProgressFn), NIL);
- ok := QueryInt("SELECT 1;", 1) AND (progressCalls > 0);
- sqlite3_progress_handler(db, 0, NIL, NIL)
- END;
- (* Trace callback sees prepared statements. *)
- IF ok THEN
- traceCalls := 0;
- ok := sqlite3_trace_v2(db, SQLiteTraceStmt,
- ADR(TraceFn), NIL) = SQLiteOk
- END;
- IF ok THEN ok := QueryInt("SELECT 1;", 1) AND (traceCalls > 0) END;
- IF ok THEN
- ok := sqlite3_trace_v2(db, 0, NIL, NIL) = SQLiteOk
- END;
- (* Update hook fires for INSERT, UPDATE and DELETE. *)
- IF ok THEN
- updateCalls := 0; hookBad := FALSE;
- prev := sqlite3_update_hook(db, ADR(UpdateFn), NIL)
- END;
- IF ok THEN
- ok := ExecSQL("INSERT INTO h(v) VALUES ('a');")
- AND (updateCalls = 1) AND (lastOp = SQLiteOpInsert)
- END;
- IF ok THEN
- ok := ExecSQL("UPDATE h SET v = 'b' WHERE id = 1;")
- AND (updateCalls = 2) AND (lastOp = SQLiteOpUpdate)
- END;
- IF ok THEN
- ok := ExecSQL("DELETE FROM h WHERE id = 1;")
- AND (updateCalls = 3) AND (lastOp = SQLiteOpDelete)
- AND (NOT hookBad)
- END;
- IF ok THEN prev := sqlite3_update_hook(db, NIL, NIL) END;
- (* Commit and rollback hooks. *)
- IF ok THEN
- commitCalls := 0; rollbackCalls := 0;
- prev := sqlite3_commit_hook(db, ADR(CommitFn), NIL);
- sqlite3_rollback_hook(db, ADR(RollbackFn), NIL)
- END;
- IF ok THEN
- ok := ExecSQL("BEGIN; INSERT INTO h(v) VALUES ('c'); COMMIT;")
- AND (commitCalls = 1)
- END;
- IF ok THEN
- ok := ExecSQL("BEGIN; INSERT INTO h(v) VALUES ('d'); ROLLBACK;")
- AND (rollbackCalls = 1)
- END;
- IF ok THEN
- prev := sqlite3_commit_hook(db, NIL, NIL);
- sqlite3_rollback_hook(db, NIL, NIL)
- END;
- (* Authorizer: allow SELECT, deny DELETE. *)
- IF ok THEN
- authCalls := 0;
- ok := sqlite3_set_authorizer(db, ADR(AuthFn), NIL) = SQLiteOk
- END;
- IF ok THEN
- stmt := NIL;
- rc := sqlite3_prepare_v2(db, "DELETE FROM h;", -1, stmt, NIL);
- ok := (rc = SQLiteAuth) AND (stmt = NIL) AND (authCalls > 0);
- IF stmt # NIL THEN
- IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END;
- stmt := NIL
- END
- END;
- IF ok THEN ok := QueryInt("SELECT count(*) FROM h;", 1) END;
- IF ok THEN
- ok := sqlite3_set_authorizer(db, NIL, NIL) = SQLiteOk
- END;
- IF ok THEN ok := ExecSQL("DELETE FROM h;") END;
- IF ok THEN sqlite3_interrupt(db) END;
- IF stmt # NIL THEN
- IF sqlite3_finalize(stmt) # SQLiteOk THEN ok := FALSE END;
- stmt := 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 SQLiteHooks.
|