Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gosqlite
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
gosqlite
Commits
67fc1ed7
Commit
67fc1ed7
authored
Jul 17, 2011
by
gwenn
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Customize to my needs.
parent
8e81aa1b
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
423 additions
and
365 deletions
+423
-365
.gitignore
.gitignore
+4
-12
Makefile
Makefile
+2
-8
README
README
+9
-7
sqlite.go
sqlite.go
+289
-214
sqlite_test.go
sqlite_test.go
+119
-124
No files found.
.gitignore
View file @
67fc1ed7
*.6
*.8
*.swp
*~
*.[568ao]
[568a].out
_testmain.go
_obj
*.o
*.out
*.cgo*
_test
cgo_*
_cgo_*
_testmain.go
*.swp
Makefile
View file @
67fc1ed7
...
...
@@ -4,15 +4,9 @@
include
$(GOROOT)/src/Make.inc
TARG
=
f
sqlite
TARG
=
github.com/gwenn/
sqlite
CGOFILES
=
\
fsqlite.go
ifeq
($(GOOS),darwin)
CGO_LDFLAGS
=
/usr/lib/libsqlite3.0.dylib
else
CGO_LDFLAGS
=
-lsqlite3
endif
sqlite.go
include
$(GOROOT)/src/Make.pkg
README
View file @
67fc1ed7
This is based on Russ Cox's original gosqlite package:
Yet another SQLite binding based on:
- original [Russ Cox's][http://code.google.com/p/gosqlite/] implementation,
- the [Patrick Crosby's][https://github.com/patrickxb/fgosqlite/] fork.
http://code.google.com/p/gosqlite/
I added Scan2 and Exec2 which use sqlite api calls to get/bind values instead of the
original which basically converted everything to strings
.
S
can2 and Exec2 are significantly faster. See the benchmarks in fgosqlite_test.go
.
Conn#Exec handles multiple statements (separated by semicolons) properly.
Stmt#Exec is renamed in Stmt#Bind and a new Stmt#Exec method is introduced to #Bind and #Step.
Stmt#Bind uses native sqlite3_bind_x methods and failed if unsupported type.
Stmt#Next returns a (bool, os.Error) couple like Reader#Read. But its use in for loop becomes inelegant..
.
Stmt#Scan uses native sqlite3_column_x methods.
S
tmt#NamedScan is added. It's compliant with [go-dbi][https://github.com/thomaslee/go-dbi/] API but I think its signature should be improved/modified
.
sqlite.go
View file @
67fc1ed7
...
...
@@ -3,11 +3,14 @@
// license that can be found in the LICENSE file.
// Package sqlite provides access to the SQLite library, version 3.
package
f
sqlite
package
sqlite
/*
#cgo LDFLAGS: -lsqlite3
#include <sqlite3.h>
#include <stdlib.h>
#include <string.h>
// These wrappers are necessary because SQLITE_TRANSIENT
// is a pointer constant, and cgo doesn't translate them correctly.
...
...
@@ -18,10 +21,10 @@ package fsqlite
// #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
}
static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
}
*/
...
...
@@ -31,9 +34,8 @@ import (
"fmt"
"os"
"reflect"
"strconv"
"unsafe"
"time"
"unsafe"
)
type
Errno
int
...
...
@@ -41,38 +43,38 @@ type Errno int
func
(
e
Errno
)
String
()
string
{
s
:=
errText
[
e
]
if
s
==
""
{
return
fmt
.
Sprintf
(
"errno %d"
,
e
)
return
fmt
.
Sprintf
(
"errno %d"
,
int
(
e
)
)
}
return
s
}
var
(
ErrError
os
.
Error
=
Errno
(
1
)
// /* SQL error or missing database */
ErrInternal
os
.
Error
=
Errno
(
2
)
// /* Internal logic error in SQLite */
ErrPerm
os
.
Error
=
Errno
(
3
)
// /* Access permission denied */
ErrAbort
os
.
Error
=
Errno
(
4
)
// /* Callback routine requested an abort */
ErrBusy
os
.
Error
=
Errno
(
5
)
// /* The database file is locked */
ErrLocked
os
.
Error
=
Errno
(
6
)
// /* A table in the database is locked */
ErrNoMem
os
.
Error
=
Errno
(
7
)
// /* A malloc() failed */
ErrReadOnly
os
.
Error
=
Errno
(
8
)
// /* Attempt to write a readonly database */
ErrInterrupt
os
.
Error
=
Errno
(
9
)
// /* Operation terminated by sqlite3_interrupt()*/
ErrIOErr
os
.
Error
=
Errno
(
10
)
// /* Some kind of disk I/O error occurred */
ErrCorrupt
os
.
Error
=
Errno
(
11
)
// /* The database disk image is malformed */
ErrFull
os
.
Error
=
Errno
(
13
)
// /* Insertion failed because database is full */
ErrCantOpen
os
.
Error
=
Errno
(
14
)
// /* Unable to open the database file */
ErrEmpty
os
.
Error
=
Errno
(
16
)
// /* Database is empty */
ErrSchema
os
.
Error
=
Errno
(
17
)
// /* The database schema changed */
ErrTooBig
os
.
Error
=
Errno
(
18
)
// /* String or BLOB exceeds size limit */
ErrConstraint
os
.
Error
=
Errno
(
19
)
// /* Abort due to constraint violation */
ErrMismatch
os
.
Error
=
Errno
(
20
)
// /* Data type mismatch */
ErrMisuse
os
.
Error
=
Errno
(
21
)
// /* Library used incorrectly */
ErrNolfs
os
.
Error
=
Errno
(
22
)
// /* Uses OS features not supported on host */
ErrAuth
os
.
Error
=
Errno
(
23
)
// /* Authorization denied */
ErrFormat
os
.
Error
=
Errno
(
24
)
// /* Auxiliary database format error */
ErrRange
os
.
Error
=
Errno
(
25
)
// /* 2nd parameter to sqlite3_bind out of range */
ErrNotDB
os
.
Error
=
Errno
(
26
)
// /* File opened that is not a database file */
Row
=
Errno
(
100
)
// /* sqlite3_step() has another row ready */
Done
=
Errno
(
101
)
// /* sqlite3_step() has finished executing */
ErrError
os
.
Error
=
Errno
(
1
)
// /* SQL error or missing database */
ErrInternal
os
.
Error
=
Errno
(
2
)
// /* Internal logic error in SQLite */
ErrPerm
os
.
Error
=
Errno
(
3
)
// /* Access permission denied */
ErrAbort
os
.
Error
=
Errno
(
4
)
// /* Callback routine requested an abort */
ErrBusy
os
.
Error
=
Errno
(
5
)
// /* The database file is locked */
ErrLocked
os
.
Error
=
Errno
(
6
)
// /* A table in the database is locked */
ErrNoMem
os
.
Error
=
Errno
(
7
)
// /* A malloc() failed */
ErrReadOnly
os
.
Error
=
Errno
(
8
)
// /* Attempt to write a readonly database */
ErrInterrupt
os
.
Error
=
Errno
(
9
)
// /* Operation terminated by sqlite3_interrupt()*/
ErrIOErr
os
.
Error
=
Errno
(
10
)
// /* Some kind of disk I/O error occurred */
ErrCorrupt
os
.
Error
=
Errno
(
11
)
// /* The database disk image is malformed */
ErrFull
os
.
Error
=
Errno
(
13
)
// /* Insertion failed because database is full */
ErrCantOpen
os
.
Error
=
Errno
(
14
)
// /* Unable to open the database file */
ErrEmpty
os
.
Error
=
Errno
(
16
)
// /* Database is empty */
ErrSchema
os
.
Error
=
Errno
(
17
)
// /* The database schema changed */
ErrTooBig
os
.
Error
=
Errno
(
18
)
// /* String or BLOB exceeds size limit */
ErrConstraint
os
.
Error
=
Errno
(
19
)
// /* Abort due to constraint violation */
ErrMismatch
os
.
Error
=
Errno
(
20
)
// /* Data type mismatch */
ErrMisuse
os
.
Error
=
Errno
(
21
)
// /* Library used incorrectly */
ErrNolfs
os
.
Error
=
Errno
(
22
)
// /* Uses OS features not supported on host */
ErrAuth
os
.
Error
=
Errno
(
23
)
// /* Authorization denied */
ErrFormat
os
.
Error
=
Errno
(
24
)
// /* Auxiliary database format error */
ErrRange
os
.
Error
=
Errno
(
25
)
// /* 2nd parameter to sqlite3_bind out of range */
ErrNotDB
os
.
Error
=
Errno
(
26
)
// /* File opened that is not a database file */
Row
=
Errno
(
100
)
// /* sqlite3_step() has another row ready */
Done
=
Errno
(
101
)
// /* sqlite3_step() has finished executing */
)
var
errText
=
map
[
Errno
]
string
{
...
...
@@ -110,6 +112,9 @@ func (c *Conn) error(rv C.int) os.Error {
if
c
==
nil
||
c
.
db
==
nil
{
return
os
.
NewError
(
"nil sqlite database"
)
}
if
rv
==
0
{
return
nil
}
if
rv
==
21
{
// misuse
return
Errno
(
rv
)
}
...
...
@@ -125,6 +130,16 @@ func Version() string {
return
C
.
GoString
(
p
)
}
func
EnableSharedCache
(
b
bool
)
os
.
Error
{
rv
:=
C
.
sqlite3_enable_shared_cache
(
btocint
(
b
))
if
rv
!=
0
{
return
Errno
(
rv
)
}
return
nil
}
// ':memory:' for memory db
// '' for temp db
func
Open
(
filename
string
)
(
*
Conn
,
os
.
Error
)
{
if
C
.
sqlite3_threadsafe
()
==
0
{
return
nil
,
os
.
NewError
(
"sqlite library was not compiled for thread-safe operation"
)
...
...
@@ -136,9 +151,13 @@ func Open(filename string) (*Conn, os.Error) {
rv
:=
C
.
sqlite3_open_v2
(
name
,
&
db
,
C
.
SQLITE_OPEN_FULLMUTEX
|
C
.
SQLITE_OPEN_READWRITE
|
C
.
SQLITE_OPEN_CREATE
,
C
.
SQLITE_OPEN_CREATE
|
C
.
SQLITE_OPEN_URI
,
nil
)
if
rv
!=
0
{
if
db
!=
nil
{
C
.
sqlite3_close
(
db
)
}
return
nil
,
Errno
(
rv
)
}
if
db
==
nil
{
...
...
@@ -147,30 +166,119 @@ func Open(filename string) (*Conn, os.Error) {
return
&
Conn
{
db
},
nil
}
func
(
c
*
Conn
)
Exec
(
cmd
string
,
args
...
interface
{})
os
.
Error
{
s
,
err
:=
c
.
Prepare
(
cmd
)
if
err
!=
nil
{
return
err
func
NewBackup
(
dst
*
Conn
,
dstTable
string
,
src
*
Conn
,
srcTable
string
)
(
*
Backup
,
os
.
Error
)
{
dname
:=
C
.
CString
(
dstTable
)
sname
:=
C
.
CString
(
srcTable
)
defer
C
.
free
(
unsafe
.
Pointer
(
dname
))
defer
C
.
free
(
unsafe
.
Pointer
(
sname
))
sb
:=
C
.
sqlite3_backup_init
(
dst
.
db
,
dname
,
src
.
db
,
sname
)
if
sb
==
nil
{
return
nil
,
dst
.
error
(
C
.
sqlite3_errcode
(
dst
.
db
))
}
defer
s
.
Finalize
()
err
=
s
.
Exec
(
args
...
)
if
err
!=
nil
{
return
err
return
&
Backup
{
sb
,
dst
,
src
},
nil
}
type
Backup
struct
{
sb
*
C
.
sqlite3_backup
dst
,
src
*
Conn
}
func
(
b
*
Backup
)
Step
(
npage
int
)
os
.
Error
{
rv
:=
C
.
sqlite3_backup_step
(
b
.
sb
,
C
.
int
(
npage
))
if
rv
==
0
||
Errno
(
rv
)
==
ErrBusy
||
Errno
(
rv
)
==
ErrLocked
{
return
nil
}
rv
:=
C
.
sqlite3_step
(
s
.
stmt
)
if
Errno
(
rv
)
!=
Done
{
return
c
.
error
(
rv
)
return
Errno
(
rv
)
}
type
BackupStatus
struct
{
Remaining
int
PageCount
int
}
func
(
b
*
Backup
)
Status
()
BackupStatus
{
return
BackupStatus
{
int
(
C
.
sqlite3_backup_remaining
(
b
.
sb
)),
int
(
C
.
sqlite3_backup_pagecount
(
b
.
sb
))}
}
func
(
b
*
Backup
)
Run
(
npage
int
,
sleepNs
int64
,
c
chan
<-
BackupStatus
)
os
.
Error
{
var
err
os
.
Error
for
{
err
=
b
.
Step
(
npage
)
if
err
!=
nil
{
break
}
if
c
!=
nil
{
c
<-
b
.
Status
()
}
time
.
Sleep
(
sleepNs
)
}
return
b
.
dst
.
error
(
C
.
sqlite3_errcode
(
b
.
dst
.
db
))
}
func
(
b
*
Backup
)
Close
()
os
.
Error
{
if
b
.
sb
==
nil
{
return
os
.
EINVAL
}
C
.
sqlite3_backup_finish
(
b
.
sb
)
b
.
sb
=
nil
return
nil
}
func
(
c
*
Conn
)
BusyTimeout
(
ms
int
)
os
.
Error
{
rv
:=
C
.
sqlite3_busy_timeout
(
c
.
db
,
C
.
int
(
ms
))
if
rv
==
0
{
return
nil
}
return
Errno
(
rv
)
}
// Don't use it with SELECT or anything that returns data.
func
(
c
*
Conn
)
Exec
(
cmd
string
,
args
...
interface
{})
os
.
Error
{
for
len
(
cmd
)
>
0
{
s
,
err
:=
c
.
Prepare
(
cmd
)
if
err
!=
nil
{
return
err
}
else
if
s
.
stmt
==
nil
{
// this happens for a comment or white-space
cmd
=
s
.
tail
s
.
Finalize
()
continue
}
err
=
s
.
Exec
(
args
...
)
if
err
!=
nil
{
s
.
Finalize
()
return
err
}
if
len
(
s
.
tail
)
>
0
{
if
len
(
args
)
>
0
{
s
.
Finalize
()
return
os
.
NewError
(
"Cannot execute multiple statements when args are specified"
)
}
}
cmd
=
s
.
tail
s
.
Finalize
()
}
return
nil
}
func
(
c
*
Conn
)
Changes
()
int
{
return
int
(
C
.
sqlite3_changes
(
c
.
db
))
}
func
(
c
*
Conn
)
LastInsertRowid
()
int64
{
return
int64
(
C
.
sqlite3_last_insert_rowid
(
c
.
db
))
}
func
(
c
*
Conn
)
Interrupt
()
{
C
.
sqlite3_interrupt
(
c
.
db
)
}
type
Stmt
struct
{
c
*
Conn
stmt
*
C
.
sqlite3_stmt
err
os
.
Error
t0
int64
sql
string
args
string
tail
string
cols
map
[
string
]
int
// cached columns index by name
}
func
(
c
*
Conn
)
Prepare
(
cmd
string
)
(
*
Stmt
,
os
.
Error
)
{
...
...
@@ -181,114 +289,79 @@ func (c *Conn) Prepare(cmd string) (*Stmt, os.Error) {
defer
C
.
free
(
unsafe
.
Pointer
(
cmdstr
))
var
stmt
*
C
.
sqlite3_stmt
var
tail
*
C
.
char
rv
:=
C
.
sqlite3_prepare_v2
(
c
.
db
,
cmdstr
,
C
.
int
(
len
(
cmd
)
+
1
)
,
&
stmt
,
&
tail
)
rv
:=
C
.
sqlite3_prepare_v2
(
c
.
db
,
cmdstr
,
-
1
,
&
stmt
,
&
tail
)
if
rv
!=
0
{
return
nil
,
c
.
error
(
rv
)
}
return
&
Stmt
{
c
:
c
,
stmt
:
stmt
,
sql
:
cmd
,
t0
:
time
.
Nanoseconds
()},
nil
var
t
string
if
tail
!=
nil
&&
C
.
strlen
(
tail
)
>
0
{
t
=
C
.
GoString
(
tail
)
}
return
&
Stmt
{
c
:
c
,
stmt
:
stmt
,
tail
:
t
},
nil
}
// Don't use it with SELECT or anything that returns data.
func
(
s
*
Stmt
)
Exec
(
args
...
interface
{})
os
.
Error
{
s
.
args
=
fmt
.
Sprintf
(
" %v"
,
[]
interface
{}(
args
))
rv
:=
C
.
sqlite3_reset
(
s
.
stmt
)
if
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
err
:=
s
.
Bind
(
args
...
)
if
err
!=
nil
{
return
err
}
n
:=
int
(
C
.
sqlite3_bind_parameter_count
(
s
.
stmt
))
if
n
!=
len
(
args
)
{
return
os
.
NewError
(
fmt
.
Sprintf
(
"incorrect argument count for Stmt.Exec: have %d want %d"
,
len
(
args
),
n
))
rv
:=
C
.
sqlite3_step
(
s
.
stmt
)
if
Errno
(
rv
)
!=
Done
{
return
s
.
c
.
error
(
rv
)
}
return
nil
}
for
i
,
v
:=
range
args
{
var
str
string
switch
v
:=
v
.
(
type
)
{
case
[]
byte
:
var
p
*
byte
if
len
(
v
)
>
0
{
p
=
&
v
[
0
]
}
if
rv
:=
C
.
my_bind_blob
(
s
.
stmt
,
C
.
int
(
i
+
1
),
unsafe
.
Pointer
(
p
),
C
.
int
(
len
(
v
)));
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
continue
case
bool
:
if
v
{
str
=
"1"
}
else
{
str
=
"0"
}
default
:
str
=
fmt
.
Sprint
(
v
)
}
func
(
s
*
Stmt
)
BindParameterCount
()
int
{
return
int
(
C
.
sqlite3_bind_parameter_count
(
s
.
stmt
))
}
cstr
:=
C
.
CString
(
str
)
rv
:=
C
.
my_bind_text
(
s
.
stmt
,
C
.
int
(
i
+
1
),
cstr
,
C
.
int
(
len
(
str
)))
C
.
free
(
unsafe
.
Pointer
(
cstr
))
if
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
}
return
nil
func
(
s
*
Stmt
)
BindParameterIndex
(
name
string
)
int
{
cname
:=
C
.
CString
(
name
)
defer
C
.
free
(
unsafe
.
Pointer
(
cname
))
return
int
(
C
.
sqlite3_bind_parameter_index
(
s
.
stmt
,
cname
))
}
func
(
s
*
Stmt
)
Exec2
(
args
...
interface
{})
os
.
Error
{
s
.
args
=
fmt
.
Sprintf
(
" %v"
,
[]
interface
{}(
args
))
func
(
s
*
Stmt
)
Bind
(
args
...
interface
{})
os
.
Error
{
rv
:=
C
.
sqlite3_reset
(
s
.
stmt
)
if
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
n
:=
int
(
C
.
sqlite3_bind_parameter_count
(
s
.
stmt
)
)
n
:=
s
.
BindParameterCount
(
)
if
n
!=
len
(
args
)
{
return
os
.
NewError
(
fmt
.
Sprintf
(
"incorrect argument count for Stmt.
Exec
: have %d want %d"
,
len
(
args
),
n
))
return
os
.
NewError
(
fmt
.
Sprintf
(
"incorrect argument count for Stmt.
Bind
: have %d want %d"
,
len
(
args
),
n
))
}
for
i
,
v
:=
range
args
{
var
str
string
index
:=
C
.
int
(
i
+
1
)
switch
v
:=
v
.
(
type
)
{
case
nil
:
rv
=
C
.
sqlite3_bind_null
(
s
.
stmt
,
index
)
case
string
:
cstr
:=
C
.
CString
(
v
)
rv
=
C
.
my_bind_text
(
s
.
stmt
,
index
,
cstr
,
C
.
int
(
len
(
v
)))
C
.
free
(
unsafe
.
Pointer
(
cstr
))
case
int
:
if
rv
:=
C
.
sqlite3_bind_int
(
s
.
stmt
,
C
.
int
(
i
+
1
),
C
.
int
(
v
));
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
continue
rv
=
C
.
sqlite3_bind_int
(
s
.
stmt
,
index
,
C
.
int
(
v
))
case
int64
:
if
rv
:=
C
.
sqlite3_bind_int64
(
s
.
stmt
,
C
.
int
(
i
+
1
),
C
.
sqlite3_int64
(
v
));
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
continue
rv
=
C
.
sqlite3_bind_int64
(
s
.
stmt
,
index
,
C
.
sqlite3_int64
(
v
))
case
byte
:
rv
=
C
.
sqlite3_bind_int
(
s
.
stmt
,
index
,
C
.
int
(
v
))
case
bool
:
rv
=
C
.
sqlite3_bind_int
(
s
.
stmt
,
index
,
btocint
(
v
))
case
float32
:
rv
=
C
.
sqlite3_bind_double
(
s
.
stmt
,
index
,
C
.
double
(
v
))
case
float64
:
if
rv
:=
C
.
sqlite3_bind_double
(
s
.
stmt
,
C
.
int
(
i
+
1
),
C
.
double
(
v
));
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
continue
rv
=
C
.
sqlite3_bind_double
(
s
.
stmt
,
index
,
C
.
double
(
v
))
case
[]
byte
:
var
p
*
byte
if
len
(
v
)
>
0
{
p
=
&
v
[
0
]
}
if
rv
:=
C
.
my_bind_blob
(
s
.
stmt
,
C
.
int
(
i
+
1
),
unsafe
.
Pointer
(
p
),
C
.
int
(
len
(
v
)));
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
continue
case
bool
:
if
v
{
str
=
"1"
}
else
{
str
=
"0"
}
default
:
str
=
fmt
.
Sprint
(
v
)
rv
=
C
.
my_bind_blob
(
s
.
stmt
,
index
,
unsafe
.
Pointer
(
p
),
C
.
int
(
len
(
v
)))
}
cstr
:=
C
.
CString
(
str
)
rv
:=
C
.
my_bind_text
(
s
.
stmt
,
C
.
int
(
i
+
1
),
cstr
,
C
.
int
(
len
(
str
)))
C
.
free
(
unsafe
.
Pointer
(
cstr
))
if
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
...
...
@@ -296,130 +369,125 @@ func (s *Stmt) Exec2(args ...interface{}) os.Error {
return
nil
}
func
(
s
*
Stmt
)
Error
()
os
.
Error
{
return
s
.
err
}
func
(
s
*
Stmt
)
Next
()
bool
{
func
(
s
*
Stmt
)
Next
()
(
bool
,
os
.
Error
)
{
rv
:=
C
.
sqlite3_step
(
s
.
stmt
)
err
:=
Errno
(
rv
)
if
err
==
Row
{
return
true
return
true
,
nil
}
if
err
!=
Done
{
s
.
err
=
s
.
c
.
error
(
rv
)
return
false
,
s
.
c
.
error
(
rv
)
}
return
false
return
false
,
nil
}
func
(
s
*
Stmt
)
Scan
(
args
...
interface
{}
)
os
.
Error
{
n
:=
int
(
C
.
sqlite3_column_count
(
s
.
stmt
)
)
if
n
!=
len
(
args
)
{
return
os
.
NewError
(
fmt
.
Sprintf
(
"incorrect argument count for Stmt.Scan: have %d want %d"
,
len
(
args
),
n
)
)
func
(
s
*
Stmt
)
Reset
(
)
os
.
Error
{
rv
:=
C
.
sqlite3_reset
(
s
.
stmt
)
if
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
return
nil
}
for
i
,
v
:=
range
args
{
n
:=
C
.
sqlite3_column_bytes
(
s
.
stmt
,
C
.
int
(
i
))
p
:=
C
.
sqlite3_column_blob
(
s
.
stmt
,
C
.
int
(
i
))
if
p
==
nil
&&
n
>
0
{
return
os
.
NewError
(
"got nil blob"
)
func
(
s
*
Stmt
)
ColumnCount
()
int
{
return
int
(
C
.
sqlite3_column_count
(
s
.
stmt
))
}
func
(
s
*
Stmt
)
ColumnName
(
index
int
)
string
{
return
C
.
GoString
(
C
.
sqlite3_column_name
(
s
.
stmt
,
C
.
int
(
index
)))
}
func
(
s
*
Stmt
)
NamedScan
(
args
...
interface
{})
os
.
Error
{
if
len
(
args
)
%
2
!=
0
{
return
os
.
NewError
(
"Expected an even number of arguments"
)
}
for
i
:=
0
;
i
<
len
(
args
);
i
+=
2
{
name
,
ok
:=
args
[
i
]
.
(
string
)
if
!
ok
{
return
os
.
NewError
(
"non-string field name field"
)
}
var
data
[]
byte
if
n
>
0
{
data
=
(
*
[
1
<<
30
]
byte
)(
unsafe
.
Pointer
(
p
))[
0
:
n
]
index
,
err
:=
s
.
fieldIndex
(
name
)
// How to look up only once for one statement ?
if
err
!=
nil
{
return
err
}
switch
v
:=
v
.
(
type
)
{
case
*
[]
byte
:
*
v
=
data
case
*
string
:
*
v
=
string
(
data
)
case
*
bool
:
*
v
=
string
(
data
)
==
"1"
case
*
int
:
x
,
err
:=
strconv
.
Atoi
(
string
(
data
))
if
err
!=
nil
{
return
os
.
NewError
(
"arg "
+
strconv
.
Itoa
(
i
)
+
" as int: "
+
err
.
String
())
}
*
v
=
x
case
*
int64
:
x
,
err
:=
strconv
.
Atoi64
(
string
(
data
))
if
err
!=
nil
{
return
os
.
NewError
(
"arg "
+
strconv
.
Itoa
(
i
)
+
" as int64: "
+
err
.
String
())
}
*
v
=
x
case
*
float64
:
x
,
err
:=
strconv
.
Atof64
(
string
(
data
))
if
err
!=
nil
{
return
os
.
NewError
(
"arg "
+
strconv
.
Itoa
(
i
)
+
" as float64: "
+
err
.
String
())
}
*
v
=
x
default
:
return
os
.
NewError
(
"unsupported type in Scan: "
+
reflect
.
Typeof
(
v
)
.
String
())
ptr
:=
args
[
i
+
1
]
err
=
s
.
scanField
(
index
,
ptr
)
if
err
!=
nil
{
return
err
}
}
return
nil
}
func
(
s
*
Stmt
)
Scan
2
(
args
...
interface
{})
os
.
Error
{
n
:=
int
(
C
.
sqlite3_column_count
(
s
.
stmt
)
)
func
(
s
*
Stmt
)
Scan
(
args
...
interface
{})
os
.
Error
{
n
:=
s
.
ColumnCount
(
)
if
n
!=
len
(
args
)
{
return
os
.
NewError
(
fmt
.
Sprintf
(
"incorrect argument count for Stmt.Scan: have %d want %d"
,
len
(
args
),
n
))
}
for
i
,
v
:=
range
args
{
/*
n := C.sqlite3_column_bytes(s.stmt, C.int(i))
p := C.sqlite3_column_blob(s.stmt, C.int(i))
if p == nil && n > 0 {
return os.NewError("got nil blob")
}
var data []byte
if n > 0 {
data = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
}
*/
switch
v
:=
v
.
(
type
)
{
/*
case *[]byte:
*v = data
case *string:
*v = string(data)
case *bool:
*v = string(data) == "1"
case *int:
x, err := strconv.Atoi(string(data))
if err != nil {
return os.NewError("arg " + strconv.Itoa(i) + " as int: " + err.String())
}
*v = x
*/
case
*
int64
:
x
:=
C
.
sqlite3_column_int64
(
s
.
stmt
,
C
.
int
(
i
))
*
v
=
int64
(
x
)
case
*
float64
:
x
:=
C
.
sqlite3_column_double
(
s
.
stmt
,
C
.
int
(
i
))
*
v
=
float64
(
x
)
default
:
return
os
.
NewError
(
"unsupported type in Scan: "
+
reflect
.
Typeof
(
v
)
.
String
())
err
:=
s
.
scanField
(
i
,
v
)
if
err
!=
nil
{
return
err
}
}
return
nil
}
func
(
s
*
Stmt
)
SQL
()
string
{
return
s
.
sql
+
s
.
args
func
(
s
*
Stmt
)
fieldIndex
(
name
string
)
(
int
,
os
.
Error
)
{
if
s
.
cols
==
nil
{
count
:=
s
.
ColumnCount
()
s
.
cols
=
make
(
map
[
string
]
int
,
count
)
for
i
:=
0
;
i
<
count
;
i
++
{
s
.
cols
[
s
.
ColumnName
(
i
)]
=
i
}
}
index
,
ok
:=
s
.
cols
[
name
]
if
ok
{
return
index
,
nil
}
return
0
,
os
.
NewError
(
"invalid column name: "
+
name
)
}
func
(
s
*
Stmt
)
Nanoseconds
()
int64
{
return
time
.
Nanoseconds
()
-
s
.
t0
func
(
s
*
Stmt
)
scanField
(
index
int
,
value
interface
{})
os
.
Error
{
switch
value
:=
value
.
(
type
)
{
case
*
string
:
p
:=
C
.
sqlite3_column_text
(
s
.
stmt
,
C
.
int
(
index
))
if
p
==
nil
{
value
=
nil
}
else
{
n
:=
C
.
sqlite3_column_bytes
(
s
.
stmt
,
C
.
int
(
index
))
*
value
=
C
.
GoStringN
((
*
C
.
char
)(
unsafe
.
Pointer
(
p
)),
n
)
}
case
*
int
:
*
value
=
int
(
C
.
sqlite3_column_int
(
s
.
stmt
,
C
.
int
(
index
)))
case
*
int64
:
*
value
=
int64
(
C
.
sqlite3_column_int64
(
s
.
stmt
,
C
.
int
(
index
)))
case
*
byte
:
*
value
=
byte
(
C
.
sqlite3_column_int
(
s
.
stmt
,
C
.
int
(
index
)))
case
*
bool
:
*
value
=
C
.
sqlite3_column_int
(
s
.
stmt
,
C
.
int
(
index
))
==
1
case
*
float64
:
*
value
=
float64
(
C
.
sqlite3_column_double
(
s
.
stmt
,
C
.
int
(
index
)))
case
*
[]
byte
:
p
:=
C
.
sqlite3_column_blob
(
s
.
stmt
,
C
.
int
(
index
))
if
p
==
nil
{
value
=
nil
}
else
{
n
:=
C
.
sqlite3_column_bytes
(
s
.
stmt
,
C
.
int
(
index
))
*
value
=
(
*
[
1
<<
30
]
byte
)(
unsafe
.
Pointer
(
p
))[
0
:
n
]
}
default
:
return
os
.
NewError
(
"unsupported type in Scan: "
+
reflect
.
TypeOf
(
value
)
.
String
())
}
return
nil
}
func
(
s
*
Stmt
)
Finalize
()
os
.
Error
{
rv
:=
C
.
sqlite3_finalize
(
s
.
stmt
)
if
rv
!=
0
{
return
s
.
c
.
error
(
rv
)
}
s
.
stmt
=
nil
return
nil
}
...
...
@@ -434,3 +502,10 @@ func (c *Conn) Close() os.Error {
c
.
db
=
nil
return
nil
}
func
btocint
(
b
bool
)
C
.
int
{
if
b
{
return
1
}
return
0
}
sqlite_test.go
View file @
67fc1ed7
package
f
sqlite
package
sqlite
import
(
"testing"
"fmt
"
"os
"
)
func
TestOpen
(
t
*
testing
.
T
)
{
db
,
err
:=
Open
(
"
/tmp/test.db
"
)
func
open
(
t
*
testing
.
T
)
*
Conn
{
db
,
err
:=
Open
(
""
)
if
err
!=
nil
{
t
.
Error
f
(
"couldn't open database file: %s"
,
err
)
t
.
Fatal
f
(
"couldn't open database file: %s"
,
err
)
}
if
db
==
nil
{
t
.
Error
(
"opened database is nil"
)
t
.
Fatal
(
"opened database is nil"
)
}
db
.
Close
()
return
db
}
func
TestCreateTable
(
t
*
testing
.
T
)
{
db
,
err
:=
Open
(
"/tmp/test.db"
)
db
.
Exec
(
"DROP TABLE test"
)
err
=
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)
"
)
func
createTable
(
db
*
Conn
,
t
*
testing
.
T
)
{
err
:=
db
.
Exec
(
"DROP TABLE IF EXISTS test;"
+
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT,"
+
" float_num REAL, int_num INTEGER, a_string TEXT); -- bim
"
)
if
err
!=
nil
{
t
.
Error
f
(
"error creating table: %s"
,
err
)
t
.
Fatal
f
(
"error creating table: %s"
,
err
)
}
}
type
OutRow
struct
{
Key
int64
FloatNum
float64
IntNum
int64
AString
string
func
TestOpen
(
t
*
testing
.
T
)
{
db
:=
open
(
t
)
db
.
Close
()
}
func
TestCreateTable
(
t
*
testing
.
T
)
{
db
:=
open
(
t
)
defer
db
.
Close
()
createTable
(
db
,
t
)
}
func
TestInsert
(
t
*
testing
.
T
)
{
db
,
_
:=
Open
(
"/tmp/test.db"
)
d
b
.
Exec
(
"DROP TABLE test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)"
)
db
:=
open
(
t
)
d
efer
db
.
Close
(
)
createTable
(
db
,
t
)
for
i
:=
0
;
i
<
1000
;
i
++
{
ierr
:=
db
.
Exec
(
"INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)"
,
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
if
ierr
!=
nil
{
t
.
Errorf
(
"insert error: %s"
,
ierr
)
t
.
Fatalf
(
"insert error: %s"
,
ierr
)
}
c
:=
db
.
Changes
()
if
c
!=
1
{
t
.
Errorf
(
"insert error: %d <> 1"
,
c
)
}
}
cs
,
_
:=
db
.
Prepare
(
"SELECT COUNT(*) FROM test"
)
cs
.
Exec
()
if
!
cs
.
Next
()
{
t
.
Error
(
"no result for count"
)
defer
cs
.
Finalize
()
if
ok
,
err
:=
cs
.
Next
();
!
ok
{
if
err
!=
nil
{
t
.
Fatalf
(
"error preparing count: %s"
,
err
)
}
t
.
Fatal
(
"no result for count"
)
}
var
i
int
err
:=
cs
.
Scan
(
&
i
)
if
err
!=
nil
{
t
.
Error
f
(
"error scanning count: %s"
,
err
)
t
.
Fatal
f
(
"error scanning count: %s"
,
err
)
}
if
i
!=
1000
{
t
.
Errorf
(
"count should be 1000, but it is %d"
,
i
)
...
...
@@ -59,169 +70,153 @@ func TestInsert(t *testing.T) {
}
func
TestInsertWithStatement
(
t
*
testing
.
T
)
{
db
,
_
:=
Open
(
"/tmp/test_is.db"
)
d
b
.
Exec
(
"DROP TABLE test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)"
)
db
:=
open
(
t
)
d
efer
db
.
Close
(
)
createTable
(
db
,
t
)
s
,
serr
:=
db
.
Prepare
(
"INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)"
)
if
serr
!=
nil
{
t
.
Error
f
(
"prepare error: %s"
,
serr
)
t
.
Fatal
f
(
"prepare error: %s"
,
serr
)
}
if
s
==
nil
{
t
.
Error
(
"statement is nil"
)
t
.
Fatal
(
"statement is nil"
)
}
defer
s
.
Finalize
()
for
i
:=
0
;
i
<
1000
;
i
++
{
ierr
:=
s
.
Exec
(
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
if
ierr
!=
nil
{
t
.
Error
f
(
"insert error: %s"
,
ierr
)
t
.
Fatal
f
(
"insert error: %s"
,
ierr
)
}
s
.
Next
()
}
s
.
Finalize
()
cs
,
_
:=
db
.
Prepare
(
"SELECT COUNT(*) FROM test"
)
cs
.
Exec
()
if
!
cs
.
Next
()
{
t
.
Error
(
"no result for count"
)
}
var
i
int
err
:=
cs
.
Scan
(
&
i
)
if
err
!=
nil
{
t
.
Errorf
(
"error scanning count: %s"
,
err
)
}
if
i
!=
1000
{
t
.
Errorf
(
"count should be 1000, but it is %d"
,
i
)
}
}
func
TestInsertWithStatement2
(
t
*
testing
.
T
)
{
db
,
_
:=
Open
(
"/tmp/test_is2.db"
)
db
.
Exec
(
"DROP TABLE test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)"
)
s
,
serr
:=
db
.
Prepare
(
"INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)"
)
if
serr
!=
nil
{
t
.
Errorf
(
"prepare error: %s"
,
serr
)
}
if
s
==
nil
{
t
.
Error
(
"statement is nil"
)
}
for
i
:=
0
;
i
<
1000
;
i
++
{
ierr
:=
s
.
Exec2
(
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
if
ierr
!=
nil
{
t
.
Errorf
(
"insert error: %s"
,
ierr
)
c
:=
db
.
Changes
()
if
c
!=
1
{
t
.
Errorf
(
"insert error: %d <> 1"
,
c
)
}
s
.
Next
()
}
s
.
Finalize
()
cs
,
_
:=
db
.
Prepare
(
"SELECT COUNT(*) FROM test"
)
cs
.
Exec
()
if
!
cs
.
Next
()
{
t
.
Error
(
"no result for count"
)
defer
cs
.
Finalize
()
if
ok
,
_
:=
cs
.
Next
();
!
ok
{
t
.
Fatal
(
"no result for count"
)
}
var
i
int
err
:=
cs
.
Scan
(
&
i
)
if
err
!=
nil
{
t
.
Error
f
(
"error scanning count: %s"
,
err
)
t
.
Fatal
f
(
"error scanning count: %s"
,
err
)
}
if
i
!=
1000
{
t
.
Errorf
(
"count should be 1000, but it is %d"
,
i
)
}
rs
,
_
:=
db
.
Prepare
(
"SELECT float_num, int_num, a_string FROM test ORDER BY int_num LIMIT 10
"
)
rs
,
_
:=
db
.
Prepare
(
"SELECT float_num, int_num, a_string FROM test ORDER BY int_num LIMIT 2
"
)
var
fnum
float64
var
inum
int64
var
sstr
string
for
rs
.
Next
()
{
rs
.
Scan
(
&
fnum
,
&
inum
,
&
sstr
)
fmt
.
Printf
(
"fnum = %f, inum = %d, sstre = %s
\n
"
,
fnum
,
inum
,
sstr
)
}
if
ok
,
_
:=
rs
.
Next
();
ok
{
rs
.
Scan
(
&
fnum
,
&
inum
,
&
sstr
)
if
fnum
!=
0
{
t
.
Errorf
(
"Expected 0 <> %f
\n
"
,
fnum
)
}
if
inum
!=
0
{
t
.
Errorf
(
"Expected 0 <> %d
\n
"
,
inum
)
}
if
sstr
!=
"hello"
{
t
.
Errorf
(
"Expected 'hello' <> %s
\n
"
,
sstr
)
}
}
if
ok
,
_
:=
rs
.
Next
();
ok
{
var
fnum
float64
var
inum
int64
var
sstr
string
rs
.
NamedScan
(
"a_string"
,
&
sstr
,
"float_num"
,
&
fnum
,
"int_num"
,
&
inum
)
if
fnum
!=
3.14
{
t
.
Errorf
(
"Expected 3.14 <> %f
\n
"
,
fnum
)
}
if
inum
!=
1
{
t
.
Errorf
(
"Expected 1 <> %d
\n
"
,
inum
)
}
if
sstr
!=
"hello"
{
t
.
Errorf
(
"Expected 'hello' <> %s
\n
"
,
sstr
)
}
}
}
func
BenchmarkScan
(
b
*
testing
.
B
)
{
b
.
StopTimer
()
db
,
_
:=
Open
(
"/tmp/test_bs.db"
)
db
.
Exec
(
"DROP TABLE test"
)
db
,
_
:=
Open
(
""
)
defer
db
.
Close
()
db
.
Exec
(
"DROP TABLE IF EXISTS test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)"
)
s
,
_
:=
db
.
Prepare
(
"INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)"
)
for
i
:=
0
;
i
<
1000
;
i
++
{
s
.
Exec
(
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
s
.
Next
()
}
s
.
Finalize
()
b
.
StartTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
cs
,
_
:=
db
.
Prepare
(
"SELECT float_num, int_num, a_string FROM test"
)
cs
,
_
:=
db
.
Prepare
(
"SELECT float_num, int_num, a_string FROM test"
)
cs
.
Exec
()
var
fnum
float64
var
inum
int64
var
sstr
string
var
fnum
float64
var
inum
int64
var
sstr
string
for
cs
.
Next
()
{
var
ok
bool
var
err
os
.
Error
for
ok
,
err
=
cs
.
Next
();
ok
;
ok
,
err
=
cs
.
Next
()
{
cs
.
Scan
(
&
fnum
,
&
inum
,
&
sstr
)
}
if
err
!=
nil
{
panic
(
err
)
}
cs
.
Finalize
()
}
}
func
Benchmark
Scan2
(
b
*
testing
.
B
)
{
func
Benchmark
NamedScan
(
b
*
testing
.
B
)
{
b
.
StopTimer
()
db
,
_
:=
Open
(
"/tmp/test_bs2.db"
)
db
.
Exec
(
"DROP TABLE test"
)
db
,
_
:=
Open
(
""
)
defer
db
.
Close
()
db
.
Exec
(
"DROP TABLE IF EXISTS test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)"
)
s
,
_
:=
db
.
Prepare
(
"INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)"
)
for
i
:=
0
;
i
<
1000
;
i
++
{
s
.
Exec
(
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
s
.
Next
()
}
s
.
Finalize
()
b
.
StartTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
cs
,
_
:=
db
.
Prepare
(
"SELECT float_num, int_num, a_string FROM test"
)
cs
.
Exec
()
cs
,
_
:=
db
.
Prepare
(
"SELECT float_num, int_num, a_string FROM test"
)
var
fnum
float64
var
inum
int64
var
sstr
string
var
fnum
float64
var
inum
int64
var
sstr
string
for
cs
.
Next
()
{
cs
.
Scan2
(
&
fnum
,
&
inum
,
&
sstr
)
var
ok
bool
var
err
os
.
Error
for
ok
,
err
=
cs
.
Next
();
ok
;
ok
,
err
=
cs
.
Next
()
{
cs
.
NamedScan
(
"float_num"
,
&
fnum
,
"int_num"
,
&
inum
,
"a_string"
,
&
sstr
)
}
if
err
!=
nil
{
panic
(
err
)
}
cs
.
Finalize
()
}
}
func
BenchmarkInsert
(
b
*
testing
.
B
)
{
db
,
_
:=
Open
(
"/tmp/test_bi.db"
)
db
.
Exec
(
"DROP TABLE test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)"
)
s
,
_
:=
db
.
Prepare
(
"INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)"
)
// for x := 0; x < b.N; x++ {
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
s
.
Exec
(
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
s
.
Next
()
}
// }
s
.
Finalize
()
}
func
BenchmarkInsert2
(
b
*
testing
.
B
)
{
db
,
_
:=
Open
(
"/tmp/test_bi2.db"
)
db
.
Exec
(
"DROP TABLE test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT, float_num REAL, int_num INTEGER, a_string TEXT)"
)
s
,
_
:=
db
.
Prepare
(
"INSERT INTO test (float_num, int_num, a_string) VALUES (?, ?, ?)"
)
db
,
_
:=
Open
(
""
)
defer
db
.
Close
()
db
.
Exec
(
"DROP TABLE IF EXISTS test"
)
db
.
Exec
(
"CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT,"
+
" float_num REAL, int_num INTEGER, a_string TEXT)"
)
s
,
_
:=
db
.
Prepare
(
"INSERT INTO test (float_num, int_num, a_string)"
+
" VALUES (?, ?, ?)"
)
defer
s
.
Finalize
()
// for x := 0; x < b.N; x++ {
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
s
.
Exec2
(
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
s
.
Next
()
}
// }
s
.
Finalize
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
s
.
Exec
(
float64
(
i
)
*
float64
(
3.14
),
i
,
"hello"
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment