diff --git a/.gitignore b/.gitignore index 58b5f43..722019c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ tools # Project +demo.db sqlite3/sqlite3* \ No newline at end of file diff --git a/README.md b/README.md index 97799a4..ab486d5 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,13 @@ and WAL databases are not supported. - [ ] snapshots - [ ] session extension - [ ] resumable bulk update - - [ ] SQL functions + - [ ] shared cache mode +- [ ] custom SQL functions - [ ] custom VFSes - [ ] read-only VFS, wrapping an [`io.ReaderAt`](https://pkg.go.dev/io#ReaderAt) - [ ] in-memory VFS, wrapping a [`bytes.Buffer`](https://pkg.go.dev/bytes#Buffer) - - [ ] expose a custom VFS API + - [ ] cloud-based VFS, based on [Cloud Backed SQLite](https://sqlite.org/cloudsqlite/doc/trunk/www/index.wiki) + - [ ] custom VFS API ### Alternatives diff --git a/driver/driver.go b/driver/driver.go index bedef62..cea9e34 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -1,4 +1,27 @@ // Package driver provides a database/sql driver for SQLite. +// +// Importing package driver registers a [database/sql] driver named "sqlite3". +// You may also need to import package embed. +// +// import _ "github.com/ncruces/go-sqlite3/driver" +// import _ "github.com/ncruces/go-sqlite3/embed" +// +// The data source name for "sqlite3" databases can be a filename or a "file:" [URI]. +// +// The [TRANSACTION] mode can be specified using "_txlock": +// +// sql.Open("sqlite3", "file:demo.db?_txlock=immediate") +// +// [PRAGMA] statements can be specified using "_pragma": +// +// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)&_pragma=locking_mode(normal)") +// +// If no PRAGMAs are specifed, a busy timeout of 1 minute +// and normal locking mode are used. +// +// [URI]: https://www.sqlite.org/uri.html +// [PRAGMA]: https://www.sqlite.org/pragma.html +// [TRANSACTION]: https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions package driver import ( diff --git a/driver/driver_test.go b/driver/driver_test.go index 40901cf..4ab9c1d 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -72,7 +72,7 @@ func Test_Open_pragma_invalid(t *testing.T) { t.Errorf("got %d, want sqlite3.ERROR", rc) } if got := err.Error(); got != `sqlite3: invalid _pragma: sqlite3: SQL logic error: near "1000": syntax error` { - t.Error("got message: ", got) + t.Error("got message:", got) } } @@ -124,7 +124,7 @@ func Test_Open_txLock_invalid(t *testing.T) { t.Fatal("want error") } if got := err.Error(); got != `sqlite3: invalid _txlock: xclusive` { - t.Error("got message: ", got) + t.Error("got message:", got) } } @@ -201,7 +201,7 @@ func Test_Prepare(t *testing.T) { t.Errorf("got %d, want sqlite3.ERROR", rc) } if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` { - t.Error("got message: ", got) + t.Error("got message:", got) } _, err = db.Prepare(`SELECT 1; SELECT`) @@ -215,7 +215,7 @@ func Test_Prepare(t *testing.T) { t.Errorf("got %d, want sqlite3.ERROR", rc) } if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` { - t.Error("got message: ", got) + t.Error("got message:", got) } _, err = db.Prepare(`SELECT 1; SELECT 2`) diff --git a/driver_test.go b/driver_test.go index 5e569d2..5c10fc4 100644 --- a/driver_test.go +++ b/driver_test.go @@ -11,11 +11,13 @@ import ( _ "github.com/ncruces/go-sqlite3/embed" ) +const demo = "demo.db" + func ExampleDriverConn() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - db, err := sql.Open("sqlite3", ":memory:") + db, err := sql.Open("sqlite3", demo) if err != nil { log.Fatal(err) } diff --git a/embed/init.go b/embed/init.go index 2e8420e..9ad7c9a 100644 --- a/embed/init.go +++ b/embed/init.go @@ -1,5 +1,10 @@ // Package embed embeds SQLite into your application. // +// Importing package embed initializes the [sqlite3.Binary] variable +// with an appropriate build of SQLite: +// +// import _ "github.com/ncruces/go-sqlite3/embed" +// // You can obtain this build of SQLite from: // https://github.com/ncruces/go-sqlite3/tree/main/embed package embed diff --git a/tests/conn_test.go b/tests/conn_test.go index 8d88d52..df7b0b4 100644 --- a/tests/conn_test.go +++ b/tests/conn_test.go @@ -54,7 +54,7 @@ func TestConn_Close_BUSY(t *testing.T) { t.Error("not temporary", err) } if got := err.Error(); got != `sqlite3: database is locked: unable to close due to unfinalized statements or unfinished backups` { - t.Error("got message: ", got) + t.Error("got message:", got) } } @@ -182,7 +182,7 @@ func TestConn_Prepare_invalid(t *testing.T) { t.Errorf("got %d, want sqlite3.ERROR", rc) } if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` { - t.Error("got message: ", got) + t.Error("got message:", got) } _, _, err = db.Prepare(`SELECT * FRM sqlite_schema`) @@ -196,10 +196,10 @@ func TestConn_Prepare_invalid(t *testing.T) { t.Errorf("got %d, want sqlite3.ERROR", rc) } if got := serr.SQL(); got != `FRM sqlite_schema` { - t.Error("got SQL: ", got) + t.Error("got SQL:", got) } if got := serr.Error(); got != `sqlite3: SQL logic error: near "FRM": syntax error` { - t.Error("got message: ", got) + t.Error("got message:", got) } } diff --git a/time.go b/time.go index 1ac48a2..2fc8c98 100644 --- a/time.go +++ b/time.go @@ -11,6 +11,9 @@ import ( // TimeFormat specifies how to encode/decode time values. // +// See the documentation for the [TimeFormatDefault] constant +// for formats recognized by SQLite. +// // https://www.sqlite.org/lang_datefunc.html type TimeFormat string @@ -59,6 +62,14 @@ const ( // [TimeFormatDefault] and [TimeFormatAuto] encode using [time.RFC3339Nano], // with nanosecond accuracy, and preserving any timezone offset. // +// This is the format used by the database/sql driver: +// [database/sql.Row.Scan] is able to decode as [time.Time] +// values encoded with [time.RFC3339Nano]. +// +// Time values encoded with [time.RFC3339Nano] cannot be sorted as strings +// to produce a time-ordered sequence. +// Use [TimeFormat7TZ] for time-ordered encoding. +// // Formats [TimeFormat1] through [TimeFormat10] // convert time values to UTC before encoding. //