mirror of https://github.com/docker/docs.git
bump godeps
This commit is contained in:
parent
b858e939a3
commit
97e83d3238
|
@ -6,9 +6,9 @@
|
||||||
],
|
],
|
||||||
"Deps": [
|
"Deps": [
|
||||||
{
|
{
|
||||||
"ImportPath": "code.google.com/p/go-sqlite/go1/sqlite3",
|
"ImportPath": "code.google.com/p/gosqlite/sqlite3",
|
||||||
"Comment": "null-50",
|
"Comment": "null-16",
|
||||||
"Rev": "6e75c20f8fc4b936bccab88336915333ae165754"
|
"Rev": "74691fb6f83716190870cde1b658538dd4b18eb0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/agl/ed25519",
|
"ImportPath": "github.com/agl/ed25519",
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include "sqlite3.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Backup is a handle to an online backup operation between two databases.
|
|
||||||
// [http://www.sqlite.org/c3ref/backup.html]
|
|
||||||
type Backup struct {
|
|
||||||
src *Conn
|
|
||||||
dst *Conn
|
|
||||||
bkup *C.sqlite3_backup
|
|
||||||
}
|
|
||||||
|
|
||||||
// newBackup initializes an online backup operation from src.srcName to
|
|
||||||
// dst.dstName.
|
|
||||||
func newBackup(src *Conn, srcName string, dst *Conn, dstName string) (*Backup, error) {
|
|
||||||
srcName += "\x00"
|
|
||||||
dstName += "\x00"
|
|
||||||
|
|
||||||
bkup := C.sqlite3_backup_init(dst.db, cStr(dstName), src.db, cStr(srcName))
|
|
||||||
if bkup == nil {
|
|
||||||
return nil, libErr(C.sqlite3_errcode(dst.db), dst.db)
|
|
||||||
}
|
|
||||||
|
|
||||||
b := &Backup{src, dst, bkup}
|
|
||||||
runtime.SetFinalizer(b, (*Backup).Close)
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close releases all resources associated with the backup operation. It is safe
|
|
||||||
// to call this method prior to backup completion to abort the operation.
|
|
||||||
// [http://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish]
|
|
||||||
func (b *Backup) Close() error {
|
|
||||||
if bkup := b.bkup; bkup != nil {
|
|
||||||
b.bkup = nil
|
|
||||||
runtime.SetFinalizer(b, nil)
|
|
||||||
if rc := C.sqlite3_backup_finish(bkup); rc != OK {
|
|
||||||
return libErr(rc, b.dst.db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conn returns the source and destination connections that are used by this
|
|
||||||
// backup operation. The destination connection must not be used until the
|
|
||||||
// backup operation is closed.
|
|
||||||
func (b *Backup) Conn() (src, dst *Conn) {
|
|
||||||
return b.src, b.dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step copies up to n pages to the destination database. If n is negative, all
|
|
||||||
// remaining pages are copied. io.EOF is returned upon successful backup
|
|
||||||
// completion.
|
|
||||||
// [http://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupstep]
|
|
||||||
func (b *Backup) Step(n int) error {
|
|
||||||
if b.bkup == nil {
|
|
||||||
return ErrBadBackup
|
|
||||||
}
|
|
||||||
if rc := C.sqlite3_backup_step(b.bkup, C.int(n)); rc != OK {
|
|
||||||
// Do not close automatically since that clears the progress info
|
|
||||||
if rc == DONE {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
return libErr(rc, b.dst.db)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress returns the number of pages that still need to be backed up and the
|
|
||||||
// total number of pages in the source database. The values are updated after
|
|
||||||
// each call to Step and are reset to 0 after the backup is closed. The total
|
|
||||||
// number of pages may change if the source database is modified during the
|
|
||||||
// backup operation.
|
|
||||||
// [http://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining]
|
|
||||||
func (b *Backup) Progress() (remaining, total int) {
|
|
||||||
if b.bkup != nil {
|
|
||||||
remaining = int(C.sqlite3_backup_remaining(b.bkup))
|
|
||||||
total = int(C.sqlite3_backup_pagecount(b.bkup))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,214 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include "sqlite3.h"
|
|
||||||
#include "lib/codec.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CodecFunc is a codec initialization function registered for a specific key
|
|
||||||
// prefix via RegisterCodec. It is called when a key with a matching prefix is
|
|
||||||
// specified for an attached database. It returns the Codec implementation that
|
|
||||||
// should be used to encode and decode all database and journal pages. Returning
|
|
||||||
// (nil, nil) disables the codec.
|
|
||||||
type CodecFunc func(ctx *CodecCtx, key []byte) (Codec, *Error)
|
|
||||||
|
|
||||||
// CodecCtx describes the database to which a codec is being attached.
|
|
||||||
type CodecCtx struct {
|
|
||||||
Path string // Full path to the database file
|
|
||||||
Name string // Database name as it is known to SQLite (e.g. "main")
|
|
||||||
PageSize int // Current page size in bytes
|
|
||||||
Reserve int // Current number of bytes reserved in each page
|
|
||||||
Fixed bool // True if the PageSize and Reserve values cannot be changed
|
|
||||||
}
|
|
||||||
|
|
||||||
// newCodecCtx converts the C CodecCtx struct into its Go representation.
|
|
||||||
func newCodecCtx(ctx *C.CodecCtx) *CodecCtx {
|
|
||||||
return &CodecCtx{
|
|
||||||
Path: C.GoString(ctx.zPath),
|
|
||||||
Name: C.GoString(ctx.zName),
|
|
||||||
PageSize: int(ctx.nBuf),
|
|
||||||
Reserve: int(ctx.nRes),
|
|
||||||
Fixed: ctx.fixed != 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Codec is the interface used to encode/decode database and journal pages as
|
|
||||||
// they are written to and read from the disk.
|
|
||||||
//
|
|
||||||
// The op value passed to Encode and Decode methods identifies the operation
|
|
||||||
// being performed. It is undocumented and changed meanings over time since the
|
|
||||||
// codec API was first introduced in 2004. It is believed to be a bitmask of the
|
|
||||||
// following values:
|
|
||||||
//
|
|
||||||
// 1 = journal page, not set for WAL, always set when decoding
|
|
||||||
// 2 = disk I/O, always set
|
|
||||||
// 4 = encode
|
|
||||||
//
|
|
||||||
// In the current implementation, op is always 3 when decoding, 6 when encoding
|
|
||||||
// for the database file or the WAL, and 7 when encoding for the journal. Search
|
|
||||||
// lib/sqlite3.c for "CODEC1" and "CODEC2" for more information.
|
|
||||||
type Codec interface {
|
|
||||||
// Reserve returns the number of bytes that should be reserved for the codec
|
|
||||||
// at the end of each page. The upper limit is 255 (32 if the page size is
|
|
||||||
// 512). Returning -1 leaves the current value unchanged.
|
|
||||||
Reserve() int
|
|
||||||
|
|
||||||
// Resize is called when the codec is first attached to the pager and for
|
|
||||||
// all subsequent page size changes. It can be used to allocate the encode
|
|
||||||
// buffer.
|
|
||||||
Resize(pageSize, reserve int)
|
|
||||||
|
|
||||||
// Encode returns an encoded copy of a page. The data outside of the reserve
|
|
||||||
// area in the original page must not be modified. The codec can either copy
|
|
||||||
// this data into a buffer for encoding or return the original page without
|
|
||||||
// making any changes. Bytes 16 through 23 of page 1 cannot be encoded. Any
|
|
||||||
// non-nil error will be interpreted by SQLite as a NOMEM condition. This is
|
|
||||||
// a limitation of underlying C API.
|
|
||||||
Encode(page []byte, pageNum uint32, op int) ([]byte, *Error)
|
|
||||||
|
|
||||||
// Decode decodes the page in-place, but it may use the encode buffer as
|
|
||||||
// scratch space. Bytes 16 through 23 of page 1 must be left at their
|
|
||||||
// original values. Any non-nil error will be interpreted by SQLite as a
|
|
||||||
// NOMEM condition. This is a limitation of underlying C API.
|
|
||||||
Decode(page []byte, pageNum uint32, op int) *Error
|
|
||||||
|
|
||||||
// Key returns the original key that was used to initialize the codec. Some
|
|
||||||
// implementations may be better off returning nil or a fake value. Search
|
|
||||||
// lib/sqlite3.c for "sqlite3CodecGetKey" to see how the key is used.
|
|
||||||
Key() []byte
|
|
||||||
|
|
||||||
// Free releases codec resources when the pager is destroyed or when the
|
|
||||||
// codec attachment fails.
|
|
||||||
Free()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Codec registry and state reference maps.
|
|
||||||
var (
|
|
||||||
codecReg map[string]CodecFunc
|
|
||||||
codecState map[*codec]struct{}
|
|
||||||
codecMu sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterCodec adds a new codec to the internal registry. Function f will be
|
|
||||||
// called when a key in the format "<name>:<...>" is provided to an attached
|
|
||||||
// database.
|
|
||||||
func RegisterCodec(name string, f CodecFunc) {
|
|
||||||
codecMu.Lock()
|
|
||||||
defer codecMu.Unlock()
|
|
||||||
if f == nil {
|
|
||||||
delete(codecReg, name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if codecReg == nil {
|
|
||||||
codecReg = make(map[string]CodecFunc, 8)
|
|
||||||
}
|
|
||||||
codecReg[name] = f
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCodec returns the CodecFunc for the given key.
|
|
||||||
func getCodec(key []byte) CodecFunc {
|
|
||||||
i := bytes.IndexByte(key, ':')
|
|
||||||
if i == -1 {
|
|
||||||
i = len(key)
|
|
||||||
}
|
|
||||||
codecMu.Lock()
|
|
||||||
defer codecMu.Unlock()
|
|
||||||
if codecReg == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return codecReg[bstr(key[:i])]
|
|
||||||
}
|
|
||||||
|
|
||||||
// codec is a wrapper around the actual Codec interface. It keeps track of the
|
|
||||||
// current page size in order to convert page pointers into byte slices.
|
|
||||||
type codec struct {
|
|
||||||
Codec
|
|
||||||
pageSize C.int
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_codec_init
|
|
||||||
func go_codec_init(ctx *C.CodecCtx, pCodec *unsafe.Pointer, pzErrMsg **C.char) C.int {
|
|
||||||
cf := getCodec(goBytes(ctx.pKey, ctx.nKey))
|
|
||||||
if cf == nil {
|
|
||||||
*pzErrMsg = C.CString("codec not found")
|
|
||||||
return ERROR
|
|
||||||
}
|
|
||||||
ci, err := cf(newCodecCtx(ctx), C.GoBytes(ctx.pKey, ctx.nKey))
|
|
||||||
if err != nil && err.rc != OK {
|
|
||||||
if ci != nil {
|
|
||||||
ci.Free()
|
|
||||||
}
|
|
||||||
if err.msg != "" {
|
|
||||||
*pzErrMsg = C.CString(err.msg)
|
|
||||||
}
|
|
||||||
return C.int(err.rc)
|
|
||||||
}
|
|
||||||
if ci != nil {
|
|
||||||
cs := &codec{ci, ctx.nBuf}
|
|
||||||
*pCodec = unsafe.Pointer(cs)
|
|
||||||
codecMu.Lock()
|
|
||||||
defer codecMu.Unlock()
|
|
||||||
if codecState == nil {
|
|
||||||
codecState = make(map[*codec]struct{}, 8)
|
|
||||||
}
|
|
||||||
codecState[cs] = struct{}{}
|
|
||||||
}
|
|
||||||
return OK
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_codec_reserve
|
|
||||||
func go_codec_reserve(pCodec unsafe.Pointer) C.int {
|
|
||||||
return C.int((*codec)(pCodec).Reserve())
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_codec_resize
|
|
||||||
func go_codec_resize(pCodec unsafe.Pointer, nBuf, nRes C.int) {
|
|
||||||
cs := (*codec)(pCodec)
|
|
||||||
cs.pageSize = nBuf
|
|
||||||
cs.Resize(int(nBuf), int(nRes))
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_codec_exec
|
|
||||||
func go_codec_exec(pCodec, pData unsafe.Pointer, pgno uint32, op C.int) unsafe.Pointer {
|
|
||||||
cs := (*codec)(pCodec)
|
|
||||||
page := goBytes(pData, cs.pageSize)
|
|
||||||
var err *Error
|
|
||||||
if op&4 == 0 {
|
|
||||||
err = cs.Decode(page, pgno, int(op))
|
|
||||||
} else {
|
|
||||||
page, err = cs.Encode(page, pgno, int(op))
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
return cBytes(page)
|
|
||||||
}
|
|
||||||
return nil // Can't do anything with the error at the moment
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_codec_get_key
|
|
||||||
func go_codec_get_key(pCodec unsafe.Pointer, pKey *unsafe.Pointer, nKey *C.int) {
|
|
||||||
if key := (*codec)(pCodec).Key(); len(key) > 0 {
|
|
||||||
*pKey = cBytes(key)
|
|
||||||
*nKey = C.int(len(key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_codec_free
|
|
||||||
func go_codec_free(pCodec unsafe.Pointer) {
|
|
||||||
cs := (*codec)(pCodec)
|
|
||||||
codecMu.Lock()
|
|
||||||
delete(codecState, cs)
|
|
||||||
codecMu.Unlock()
|
|
||||||
cs.Free()
|
|
||||||
cs.Codec = nil
|
|
||||||
}
|
|
183
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/codec/aes-hmac.go
generated
vendored
183
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/codec/aes-hmac.go
generated
vendored
|
@ -1,183 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package codec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"hash"
|
|
||||||
|
|
||||||
. "code.google.com/p/go-sqlite/go1/sqlite3"
|
|
||||||
)
|
|
||||||
|
|
||||||
type aesHmac struct {
|
|
||||||
key []byte // Key provided to newAesHmac with the master key removed
|
|
||||||
buf []byte // Page encryption buffer
|
|
||||||
hdr [4]byte // Header included in each HMAC calculation (page number)
|
|
||||||
tLen int // Tag length in bytes (HMAC truncation)
|
|
||||||
|
|
||||||
// Hash function and chaining mode constructors
|
|
||||||
hash func() hash.Hash
|
|
||||||
mode func(block cipher.Block, iv []byte) cipher.Stream
|
|
||||||
|
|
||||||
// Block cipher and HMAC initialized from the master key
|
|
||||||
block cipher.Block
|
|
||||||
hmac hash.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAesHmac(ctx *CodecCtx, key []byte) (Codec, *Error) {
|
|
||||||
name, opts, mk := parseKey(key)
|
|
||||||
if len(mk) == 0 {
|
|
||||||
return nil, keyErr
|
|
||||||
}
|
|
||||||
defer wipe(mk)
|
|
||||||
|
|
||||||
// Configure the codec
|
|
||||||
c := &aesHmac{
|
|
||||||
key: key[:len(key)-len(mk)],
|
|
||||||
tLen: 16,
|
|
||||||
hash: sha1.New,
|
|
||||||
mode: cipher.NewCTR,
|
|
||||||
}
|
|
||||||
suite := suiteId{
|
|
||||||
Cipher: "aes",
|
|
||||||
KeySize: "128",
|
|
||||||
Mode: "ctr",
|
|
||||||
MAC: "hmac",
|
|
||||||
Hash: "sha1",
|
|
||||||
Trunc: "128",
|
|
||||||
}
|
|
||||||
kLen := 16
|
|
||||||
if err := c.config(opts, &suite, &kLen); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive encryption and authentication keys
|
|
||||||
hLen := c.hash().Size()
|
|
||||||
salt := make([]byte, hLen)
|
|
||||||
copy(salt, name)
|
|
||||||
dk := hkdf(mk, salt, kLen+hLen, c.hash)(suite.Id())
|
|
||||||
defer wipe(dk)
|
|
||||||
|
|
||||||
// Initialize the block cipher and HMAC
|
|
||||||
var err error
|
|
||||||
if c.block, err = aes.NewCipher(dk[:kLen]); err != nil {
|
|
||||||
return nil, NewError(MISUSE, err.Error())
|
|
||||||
}
|
|
||||||
c.hmac = hmac.New(c.hash, dk[kLen:])
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *aesHmac) Reserve() int {
|
|
||||||
return aes.BlockSize + c.tLen
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *aesHmac) Resize(pageSize, reserve int) {
|
|
||||||
if reserve != c.Reserve() {
|
|
||||||
panic("sqlite3: codec reserve value mismatch")
|
|
||||||
}
|
|
||||||
hLen := c.hash().Size()
|
|
||||||
c.buf = make([]byte, pageSize, pageSize-c.tLen+hLen)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *aesHmac) Encode(p []byte, n uint32, op int) ([]byte, *Error) {
|
|
||||||
iv := c.pIV(c.buf)
|
|
||||||
if !rnd(iv) {
|
|
||||||
return nil, prngErr
|
|
||||||
}
|
|
||||||
c.mode(c.block, iv).XORKeyStream(c.buf, c.pText(p))
|
|
||||||
if n == 1 {
|
|
||||||
copy(c.buf[16:], p[16:24])
|
|
||||||
}
|
|
||||||
c.auth(c.buf, n, false)
|
|
||||||
return c.buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *aesHmac) Decode(p []byte, n uint32, op int) *Error {
|
|
||||||
if !c.auth(p, n, true) {
|
|
||||||
return codecErr
|
|
||||||
}
|
|
||||||
if n == 1 {
|
|
||||||
copy(c.buf, p[16:24])
|
|
||||||
}
|
|
||||||
c.mode(c.block, c.pIV(p)).XORKeyStream(p, c.pText(p))
|
|
||||||
if n == 1 {
|
|
||||||
copy(p[16:24], c.buf)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *aesHmac) Key() []byte {
|
|
||||||
return c.key
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *aesHmac) Free() {
|
|
||||||
c.buf = nil
|
|
||||||
c.block = nil
|
|
||||||
c.hmac = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// config applies the codec options that were provided in the key.
|
|
||||||
func (c *aesHmac) config(opts map[string]string, s *suiteId, kLen *int) *Error {
|
|
||||||
for k := range opts {
|
|
||||||
switch k {
|
|
||||||
case "192":
|
|
||||||
s.KeySize = k
|
|
||||||
*kLen = 24
|
|
||||||
case "256":
|
|
||||||
s.KeySize = k
|
|
||||||
*kLen = 32
|
|
||||||
case "ofb":
|
|
||||||
s.Mode = k
|
|
||||||
c.mode = cipher.NewOFB
|
|
||||||
case "sha256":
|
|
||||||
s.Hash = k
|
|
||||||
c.hash = sha256.New
|
|
||||||
default:
|
|
||||||
return NewError(MISUSE, "invalid codec option: "+k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// auth calculates and verifies the HMAC tag for page p. It returns true iff the
|
|
||||||
// tag is successfully verified.
|
|
||||||
func (c *aesHmac) auth(p []byte, n uint32, verify bool) bool {
|
|
||||||
c.hdr[0] = byte(n >> 24)
|
|
||||||
c.hdr[1] = byte(n >> 16)
|
|
||||||
c.hdr[2] = byte(n >> 8)
|
|
||||||
c.hdr[3] = byte(n)
|
|
||||||
|
|
||||||
tag := c.pTag(c.buf)
|
|
||||||
c.hmac.Reset()
|
|
||||||
c.hmac.Write(c.hdr[:])
|
|
||||||
c.hmac.Write(c.pAuth(p))
|
|
||||||
c.hmac.Sum(tag[:0])
|
|
||||||
|
|
||||||
return verify && hmac.Equal(tag, c.pTag(p))
|
|
||||||
}
|
|
||||||
|
|
||||||
// pAuth returns the page subslice that gets authenticated.
|
|
||||||
func (c *aesHmac) pAuth(p []byte) []byte {
|
|
||||||
return p[:len(p)-c.tLen]
|
|
||||||
}
|
|
||||||
|
|
||||||
// pText returns the page subslice that gets encrypted.
|
|
||||||
func (c *aesHmac) pText(p []byte) []byte {
|
|
||||||
return p[:len(p)-c.tLen-aes.BlockSize]
|
|
||||||
}
|
|
||||||
|
|
||||||
// pIV returns the page initialization vector.
|
|
||||||
func (c *aesHmac) pIV(p []byte) []byte {
|
|
||||||
return p[len(p)-c.tLen-aes.BlockSize : len(p)-c.tLen]
|
|
||||||
}
|
|
||||||
|
|
||||||
// pTag returns the page authentication tag.
|
|
||||||
func (c *aesHmac) pTag(p []byte) []byte {
|
|
||||||
return p[len(p)-c.tLen:]
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package codec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rand"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
. "code.google.com/p/go-sqlite/go1/sqlite3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterCodec("aes-hmac", newAesHmac)
|
|
||||||
RegisterCodec("hexdump", newHexDump)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors returned by codec implementations.
|
|
||||||
var (
|
|
||||||
codecErr = NewError(ERROR, "unspecified codec error")
|
|
||||||
prngErr = NewError(ERROR, "csprng not available")
|
|
||||||
keyErr = NewError(MISUSE, "invalid codec key format")
|
|
||||||
)
|
|
||||||
|
|
||||||
// parseKey extracts the codec name, options, and anything left over from a key
|
|
||||||
// in the format "<name>:<options>:<tail...>".
|
|
||||||
func parseKey(key []byte) (name string, opts map[string]string, tail []byte) {
|
|
||||||
k := bytes.SplitN(key, []byte{':'}, 3)
|
|
||||||
name = string(k[0])
|
|
||||||
opts = make(map[string]string)
|
|
||||||
if len(k) > 1 && len(k[1]) > 0 {
|
|
||||||
for _, opt := range strings.Split(string(k[1]), ",") {
|
|
||||||
if i := strings.Index(opt, "="); i > 0 {
|
|
||||||
opts[opt[:i]] = opt[i+1:]
|
|
||||||
} else {
|
|
||||||
opts[opt] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(k) > 2 && len(k[2]) > 0 {
|
|
||||||
tail = k[2]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// hkdf implements the HMAC-based Key Derivation Function, as described in RFC
|
|
||||||
// 5869. The extract step is skipped if salt == nil. It is the caller's
|
|
||||||
// responsibility to set salt "to a string of HashLen zeros," if such behavior
|
|
||||||
// is desired. It returns the function that performs the expand step using the
|
|
||||||
// provided info value, which must be appendable. The derived key is valid until
|
|
||||||
// the next expansion.
|
|
||||||
func hkdf(ikm, salt []byte, dkLen int, h func() hash.Hash) func(info []byte) []byte {
|
|
||||||
if salt != nil {
|
|
||||||
prf := hmac.New(h, salt)
|
|
||||||
prf.Write(ikm)
|
|
||||||
ikm = prf.Sum(nil)
|
|
||||||
}
|
|
||||||
prf := hmac.New(h, ikm)
|
|
||||||
hLen := prf.Size()
|
|
||||||
n := (dkLen + hLen - 1) / hLen
|
|
||||||
dk := make([]byte, dkLen, n*hLen)
|
|
||||||
|
|
||||||
return func(info []byte) []byte {
|
|
||||||
info = append(info, 0)
|
|
||||||
ctr := &info[len(info)-1]
|
|
||||||
for i, t := 1, dk[:0]; i <= n; i++ {
|
|
||||||
*ctr = byte(i)
|
|
||||||
prf.Reset()
|
|
||||||
prf.Write(t)
|
|
||||||
prf.Write(info)
|
|
||||||
t = prf.Sum(t[len(t):])
|
|
||||||
}
|
|
||||||
return dk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rnd fills b with bytes from a CSPRNG.
|
|
||||||
func rnd(b []byte) bool {
|
|
||||||
_, err := io.ReadFull(rand.Reader, b)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// wipe overwrites b with zeros.
|
|
||||||
func wipe(b []byte) {
|
|
||||||
for i := range b {
|
|
||||||
b[i] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// suiteId constructs a canonical cipher suite identifier.
|
|
||||||
type suiteId struct {
|
|
||||||
Cipher string
|
|
||||||
KeySize string
|
|
||||||
Mode string
|
|
||||||
MAC string
|
|
||||||
Hash string
|
|
||||||
Trunc string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *suiteId) Id() []byte {
|
|
||||||
id := make([]byte, 0, 64)
|
|
||||||
section := func(parts ...string) {
|
|
||||||
for i, p := range parts {
|
|
||||||
if p != "" {
|
|
||||||
parts = parts[i:]
|
|
||||||
goto write
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
write:
|
|
||||||
if len(id) > 0 {
|
|
||||||
id = append(id, ',')
|
|
||||||
}
|
|
||||||
id = append(id, parts[0]...)
|
|
||||||
for _, p := range parts[1:] {
|
|
||||||
if p != "" {
|
|
||||||
id = append(id, '-')
|
|
||||||
id = append(id, p...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
section(s.Cipher, s.KeySize, s.Mode)
|
|
||||||
section(s.MAC, s.Hash, s.Trunc)
|
|
||||||
return id
|
|
||||||
}
|
|
128
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/codec/codec_test.go
generated
vendored
128
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/codec/codec_test.go
generated
vendored
|
@ -1,128 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package codec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"hash"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHKDF(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
ikm string
|
|
||||||
salt string
|
|
||||||
info string
|
|
||||||
dkLen int
|
|
||||||
h func() hash.Hash
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
// RFC 5869 Test Vectors
|
|
||||||
{
|
|
||||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
|
|
||||||
"000102030405060708090a0b0c",
|
|
||||||
"f0f1f2f3f4f5f6f7f8f9",
|
|
||||||
42,
|
|
||||||
sha256.New,
|
|
||||||
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
|
|
||||||
}, {
|
|
||||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
|
|
||||||
"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
|
|
||||||
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
|
||||||
82,
|
|
||||||
sha256.New,
|
|
||||||
"b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87",
|
|
||||||
}, {
|
|
||||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
42,
|
|
||||||
sha256.New,
|
|
||||||
"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8",
|
|
||||||
}, {
|
|
||||||
"0b0b0b0b0b0b0b0b0b0b0b",
|
|
||||||
"000102030405060708090a0b0c",
|
|
||||||
"f0f1f2f3f4f5f6f7f8f9",
|
|
||||||
42,
|
|
||||||
sha1.New,
|
|
||||||
"085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896",
|
|
||||||
}, {
|
|
||||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
|
|
||||||
"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
|
|
||||||
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
|
||||||
82,
|
|
||||||
sha1.New,
|
|
||||||
"0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4",
|
|
||||||
}, {
|
|
||||||
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
42,
|
|
||||||
sha1.New,
|
|
||||||
"0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918",
|
|
||||||
}, {
|
|
||||||
"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c",
|
|
||||||
"0000000000000000000000000000000000000000",
|
|
||||||
"",
|
|
||||||
42,
|
|
||||||
sha1.New,
|
|
||||||
"2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, test := range tests {
|
|
||||||
ikm, _ := hex.DecodeString(test.ikm)
|
|
||||||
salt, _ := hex.DecodeString(test.salt)
|
|
||||||
info, _ := hex.DecodeString(test.info)
|
|
||||||
dk := hkdf(ikm, salt, test.dkLen, test.h)(info)
|
|
||||||
if out := hex.EncodeToString(dk); out != test.out {
|
|
||||||
t.Errorf("hkdf(%d) expected %q; got %q", i, test.out, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSuiteId(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
suite suiteId
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
{suiteId{},
|
|
||||||
""},
|
|
||||||
{suiteId{Cipher: "aes"},
|
|
||||||
"aes"},
|
|
||||||
{suiteId{KeySize: "128"},
|
|
||||||
"128"},
|
|
||||||
{suiteId{Cipher: "aes", KeySize: "128"},
|
|
||||||
"aes-128"},
|
|
||||||
{suiteId{Cipher: "aes", Mode: "ctr"},
|
|
||||||
"aes-ctr"},
|
|
||||||
{suiteId{Cipher: "aes", KeySize: "128", Mode: "ctr"},
|
|
||||||
"aes-128-ctr"},
|
|
||||||
{suiteId{MAC: "hmac"},
|
|
||||||
"hmac"},
|
|
||||||
{suiteId{MAC: "hmac", Hash: "sha1"},
|
|
||||||
"hmac-sha1"},
|
|
||||||
{suiteId{MAC: "hmac", Hash: "sha1", Trunc: "128"},
|
|
||||||
"hmac-sha1-128"},
|
|
||||||
{suiteId{Cipher: "aes", MAC: "hmac"},
|
|
||||||
"aes,hmac"},
|
|
||||||
{suiteId{Cipher: "aes", Hash: "sha1"},
|
|
||||||
"aes,sha1"},
|
|
||||||
{suiteId{Mode: "ctr", Hash: "sha1"},
|
|
||||||
"ctr,sha1"},
|
|
||||||
{suiteId{Cipher: "aes", KeySize: "128", MAC: "hmac", Hash: "sha256"},
|
|
||||||
"aes-128,hmac-sha256"},
|
|
||||||
{suiteId{Cipher: "aes", Mode: "ctr", Hash: "sha256", Trunc: "128"},
|
|
||||||
"aes-ctr,sha256-128"},
|
|
||||||
{suiteId{Cipher: "aes", KeySize: "256", Mode: "ctr", MAC: "hmac", Hash: "sha256", Trunc: "128"},
|
|
||||||
"aes-256-ctr,hmac-sha256-128"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
if out := string(test.suite.Id()); out != test.out {
|
|
||||||
t.Errorf("%#v expected %q; got %q", test.suite, test.out, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package codec provides authenticated encryption and other codecs for the sqlite3
|
|
||||||
package.
|
|
||||||
|
|
||||||
This package has no public interface and should be imported with the blank
|
|
||||||
identifier to register the codecs. Use Conn.Key after opening a new connection,
|
|
||||||
or the KEY clause in an ATTACH statement, to use one of the codecs:
|
|
||||||
|
|
||||||
c, _ := sqlite3.Open("file1.db")
|
|
||||||
c.Key("main", []byte("aes-hmac::secretkey1"))
|
|
||||||
c.Exec("ATTACH DATABASE 'file2.db' AS two KEY 'aes-hmac::secretkey2'")
|
|
||||||
|
|
||||||
If the KEY clause is omitted, SQLite uses the key from the main database, which
|
|
||||||
may no longer be valid depending on how the codec is implemented (e.g. aes-hmac
|
|
||||||
destroys the master key after initialization). Specify an empty string as the
|
|
||||||
key to disable this behavior.
|
|
||||||
|
|
||||||
Codec Operation
|
|
||||||
|
|
||||||
Each SQLite database and journal file consists of one or more pages of identical
|
|
||||||
size. Each page may have extra space reserved at the end, which SQLite will not
|
|
||||||
use in any way. The exact number of bytes reserved is stored at offset 20 of the
|
|
||||||
database file header, so the value is between 0 and 255. SQLite requires each
|
|
||||||
page to have at least 480 usable bytes, so the value cannot exceed 32 bytes with
|
|
||||||
a page size of 512. This extra space may be used by a codec to store per-page
|
|
||||||
Initialization Vectors (IVs), Message Authentication Codes (MACs), or any other
|
|
||||||
information.
|
|
||||||
|
|
||||||
CodecFunc is called to initialize a registered codec when a key with a matching
|
|
||||||
prefix is provided. If it returns a non-nil Codec implementation, Codec.Reserve
|
|
||||||
is called to determine how much space this codec needs reserved in each page for
|
|
||||||
correct operation. Codec.Resize is called to provide the current page size and
|
|
||||||
reserve values, and for all subsequent changes. The page size may be changed
|
|
||||||
before the database file is created. Once the first CREATE TABLE statement is
|
|
||||||
executed, the page size and reserve values are fixed.
|
|
||||||
|
|
||||||
Codec.Encode is called when a page is about to be written to the disk.
|
|
||||||
Codec.Decode is called when a page was just read from the disk. This happens for
|
|
||||||
both the main database file and the journal/WAL, so the pages are always encoded
|
|
||||||
on the disk and decoded in memory. Codec.Free is called to free all codec
|
|
||||||
resources when the database is detached.
|
|
||||||
|
|
||||||
AES-HMAC
|
|
||||||
|
|
||||||
The aes-hmac codec provides authenticated encryption using the Advanced
|
|
||||||
Encryption Standard (AES) cipher and the Hash-based Message Authentication Code
|
|
||||||
(HMAC) in Encrypt-then-MAC mode. Each page has an independent, pseudorandom IV,
|
|
||||||
which is regenerated every time the page is encrypted, and an authentication
|
|
||||||
tag, which is verified before the page is decrypted. The codec requires 32 bytes
|
|
||||||
per page to store this information.
|
|
||||||
|
|
||||||
The key format is "aes-hmac:<options>:<master-key>", where <options> is a
|
|
||||||
comma-separated list of codec options described below, and <master-key> is the
|
|
||||||
key from which separate encryption and authentication keys are derived.
|
|
||||||
|
|
||||||
SECURITY WARNING: The master key is called a "key" and not a "password" for a
|
|
||||||
reason. It is not passed through pbkdf2, bcrypt, scrypt, or any other key
|
|
||||||
stretching function. The application is expected to ensure that this key is
|
|
||||||
sufficiently resistant to brute-force attacks. Ideally, it should be obtained
|
|
||||||
from a cryptographically secure pseudorandom number generator (CSPRNG), such as
|
|
||||||
the one provided by the crypto/rand package.
|
|
||||||
|
|
||||||
The encryption and authentication keys are derived from the master key using the
|
|
||||||
HMAC-based Key Derivation Function (HKDF), as described in RFC 5869. The salt is
|
|
||||||
the codec name ("aes-hmac") extended with NULLs to HashLen bytes, and info is
|
|
||||||
the codec configuration string (e.g. "aes-128-ctr,hmac-sha1-128"). This is done
|
|
||||||
to obtain two keys of the required lengths, which are also bound to the codec
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
The default configuration is AES-128-CTR cipher and HMAC-SHA1-128 authentication
|
|
||||||
(HMAC output is truncated to 128 bits). The following options may be used to
|
|
||||||
change the defaults:
|
|
||||||
|
|
||||||
192
|
|
||||||
AES-192 block cipher.
|
|
||||||
256
|
|
||||||
AES-256 block cipher.
|
|
||||||
ofb
|
|
||||||
Output feedback mode of operation.
|
|
||||||
sha256
|
|
||||||
SHA-256 hash function used by HKDF and HMAC.
|
|
||||||
|
|
||||||
For example, "aes-hmac:256,ofb,sha256:<master-key>" will use the AES-256-OFB
|
|
||||||
cipher and HMAC-SHA256-128 authentication.
|
|
||||||
|
|
||||||
HEXDUMP
|
|
||||||
|
|
||||||
The hexdump codec logs all method calls and dumps the page content for each
|
|
||||||
encode/decode operation to a file. It is intended to be used as an aid when
|
|
||||||
writing your own codecs.
|
|
||||||
|
|
||||||
The key format is "hexdump:<options>:<file>", where <options> is a
|
|
||||||
comma-separated list of codec options described below, and <file> is the output
|
|
||||||
destination. The default destination is stderr. Dash ("-") means stdout. For
|
|
||||||
obvious reasons, this codec cannot be used with an encrypted database except to
|
|
||||||
see the first Codec.Decode call for page 1.
|
|
||||||
|
|
||||||
The following options are supported:
|
|
||||||
|
|
||||||
quiet
|
|
||||||
Do not output a hex dump of each page.
|
|
||||||
reserve=N
|
|
||||||
Reserve N bytes in each page. The default is -1, which means don't
|
|
||||||
change the current reserve value.
|
|
||||||
*/
|
|
||||||
package codec
|
|
105
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/codec/hexdump.go
generated
vendored
105
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/codec/hexdump.go
generated
vendored
|
@ -1,105 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package codec
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
. "code.google.com/p/go-sqlite/go1/sqlite3"
|
|
||||||
)
|
|
||||||
|
|
||||||
type hexDump struct {
|
|
||||||
key []byte
|
|
||||||
out *os.File
|
|
||||||
quiet bool
|
|
||||||
res int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHexDump(ctx *CodecCtx, key []byte) (Codec, *Error) {
|
|
||||||
_, opts, tail := parseKey(key)
|
|
||||||
c := &hexDump{key, os.Stderr, false, -1}
|
|
||||||
|
|
||||||
// Set options
|
|
||||||
for k, v := range opts {
|
|
||||||
switch k {
|
|
||||||
case "quiet":
|
|
||||||
c.quiet = true
|
|
||||||
case "reserve":
|
|
||||||
if n, err := strconv.ParseUint(v, 10, 8); err == nil {
|
|
||||||
c.res = int(n)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, NewError(MISUSE, "invalid codec option: "+k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open output file
|
|
||||||
switch file := string(tail); file {
|
|
||||||
case "":
|
|
||||||
case "-":
|
|
||||||
c.out = os.Stdout
|
|
||||||
default:
|
|
||||||
var err error
|
|
||||||
c.out, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewError(ERROR, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(c.out, "Init (\n"+
|
|
||||||
" Path=%s\n"+
|
|
||||||
" Name=%s\n"+
|
|
||||||
" PageSize=%d\n"+
|
|
||||||
" Reserve=%d\n"+
|
|
||||||
" Fixed=%t\n"+
|
|
||||||
")\n",
|
|
||||||
ctx.Path, ctx.Name, ctx.PageSize, ctx.Reserve, ctx.Fixed)
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *hexDump) Reserve() int {
|
|
||||||
fmt.Fprintf(c.out, "Reserve\n")
|
|
||||||
return c.res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *hexDump) Resize(pageSize, reserve int) {
|
|
||||||
fmt.Fprintf(c.out, "Resize (pageSize=%d, reserve=%d)\n", pageSize, reserve)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *hexDump) Encode(page []byte, pageNum uint32, op int) ([]byte, *Error) {
|
|
||||||
fmt.Fprintf(c.out, "Encode (pageNum=%d, op=%d)\n", pageNum, op)
|
|
||||||
c.dump(page)
|
|
||||||
return page, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *hexDump) Decode(page []byte, pageNum uint32, op int) *Error {
|
|
||||||
fmt.Fprintf(c.out, "Decode (pageNum=%d, op=%d)\n", pageNum, op)
|
|
||||||
c.dump(page)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *hexDump) Key() []byte {
|
|
||||||
fmt.Fprintf(c.out, "Key\n")
|
|
||||||
return c.key
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *hexDump) Free() {
|
|
||||||
fmt.Fprintf(c.out, "Free\n")
|
|
||||||
if c.out != os.Stdout && c.out != os.Stderr {
|
|
||||||
c.out.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *hexDump) dump(b []byte) {
|
|
||||||
if !c.quiet {
|
|
||||||
hd := hex.Dumper(c.out)
|
|
||||||
hd.Write(b)
|
|
||||||
hd.Close()
|
|
||||||
c.out.Write([]byte("\n"))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,208 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include "sqlite3.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Fundamental SQLite data types. These are returned by Stmt.DataTypes method.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_blob.html]
|
|
||||||
const (
|
|
||||||
INTEGER = C.SQLITE_INTEGER // 1
|
|
||||||
FLOAT = C.SQLITE_FLOAT // 2
|
|
||||||
TEXT = C.SQLITE_TEXT // 3
|
|
||||||
BLOB = C.SQLITE_BLOB // 4
|
|
||||||
NULL = C.SQLITE_NULL // 5
|
|
||||||
)
|
|
||||||
|
|
||||||
// General result codes returned by the SQLite API. When converted to an error,
|
|
||||||
// OK and ROW become nil, and DONE becomes either nil or io.EOF, depending on
|
|
||||||
// the context in which the statement is executed. All other codes are returned
|
|
||||||
// via the Error struct.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_abort.html]
|
|
||||||
const (
|
|
||||||
OK = C.SQLITE_OK // 0 = Successful result
|
|
||||||
ERROR = C.SQLITE_ERROR // 1 = SQL error or missing database
|
|
||||||
INTERNAL = C.SQLITE_INTERNAL // 2 = Internal logic error in SQLite
|
|
||||||
PERM = C.SQLITE_PERM // 3 = Access permission denied
|
|
||||||
ABORT = C.SQLITE_ABORT // 4 = Callback routine requested an abort
|
|
||||||
BUSY = C.SQLITE_BUSY // 5 = The database file is locked
|
|
||||||
LOCKED = C.SQLITE_LOCKED // 6 = A table in the database is locked
|
|
||||||
NOMEM = C.SQLITE_NOMEM // 7 = A malloc() failed
|
|
||||||
READONLY = C.SQLITE_READONLY // 8 = Attempt to write a readonly database
|
|
||||||
INTERRUPT = C.SQLITE_INTERRUPT // 9 = Operation terminated by sqlite3_interrupt()
|
|
||||||
IOERR = C.SQLITE_IOERR // 10 = Some kind of disk I/O error occurred
|
|
||||||
CORRUPT = C.SQLITE_CORRUPT // 11 = The database disk image is malformed
|
|
||||||
NOTFOUND = C.SQLITE_NOTFOUND // 12 = Unknown opcode in sqlite3_file_control()
|
|
||||||
FULL = C.SQLITE_FULL // 13 = Insertion failed because database is full
|
|
||||||
CANTOPEN = C.SQLITE_CANTOPEN // 14 = Unable to open the database file
|
|
||||||
PROTOCOL = C.SQLITE_PROTOCOL // 15 = Database lock protocol error
|
|
||||||
EMPTY = C.SQLITE_EMPTY // 16 = Database is empty
|
|
||||||
SCHEMA = C.SQLITE_SCHEMA // 17 = The database schema changed
|
|
||||||
TOOBIG = C.SQLITE_TOOBIG // 18 = String or BLOB exceeds size limit
|
|
||||||
CONSTRAINT = C.SQLITE_CONSTRAINT // 19 = Abort due to constraint violation
|
|
||||||
MISMATCH = C.SQLITE_MISMATCH // 20 = Data type mismatch
|
|
||||||
MISUSE = C.SQLITE_MISUSE // 21 = Library used incorrectly
|
|
||||||
NOLFS = C.SQLITE_NOLFS // 22 = Uses OS features not supported on host
|
|
||||||
AUTH = C.SQLITE_AUTH // 23 = Authorization denied
|
|
||||||
FORMAT = C.SQLITE_FORMAT // 24 = Auxiliary database format error
|
|
||||||
RANGE = C.SQLITE_RANGE // 25 = 2nd parameter to sqlite3_bind out of range
|
|
||||||
NOTADB = C.SQLITE_NOTADB // 26 = File opened that is not a database file
|
|
||||||
NOTICE = C.SQLITE_NOTICE // 27 = Notifications from sqlite3_log()
|
|
||||||
WARNING = C.SQLITE_WARNING // 28 = Warnings from sqlite3_log()
|
|
||||||
ROW = C.SQLITE_ROW // 100 = sqlite3_step() has another row ready
|
|
||||||
DONE = C.SQLITE_DONE // 101 = sqlite3_step() has finished executing
|
|
||||||
)
|
|
||||||
|
|
||||||
// Extended result codes returned by the SQLite API. Extended result codes are
|
|
||||||
// enabled by default for all new Conn objects. Use Error.Code()&0xFF to convert
|
|
||||||
// an extended code to a general one.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_abort_rollback.html]
|
|
||||||
const (
|
|
||||||
IOERR_READ = C.SQLITE_IOERR_READ // (SQLITE_IOERR | (1<<8))
|
|
||||||
IOERR_SHORT_READ = C.SQLITE_IOERR_SHORT_READ // (SQLITE_IOERR | (2<<8))
|
|
||||||
IOERR_WRITE = C.SQLITE_IOERR_WRITE // (SQLITE_IOERR | (3<<8))
|
|
||||||
IOERR_FSYNC = C.SQLITE_IOERR_FSYNC // (SQLITE_IOERR | (4<<8))
|
|
||||||
IOERR_DIR_FSYNC = C.SQLITE_IOERR_DIR_FSYNC // (SQLITE_IOERR | (5<<8))
|
|
||||||
IOERR_TRUNCATE = C.SQLITE_IOERR_TRUNCATE // (SQLITE_IOERR | (6<<8))
|
|
||||||
IOERR_FSTAT = C.SQLITE_IOERR_FSTAT // (SQLITE_IOERR | (7<<8))
|
|
||||||
IOERR_UNLOCK = C.SQLITE_IOERR_UNLOCK // (SQLITE_IOERR | (8<<8))
|
|
||||||
IOERR_RDLOCK = C.SQLITE_IOERR_RDLOCK // (SQLITE_IOERR | (9<<8))
|
|
||||||
IOERR_DELETE = C.SQLITE_IOERR_DELETE // (SQLITE_IOERR | (10<<8))
|
|
||||||
IOERR_BLOCKED = C.SQLITE_IOERR_BLOCKED // (SQLITE_IOERR | (11<<8))
|
|
||||||
IOERR_NOMEM = C.SQLITE_IOERR_NOMEM // (SQLITE_IOERR | (12<<8))
|
|
||||||
IOERR_ACCESS = C.SQLITE_IOERR_ACCESS // (SQLITE_IOERR | (13<<8))
|
|
||||||
IOERR_CHECKRESERVEDLOCK = C.SQLITE_IOERR_CHECKRESERVEDLOCK // (SQLITE_IOERR | (14<<8))
|
|
||||||
IOERR_LOCK = C.SQLITE_IOERR_LOCK // (SQLITE_IOERR | (15<<8))
|
|
||||||
IOERR_CLOSE = C.SQLITE_IOERR_CLOSE // (SQLITE_IOERR | (16<<8))
|
|
||||||
IOERR_DIR_CLOSE = C.SQLITE_IOERR_DIR_CLOSE // (SQLITE_IOERR | (17<<8))
|
|
||||||
IOERR_SHMOPEN = C.SQLITE_IOERR_SHMOPEN // (SQLITE_IOERR | (18<<8))
|
|
||||||
IOERR_SHMSIZE = C.SQLITE_IOERR_SHMSIZE // (SQLITE_IOERR | (19<<8))
|
|
||||||
IOERR_SHMLOCK = C.SQLITE_IOERR_SHMLOCK // (SQLITE_IOERR | (20<<8))
|
|
||||||
IOERR_SHMMAP = C.SQLITE_IOERR_SHMMAP // (SQLITE_IOERR | (21<<8))
|
|
||||||
IOERR_SEEK = C.SQLITE_IOERR_SEEK // (SQLITE_IOERR | (22<<8))
|
|
||||||
IOERR_DELETE_NOENT = C.SQLITE_IOERR_DELETE_NOENT // (SQLITE_IOERR | (23<<8))
|
|
||||||
IOERR_MMAP = C.SQLITE_IOERR_MMAP // (SQLITE_IOERR | (24<<8))
|
|
||||||
IOERR_GETTEMPPATH = C.SQLITE_IOERR_GETTEMPPATH // (SQLITE_IOERR | (25<<8))
|
|
||||||
LOCKED_SHAREDCACHE = C.SQLITE_LOCKED_SHAREDCACHE // (SQLITE_LOCKED | (1<<8))
|
|
||||||
BUSY_RECOVERY = C.SQLITE_BUSY_RECOVERY // (SQLITE_BUSY | (1<<8))
|
|
||||||
BUSY_SNAPSHOT = C.SQLITE_BUSY_SNAPSHOT // (SQLITE_BUSY | (2<<8))
|
|
||||||
CANTOPEN_NOTEMPDIR = C.SQLITE_CANTOPEN_NOTEMPDIR // (SQLITE_CANTOPEN | (1<<8))
|
|
||||||
CANTOPEN_ISDIR = C.SQLITE_CANTOPEN_ISDIR // (SQLITE_CANTOPEN | (2<<8))
|
|
||||||
CANTOPEN_FULLPATH = C.SQLITE_CANTOPEN_FULLPATH // (SQLITE_CANTOPEN | (3<<8))
|
|
||||||
CORRUPT_VTAB = C.SQLITE_CORRUPT_VTAB // (SQLITE_CORRUPT | (1<<8))
|
|
||||||
READONLY_RECOVERY = C.SQLITE_READONLY_RECOVERY // (SQLITE_READONLY | (1<<8))
|
|
||||||
READONLY_CANTLOCK = C.SQLITE_READONLY_CANTLOCK // (SQLITE_READONLY | (2<<8))
|
|
||||||
READONLY_ROLLBACK = C.SQLITE_READONLY_ROLLBACK // (SQLITE_READONLY | (3<<8))
|
|
||||||
ABORT_ROLLBACK = C.SQLITE_ABORT_ROLLBACK // (SQLITE_ABORT | (2<<8))
|
|
||||||
CONSTRAINT_CHECK = C.SQLITE_CONSTRAINT_CHECK // (SQLITE_CONSTRAINT | (1<<8))
|
|
||||||
CONSTRAINT_COMMITHOOK = C.SQLITE_CONSTRAINT_COMMITHOOK // (SQLITE_CONSTRAINT | (2<<8))
|
|
||||||
CONSTRAINT_FOREIGNKEY = C.SQLITE_CONSTRAINT_FOREIGNKEY // (SQLITE_CONSTRAINT | (3<<8))
|
|
||||||
CONSTRAINT_FUNCTION = C.SQLITE_CONSTRAINT_FUNCTION // (SQLITE_CONSTRAINT | (4<<8))
|
|
||||||
CONSTRAINT_NOTNULL = C.SQLITE_CONSTRAINT_NOTNULL // (SQLITE_CONSTRAINT | (5<<8))
|
|
||||||
CONSTRAINT_PRIMARYKEY = C.SQLITE_CONSTRAINT_PRIMARYKEY // (SQLITE_CONSTRAINT | (6<<8))
|
|
||||||
CONSTRAINT_TRIGGER = C.SQLITE_CONSTRAINT_TRIGGER // (SQLITE_CONSTRAINT | (7<<8))
|
|
||||||
CONSTRAINT_UNIQUE = C.SQLITE_CONSTRAINT_UNIQUE // (SQLITE_CONSTRAINT | (8<<8))
|
|
||||||
CONSTRAINT_VTAB = C.SQLITE_CONSTRAINT_VTAB // (SQLITE_CONSTRAINT | (9<<8))
|
|
||||||
NOTICE_RECOVER_WAL = C.SQLITE_NOTICE_RECOVER_WAL // (SQLITE_NOTICE | (1<<8))
|
|
||||||
NOTICE_RECOVER_ROLLBACK = C.SQLITE_NOTICE_RECOVER_ROLLBACK // (SQLITE_NOTICE | (2<<8))
|
|
||||||
WARNING_AUTOINDEX = C.SQLITE_WARNING_AUTOINDEX // (SQLITE_WARNING | (1<<8))
|
|
||||||
)
|
|
||||||
|
|
||||||
// Codes used by SQLite to indicate the operation type when invoking authorizer
|
|
||||||
// and row update callbacks.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_alter_table.html]
|
|
||||||
const (
|
|
||||||
CREATE_INDEX = C.SQLITE_CREATE_INDEX // 1
|
|
||||||
CREATE_TABLE = C.SQLITE_CREATE_TABLE // 2
|
|
||||||
CREATE_TEMP_INDEX = C.SQLITE_CREATE_TEMP_INDEX // 3
|
|
||||||
CREATE_TEMP_TABLE = C.SQLITE_CREATE_TEMP_TABLE // 4
|
|
||||||
CREATE_TEMP_TRIGGER = C.SQLITE_CREATE_TEMP_TRIGGER // 5
|
|
||||||
CREATE_TEMP_VIEW = C.SQLITE_CREATE_TEMP_VIEW // 6
|
|
||||||
CREATE_TRIGGER = C.SQLITE_CREATE_TRIGGER // 7
|
|
||||||
CREATE_VIEW = C.SQLITE_CREATE_VIEW // 8
|
|
||||||
DELETE = C.SQLITE_DELETE // 9
|
|
||||||
DROP_INDEX = C.SQLITE_DROP_INDEX // 10
|
|
||||||
DROP_TABLE = C.SQLITE_DROP_TABLE // 11
|
|
||||||
DROP_TEMP_INDEX = C.SQLITE_DROP_TEMP_INDEX // 12
|
|
||||||
DROP_TEMP_TABLE = C.SQLITE_DROP_TEMP_TABLE // 13
|
|
||||||
DROP_TEMP_TRIGGER = C.SQLITE_DROP_TEMP_TRIGGER // 14
|
|
||||||
DROP_TEMP_VIEW = C.SQLITE_DROP_TEMP_VIEW // 15
|
|
||||||
DROP_TRIGGER = C.SQLITE_DROP_TRIGGER // 16
|
|
||||||
DROP_VIEW = C.SQLITE_DROP_VIEW // 17
|
|
||||||
INSERT = C.SQLITE_INSERT // 18
|
|
||||||
PRAGMA = C.SQLITE_PRAGMA // 19
|
|
||||||
READ = C.SQLITE_READ // 20
|
|
||||||
SELECT = C.SQLITE_SELECT // 21
|
|
||||||
TRANSACTION = C.SQLITE_TRANSACTION // 22
|
|
||||||
UPDATE = C.SQLITE_UPDATE // 23
|
|
||||||
ATTACH = C.SQLITE_ATTACH // 24
|
|
||||||
DETACH = C.SQLITE_DETACH // 25
|
|
||||||
ALTER_TABLE = C.SQLITE_ALTER_TABLE // 26
|
|
||||||
REINDEX = C.SQLITE_REINDEX // 27
|
|
||||||
ANALYZE = C.SQLITE_ANALYZE // 28
|
|
||||||
CREATE_VTABLE = C.SQLITE_CREATE_VTABLE // 29
|
|
||||||
DROP_VTABLE = C.SQLITE_DROP_VTABLE // 30
|
|
||||||
FUNCTION = C.SQLITE_FUNCTION // 31
|
|
||||||
SAVEPOINT = C.SQLITE_SAVEPOINT // 32
|
|
||||||
)
|
|
||||||
|
|
||||||
// Core SQLite performance counters that can be queried with Status.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_status_malloc_count.html]
|
|
||||||
const (
|
|
||||||
STATUS_MEMORY_USED = C.SQLITE_STATUS_MEMORY_USED // 0
|
|
||||||
STATUS_PAGECACHE_USED = C.SQLITE_STATUS_PAGECACHE_USED // 1
|
|
||||||
STATUS_PAGECACHE_OVERFLOW = C.SQLITE_STATUS_PAGECACHE_OVERFLOW // 2
|
|
||||||
STATUS_SCRATCH_USED = C.SQLITE_STATUS_SCRATCH_USED // 3
|
|
||||||
STATUS_SCRATCH_OVERFLOW = C.SQLITE_STATUS_SCRATCH_OVERFLOW // 4
|
|
||||||
STATUS_MALLOC_SIZE = C.SQLITE_STATUS_MALLOC_SIZE // 5
|
|
||||||
STATUS_PARSER_STACK = C.SQLITE_STATUS_PARSER_STACK // 6
|
|
||||||
STATUS_PAGECACHE_SIZE = C.SQLITE_STATUS_PAGECACHE_SIZE // 7
|
|
||||||
STATUS_SCRATCH_SIZE = C.SQLITE_STATUS_SCRATCH_SIZE // 8
|
|
||||||
STATUS_MALLOC_COUNT = C.SQLITE_STATUS_MALLOC_COUNT // 9
|
|
||||||
)
|
|
||||||
|
|
||||||
// Connection performance counters that can be queried with Conn.Status.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_dbstatus_options.html]
|
|
||||||
const (
|
|
||||||
DBSTATUS_LOOKASIDE_USED = C.SQLITE_DBSTATUS_LOOKASIDE_USED // 0
|
|
||||||
DBSTATUS_CACHE_USED = C.SQLITE_DBSTATUS_CACHE_USED // 1
|
|
||||||
DBSTATUS_SCHEMA_USED = C.SQLITE_DBSTATUS_SCHEMA_USED // 2
|
|
||||||
DBSTATUS_STMT_USED = C.SQLITE_DBSTATUS_STMT_USED // 3
|
|
||||||
DBSTATUS_LOOKASIDE_HIT = C.SQLITE_DBSTATUS_LOOKASIDE_HIT // 4
|
|
||||||
DBSTATUS_LOOKASIDE_MISS_SIZE = C.SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE // 5
|
|
||||||
DBSTATUS_LOOKASIDE_MISS_FULL = C.SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL // 6
|
|
||||||
DBSTATUS_CACHE_HIT = C.SQLITE_DBSTATUS_CACHE_HIT // 7
|
|
||||||
DBSTATUS_CACHE_MISS = C.SQLITE_DBSTATUS_CACHE_MISS // 8
|
|
||||||
DBSTATUS_CACHE_WRITE = C.SQLITE_DBSTATUS_CACHE_WRITE // 9
|
|
||||||
DBSTATUS_DEFERRED_FKS = C.SQLITE_DBSTATUS_DEFERRED_FKS // 10
|
|
||||||
)
|
|
||||||
|
|
||||||
// Statement performance counters that can be queried with Stmt.Status.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_stmtstatus_counter.html]
|
|
||||||
const (
|
|
||||||
STMTSTATUS_FULLSCAN_STEP = C.SQLITE_STMTSTATUS_FULLSCAN_STEP // 1
|
|
||||||
STMTSTATUS_SORT = C.SQLITE_STMTSTATUS_SORT // 2
|
|
||||||
STMTSTATUS_AUTOINDEX = C.SQLITE_STMTSTATUS_AUTOINDEX // 3
|
|
||||||
STMTSTATUS_VM_STEP = C.SQLITE_STMTSTATUS_VM_STEP // 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// Per-connection limits that can be queried and changed with Conn.Limit.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_limit_attached.html]
|
|
||||||
const (
|
|
||||||
LIMIT_LENGTH = C.SQLITE_LIMIT_LENGTH // 0
|
|
||||||
LIMIT_SQL_LENGTH = C.SQLITE_LIMIT_SQL_LENGTH // 1
|
|
||||||
LIMIT_COLUMN = C.SQLITE_LIMIT_COLUMN // 2
|
|
||||||
LIMIT_EXPR_DEPTH = C.SQLITE_LIMIT_EXPR_DEPTH // 3
|
|
||||||
LIMIT_COMPOUND_SELECT = C.SQLITE_LIMIT_COMPOUND_SELECT // 4
|
|
||||||
LIMIT_VDBE_OP = C.SQLITE_LIMIT_VDBE_OP // 5
|
|
||||||
LIMIT_FUNCTION_ARG = C.SQLITE_LIMIT_FUNCTION_ARG // 6
|
|
||||||
LIMIT_ATTACHED = C.SQLITE_LIMIT_ATTACHED // 7
|
|
||||||
LIMIT_LIKE_PATTERN_LENGTH = C.SQLITE_LIMIT_LIKE_PATTERN_LENGTH // 8
|
|
||||||
LIMIT_VARIABLE_NUMBER = C.SQLITE_LIMIT_VARIABLE_NUMBER // 9
|
|
||||||
LIMIT_TRIGGER_DEPTH = C.SQLITE_LIMIT_TRIGGER_DEPTH // 10
|
|
||||||
)
|
|
|
@ -1,176 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package sqlite3 provides an interface to SQLite version 3 databases.
|
|
||||||
|
|
||||||
Database connections are created either by using this package directly or with
|
|
||||||
the "sqlite3" database/sql driver. The direct interface, which is described
|
|
||||||
below, exposes SQLite-specific features, such as incremental I/O and online
|
|
||||||
backups. The driver is recommended when your application has to support multiple
|
|
||||||
database engines.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
|
|
||||||
Minimum requirements are Go 1.1+ with CGO enabled and GCC/MinGW C compiler. The
|
|
||||||
SQLite amalgamation version 3.8.0.2 (2013-09-03) is compiled as part of the
|
|
||||||
package (see http://www.sqlite.org/amalgamation.html). Compilation options are
|
|
||||||
defined at the top of sqlite3.go (#cgo CFLAGS). Dynamic linking with a shared
|
|
||||||
SQLite library is not supported.
|
|
||||||
|
|
||||||
Windows users should install mingw-w64 (http://mingw-w64.sourceforge.net/),
|
|
||||||
TDM64-GCC (http://tdm-gcc.tdragon.net/), or another MinGW distribution, and make
|
|
||||||
sure that gcc.exe is available from the %PATH%. MSYS is not required.
|
|
||||||
|
|
||||||
Run 'go get code.google.com/p/go-sqlite/go1/sqlite3' to download, build, and
|
|
||||||
install the package.
|
|
||||||
|
|
||||||
Concurrency
|
|
||||||
|
|
||||||
A single connection instance and all of its derived objects (prepared
|
|
||||||
statements, backup operations, etc.) may NOT be used concurrently from multiple
|
|
||||||
goroutines without external synchronization. The only exception is
|
|
||||||
Conn.Interrupt(), which may be called from another goroutine to abort a
|
|
||||||
long-running operation. It is safe to use separate connection instances
|
|
||||||
concurrently, even if they are accessing the same database file. For example:
|
|
||||||
|
|
||||||
// ERROR (without any extra synchronization)
|
|
||||||
c, _ := sqlite3.Open("sqlite.db")
|
|
||||||
go use(c)
|
|
||||||
go use(c)
|
|
||||||
|
|
||||||
// OK
|
|
||||||
c1, _ := sqlite3.Open("sqlite.db")
|
|
||||||
c2, _ := sqlite3.Open("sqlite.db")
|
|
||||||
go use(c1)
|
|
||||||
go use(c2)
|
|
||||||
|
|
||||||
Maps
|
|
||||||
|
|
||||||
Use NamedArgs map to bind values to named statement parameters (see
|
|
||||||
http://www.sqlite.org/lang_expr.html#varparam). Use RowMap to retrieve the
|
|
||||||
current row as a map of column/value pairs. Here is a short example with the
|
|
||||||
error-handling code omitted for brevity:
|
|
||||||
|
|
||||||
c, _ := sqlite3.Open(":memory:")
|
|
||||||
c.Exec("CREATE TABLE x(a, b, c)")
|
|
||||||
|
|
||||||
args := sqlite3.NamedArgs{"$a": 1, "$b": "demo"}
|
|
||||||
c.Exec("INSERT INTO x VALUES($a, $b, $c)", args) // $c will be NULL
|
|
||||||
|
|
||||||
sql := "SELECT rowid, * FROM x"
|
|
||||||
row := make(sqlite3.RowMap)
|
|
||||||
for s, err := c.Query(sql); err == nil; err = s.Next() {
|
|
||||||
var rowid int64
|
|
||||||
s.Scan(&rowid, row) // Assigns 1st column to rowid, the rest to row
|
|
||||||
fmt.Println(rowid, row) // Prints "1 map[a:1 b:demo c:<nil>]"
|
|
||||||
}
|
|
||||||
|
|
||||||
Data Types
|
|
||||||
|
|
||||||
See http://www.sqlite.org/datatype3.html for a description of the SQLite data
|
|
||||||
type system. The following Go data types are supported as arguments to prepared
|
|
||||||
statements and may be used in NamedArgs:
|
|
||||||
|
|
||||||
Go Type SQLite Type Notes
|
|
||||||
--------- ----------- ----------------------------------------------------
|
|
||||||
<nil> NULL Unbound parameters are NULL by default.
|
|
||||||
int INTEGER
|
|
||||||
int64 INTEGER
|
|
||||||
float64 FLOAT
|
|
||||||
bool INTEGER Converted as false = 0, true = 1.
|
|
||||||
string TEXT SQLite makes a private copy when the value is bound.
|
|
||||||
[]byte BLOB SQLite makes a private copy when the value is bound.
|
|
||||||
time.Time INTEGER Converted by calling Unix().
|
|
||||||
RawString TEXT SQLite uses the value directly without copying. The
|
|
||||||
caller must keep a reference to the value for the
|
|
||||||
duration of the query to prevent garbage collection.
|
|
||||||
RawBytes BLOB Same as RawString. The value must not be modified
|
|
||||||
for the duration of the query.
|
|
||||||
ZeroBlob BLOB Allocates a zero-filled BLOB of the specified length
|
|
||||||
(e.g. ZeroBlob(4096) allocates 4KB).
|
|
||||||
|
|
||||||
Note that the table above describes how the value is bound to the statement. The
|
|
||||||
final storage class is determined according to the column affinity rules.
|
|
||||||
|
|
||||||
See http://www.sqlite.org/c3ref/column_blob.html for a description of how column
|
|
||||||
values are retrieved from the results of a query. The following static Go data
|
|
||||||
types are supported for retrieving column values:
|
|
||||||
|
|
||||||
Go Type Req. Type Notes
|
|
||||||
---------- --------- ---------------------------------------------------
|
|
||||||
*int INTEGER
|
|
||||||
*int64 INTEGER
|
|
||||||
*float64 FLOAT
|
|
||||||
*bool INTEGER Converted as 0 = false, otherwise true.
|
|
||||||
*string TEXT The caller receives a copy of the value.
|
|
||||||
*[]byte BLOB The caller receives a copy of the value.
|
|
||||||
*time.Time INTEGER Converted by calling time.Unix(). Text values are not
|
|
||||||
supported, but the conversion can be performed with
|
|
||||||
the date and time SQL functions.
|
|
||||||
*RawString TEXT The value is used directly without copying and
|
|
||||||
remains valid until the next Stmt method call.
|
|
||||||
*RawBytes BLOB Same as *RawString. The value must not be modified.
|
|
||||||
Re-slicing is ok, but be careful with append().
|
|
||||||
io.Writer BLOB The value is written out directly into the writer.
|
|
||||||
|
|
||||||
For *interface{} and RowMap arguments, the Go data type is dynamically selected
|
|
||||||
based on the SQLite storage class and column declaration prefix:
|
|
||||||
|
|
||||||
SQLite Type Col. Decl. Go Type Notes
|
|
||||||
----------- ---------- --------- ----------------------------------------
|
|
||||||
NULL <nil>
|
|
||||||
INTEGER "DATE..." time.Time Converted by calling time.Unix().
|
|
||||||
INTEGER "TIME..." time.Time Converted by calling time.Unix().
|
|
||||||
INTEGER "BOOL..." bool Converted as 0 = false, otherwise true.
|
|
||||||
INTEGER int64
|
|
||||||
FLOAT float64
|
|
||||||
TEXT string
|
|
||||||
BLOB []byte
|
|
||||||
|
|
||||||
Database Names
|
|
||||||
|
|
||||||
Methods that require a database name as one of the arguments (e.g. Conn.Path())
|
|
||||||
expect the symbolic name by which the database is known to the connection, not a
|
|
||||||
path to a file. Valid database names are "main", "temp", or a name specified
|
|
||||||
after the AS clause in an ATTACH statement.
|
|
||||||
|
|
||||||
Callbacks
|
|
||||||
|
|
||||||
SQLite can execute callbacks for various internal events. The package provides
|
|
||||||
types and methods for registering callback handlers. Unless stated otherwise in
|
|
||||||
SQLite documentation, callback handlers are not reentrant and must not do
|
|
||||||
anything to modify the associated database connection. This includes
|
|
||||||
preparing/running any other SQL statements. The safest bet is to avoid all
|
|
||||||
interactions with Conn, Stmt, and other related objects within the handler.
|
|
||||||
|
|
||||||
Codecs and Encryption
|
|
||||||
|
|
||||||
SQLite has an undocumented codec API, which operates between the pager and VFS
|
|
||||||
layers, and is used by the SQLite Encryption Extension (SEE) to encrypt database
|
|
||||||
and journal contents. Consider purchasing a SEE license if you require
|
|
||||||
production-quality encryption support (http://www.hwaci.com/sw/sqlite/see.html).
|
|
||||||
|
|
||||||
This package has an experimental API (read: unstable, may eat your data) for
|
|
||||||
writing codecs in Go. The "codec" subpackage provides additional documentation
|
|
||||||
and several existing codec implementations.
|
|
||||||
|
|
||||||
Codecs are registered via the RegisterCodec function for a specific key prefix.
|
|
||||||
For example, the "aes-hmac" codec is initialized when a key in the format
|
|
||||||
"aes-hmac:<...>" is provided to an attached database. The key format after the
|
|
||||||
first colon is codec-specific. See CodecFunc for more information.
|
|
||||||
|
|
||||||
The codec API has several limitations. Codecs cannot be used for in-memory or
|
|
||||||
temporary databases. Once a database is created, the page size and the amount of
|
|
||||||
reserved space at the end of each page cannot be changed (i.e. "PRAGMA
|
|
||||||
page_size=N; VACUUM;" will not work). Online backups will fail unless the
|
|
||||||
destination database has the same page size and reserve values. Bytes 16 through
|
|
||||||
23 of page 1 (the database header, see http://www.sqlite.org/fileformat2.html)
|
|
||||||
cannot be altered, so it is always possible to identify encrypted SQLite
|
|
||||||
databases.
|
|
||||||
|
|
||||||
The rekey function is currently not implemented. The key can only be changed via
|
|
||||||
the backup API or by dumping and restoring the database contents.
|
|
||||||
*/
|
|
||||||
package sqlite3
|
|
|
@ -1,161 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Driver implements the interface required by database/sql.
|
|
||||||
type Driver string
|
|
||||||
|
|
||||||
func register(name string) {
|
|
||||||
defer func() { recover() }()
|
|
||||||
sql.Register(name, Driver(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Driver) Open(name string) (driver.Conn, error) {
|
|
||||||
c, err := Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c.BusyTimeout(5 * time.Second)
|
|
||||||
return &conn{c}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// conn implements driver.Conn.
|
|
||||||
type conn struct {
|
|
||||||
*Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Prepare(query string) (driver.Stmt, error) {
|
|
||||||
if c.Conn.db == nil {
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
s, err := c.Conn.Prepare(query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &stmt{s, false}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Begin() (driver.Tx, error) {
|
|
||||||
if c.Conn.db == nil {
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
if err := c.Conn.Begin(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c.Conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
|
||||||
if c.Conn.db == nil {
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
if err := c.Conn.Exec(query, vtoi(args)...); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// TODO: Do the driver.Result values need to be cached?
|
|
||||||
return result{c.Conn}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// stmt implements driver.Stmt.
|
|
||||||
type stmt struct {
|
|
||||||
*Stmt
|
|
||||||
closed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) Close() error {
|
|
||||||
if !s.closed {
|
|
||||||
s.closed = true
|
|
||||||
if !s.Stmt.Busy() {
|
|
||||||
return s.Stmt.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) NumInput() int {
|
|
||||||
return s.Stmt.NumParams()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
|
|
||||||
if err := s.Stmt.Exec(vtoi(args)...); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return result{s.Stmt.Conn()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
|
|
||||||
if err := s.Stmt.Query(vtoi(args)...); err != nil && err != io.EOF {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &rows{s, true}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// result implements driver.Result.
|
|
||||||
type result struct {
|
|
||||||
*Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r result) LastInsertId() (int64, error) {
|
|
||||||
return int64(r.Conn.LastInsertId()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r result) RowsAffected() (int64, error) {
|
|
||||||
return int64(r.Conn.RowsAffected()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// rows implements driver.Rows.
|
|
||||||
type rows struct {
|
|
||||||
*stmt
|
|
||||||
first bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rows) Close() error {
|
|
||||||
if r.stmt.closed {
|
|
||||||
return r.stmt.Stmt.Close()
|
|
||||||
}
|
|
||||||
r.stmt.Stmt.Reset()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rows) Next(dest []driver.Value) error {
|
|
||||||
if r.first {
|
|
||||||
r.first = false
|
|
||||||
if !r.stmt.Stmt.Busy() {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
} else if err := r.stmt.Stmt.Next(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := range dest {
|
|
||||||
v := (*interface{})(&dest[i])
|
|
||||||
err := r.stmt.Stmt.scanDynamic(C.int(i), v, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// vtoi converts []driver.Value to []interface{} without copying the contents.
|
|
||||||
func vtoi(v []driver.Value) (i []interface{}) {
|
|
||||||
if len(v) > 0 {
|
|
||||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&i))
|
|
||||||
h.Data = uintptr(unsafe.Pointer(&v[0]))
|
|
||||||
h.Len = len(v)
|
|
||||||
h.Cap = cap(v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,182 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include "sqlite3.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrBlobFull is returned by BlobIO.Write when there isn't enough space left to
|
|
||||||
// write the provided bytes.
|
|
||||||
var ErrBlobFull = &Error{ERROR, "incremental write failed, no space left"}
|
|
||||||
|
|
||||||
// BlobIO is a handle to a single BLOB (binary large object) or TEXT value
|
|
||||||
// opened for incremental I/O. This allows the value to be treated as a file for
|
|
||||||
// reading and writing. The value length cannot be changed using this API; use
|
|
||||||
// an UPDATE statement for that. The recommended way of allocating space for a
|
|
||||||
// BLOB is to use the ZeroBlob type or the zeroblob() SQL function.
|
|
||||||
// [http://www.sqlite.org/c3ref/blob.html]
|
|
||||||
type BlobIO struct {
|
|
||||||
conn *Conn
|
|
||||||
blob *C.sqlite3_blob
|
|
||||||
|
|
||||||
row int64 // ROWID of the row containing the BLOB/TEXT value
|
|
||||||
len int // Value length in bytes
|
|
||||||
off int // Current read/write offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// newBlobIO initializes an incremental I/O operation.
|
|
||||||
func newBlobIO(c *Conn, db, tbl, col string, row int64, rw bool) (*BlobIO, error) {
|
|
||||||
db += "\x00"
|
|
||||||
tbl += "\x00"
|
|
||||||
col += "\x00"
|
|
||||||
|
|
||||||
var blob *C.sqlite3_blob
|
|
||||||
rc := C.sqlite3_blob_open(c.db, cStr(db), cStr(tbl), cStr(col),
|
|
||||||
C.sqlite3_int64(row), cBool(rw), &blob)
|
|
||||||
if rc != OK {
|
|
||||||
return nil, libErr(rc, c.db)
|
|
||||||
}
|
|
||||||
|
|
||||||
b := &BlobIO{
|
|
||||||
conn: c,
|
|
||||||
blob: blob,
|
|
||||||
row: row,
|
|
||||||
len: int(C.sqlite3_blob_bytes(blob)),
|
|
||||||
}
|
|
||||||
runtime.SetFinalizer(b, (*BlobIO).Close)
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close releases all resources associated with the incremental I/O operation.
|
|
||||||
// It is important to check the error returned by this method, since disk I/O
|
|
||||||
// and other types of errors may not be reported until the changes are actually
|
|
||||||
// committed to the database.
|
|
||||||
// [http://www.sqlite.org/c3ref/blob_close.html]
|
|
||||||
func (b *BlobIO) Close() error {
|
|
||||||
if blob := b.blob; blob != nil {
|
|
||||||
b.blob = nil
|
|
||||||
b.len = 0
|
|
||||||
b.off = 0
|
|
||||||
runtime.SetFinalizer(b, nil)
|
|
||||||
if rc := C.sqlite3_blob_close(blob); rc != OK {
|
|
||||||
return libErr(rc, b.conn.db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conn returns the connection that that created this incremental I/O operation.
|
|
||||||
func (b *BlobIO) Conn() *Conn {
|
|
||||||
return b.conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Row returns the ROWID of the row containing the BLOB/TEXT value.
|
|
||||||
func (b *BlobIO) Row() int64 {
|
|
||||||
return b.row
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the length of the BLOB/TEXT value in bytes. It is not possible to
|
|
||||||
// read/write/seek beyond this length. The length changes to 0 if the I/O handle
|
|
||||||
// expires due to an update of any column in the same row. This condition is
|
|
||||||
// indicated by an ABORT error code returned from Read or Write. An expired
|
|
||||||
// handle is closed automatically and cannot be reopened. Any writes that
|
|
||||||
// occurred before the abort are not rolled back.
|
|
||||||
// [http://www.sqlite.org/c3ref/blob_bytes.html]
|
|
||||||
func (b *BlobIO) Len() int {
|
|
||||||
return b.len
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read implements the io.Reader interface.
|
|
||||||
// [http://www.sqlite.org/c3ref/blob_read.html]
|
|
||||||
func (b *BlobIO) Read(p []byte) (n int, err error) {
|
|
||||||
if b.blob == nil {
|
|
||||||
return 0, ErrBadIO
|
|
||||||
}
|
|
||||||
if b.off >= b.len {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
if n = b.len - b.off; len(p) < n {
|
|
||||||
n = len(p)
|
|
||||||
}
|
|
||||||
rc := C.sqlite3_blob_read(b.blob, cBytes(p), C.int(n), C.int(b.off))
|
|
||||||
return b.io(rc, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write implements the io.Writer interface. The number of bytes written is
|
|
||||||
// always either 0 or len(p). ErrBlobFull is returned if there isn't enough
|
|
||||||
// space left to write all of p.
|
|
||||||
// [http://www.sqlite.org/c3ref/blob_write.html]
|
|
||||||
func (b *BlobIO) Write(p []byte) (n int, err error) {
|
|
||||||
if b.blob == nil {
|
|
||||||
return 0, ErrBadIO
|
|
||||||
}
|
|
||||||
if n = len(p); b.off+n > b.len {
|
|
||||||
// Doesn't make sense to do a partial write. Better to return quickly
|
|
||||||
// and let the caller reallocate the BLOB.
|
|
||||||
return 0, ErrBlobFull
|
|
||||||
}
|
|
||||||
rc := C.sqlite3_blob_write(b.blob, cBytes(p), C.int(n), C.int(b.off))
|
|
||||||
return b.io(rc, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seek implements the io.Seeker interface.
|
|
||||||
func (b *BlobIO) Seek(offset int64, whence int) (ret int64, err error) {
|
|
||||||
if b.blob == nil {
|
|
||||||
return 0, ErrBadIO
|
|
||||||
}
|
|
||||||
switch whence {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
offset += int64(b.off)
|
|
||||||
case 2:
|
|
||||||
offset += int64(b.len)
|
|
||||||
default:
|
|
||||||
return 0, pkgErr(MISUSE, "invalid whence for BlobIO.Seek (%d)", whence)
|
|
||||||
}
|
|
||||||
if offset < 0 || offset > int64(b.len) {
|
|
||||||
return 0, pkgErr(MISUSE, "invalid offset for BlobIO.Seek (%d)", offset)
|
|
||||||
}
|
|
||||||
b.off = int(offset)
|
|
||||||
return offset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reopen closes the current value and opens another one in the same column,
|
|
||||||
// specified by its ROWID. If an error is encountered, the I/O handle becomes
|
|
||||||
// unusable and is automatically closed.
|
|
||||||
// [http://www.sqlite.org/c3ref/blob_reopen.html]
|
|
||||||
func (b *BlobIO) Reopen(row int64) error {
|
|
||||||
if b.blob == nil {
|
|
||||||
return ErrBadIO
|
|
||||||
}
|
|
||||||
if rc := C.sqlite3_blob_reopen(b.blob, C.sqlite3_int64(row)); rc != OK {
|
|
||||||
err := libErr(rc, b.conn.db)
|
|
||||||
b.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.row = row
|
|
||||||
b.len = int(C.sqlite3_blob_bytes(b.blob))
|
|
||||||
b.off = 0
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// io handles the completion of a single Read/Write call.
|
|
||||||
func (b *BlobIO) io(rc C.int, n int) (int, error) {
|
|
||||||
if rc == OK {
|
|
||||||
b.off += n
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
err := libErr(rc, b.conn.db)
|
|
||||||
if rc == ABORT {
|
|
||||||
b.Close()
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
#if defined(SQLITE_AMALGAMATION) && defined(SQLITE_HAS_CODEC)
|
|
||||||
|
|
||||||
#include "codec.h"
|
|
||||||
|
|
||||||
// codec.go exports.
|
|
||||||
int go_codec_init(const CodecCtx*,void**,char**);
|
|
||||||
int go_codec_reserve(void*);
|
|
||||||
void go_codec_resize(void*,int,int);
|
|
||||||
void *go_codec_exec(void*,void*,u32,int);
|
|
||||||
void go_codec_get_key(void*,void**,int*);
|
|
||||||
void go_codec_free(void*);
|
|
||||||
|
|
||||||
// sqlite3_key sets the codec key for the main database.
|
|
||||||
SQLITE_API int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) {
|
|
||||||
return sqlite3_key_v2(db, 0, pKey, nKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sqlite3_key_v2 sets the codec key for the specified database.
|
|
||||||
SQLITE_API int sqlite3_key_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey) {
|
|
||||||
int iDb = 0;
|
|
||||||
int rc;
|
|
||||||
sqlite3_mutex_enter(db->mutex);
|
|
||||||
if (zDb && zDb[0]) {
|
|
||||||
iDb = sqlite3FindDbName(db, zDb);
|
|
||||||
}
|
|
||||||
if (iDb < 0) {
|
|
||||||
rc = SQLITE_ERROR;
|
|
||||||
sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb);
|
|
||||||
} else {
|
|
||||||
rc = sqlite3CodecAttach(db, iDb, pKey, nKey);
|
|
||||||
}
|
|
||||||
rc = sqlite3ApiExit(db, rc);
|
|
||||||
sqlite3_mutex_leave(db->mutex);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sqlite3_rekey changes the codec key for the main database.
|
|
||||||
SQLITE_API int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
|
|
||||||
return sqlite3_rekey_v2(db, 0, pKey, nKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sqlite3_rekey_v2 changes the codec key for the specified database.
|
|
||||||
SQLITE_API int sqlite3_rekey_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey) {
|
|
||||||
int iDb = 0;
|
|
||||||
int rc;
|
|
||||||
sqlite3_mutex_enter(db->mutex);
|
|
||||||
|
|
||||||
rc = SQLITE_ERROR;
|
|
||||||
sqlite3Error(db, SQLITE_ERROR, "rekey is not implemented");
|
|
||||||
|
|
||||||
rc = sqlite3ApiExit(db, rc);
|
|
||||||
sqlite3_mutex_leave(db->mutex);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sqlite3_activate_see isn't used by Go codecs, but it needs to be linked in.
|
|
||||||
SQLITE_API void sqlite3_activate_see(const char *zPassPhrase) {}
|
|
||||||
|
|
||||||
// sqlite3CodecAttach initializes the codec, reserves space at the end of each
|
|
||||||
// page, and attaches the codec to the specified database.
|
|
||||||
int sqlite3CodecAttach(sqlite3 *db, int iDb, const void *pKey, int nKey) {
|
|
||||||
Btree *pBt = db->aDb[iDb].pBt;
|
|
||||||
Pager *pPager = sqlite3BtreePager(pBt);
|
|
||||||
CodecCtx ctx;
|
|
||||||
void *pCodec = 0;
|
|
||||||
char *zErrMsg = 0;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
// An empty KEY clause in an ATTACH statement disables the codec and SQLite
|
|
||||||
// doesn't support codecs for in-memory databases.
|
|
||||||
if (nKey <= 0 || pPager->memDb) return SQLITE_OK;
|
|
||||||
|
|
||||||
ctx.db = db;
|
|
||||||
ctx.zPath = sqlite3BtreeGetFilename(pBt);
|
|
||||||
ctx.zName = db->aDb[iDb].zName;
|
|
||||||
ctx.nBuf = sqlite3BtreeGetPageSize(pBt);
|
|
||||||
ctx.nRes = sqlite3BtreeGetReserve(pBt);
|
|
||||||
ctx.pKey = pKey;
|
|
||||||
ctx.nKey = nKey;
|
|
||||||
|
|
||||||
sqlite3BtreeEnter(pBt);
|
|
||||||
ctx.fixed = (pBt->pBt->btsFlags & BTS_PAGESIZE_FIXED) != 0;
|
|
||||||
sqlite3BtreeLeave(pBt);
|
|
||||||
|
|
||||||
if ((rc=go_codec_init(&ctx, &pCodec, &zErrMsg)) != SQLITE_OK) {
|
|
||||||
sqlite3Error(db, rc, (zErrMsg ? "%s" : 0), zErrMsg);
|
|
||||||
free(zErrMsg);
|
|
||||||
} else if (pCodec) {
|
|
||||||
int nRes = go_codec_reserve(pCodec);
|
|
||||||
if (nRes != ctx.nRes && nRes >= 0) {
|
|
||||||
rc = sqlite3BtreeSetPageSize(pBt, -1, nRes, 0);
|
|
||||||
}
|
|
||||||
if (rc != SQLITE_OK) {
|
|
||||||
go_codec_free(pCodec);
|
|
||||||
sqlite3Error(db, rc, "unable to reserve page space for the codec");
|
|
||||||
} else {
|
|
||||||
sqlite3PagerSetCodec(pPager, go_codec_exec, go_codec_resize, go_codec_free, pCodec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sqlite3CodecGetKey returns the codec key for the specified database.
|
|
||||||
void sqlite3CodecGetKey(sqlite3 *db, int iDb, void **pKey, int *nKey) {
|
|
||||||
void *pCodec = sqlite3PagerGetCodec(sqlite3BtreePager(db->aDb[iDb].pBt));
|
|
||||||
*pKey = 0;
|
|
||||||
*nKey = 0;
|
|
||||||
if (pCodec) {
|
|
||||||
go_codec_get_key(pCodec, pKey, nKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
#ifndef _CODEC_H_
|
|
||||||
#define _CODEC_H_
|
|
||||||
|
|
||||||
// Codec initialization context.
|
|
||||||
typedef struct {
|
|
||||||
sqlite3 *db;
|
|
||||||
const char *zPath;
|
|
||||||
const char *zName;
|
|
||||||
int nBuf;
|
|
||||||
int nRes;
|
|
||||||
int fixed;
|
|
||||||
const void *pKey;
|
|
||||||
int nKey;
|
|
||||||
} CodecCtx;
|
|
||||||
|
|
||||||
// SQLite codec hooks.
|
|
||||||
int sqlite3CodecAttach(sqlite3*,int,const void*,int);
|
|
||||||
void sqlite3CodecGetKey(sqlite3*,int,void**,int*);
|
|
||||||
|
|
||||||
#endif
|
|
141343
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/lib/sqlite3.c
generated
vendored
141343
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/lib/sqlite3.c
generated
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "lib/sqlite3.c"
|
|
||||||
#include "lib/codec.c"
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "lib/sqlite3.h"
|
|
||||||
#include "lib/codec.h"
|
|
1307
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/sqlite3_test.go
generated
vendored
1307
Godeps/_workspace/src/code.google.com/p/go-sqlite/go1/sqlite3/sqlite3_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
|
@ -1,375 +0,0 @@
|
||||||
// Copyright 2013 The Go-SQLite Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include "sqlite3.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NamedArgs is a name/value map of arguments passed to a prepared statement
|
|
||||||
// that uses ?NNN, :AAA, @AAA, and/or $AAA parameter formats. Name matching is
|
|
||||||
// case-sensitive and the prefix character (one of [?:@$]) must be included in
|
|
||||||
// the name. Names that are missing from the map are treated as NULL. Names that
|
|
||||||
// are not used in the prepared statement are ignored.
|
|
||||||
//
|
|
||||||
// It is not possible to mix named and anonymous ("?") parameters in the same
|
|
||||||
// statement.
|
|
||||||
// [http://www.sqlite.org/lang_expr.html#varparam]
|
|
||||||
type NamedArgs map[string]interface{}
|
|
||||||
|
|
||||||
// RowMap may be passed as the last (or only) argument to Stmt.Scan to create a
|
|
||||||
// map of all remaining column/value pairs in the current row. The map is not
|
|
||||||
// cleared before being populated with new column values. Assignment is
|
|
||||||
// performed in left-to-right column order, and values may be overwritten if the
|
|
||||||
// query returns two or more columns with identical names.
|
|
||||||
type RowMap map[string]interface{}
|
|
||||||
|
|
||||||
// RawString and RawBytes are special string and []byte types that may be used
|
|
||||||
// for database input and output without the cost of an extra copy operation.
|
|
||||||
//
|
|
||||||
// When used as an argument to a statement, the contents are bound using
|
|
||||||
// SQLITE_STATIC instead of SQLITE_TRANSIENT flag. This requires the contents to
|
|
||||||
// remain valid and unmodified until the end of statement execution. In
|
|
||||||
// particular, the caller must keep a reference to the value to prevent it from
|
|
||||||
// being garbage collected.
|
|
||||||
//
|
|
||||||
// When used for retrieving query output, the internal string/[]byte pointer is
|
|
||||||
// set to reference memory belonging to SQLite. The memory remains valid until
|
|
||||||
// another method is called on the Stmt object and should not be modified.
|
|
||||||
type (
|
|
||||||
RawString string
|
|
||||||
RawBytes []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
// Copy returns a Go-managed copy of s.
|
|
||||||
func (s RawString) Copy() string {
|
|
||||||
if s == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
h := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
return C.GoStringN((*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a Go-managed copy of b.
|
|
||||||
func (b RawBytes) Copy() []byte {
|
|
||||||
if len(b) == 0 {
|
|
||||||
if b == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return []byte("")
|
|
||||||
}
|
|
||||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
|
||||||
return C.GoBytes(unsafe.Pointer(h.Data), C.int(h.Len))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ZeroBlob is a special argument type used to allocate a zero-filled BLOB of
|
|
||||||
// the specified length. The BLOB can then be opened for incremental I/O to
|
|
||||||
// efficiently transfer a large amount of data. The maximum BLOB size can be
|
|
||||||
// queried with Conn.Limit(LIMIT_LENGTH, -1).
|
|
||||||
type ZeroBlob int
|
|
||||||
|
|
||||||
// BusyFunc is a callback function invoked by SQLite when it is unable to
|
|
||||||
// acquire a lock on a table. Count is the number of times that the callback has
|
|
||||||
// been invoked for this locking event so far. If the function returns false,
|
|
||||||
// then the operation is aborted. Otherwise, the function should block for a
|
|
||||||
// while before returning true and letting SQLite make another locking attempt.
|
|
||||||
type BusyFunc func(count int) (retry bool)
|
|
||||||
|
|
||||||
// CommitFunc is a callback function invoked by SQLite before a transaction is
|
|
||||||
// committed. If the function returns true, the transaction is rolled back.
|
|
||||||
type CommitFunc func() (abort bool)
|
|
||||||
|
|
||||||
// RollbackFunc is a callback function invoked by SQLite when a transaction is
|
|
||||||
// rolled back.
|
|
||||||
type RollbackFunc func()
|
|
||||||
|
|
||||||
// UpdateFunc is a callback function invoked by SQLite when a row is updated,
|
|
||||||
// inserted, or deleted.
|
|
||||||
type UpdateFunc func(op int, db, tbl RawString, row int64)
|
|
||||||
|
|
||||||
// Error is returned for all SQLite API result codes other than OK, ROW, and
|
|
||||||
// DONE.
|
|
||||||
type Error struct {
|
|
||||||
rc int
|
|
||||||
msg string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewError creates a new Error instance using the specified SQLite result code
|
|
||||||
// and error message.
|
|
||||||
func NewError(rc int, msg string) *Error {
|
|
||||||
return &Error{rc, msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
// libErr reports an error originating in SQLite. The error message is obtained
|
|
||||||
// from the database connection when possible, which may include some additional
|
|
||||||
// information. Otherwise, the result code is translated to a generic message.
|
|
||||||
func libErr(rc C.int, db *C.sqlite3) error {
|
|
||||||
if db != nil && rc == C.sqlite3_errcode(db) {
|
|
||||||
return &Error{int(rc), C.GoString(C.sqlite3_errmsg(db))}
|
|
||||||
}
|
|
||||||
return &Error{int(rc), C.GoString(C.sqlite3_errstr(rc))}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pkgErr reports an error originating in this package.
|
|
||||||
func pkgErr(rc int, msg string, v ...interface{}) error {
|
|
||||||
if len(v) == 0 {
|
|
||||||
return &Error{rc, msg}
|
|
||||||
}
|
|
||||||
return &Error{rc, fmt.Sprintf(msg, v...)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code returns the SQLite extended result code.
|
|
||||||
func (err *Error) Code() int {
|
|
||||||
return err.rc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error implements the error interface.
|
|
||||||
func (err *Error) Error() string {
|
|
||||||
return fmt.Sprintf("sqlite3: %s [%d]", err.msg, err.rc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errors returned for access attempts to closed or invalid objects.
|
|
||||||
var (
|
|
||||||
ErrBadConn = &Error{MISUSE, "closed or invalid connection"}
|
|
||||||
ErrBadStmt = &Error{MISUSE, "closed or invalid statement"}
|
|
||||||
ErrBadIO = &Error{MISUSE, "closed or invalid incremental I/O operation"}
|
|
||||||
ErrBadBackup = &Error{MISUSE, "closed or invalid backup operation"}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Complete returns true if sql appears to contain a complete statement that is
|
|
||||||
// ready to be parsed. This does not validate the statement syntax.
|
|
||||||
// [http://www.sqlite.org/c3ref/complete.html]
|
|
||||||
func Complete(sql string) bool {
|
|
||||||
if initErr != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
sql += "\x00"
|
|
||||||
return C.sqlite3_complete(cStr(sql)) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReleaseMemory attempts to free n bytes of heap memory by deallocating
|
|
||||||
// non-essential memory held by the SQLite library. It returns the number of
|
|
||||||
// bytes actually freed.
|
|
||||||
//
|
|
||||||
// This function is currently a no-op because SQLite is not compiled with the
|
|
||||||
// SQLITE_ENABLE_MEMORY_MANAGEMENT option.
|
|
||||||
// [http://www.sqlite.org/c3ref/release_memory.html]
|
|
||||||
func ReleaseMemory(n int) int {
|
|
||||||
if initErr != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(C.sqlite3_release_memory(C.int(n)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SingleThread returns true if the SQLite library was compiled with
|
|
||||||
// -DSQLITE_THREADSAFE=0. In this threading mode all mutex code is omitted and
|
|
||||||
// the package becomes unsafe for concurrent access, even to separate database
|
|
||||||
// connections.
|
|
||||||
//
|
|
||||||
// The SQLite source that's part of this package is compiled with
|
|
||||||
// -DSQLITE_THREADSAFE=2, so this function should always return false. It is
|
|
||||||
// kept for backward compatibility when dynamic linking was supported in Go 1.0.
|
|
||||||
// [http://www.sqlite.org/threadsafe.html]
|
|
||||||
func SingleThread() bool {
|
|
||||||
return initErr == nil && C.sqlite3_threadsafe() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SoftHeapLimit sets and/or queries the soft limit on the amount of heap memory
|
|
||||||
// that may be allocated by SQLite. A negative value for n keeps the current
|
|
||||||
// limit, while 0 removes the limit. The previous limit value is returned, with
|
|
||||||
// negative values indicating an error.
|
|
||||||
// [http://www.sqlite.org/c3ref/soft_heap_limit64.html]
|
|
||||||
func SoftHeapLimit(n int64) int64 {
|
|
||||||
if initErr != nil {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return int64(C.sqlite3_soft_heap_limit64(C.sqlite3_int64(n)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SourceId returns the check-in identifier of SQLite within its configuration
|
|
||||||
// management system.
|
|
||||||
// [http://www.sqlite.org/c3ref/c_source_id.html]
|
|
||||||
func SourceId() string {
|
|
||||||
if initErr != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return C.GoString(C.sqlite3_sourceid())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status returns the current and peak values of a core performance
|
|
||||||
// counter, specified by one of the STATUS constants. If reset is true, the peak
|
|
||||||
// value is reset back down to the current value after retrieval.
|
|
||||||
// [http://www.sqlite.org/c3ref/status.html]
|
|
||||||
func Status(op int, reset bool) (cur, peak int, err error) {
|
|
||||||
if initErr != nil {
|
|
||||||
return 0, 0, initErr
|
|
||||||
}
|
|
||||||
var cCur, cPeak C.int
|
|
||||||
rc := C.sqlite3_status(C.int(op), &cCur, &cPeak, cBool(reset))
|
|
||||||
if rc != OK {
|
|
||||||
return 0, 0, pkgErr(MISUSE, "invalid status op (%d)", op)
|
|
||||||
}
|
|
||||||
return int(cCur), int(cPeak), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version returns the SQLite version as a string in the format "X.Y.Z[.N]".
|
|
||||||
// [http://www.sqlite.org/c3ref/libversion.html]
|
|
||||||
func Version() string {
|
|
||||||
if initErr != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return goStr(C.sqlite3_libversion())
|
|
||||||
}
|
|
||||||
|
|
||||||
// VersionNum returns the SQLite version as an integer in the format X*1000000 +
|
|
||||||
// Y*1000 + Z, where X is the major version, Y is the minor version, and Z is
|
|
||||||
// the release number.
|
|
||||||
func VersionNum() int {
|
|
||||||
if initErr != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(C.sqlite3_libversion_number())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print prints out all rows returned by a query. This function is intended as a
|
|
||||||
// debugging aid and may be removed or altered in the future. Do not use it in
|
|
||||||
// production applications.
|
|
||||||
func Print(s *Stmt) error {
|
|
||||||
if s == nil || s.NumColumns() == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
if !s.Busy() {
|
|
||||||
if err = s.Query(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cols := s.Columns()
|
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, len(cols)*10))
|
|
||||||
row := make(RowMap, len(cols))
|
|
||||||
|
|
||||||
buf.WriteByte('~')
|
|
||||||
for _, col := range cols {
|
|
||||||
fmt.Fprintf(buf, " %s ~", col)
|
|
||||||
}
|
|
||||||
fmt.Println(buf)
|
|
||||||
for ; err == nil; err = s.Next() {
|
|
||||||
if err = s.Scan(row); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf.Reset()
|
|
||||||
buf.WriteByte('|')
|
|
||||||
for _, col := range cols {
|
|
||||||
fmt.Fprintf(buf, " %*v |", len(col), row[col])
|
|
||||||
}
|
|
||||||
fmt.Println(buf)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// raw casts s to a RawString.
|
|
||||||
func raw(s string) RawString {
|
|
||||||
return RawString(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cStr returns a pointer to the first byte in s.
|
|
||||||
func cStr(s string) *C.char {
|
|
||||||
h := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
return (*C.char)(unsafe.Pointer(h.Data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// cStrOffset returns the offset of p in s or -1 if p doesn't point into s.
|
|
||||||
func cStrOffset(s string, p *C.char) int {
|
|
||||||
h := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
if off := uintptr(unsafe.Pointer(p)) - h.Data; off < uintptr(h.Len) {
|
|
||||||
return int(off)
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// cBytes returns a pointer to the first byte in b.
|
|
||||||
func cBytes(b []byte) unsafe.Pointer {
|
|
||||||
return unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&b)).Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cBool returns a C representation of a Go bool (false = 0, true = 1).
|
|
||||||
func cBool(b bool) C.int {
|
|
||||||
if b {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// goStr returns a Go representation of a null-terminated C string.
|
|
||||||
func goStr(p *C.char) (s string) {
|
|
||||||
if p != nil && *p != 0 {
|
|
||||||
h := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
h.Data = uintptr(unsafe.Pointer(p))
|
|
||||||
for *p != 0 {
|
|
||||||
p = (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1)) // p++
|
|
||||||
}
|
|
||||||
h.Len = int(uintptr(unsafe.Pointer(p)) - h.Data)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// goStrN returns a Go representation of an n-byte C string.
|
|
||||||
func goStrN(p *C.char, n C.int) (s string) {
|
|
||||||
if n > 0 {
|
|
||||||
h := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
h.Data = uintptr(unsafe.Pointer(p))
|
|
||||||
h.Len = int(n)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// goBytes returns a Go representation of an n-byte C array.
|
|
||||||
func goBytes(p unsafe.Pointer, n C.int) (b []byte) {
|
|
||||||
if n > 0 {
|
|
||||||
h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
|
||||||
h.Data = uintptr(p)
|
|
||||||
h.Len = int(n)
|
|
||||||
h.Cap = int(n)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// bstr returns a string pointing into the byte slice b.
|
|
||||||
func bstr(b []byte) (s string) {
|
|
||||||
if len(b) > 0 {
|
|
||||||
h := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
h.Data = uintptr(unsafe.Pointer(&b[0]))
|
|
||||||
h.Len = len(b)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_busy_handler
|
|
||||||
func go_busy_handler(c unsafe.Pointer, count C.int) (retry C.int) {
|
|
||||||
return cBool((*Conn)(c).busy(int(count)))
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_commit_hook
|
|
||||||
func go_commit_hook(c unsafe.Pointer) (abort C.int) {
|
|
||||||
return cBool((*Conn)(c).commit())
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_rollback_hook
|
|
||||||
func go_rollback_hook(c unsafe.Pointer) {
|
|
||||||
(*Conn)(c).rollback()
|
|
||||||
}
|
|
||||||
|
|
||||||
//export go_update_hook
|
|
||||||
func go_update_hook(c unsafe.Pointer, op C.int, db, tbl *C.char, row C.sqlite3_int64) {
|
|
||||||
(*Conn)(c).update(int(op), raw(goStr(db)), raw(goStr(tbl)), int64(row))
|
|
||||||
}
|
|
498
Godeps/_workspace/src/code.google.com/p/gosqlite/sqlite3/driver.go
generated
vendored
Normal file
498
Godeps/_workspace/src/code.google.com/p/gosqlite/sqlite3/driver.go
generated
vendored
Normal file
|
@ -0,0 +1,498 @@
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package sqlite3 provides access to the SQLite library, version 3.
|
||||||
|
//
|
||||||
|
// The package has no exported API.
|
||||||
|
// It registers a driver for the standard Go database/sql package.
|
||||||
|
//
|
||||||
|
// import _ "code.google.com/p/gosqlite/sqlite3"
|
||||||
|
//
|
||||||
|
// (For an alternate, earlier API, see the code.google.com/p/gosqlite/sqlite package.)
|
||||||
|
package sqlite
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -lsqlite3
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// These wrappers are necessary because SQLITE_TRANSIENT
|
||||||
|
// is a pointer constant, and cgo doesn't translate them correctly.
|
||||||
|
// The definition in sqlite3.h is:
|
||||||
|
//
|
||||||
|
// typedef void (*sqlite3_destructor_type)(void*);
|
||||||
|
// #define SQLITE_STATIC ((sqlite3_destructor_type)0)
|
||||||
|
// #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);
|
||||||
|
}
|
||||||
|
static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
|
||||||
|
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sql.Register("sqlite3", impl{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type errno int
|
||||||
|
|
||||||
|
func (e errno) Error() string {
|
||||||
|
s := errText[e]
|
||||||
|
if s == "" {
|
||||||
|
return fmt.Sprintf("errno %d", int(e))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errError error = errno(1) // /* SQL error or missing database */
|
||||||
|
errInternal error = errno(2) // /* Internal logic error in SQLite */
|
||||||
|
errPerm error = errno(3) // /* Access permission denied */
|
||||||
|
errAbort error = errno(4) // /* Callback routine requested an abort */
|
||||||
|
errBusy error = errno(5) // /* The database file is locked */
|
||||||
|
errLocked error = errno(6) // /* A table in the database is locked */
|
||||||
|
errNoMem error = errno(7) // /* A malloc() failed */
|
||||||
|
errReadOnly error = errno(8) // /* Attempt to write a readonly database */
|
||||||
|
errInterrupt error = errno(9) // /* Operation terminated by sqlite3_interrupt()*/
|
||||||
|
errIOErr error = errno(10) // /* Some kind of disk I/O error occurred */
|
||||||
|
errCorrupt error = errno(11) // /* The database disk image is malformed */
|
||||||
|
errFull error = errno(13) // /* Insertion failed because database is full */
|
||||||
|
errCantOpen error = errno(14) // /* Unable to open the database file */
|
||||||
|
errEmpty error = errno(16) // /* Database is empty */
|
||||||
|
errSchema error = errno(17) // /* The database schema changed */
|
||||||
|
errTooBig error = errno(18) // /* String or BLOB exceeds size limit */
|
||||||
|
errConstraint error = errno(19) // /* Abort due to constraint violation */
|
||||||
|
errMismatch error = errno(20) // /* Data type mismatch */
|
||||||
|
errMisuse error = errno(21) // /* Library used incorrectly */
|
||||||
|
errNolfs error = errno(22) // /* Uses OS features not supported on host */
|
||||||
|
errAuth error = errno(23) // /* Authorization denied */
|
||||||
|
errFormat error = errno(24) // /* Auxiliary database format error */
|
||||||
|
errRange error = errno(25) // /* 2nd parameter to sqlite3_bind out of range */
|
||||||
|
errNotDB error = errno(26) // /* File opened that is not a database file */
|
||||||
|
stepRow = errno(100) // /* sqlite3_step() has another row ready */
|
||||||
|
stepDone = errno(101) // /* sqlite3_step() has finished executing */
|
||||||
|
)
|
||||||
|
|
||||||
|
var errText = map[errno]string{
|
||||||
|
1: "SQL error or missing database",
|
||||||
|
2: "Internal logic error in SQLite",
|
||||||
|
3: "Access permission denied",
|
||||||
|
4: "Callback routine requested an abort",
|
||||||
|
5: "The database file is locked",
|
||||||
|
6: "A table in the database is locked",
|
||||||
|
7: "A malloc() failed",
|
||||||
|
8: "Attempt to write a readonly database",
|
||||||
|
9: "Operation terminated by sqlite3_interrupt()*/",
|
||||||
|
10: "Some kind of disk I/O error occurred",
|
||||||
|
11: "The database disk image is malformed",
|
||||||
|
12: "NOT USED. Table or record not found",
|
||||||
|
13: "Insertion failed because database is full",
|
||||||
|
14: "Unable to open the database file",
|
||||||
|
15: "NOT USED. Database lock protocol error",
|
||||||
|
16: "Database is empty",
|
||||||
|
17: "The database schema changed",
|
||||||
|
18: "String or BLOB exceeds size limit",
|
||||||
|
19: "Abort due to constraint violation",
|
||||||
|
20: "Data type mismatch",
|
||||||
|
21: "Library used incorrectly",
|
||||||
|
22: "Uses OS features not supported on host",
|
||||||
|
23: "Authorization denied",
|
||||||
|
24: "Auxiliary database format error",
|
||||||
|
25: "2nd parameter to sqlite3_bind out of range",
|
||||||
|
26: "File opened that is not a database file",
|
||||||
|
100: "sqlite3_step() has another row ready",
|
||||||
|
101: "sqlite3_step() has finished executing",
|
||||||
|
}
|
||||||
|
|
||||||
|
type impl struct{}
|
||||||
|
|
||||||
|
func (impl) Open(name string) (driver.Conn, error) {
|
||||||
|
if C.sqlite3_threadsafe() == 0 {
|
||||||
|
return nil, errors.New("sqlite library was not compiled for thread-safe operation")
|
||||||
|
}
|
||||||
|
|
||||||
|
var db *C.sqlite3
|
||||||
|
cname := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
rv := C.sqlite3_open_v2(cname, &db,
|
||||||
|
C.SQLITE_OPEN_FULLMUTEX|
|
||||||
|
C.SQLITE_OPEN_READWRITE|
|
||||||
|
C.SQLITE_OPEN_CREATE,
|
||||||
|
nil)
|
||||||
|
if rv != 0 {
|
||||||
|
return nil, errno(rv)
|
||||||
|
}
|
||||||
|
if db == nil {
|
||||||
|
return nil, errors.New("sqlite succeeded without returning a database")
|
||||||
|
}
|
||||||
|
return &conn{db: db}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type conn struct {
|
||||||
|
db *C.sqlite3
|
||||||
|
closed bool
|
||||||
|
tx bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) error(rv C.int) error {
|
||||||
|
if rv == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if rv == 21 || c.closed {
|
||||||
|
return errno(rv)
|
||||||
|
}
|
||||||
|
return errors.New(errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Prepare(cmd string) (driver.Stmt, error) {
|
||||||
|
if c.closed {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Prepare after Close")
|
||||||
|
}
|
||||||
|
cmdstr := C.CString(cmd)
|
||||||
|
defer C.free(unsafe.Pointer(cmdstr))
|
||||||
|
var s *C.sqlite3_stmt
|
||||||
|
var tail *C.char
|
||||||
|
rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &s, &tail)
|
||||||
|
if rv != 0 {
|
||||||
|
return nil, c.error(rv)
|
||||||
|
}
|
||||||
|
return &stmt{c: c, stmt: s, sql: cmd, t0: time.Now()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Close() error {
|
||||||
|
if c.closed {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: multiple Close")
|
||||||
|
}
|
||||||
|
c.closed = true
|
||||||
|
rv := C.sqlite3_close(c.db)
|
||||||
|
c.db = nil
|
||||||
|
return c.error(rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) exec(cmd string) error {
|
||||||
|
cstring := C.CString(cmd)
|
||||||
|
defer C.free(unsafe.Pointer(cstring))
|
||||||
|
rv := C.sqlite3_exec(c.db, cstring, nil, nil, nil)
|
||||||
|
return c.error(rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conn) Begin() (driver.Tx, error) {
|
||||||
|
if c.tx {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: multiple Tx")
|
||||||
|
}
|
||||||
|
if err := c.exec("BEGIN TRANSACTION"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.tx = true
|
||||||
|
return &tx{c}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tx struct {
|
||||||
|
c *conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tx) Commit() error {
|
||||||
|
if t.c == nil || !t.c.tx {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: extra Commit")
|
||||||
|
}
|
||||||
|
t.c.tx = false
|
||||||
|
err := t.c.exec("COMMIT TRANSACTION")
|
||||||
|
t.c = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tx) Rollback() error {
|
||||||
|
if t.c == nil || !t.c.tx {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: extra Rollback")
|
||||||
|
}
|
||||||
|
t.c.tx = false
|
||||||
|
err := t.c.exec("ROLLBACK")
|
||||||
|
t.c = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type stmt struct {
|
||||||
|
c *conn
|
||||||
|
stmt *C.sqlite3_stmt
|
||||||
|
err error
|
||||||
|
t0 time.Time
|
||||||
|
sql string
|
||||||
|
args string
|
||||||
|
closed bool
|
||||||
|
rows bool
|
||||||
|
colnames []string
|
||||||
|
coltypes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stmt) Close() error {
|
||||||
|
if s.rows {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Close with active Rows")
|
||||||
|
}
|
||||||
|
if s.closed {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: double Close of Stmt")
|
||||||
|
}
|
||||||
|
s.closed = true
|
||||||
|
rv := C.sqlite3_finalize(s.stmt)
|
||||||
|
if rv != 0 {
|
||||||
|
return s.c.error(rv)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stmt) NumInput() int {
|
||||||
|
if s.closed {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: NumInput after Close")
|
||||||
|
}
|
||||||
|
return int(C.sqlite3_bind_parameter_count(s.stmt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stmt) reset() error {
|
||||||
|
return s.c.error(C.sqlite3_reset(s.stmt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stmt) start(args []driver.Value) error {
|
||||||
|
if err := s.reset(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n := int(C.sqlite3_bind_parameter_count(s.stmt))
|
||||||
|
if n != len(args) {
|
||||||
|
return fmt.Errorf("incorrect argument count for command: have %d want %d", len(args), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range args {
|
||||||
|
var str string
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil:
|
||||||
|
if rv := C.sqlite3_bind_null(s.stmt, C.int(i+1)); rv != 0 {
|
||||||
|
return s.c.error(rv)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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:
|
||||||
|
var vi int64
|
||||||
|
if v {
|
||||||
|
vi = 1
|
||||||
|
}
|
||||||
|
if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(vi)); rv != 0 {
|
||||||
|
return s.c.error(rv)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
|
||||||
|
case time.Time:
|
||||||
|
str = v.UTC().Format(timefmt[0])
|
||||||
|
|
||||||
|
case string:
|
||||||
|
str = v
|
||||||
|
|
||||||
|
default:
|
||||||
|
str = fmt.Sprint(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||||
|
if s.closed {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Exec after Close")
|
||||||
|
}
|
||||||
|
if s.rows {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Exec with active Rows")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.start(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := C.sqlite3_step(s.stmt)
|
||||||
|
if errno(rv) != stepDone {
|
||||||
|
if rv == 0 {
|
||||||
|
rv = 21 // errMisuse
|
||||||
|
}
|
||||||
|
return nil, s.c.error(rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
id := int64(C.sqlite3_last_insert_rowid(s.c.db))
|
||||||
|
rows := int64(C.sqlite3_changes(s.c.db))
|
||||||
|
return &result{id, rows}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||||
|
if s.closed {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Query after Close")
|
||||||
|
}
|
||||||
|
if s.rows {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Query with active Rows")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.start(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.rows = true
|
||||||
|
if s.colnames == nil {
|
||||||
|
n := int64(C.sqlite3_column_count(s.stmt))
|
||||||
|
s.colnames = make([]string, n)
|
||||||
|
s.coltypes = make([]string, n)
|
||||||
|
for i := range s.colnames {
|
||||||
|
s.colnames[i] = C.GoString(C.sqlite3_column_name(s.stmt, C.int(i)))
|
||||||
|
s.coltypes[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.stmt, C.int(i))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &rows{s}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type rows struct {
|
||||||
|
s *stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rows) Columns() []string {
|
||||||
|
if r.s == nil {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Columns of closed Rows")
|
||||||
|
}
|
||||||
|
return r.s.colnames
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxslice = 1<<31 - 1
|
||||||
|
|
||||||
|
var timefmt = []string{
|
||||||
|
"2006-01-02 15:04:05.999999999",
|
||||||
|
"2006-01-02T15:04:05.999999999",
|
||||||
|
"2006-01-02 15:04:05",
|
||||||
|
"2006-01-02T15:04:05",
|
||||||
|
"2006-01-02 15:04",
|
||||||
|
"2006-01-02T15:04",
|
||||||
|
"2006-01-02",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rows) Next(dst []driver.Value) error {
|
||||||
|
if r.s == nil {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Next of closed Rows")
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := C.sqlite3_step(r.s.stmt)
|
||||||
|
if errno(rv) != stepRow {
|
||||||
|
if errno(rv) == stepDone {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
if rv == 0 {
|
||||||
|
rv = 21
|
||||||
|
}
|
||||||
|
return r.s.c.error(rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range dst {
|
||||||
|
switch typ := C.sqlite3_column_type(r.s.stmt, C.int(i)); typ {
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected sqlite3 column type %d", typ)
|
||||||
|
case C.SQLITE_INTEGER:
|
||||||
|
val := int64(C.sqlite3_column_int64(r.s.stmt, C.int(i)))
|
||||||
|
switch r.s.coltypes[i] {
|
||||||
|
case "timestamp", "datetime":
|
||||||
|
dst[i] = time.Unix(val, 0).UTC()
|
||||||
|
case "boolean":
|
||||||
|
dst[i] = val > 0
|
||||||
|
default:
|
||||||
|
dst[i] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
case C.SQLITE_FLOAT:
|
||||||
|
dst[i] = float64(C.sqlite3_column_double(r.s.stmt, C.int(i)))
|
||||||
|
|
||||||
|
case C.SQLITE_BLOB, C.SQLITE_TEXT:
|
||||||
|
n := int(C.sqlite3_column_bytes(r.s.stmt, C.int(i)))
|
||||||
|
var b []byte
|
||||||
|
if n > 0 {
|
||||||
|
p := C.sqlite3_column_blob(r.s.stmt, C.int(i))
|
||||||
|
b = (*[maxslice]byte)(unsafe.Pointer(p))[:n]
|
||||||
|
}
|
||||||
|
dst[i] = b
|
||||||
|
switch r.s.coltypes[i] {
|
||||||
|
case "timestamp", "datetime":
|
||||||
|
dst[i] = time.Time{}
|
||||||
|
s := string(b)
|
||||||
|
for _, f := range timefmt {
|
||||||
|
if t, err := time.Parse(f, s); err == nil {
|
||||||
|
dst[i] = t
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case C.SQLITE_NULL:
|
||||||
|
dst[i] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rows) Close() error {
|
||||||
|
if r.s == nil {
|
||||||
|
panic("database/sql/driver: misuse of sqlite driver: Close of closed Rows")
|
||||||
|
}
|
||||||
|
r.s.rows = false
|
||||||
|
r.s = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type result struct {
|
||||||
|
id int64
|
||||||
|
rows int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *result) LastInsertId() (int64, error) {
|
||||||
|
return r.id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *result) RowsAffected() (int64, error) {
|
||||||
|
return r.rows, nil
|
||||||
|
}
|
Loading…
Reference in New Issue