package sqlite
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"io"
"math"
"reflect"
"strconv"
"strings"
"time"
"unsafe"
"modernc.org/libc"
"modernc.org/libc/sys/types"
sqlite3 "modernc.org/sqlite/lib"
)
var (
_ driver .Conn = (*conn )(nil )
_ driver .Driver = (*Driver )(nil )
_ driver .Execer = (*conn )(nil )
_ driver .Queryer = (*conn )(nil )
_ driver .Result = (*result )(nil )
_ driver .Rows = (*rows )(nil )
_ driver .RowsColumnTypeDatabaseTypeName = (*rows )(nil )
_ driver .RowsColumnTypeLength = (*rows )(nil )
_ driver .RowsColumnTypeNullable = (*rows )(nil )
_ driver .RowsColumnTypePrecisionScale = (*rows )(nil )
_ driver .RowsColumnTypeScanType = (*rows )(nil )
_ driver .Stmt = (*stmt )(nil )
_ driver .Tx = (*tx )(nil )
_ error = (*Error )(nil )
)
const (
driverName = "sqlite"
ptrSize = unsafe .Sizeof (uintptr (0 ))
sqliteLockedSharedcache = sqlite3 .SQLITE_LOCKED | (1 << 8 )
)
type Error struct {
msg string
code int
}
func (e *Error ) Error () string { return e .msg }
func (e *Error ) Code () int { return e .code }
var (
ErrorCodeString = map [int ]string {
sqlite3 .SQLITE_ABORT : "Callback routine requested an abort (SQLITE_ABORT)" ,
sqlite3 .SQLITE_AUTH : "Authorization denied (SQLITE_AUTH)" ,
sqlite3 .SQLITE_BUSY : "The database file is locked (SQLITE_BUSY)" ,
sqlite3 .SQLITE_CANTOPEN : "Unable to open the database file (SQLITE_CANTOPEN)" ,
sqlite3 .SQLITE_CONSTRAINT : "Abort due to constraint violation (SQLITE_CONSTRAINT)" ,
sqlite3 .SQLITE_CORRUPT : "The database disk image is malformed (SQLITE_CORRUPT)" ,
sqlite3 .SQLITE_DONE : "sqlite3_step() has finished executing (SQLITE_DONE)" ,
sqlite3 .SQLITE_EMPTY : "Internal use only (SQLITE_EMPTY)" ,
sqlite3 .SQLITE_ERROR : "Generic error (SQLITE_ERROR)" ,
sqlite3 .SQLITE_FORMAT : "Not used (SQLITE_FORMAT)" ,
sqlite3 .SQLITE_FULL : "Insertion failed because database is full (SQLITE_FULL)" ,
sqlite3 .SQLITE_INTERNAL : "Internal logic error in SQLite (SQLITE_INTERNAL)" ,
sqlite3 .SQLITE_INTERRUPT : "Operation terminated by sqlite3_interrupt()(SQLITE_INTERRUPT)" ,
sqlite3 .SQLITE_IOERR | (1 << 8 ): "(SQLITE_IOERR_READ)" ,
sqlite3 .SQLITE_IOERR | (10 << 8 ): "(SQLITE_IOERR_DELETE)" ,
sqlite3 .SQLITE_IOERR | (11 << 8 ): "(SQLITE_IOERR_BLOCKED)" ,
sqlite3 .SQLITE_IOERR | (12 << 8 ): "(SQLITE_IOERR_NOMEM)" ,
sqlite3 .SQLITE_IOERR | (13 << 8 ): "(SQLITE_IOERR_ACCESS)" ,
sqlite3 .SQLITE_IOERR | (14 << 8 ): "(SQLITE_IOERR_CHECKRESERVEDLOCK)" ,
sqlite3 .SQLITE_IOERR | (15 << 8 ): "(SQLITE_IOERR_LOCK)" ,
sqlite3 .SQLITE_IOERR | (16 << 8 ): "(SQLITE_IOERR_CLOSE)" ,
sqlite3 .SQLITE_IOERR | (17 << 8 ): "(SQLITE_IOERR_DIR_CLOSE)" ,
sqlite3 .SQLITE_IOERR | (2 << 8 ): "(SQLITE_IOERR_SHORT_READ)" ,
sqlite3 .SQLITE_IOERR | (3 << 8 ): "(SQLITE_IOERR_WRITE)" ,
sqlite3 .SQLITE_IOERR | (4 << 8 ): "(SQLITE_IOERR_FSYNC)" ,
sqlite3 .SQLITE_IOERR | (5 << 8 ): "(SQLITE_IOERR_DIR_FSYNC)" ,
sqlite3 .SQLITE_IOERR | (6 << 8 ): "(SQLITE_IOERR_TRUNCATE)" ,
sqlite3 .SQLITE_IOERR | (7 << 8 ): "(SQLITE_IOERR_FSTAT)" ,
sqlite3 .SQLITE_IOERR | (8 << 8 ): "(SQLITE_IOERR_UNLOCK)" ,
sqlite3 .SQLITE_IOERR | (9 << 8 ): "(SQLITE_IOERR_RDLOCK)" ,
sqlite3 .SQLITE_IOERR : "Some kind of disk I/O error occurred (SQLITE_IOERR)" ,
sqlite3 .SQLITE_LOCKED | (1 << 8 ): "(SQLITE_LOCKED_SHAREDCACHE)" ,
sqlite3 .SQLITE_LOCKED : "A table in the database is locked (SQLITE_LOCKED)" ,
sqlite3 .SQLITE_MISMATCH : "Data type mismatch (SQLITE_MISMATCH)" ,
sqlite3 .SQLITE_MISUSE : "Library used incorrectly (SQLITE_MISUSE)" ,
sqlite3 .SQLITE_NOLFS : "Uses OS features not supported on host (SQLITE_NOLFS)" ,
sqlite3 .SQLITE_NOMEM : "A malloc() failed (SQLITE_NOMEM)" ,
sqlite3 .SQLITE_NOTADB : "File opened that is not a database file (SQLITE_NOTADB)" ,
sqlite3 .SQLITE_NOTFOUND : "Unknown opcode in sqlite3_file_control() (SQLITE_NOTFOUND)" ,
sqlite3 .SQLITE_NOTICE : "Notifications from sqlite3_log() (SQLITE_NOTICE)" ,
sqlite3 .SQLITE_PERM : "Access permission denied (SQLITE_PERM)" ,
sqlite3 .SQLITE_PROTOCOL : "Database lock protocol error (SQLITE_PROTOCOL)" ,
sqlite3 .SQLITE_RANGE : "2nd parameter to sqlite3_bind out of range (SQLITE_RANGE)" ,
sqlite3 .SQLITE_READONLY : "Attempt to write a readonly database (SQLITE_READONLY)" ,
sqlite3 .SQLITE_ROW : "sqlite3_step() has another row ready (SQLITE_ROW)" ,
sqlite3 .SQLITE_SCHEMA : "The database schema changed (SQLITE_SCHEMA)" ,
sqlite3 .SQLITE_TOOBIG : "String or BLOB exceeds size limit (SQLITE_TOOBIG)" ,
sqlite3 .SQLITE_WARNING : "Warnings from sqlite3_log() (SQLITE_WARNING)" ,
}
)
func init () {
sql .Register (driverName , newDriver ())
}
type result struct {
lastInsertID int64
rowsAffected int
}
func newResult (c *conn ) (_ *result , err error ) {
r := &result {}
if r .rowsAffected , err = c .changes (); err != nil {
return nil , err
}
if r .lastInsertID , err = c .lastInsertRowID (); err != nil {
return nil , err
}
return r , nil
}
func (r *result ) LastInsertId () (int64 , error ) {
if r == nil {
return 0 , nil
}
return r .lastInsertID , nil
}
func (r *result ) RowsAffected () (int64 , error ) {
if r == nil {
return 0 , nil
}
return int64 (r .rowsAffected ), nil
}
type rows struct {
allocs []uintptr
c *conn
columns []string
pstmt uintptr
doStep bool
empty bool
}
func newRows (c *conn , pstmt uintptr , allocs []uintptr , empty bool ) (r *rows , err error ) {
r = &rows {c : c , pstmt : pstmt , allocs : allocs , empty : empty }
defer func () {
if err != nil {
r .Close ()
r = nil
}
}()
n , err := c .columnCount (pstmt )
if err != nil {
return nil , err
}
r .columns = make ([]string , n )
for i := range r .columns {
if r .columns [i ], err = r .c .columnName (pstmt , i ); err != nil {
return nil , err
}
}
return r , nil
}
func (r *rows ) Close () (err error ) {
for _ , v := range r .allocs {
r .c .free (v )
}
r .allocs = nil
return r .c .finalize (r .pstmt )
}
func (r *rows ) Columns () (c []string ) {
return r .columns
}
func (r *rows ) Next (dest []driver .Value ) (err error ) {
if r .empty {
return io .EOF
}
rc := sqlite3 .SQLITE_ROW
if r .doStep {
if rc , err = r .c .step (r .pstmt ); err != nil {
return err
}
}
r .doStep = true
switch rc {
case sqlite3 .SQLITE_ROW :
if g , e := len (dest ), len (r .columns ); g != e {
return fmt .Errorf ("sqlite: Next: have %v destination values, expected %v" , g , e )
}
for i := range dest {
ct , err := r .c .columnType (r .pstmt , i )
if err != nil {
return err
}
switch ct {
case sqlite3 .SQLITE_INTEGER :
v , err := r .c .columnInt64 (r .pstmt , i )
if err != nil {
return err
}
dest [i ] = v
case sqlite3 .SQLITE_FLOAT :
v , err := r .c .columnDouble (r .pstmt , i )
if err != nil {
return err
}
dest [i ] = v
case sqlite3 .SQLITE_TEXT :
v , err := r .c .columnText (r .pstmt , i )
if err != nil {
return err
}
switch r .ColumnTypeDatabaseTypeName (i ) {
case "DATE" , "DATETIME" , "TIMESTAMP" :
dest [i ], _ = r .c .parseTime (v )
default :
dest [i ] = v
}
case sqlite3 .SQLITE_BLOB :
v , err := r .c .columnBlob (r .pstmt , i )
if err != nil {
return err
}
dest [i ] = v
case sqlite3 .SQLITE_NULL :
dest [i ] = nil
default :
return fmt .Errorf ("internal error: rc %d" , rc )
}
}
return nil
case sqlite3 .SQLITE_DONE :
return io .EOF
default :
return r .c .errstr (int32 (rc ))
}
}
var parseTimeFormats = []string {
"2006-01-02 15:04:05.999999999-07:00" ,
"2006-01-02T15:04:05.999999999-07:00" ,
"2006-01-02 15:04:05.999999999" ,
"2006-01-02T15:04:05.999999999" ,
"2006-01-02 15:04" ,
"2006-01-02T15:04" ,
"2006-01-02" ,
}
func (c *conn ) parseTime (s string ) (interface {}, bool ) {
if v , ok := c .parseTimeString (s , strings .Index (s , "m=" )); ok {
return v , true
}
ts := strings .TrimSuffix (s , "Z" )
for _ , f := range parseTimeFormats {
t , err := time .Parse (f , ts )
if err == nil {
return t , true
}
}
return s , false
}
func (c *conn ) parseTimeString (s0 string , x int ) (interface {}, bool ) {
s := s0
if x > 0 {
s = s [:x ]
}
s = strings .TrimSpace (s )
if t , err := time .Parse ("2006-01-02 15:04:05.999999999 -0700 MST" , s ); err == nil {
return t , true
}
return s0 , false
}
func (r *rows ) ColumnTypeDatabaseTypeName (index int ) string {
return strings .ToUpper (r .c .columnDeclType (r .pstmt , index ))
}
func (r *rows ) ColumnTypeLength (index int ) (length int64 , ok bool ) {
t , err := r .c .columnType (r .pstmt , index )
if err != nil {
return 0 , false
}
switch t {
case sqlite3 .SQLITE_INTEGER :
return 0 , false
case sqlite3 .SQLITE_FLOAT :
return 0 , false
case sqlite3 .SQLITE_TEXT :
return math .MaxInt64 , true
case sqlite3 .SQLITE_BLOB :
return math .MaxInt64 , true
case sqlite3 .SQLITE_NULL :
return 0 , false
default :
return 0 , false
}
}
func (r *rows ) ColumnTypeNullable (index int ) (nullable , ok bool ) {
return true , true
}
func (r *rows ) ColumnTypePrecisionScale (index int ) (precision , scale int64 , ok bool ) {
return 0 , 0 , false
}
func (r *rows ) ColumnTypeScanType (index int ) reflect .Type {
t , err := r .c .columnType (r .pstmt , index )
if err != nil {
return reflect .TypeOf ("" )
}
switch t {
case sqlite3 .SQLITE_INTEGER :
switch strings .ToLower (r .c .columnDeclType (r .pstmt , index )) {
case "boolean" :
return reflect .TypeOf (false )
case "date" , "datetime" , "time" , "timestamp" :
return reflect .TypeOf (time .Time {})
default :
return reflect .TypeOf (int64 (0 ))
}
case sqlite3 .SQLITE_FLOAT :
return reflect .TypeOf (float64 (0 ))
case sqlite3 .SQLITE_TEXT :
return reflect .TypeOf ("" )
case sqlite3 .SQLITE_BLOB :
return reflect .SliceOf (reflect .TypeOf ([]byte {}))
case sqlite3 .SQLITE_NULL :
return reflect .TypeOf (nil )
default :
return reflect .TypeOf ("" )
}
}
type stmt struct {
c *conn
psql uintptr
}
func newStmt (c *conn , sql string ) (*stmt , error ) {
p , err := libc .CString (sql )
if err != nil {
return nil , err
}
stm := stmt {c : c , psql : p }
return &stm , nil
}
func (s *stmt ) Close () (err error ) {
s .c .free (s .psql )
s .psql = 0
return nil
}
func (s *stmt ) Exec (args []driver .Value ) (driver .Result , error ) {
return s .exec (context .Background (), toNamedValues (args ))
}
func toNamedValues (vals []driver .Value ) (r []driver .NamedValue ) {
r = make ([]driver .NamedValue , len (vals ))
for i , val := range vals {
r [i ] = driver .NamedValue {Value : val , Ordinal : i + 1 }
}
return r
}
func (s *stmt ) exec (ctx context .Context , args []driver .NamedValue ) (r driver .Result , err error ) {
var pstmt uintptr
done := false
if ctx != nil && ctx .Done () != nil {
donech := make (chan struct {})
go func () {
select {
case <- ctx .Done ():
done = true
s .c .interrupt (s .c .db )
case <- donech :
}
}()
defer func () {
close (donech )
}()
}
for psql := s .psql ; *(*byte )(unsafe .Pointer (psql )) != 0 && !done ; {
if pstmt , err = s .c .prepareV2 (&psql ); err != nil {
return nil , err
}
if pstmt == 0 {
continue
}
err = func () (err error ) {
n , err := s .c .bindParameterCount (pstmt )
if err != nil {
return err
}
if n != 0 {
allocs , err := s .c .bind (pstmt , n , args )
if err != nil {
return err
}
if len (allocs ) != 0 {
defer func () {
for _ , v := range allocs {
s .c .free (v )
}
}()
}
}
rc , err := s .c .step (pstmt )
if err != nil {
return err
}
switch rc & 0xff {
case sqlite3 .SQLITE_DONE , sqlite3 .SQLITE_ROW :
default :
return s .c .errstr (int32 (rc ))
}
return nil
}()
if e := s .c .finalize (pstmt ); e != nil && err == nil {
err = e
}
if err != nil {
return nil , err
}
}
return newResult (s .c )
}
func (s *stmt ) NumInput () (n int ) {
return -1
}
func (s *stmt ) Query (args []driver .Value ) (driver .Rows , error ) {
return s .query (context .Background (), toNamedValues (args ))
}
func (s *stmt ) query (ctx context .Context , args []driver .NamedValue ) (r driver .Rows , err error ) {
var pstmt uintptr
done := false
if ctx != nil && ctx .Done () != nil {
donech := make (chan struct {})
go func () {
select {
case <- ctx .Done ():
done = true
s .c .interrupt (s .c .db )
case <- donech :
}
}()
defer func () {
close (donech )
}()
}
var allocs []uintptr
for psql := s .psql ; *(*byte )(unsafe .Pointer (psql )) != 0 && !done ; {
if pstmt , err = s .c .prepareV2 (&psql ); err != nil {
return nil , err
}
if pstmt == 0 {
continue
}
err = func () (err error ) {
n , err := s .c .bindParameterCount (pstmt )
if err != nil {
return err
}
if n != 0 {
if allocs , err = s .c .bind (pstmt , n , args ); err != nil {
return err
}
}
rc , err := s .c .step (pstmt )
if err != nil {
return err
}
switch rc & 0xff {
case sqlite3 .SQLITE_ROW :
if r != nil {
r .Close ()
}
if r , err = newRows (s .c , pstmt , allocs , false ); err != nil {
return err
}
pstmt = 0
return nil
case sqlite3 .SQLITE_DONE :
if r == nil {
if r , err = newRows (s .c , pstmt , allocs , true ); err != nil {
return err
}
pstmt = 0
return nil
}
default :
return s .c .errstr (int32 (rc ))
}
if *(*byte )(unsafe .Pointer (psql )) == 0 {
if r != nil {
r .Close ()
}
if r , err = newRows (s .c , pstmt , allocs , true ); err != nil {
return err
}
pstmt = 0
}
return nil
}()
if e := s .c .finalize (pstmt ); e != nil && err == nil {
err = e
}
if err != nil {
return nil , err
}
}
return r , err
}
type tx struct {
c *conn
}
func newTx (c *conn ) (*tx , error ) {
r := &tx {c : c }
if err := r .exec (context .Background (), "begin" ); err != nil {
return nil , err
}
return r , nil
}
func (t *tx ) Commit () (err error ) {
return t .exec (context .Background (), "commit" )
}
func (t *tx ) Rollback () (err error ) {
return t .exec (context .Background (), "rollback" )
}
func (t *tx ) exec (ctx context .Context , sql string ) (err error ) {
psql , err := libc .CString (sql )
if err != nil {
return err
}
defer t .c .free (psql )
if ctx != nil && ctx .Done () != nil {
donech := make (chan struct {})
go func () {
select {
case <- ctx .Done ():
t .c .interrupt (t .c .db )
case <- donech :
}
}()
defer func () {
close (donech )
}()
}
if rc := sqlite3 .Xsqlite3_exec (t .c .tls , t .c .db , psql , 0 , 0 , 0 ); rc != sqlite3 .SQLITE_OK {
return t .c .errstr (rc )
}
return nil
}
type conn struct {
db uintptr
tls *libc .TLS
}
func newConn (name string ) (*conn , error ) {
c := &conn {tls : libc .NewTLS ()}
db , err := c .openV2 (
name ,
sqlite3 .SQLITE_OPEN_READWRITE |sqlite3 .SQLITE_OPEN_CREATE |
sqlite3 .SQLITE_OPEN_FULLMUTEX |
sqlite3 .SQLITE_OPEN_URI ,
)
if err != nil {
return nil , err
}
c .db = db
if err = c .extendedResultCodes (true ); err != nil {
c .Close ()
return nil , err
}
return c , nil
}
func (c *conn ) columnBlob (pstmt uintptr , iCol int ) (v []byte , err error ) {
p := sqlite3 .Xsqlite3_column_blob (c .tls , pstmt , int32 (iCol ))
len , err := c .columnBytes (pstmt , iCol )
if err != nil {
return nil , err
}
if p == 0 || len == 0 {
return nil , nil
}
v = make ([]byte , len )
copy (v , (*libc .RawMem )(unsafe .Pointer (p ))[:len :len ])
return v , nil
}
func (c *conn ) columnBytes (pstmt uintptr , iCol int ) (_ int , err error ) {
v := sqlite3 .Xsqlite3_column_bytes (c .tls , pstmt , int32 (iCol ))
return int (v ), nil
}
func (c *conn ) columnText (pstmt uintptr , iCol int ) (v string , err error ) {
p := sqlite3 .Xsqlite3_column_text (c .tls , pstmt , int32 (iCol ))
len , err := c .columnBytes (pstmt , iCol )
if err != nil {
return "" , err
}
if p == 0 || len == 0 {
return "" , nil
}
b := make ([]byte , len )
copy (b , (*libc .RawMem )(unsafe .Pointer (p ))[:len :len ])
return string (b ), nil
}
func (c *conn ) columnDouble (pstmt uintptr , iCol int ) (v float64 , err error ) {
v = sqlite3 .Xsqlite3_column_double (c .tls , pstmt , int32 (iCol ))
return v , nil
}
func (c *conn ) columnInt64 (pstmt uintptr , iCol int ) (v int64 , err error ) {
v = sqlite3 .Xsqlite3_column_int64 (c .tls , pstmt , int32 (iCol ))
return v , nil
}
func (c *conn ) columnType (pstmt uintptr , iCol int ) (_ int , err error ) {
v := sqlite3 .Xsqlite3_column_type (c .tls , pstmt , int32 (iCol ))
return int (v ), nil
}
func (c *conn ) columnDeclType (pstmt uintptr , iCol int ) string {
return libc .GoString (sqlite3 .Xsqlite3_column_decltype (c .tls , pstmt , int32 (iCol )))
}
func (c *conn ) columnName (pstmt uintptr , n int ) (string , error ) {
p := sqlite3 .Xsqlite3_column_name (c .tls , pstmt , int32 (n ))
return libc .GoString (p ), nil
}
func (c *conn ) columnCount (pstmt uintptr ) (_ int , err error ) {
v := sqlite3 .Xsqlite3_column_count (c .tls , pstmt )
return int (v ), nil
}
func (c *conn ) lastInsertRowID () (v int64 , _ error ) {
return sqlite3 .Xsqlite3_last_insert_rowid (c .tls , c .db ), nil
}
func (c *conn ) changes () (int , error ) {
v := sqlite3 .Xsqlite3_changes (c .tls , c .db )
return int (v ), nil
}
func (c *conn ) step (pstmt uintptr ) (int , error ) {
for {
switch rc := sqlite3 .Xsqlite3_step (c .tls , pstmt ); rc {
case sqliteLockedSharedcache , sqlite3 .SQLITE_BUSY :
if err := c .retry (pstmt ); err != nil {
return sqlite3 .SQLITE_LOCKED , err
}
default :
return int (rc ), nil
}
}
}
func (c *conn ) retry (pstmt uintptr ) error {
mu := mutexAlloc (c .tls )
(*mutex )(unsafe .Pointer (mu )).Lock ()
rc := sqlite3 .Xsqlite3_unlock_notify (
c .tls ,
c .db ,
*(*uintptr )(unsafe .Pointer (&struct {
f func (*libc .TLS , uintptr , int32 )
}{unlockNotify })),
mu ,
)
if rc == sqlite3 .SQLITE_LOCKED {
(*mutex )(unsafe .Pointer (mu )).Unlock ()
mutexFree (c .tls , mu )
return c .errstr (rc )
}
(*mutex )(unsafe .Pointer (mu )).Lock ()
(*mutex )(unsafe .Pointer (mu )).Unlock ()
mutexFree (c .tls , mu )
if pstmt != 0 {
sqlite3 .Xsqlite3_reset (c .tls , pstmt )
}
return nil
}
func unlockNotify (t *libc .TLS , ppArg uintptr , nArg int32 ) {
for i := int32 (0 ); i < nArg ; i ++ {
mu := *(*uintptr )(unsafe .Pointer (ppArg ))
(*mutex )(unsafe .Pointer (mu )).Unlock ()
ppArg += ptrSize
}
}
func (c *conn ) bind (pstmt uintptr , n int , args []driver .NamedValue ) (allocs []uintptr , err error ) {
defer func () {
if err == nil {
return
}
for _ , v := range allocs {
c .free (v )
}
allocs = nil
}()
for i := 1 ; i <= n ; i ++ {
name , err := c .bindParameterName (pstmt , i )
if err != nil {
return allocs , err
}
var found bool
var v driver .NamedValue
for _, v = range args {
if name != "" {
if (name [0 ] == '?' || name [0 ] == '$' ) && name [1 :] == strconv .Itoa (v .Ordinal ) {
found = true
break
}
if name [1 :] == v .Name [:] {
found = true
break
}
} else {
if v .Ordinal == i {
found = true
break
}
}
}
if !found {
if name != "" {
return allocs , fmt .Errorf ("missing named argument %q" , name [1 :])
}
return allocs , fmt .Errorf ("missing argument with index %d" , i )
}
var p uintptr
switch x := v .Value .(type ) {
case int64 :
if err := c .bindInt64 (pstmt , i , x ); err != nil {
return allocs , err
}
case float64 :
if err := c .bindDouble (pstmt , i , x ); err != nil {
return allocs , err
}
case bool :
v := 0
if x {
v = 1
}
if err := c .bindInt (pstmt , i , v ); err != nil {
return allocs , err
}
case []byte :
if p , err = c .bindBlob (pstmt , i , x ); err != nil {
return allocs , err
}
case string :
if p , err = c .bindText (pstmt , i , x ); err != nil {
return allocs , err
}
case time .Time :
if p , err = c .bindText (pstmt , i , x .String ()); err != nil {
return allocs , err
}
case nil :
if p , err = c .bindNull (pstmt , i ); err != nil {
return allocs , err
}
default :
return allocs , fmt .Errorf ("sqlite: invalid driver.Value type %T" , x )
}
if p != 0 {
allocs = append (allocs , p )
}
}
return allocs , nil
}
func (c *conn ) bindNull (pstmt uintptr , idx1 int ) (uintptr , error ) {
if rc := sqlite3 .Xsqlite3_bind_null (c .tls , pstmt , int32 (idx1 )); rc != sqlite3 .SQLITE_OK {
return 0 , c .errstr (rc )
}
return 0 , nil
}
func (c *conn ) bindText (pstmt uintptr , idx1 int , value string ) (uintptr , error ) {
p , err := libc .CString (value )
if err != nil {
return 0 , err
}
if rc := sqlite3 .Xsqlite3_bind_text (c .tls , pstmt , int32 (idx1 ), p , int32 (len (value )), 0 ); rc != sqlite3 .SQLITE_OK {
c .free (p )
return 0 , c .errstr (rc )
}
return p , nil
}
func (c *conn ) bindBlob (pstmt uintptr , idx1 int , value []byte ) (uintptr , error ) {
p , err := c .malloc (len (value ))
if err != nil {
return 0 , err
}
if len (value ) != 0 {
copy ((*libc .RawMem )(unsafe .Pointer (p ))[:len (value ):len (value )], value )
}
if rc := sqlite3 .Xsqlite3_bind_blob (c .tls , pstmt , int32 (idx1 ), p , int32 (len (value )), 0 ); rc != sqlite3 .SQLITE_OK {
c .free (p )
return 0 , c .errstr (rc )
}
return p , nil
}
func (c *conn ) bindInt (pstmt uintptr , idx1 , value int ) (err error ) {
if rc := sqlite3 .Xsqlite3_bind_int (c .tls , pstmt , int32 (idx1 ), int32 (value )); rc != sqlite3 .SQLITE_OK {
return c .errstr (rc )
}
return nil
}
func (c *conn ) bindDouble (pstmt uintptr , idx1 int , value float64 ) (err error ) {
if rc := sqlite3 .Xsqlite3_bind_double (c .tls , pstmt , int32 (idx1 ), value ); rc != 0 {
return c .errstr (rc )
}
return nil
}
func (c *conn ) bindInt64 (pstmt uintptr , idx1 int , value int64 ) (err error ) {
if rc := sqlite3 .Xsqlite3_bind_int64 (c .tls , pstmt , int32 (idx1 ), value ); rc != sqlite3 .SQLITE_OK {
return c .errstr (rc )
}
return nil
}
func (c *conn ) bindParameterName (pstmt uintptr , i int ) (string , error ) {
p := sqlite3 .Xsqlite3_bind_parameter_name (c .tls , pstmt , int32 (i ))
return libc .GoString (p ), nil
}
func (c *conn ) bindParameterCount (pstmt uintptr ) (_ int , err error ) {
r := sqlite3 .Xsqlite3_bind_parameter_count (c .tls , pstmt )
return int (r ), nil
}
func (c *conn ) finalize (pstmt uintptr ) error {
if rc := sqlite3 .Xsqlite3_finalize (c .tls , pstmt ); rc != sqlite3 .SQLITE_OK {
return c .errstr (rc )
}
return nil
}
func (c *conn ) prepareV2 (zSQL *uintptr ) (pstmt uintptr , err error ) {
var ppstmt , pptail uintptr
defer func () {
c .free (ppstmt )
c .free (pptail )
}()
if ppstmt , err = c .malloc (int (ptrSize )); err != nil {
return 0 , err
}
if pptail , err = c .malloc (int (ptrSize )); err != nil {
return 0 , err
}
for {
switch rc := sqlite3 .Xsqlite3_prepare_v2 (c .tls , c .db , *zSQL , -1 , ppstmt , pptail ); rc {
case sqlite3 .SQLITE_OK :
*zSQL = *(*uintptr )(unsafe .Pointer (pptail ))
return *(*uintptr )(unsafe .Pointer (ppstmt )), nil
case sqliteLockedSharedcache , sqlite3 .SQLITE_BUSY :
if err := c .retry (0 ); err != nil {
return 0 , err
}
default :
return 0 , c .errstr (rc )
}
}
}
func (c *conn ) interrupt (pdb uintptr ) (err error ) {
sqlite3 .Xsqlite3_interrupt (c .tls , pdb )
return nil
}
func (c *conn ) extendedResultCodes (on bool ) error {
if rc := sqlite3 .Xsqlite3_extended_result_codes (c .tls , c .db , libc .Bool32 (on )); rc != sqlite3 .SQLITE_OK {
return c .errstr (rc )
}
return nil
}
func (c *conn ) openV2 (name string , flags int32 ) (uintptr , error ) {
var p , s uintptr
defer func () {
if p != 0 {
c .free (p )
}
if s != 0 {
c .free (s )
}
}()
p , err := c .malloc (int (ptrSize ))
if err != nil {
return 0 , err
}
if s , err = libc .CString (name ); err != nil {
return 0 , err
}
if rc := sqlite3 .Xsqlite3_open_v2 (c .tls , s , p , flags , 0 ); rc != sqlite3 .SQLITE_OK {
return 0 , c .errstr (rc )
}
return *(*uintptr )(unsafe .Pointer (p )), nil
}
func (c *conn ) malloc (n int ) (uintptr , error ) {
if p := libc .Xmalloc (c .tls , types .Size_t (n )); p != 0 || n == 0 {
return p , nil
}
return 0 , fmt .Errorf ("sqlite: cannot allocate %d bytes of memory" , n )
}
func (c *conn ) free (p uintptr ) {
if p != 0 {
libc .Xfree (c .tls , p )
}
}
func (c *conn ) errstr (rc int32 ) error {
p := sqlite3 .Xsqlite3_errstr (c .tls , rc )
str := libc .GoString (p )
p = sqlite3 .Xsqlite3_errmsg (c .tls , c .db )
switch msg := libc .GoString (p ); {
case msg == str :
return &Error {msg : fmt .Sprintf ("%s (%v)" , str , rc ), code : int (rc )}
default :
return &Error {msg : fmt .Sprintf ("%s: %s (%v)" , str , msg , rc ), code : int (rc )}
}
}
func (c *conn ) Begin () (driver .Tx , error ) {
return c .begin (context .Background (), driver .TxOptions {})
}
func (c *conn ) begin (ctx context .Context , opts driver .TxOptions ) (t driver .Tx , err error ) {
return newTx (c )
}
func (c *conn ) Close () error {
if c .db != 0 {
if err := c .closeV2 (c .db ); err != nil {
return err
}
c .db = 0
}
if c .tls != nil {
c .tls .Close ()
c .tls = nil
}
return nil
}
func (c *conn ) closeV2 (db uintptr ) error {
if rc := sqlite3 .Xsqlite3_close_v2 (c .tls , db ); rc != sqlite3 .SQLITE_OK {
return c .errstr (rc )
}
return nil
}
func (c *conn ) Exec (query string , args []driver .Value ) (driver .Result , error ) {
return c .exec (context .Background (), query , toNamedValues (args ))
}
func (c *conn ) exec (ctx context .Context , query string , args []driver .NamedValue ) (r driver .Result , err error ) {
s , err := c .prepare (ctx , query )
if err != nil {
return nil , err
}
defer func () {
if err2 := s .Close (); err2 != nil && err == nil {
err = err2
}
}()
return s .(*stmt ).exec (ctx , args )
}
func (c *conn ) Prepare (query string ) (driver .Stmt , error ) {
return c .prepare (context .Background (), query )
}
func (c *conn ) prepare (ctx context .Context , query string ) (s driver .Stmt , err error ) {
return newStmt (c , query )
}
func (c *conn ) Query (query string , args []driver .Value ) (driver .Rows , error ) {
return c .query (context .Background (), query , toNamedValues (args ))
}
func (c *conn ) query (ctx context .Context , query string , args []driver .NamedValue ) (r driver .Rows , err error ) {
s , err := c .prepare (ctx , query )
if err != nil {
return nil , err
}
defer func () {
if err2 := s .Close (); err2 != nil && err == nil {
err = err2
}
}()
return s .(*stmt ).query (ctx , args )
}
type Driver struct {}
func newDriver () *Driver { return &Driver {} }
func (d *Driver ) Open (name string ) (driver .Conn , error ) {
return newConn (name )
}
The pages are generated with Golds v0.3.6 . (GOOS=darwin GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .