Documentation.

This commit is contained in:
Nuno Cruces
2023-02-10 14:14:19 +00:00
parent ccac3032ab
commit eaf7cf57fd
6 changed files with 161 additions and 14 deletions

17
conn.go
View File

@@ -53,13 +53,18 @@ func OpenFlags(filename string, flags OpenFlag) (conn *Conn, err error) {
return c, nil
}
// Close closes a database connection.
// Close closes the database connection.
//
// If the database connection is associated with unfinalized prepared statements,
// open blob handles, and/or unfinished backup objects,
// Close will leave the database connection open and return [BUSY].
//
// https://www.sqlite.org/c3ref/close.html
func (c *Conn) Close() error {
if c == nil {
return nil
}
r, err := c.api.close.Call(c.ctx, uint64(c.handle))
if err != nil {
return err
@@ -68,6 +73,8 @@ func (c *Conn) Close() error {
if err := c.error(r[0]); err != nil {
return err
}
c.handle = 0
return c.mem.mod.Close(c.ctx)
}
@@ -86,17 +93,17 @@ func (c *Conn) Exec(sql string) error {
return c.error(r[0])
}
// Prepare calls [PrepareFlags] with no flags.
// Prepare calls [Conn.PrepareFlags] with no flags.
func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
return c.PrepareFlags(sql, 0)
}
// PrepareFlags compiles the first statement in sql;
// PrepareFlags compiles the first SQL statement in sql;
// tail is left pointing to what remains uncompiled.
// If the input text contains no SQL (if the input is an empty string or a comment),
// both stmt and err will be nil
// both stmt and err will be nil.
//
// https://www.sqlite.org/c3ref/exec.html
// https://www.sqlite.org/c3ref/prepare.html
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
sqlPtr := c.newString(sql)
stmtPtr := c.new(ptrlen)

View File

@@ -121,3 +121,8 @@ func TestConn_free(t *testing.T) {
db.free(ptr)
}
func TestConn_Close(t *testing.T) {
var conn *Conn
conn.Close()
}

2
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/ncruces/go-sqlite3
go 1.19
require (
github.com/ncruces/julianday v0.1.4
github.com/ncruces/julianday v0.1.5
github.com/tetratelabs/wazero v1.0.0-pre.8
golang.org/x/sync v0.1.0
golang.org/x/sys v0.5.0

4
go.sum
View File

@@ -1,5 +1,5 @@
github.com/ncruces/julianday v0.1.4 h1:PFv147rE9ZApvAJW8yBcASI104HyNtBTnNsZpJB4tAI=
github.com/ncruces/julianday v0.1.4/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/ncruces/julianday v0.1.5 h1:hDJ9ejiMp3DHsoZ5KW4c1lwfMjbARS7u/gbYcd0FBZk=
github.com/ncruces/julianday v0.1.5/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/tetratelabs/wazero v1.0.0-pre.8 h1:Ir82PWj79WCppH+9ny73eGY2qv+oCnE3VwMY92cBSyI=
github.com/tetratelabs/wazero v1.0.0-pre.8/go.mod h1:u8wrFmpdrykiFK0DFPiFm5a4+0RzsdmXYVtijBKqUVo=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=

109
stmt.go
View File

@@ -4,13 +4,23 @@ import (
"math"
)
// Stmt is a prepared statement object.
//
// https://www.sqlite.org/c3ref/stmt.html
type Stmt struct {
c *Conn
handle uint32
err error
}
// Close destroys the prepared statement object.
//
// https://www.sqlite.org/c3ref/finalize.html
func (s *Stmt) Close() error {
if s == nil {
return nil
}
r, err := s.c.api.finalize.Call(s.c.ctx, uint64(s.handle))
if err != nil {
return err
@@ -20,14 +30,38 @@ func (s *Stmt) Close() error {
return s.c.error(r[0])
}
// Reset resets the prepared statement object.
//
// https://www.sqlite.org/c3ref/reset.html
func (s *Stmt) Reset() error {
r, err := s.c.api.reset.Call(s.c.ctx, uint64(s.handle))
if err != nil {
return err
}
s.err = nil
return s.c.error(r[0])
}
// ClearBindings resets all bindings on the prepared statement.
//
// https://www.sqlite.org/c3ref/clear_bindings.html
func (s *Stmt) ClearBindings() error {
r, err := s.c.api.clearBindings.Call(s.c.ctx, uint64(s.handle))
if err != nil {
return err
}
return s.c.error(r[0])
}
// Step evaluates the SQL statement.
// If the SQL statement being executed returns any data,
// then true is returned each time a new row of data is ready for processing by the caller.
// The values may be accessed using the Column access functions.
// Step is called again to retrieve the next row of data.
// If an error has occurred, Step returns false;
// call [Stmt.Err] or [Stmt.Reset] to get the error.
//
// https://www.sqlite.org/c3ref/step.html
func (s *Stmt) Step() bool {
r, err := s.c.api.step.Call(s.c.ctx, uint64(s.handle))
if err != nil {
@@ -45,20 +79,28 @@ func (s *Stmt) Step() bool {
return false
}
// Err gets the last error occurred during [Stmt.Step].
// Err returns nil after [Stmt.Reset] is called.
//
// https://www.sqlite.org/c3ref/step.html
func (s *Stmt) Err() error {
return s.err
}
// Exec is a convenience function that repeatedly calls [Stmt.Step] until it returns false,
// then calls [Stmt.Reset] to reset the statement and get any error that occurred.
func (s *Stmt) Exec() error {
for s.Step() {
}
err := s.Err()
if rerr := s.Reset(); err == nil {
err = rerr
}
return err
return s.Reset()
}
// BindBool binds a bool to the prepared statement.
// The leftmost SQL parameter has an index of 1.
// SQLite does not have a separate boolean storage class.
// Instead, boolean values are stored as integers 0 (false) and 1 (true).
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindBool(param int, value bool) error {
if value {
return s.BindInt64(param, 1)
@@ -66,10 +108,18 @@ func (s *Stmt) BindBool(param int, value bool) error {
return s.BindInt64(param, 0)
}
// BindInt binds an int to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindInt(param int, value int) error {
return s.BindInt64(param, int64(value))
}
// BindInt64 binds an int64 to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindInt64(param int, value int64) error {
r, err := s.c.api.bindInteger.Call(s.c.ctx,
uint64(s.handle), uint64(param), uint64(value))
@@ -79,6 +129,10 @@ func (s *Stmt) BindInt64(param int, value int64) error {
return s.c.error(r[0])
}
// BindFloat binds a float64 to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindFloat(param int, value float64) error {
r, err := s.c.api.bindFloat.Call(s.c.ctx,
uint64(s.handle), uint64(param), math.Float64bits(value))
@@ -88,6 +142,10 @@ func (s *Stmt) BindFloat(param int, value float64) error {
return s.c.error(r[0])
}
// BindText binds a string to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindText(param int, value string) error {
ptr := s.c.newString(value)
r, err := s.c.api.bindText.Call(s.c.ctx,
@@ -100,6 +158,11 @@ func (s *Stmt) BindText(param int, value string) error {
return s.c.error(r[0])
}
// BindBlob binds a []byte to the prepared statement.
// The leftmost SQL parameter has an index of 1.
// Binding a nil slice is the same as calling [Stmt.BindNull].
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindBlob(param int, value []byte) error {
ptr := s.c.newBytes(value)
r, err := s.c.api.bindBlob.Call(s.c.ctx,
@@ -112,6 +175,10 @@ func (s *Stmt) BindBlob(param int, value []byte) error {
return s.c.error(r[0])
}
// BindNull binds a NULL to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindNull(param int) error {
r, err := s.c.api.bindNull.Call(s.c.ctx,
uint64(s.handle), uint64(param))
@@ -121,6 +188,10 @@ func (s *Stmt) BindNull(param int) error {
return s.c.error(r[0])
}
// ColumnType returns the initial [Datatype] of the result column.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnType(col int) Datatype {
r, err := s.c.api.columnType.Call(s.c.ctx,
uint64(s.handle), uint64(col))
@@ -130,6 +201,13 @@ func (s *Stmt) ColumnType(col int) Datatype {
return Datatype(r[0])
}
// ColumnBool returns the value of the result column as a bool.
// The leftmost column of the result set has the index 0.
// SQLite does not have a separate boolean storage class.
// Instead, boolean values are retrieved as integers,
// with 0 converted to false and any other value to true.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnBool(col int) bool {
if i := s.ColumnInt64(col); i != 0 {
return true
@@ -137,10 +215,18 @@ func (s *Stmt) ColumnBool(col int) bool {
return false
}
// ColumnInt returns the value of the result column as an int.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnInt(col int) int {
return int(s.ColumnInt64(col))
}
// ColumnInt64 returns the value of the result column as an int64.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnInt64(col int) int64 {
r, err := s.c.api.columnInteger.Call(s.c.ctx,
uint64(s.handle), uint64(col))
@@ -150,6 +236,10 @@ func (s *Stmt) ColumnInt64(col int) int64 {
return int64(r[0])
}
// ColumnFloat returns the value of the result column as a float64.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnFloat(col int) float64 {
r, err := s.c.api.columnFloat.Call(s.c.ctx,
uint64(s.handle), uint64(col))
@@ -159,6 +249,10 @@ func (s *Stmt) ColumnFloat(col int) float64 {
return math.Float64frombits(r[0])
}
// ColumnText returns the value of the result column as a string.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnText(col int) string {
r, err := s.c.api.columnText.Call(s.c.ctx,
uint64(s.handle), uint64(col))
@@ -186,6 +280,11 @@ func (s *Stmt) ColumnText(col int) string {
return string(mem)
}
// ColumnBlob appends to buf and returns
// the value of the result column as a []byte.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnBlob(col int, buf []byte) []byte {
r, err := s.c.api.columnBlob.Call(s.c.ctx,
uint64(s.handle), uint64(col))

View File

@@ -33,6 +33,16 @@ func TestStmt(t *testing.T) {
t.Fatal(err)
}
err = stmt.ClearBindings()
if err != nil {
t.Fatal(err)
}
err = stmt.Exec()
if err != nil {
t.Fatal(err)
}
err = stmt.BindBool(1, true)
if err != nil {
t.Fatal(err)
@@ -118,7 +128,7 @@ func TestStmt(t *testing.T) {
t.Fatal(err)
}
// The table should have: 0, 1, 2, π, NULL, "", "text", `blob`, NULL
// The table should have: 0, NULL, 1, 2, π, NULL, "", "text", `blob`, NULL
stmt, _, err = db.Prepare(`SELECT col FROM test`)
if err != nil {
t.Fatal(err)
@@ -145,6 +155,27 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if got := stmt.ColumnType(0); got != NULL {
t.Errorf("got %v, want NULL", got)
}
if got := stmt.ColumnBool(0); got != false {
t.Errorf("got %v, want false", got)
}
if got := stmt.ColumnInt(0); got != 0 {
t.Errorf("got %v, want zero", got)
}
if got := stmt.ColumnFloat(0); got != 0 {
t.Errorf("got %v, want zero", got)
}
if got := stmt.ColumnText(0); got != "" {
t.Errorf("got %q, want empty", got)
}
if got := stmt.ColumnBlob(0, nil); got != nil {
t.Errorf("got %q, want nil", got)
}
}
if stmt.Step() {
if got := stmt.ColumnType(0); got != INTEGER {
t.Errorf("got %v, want INTEGER", got)
@@ -323,3 +354,8 @@ func TestStmt(t *testing.T) {
t.Fatal(err)
}
}
func TestStmt_Close(t *testing.T) {
var stmt *Stmt
stmt.Close()
}