aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sqlite3.go7
-rw-r--r--sqlite3_go113_test.go41
2 files changed, 48 insertions, 0 deletions
diff --git a/sqlite3.go b/sqlite3.go
index d1ff406..552a2ab 100644
--- a/sqlite3.go
+++ b/sqlite3.go
@@ -2007,6 +2007,13 @@ func (s *SQLiteStmt) execSync(args []namedValue) (driver.Result, error) {
return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil
}
+// Readonly reports if this statement is considered readonly by SQLite.
+//
+// See: https://sqlite.org/c3ref/stmt_readonly.html
+func (s *SQLiteStmt) Readonly() bool {
+ return C.sqlite3_stmt_readonly(s.s) == 1
+}
+
// Close the rows.
func (rc *SQLiteRows) Close() error {
rc.s.mu.Lock()
diff --git a/sqlite3_go113_test.go b/sqlite3_go113_test.go
index 6f74e6b..a010cb7 100644
--- a/sqlite3_go113_test.go
+++ b/sqlite3_go113_test.go
@@ -11,6 +11,7 @@ import (
"context"
"database/sql"
"database/sql/driver"
+ "errors"
"os"
"testing"
)
@@ -76,3 +77,43 @@ func TestBeginTxCancel(t *testing.T) {
}()
}
}
+
+func TestStmtReadonly(t *testing.T) {
+ db, err := sql.Open("sqlite3", ":memory:")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("CREATE TABLE t (count INT)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ isRO := func(query string) bool {
+ c, err := db.Conn(context.Background())
+ if err != nil {
+ return false
+ }
+
+ var ro bool
+ c.Raw(func(dc interface{}) error {
+ stmt, err := dc.(*SQLiteConn).Prepare(query)
+ if err != nil {
+ return err
+ }
+ if stmt == nil {
+ return errors.New("stmt is nil")
+ }
+ ro = stmt.(*SQLiteStmt).Readonly()
+ return nil
+ })
+ return ro // On errors ro will remain false.
+ }
+
+ if !isRO(`select * from t`) {
+ t.Error("select not seen as read-only")
+ }
+ if isRO(`insert into t values (1), (2)`) {
+ t.Error("insert seen as read-only")
+ }
+}