├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── examples └── basic │ ├── .gitignore │ ├── main │ └── main.go └── sqlite3 ├── backup.go ├── const.go ├── io.go ├── session.go ├── session_test.go ├── sqlite3.c ├── sqlite3.go ├── sqlite3.h ├── sqlite3_test.go ├── upgrading.md └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | /TODO -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.9.x 4 | - 1.10.x 5 | - 1.11.x 6 | - 1.x 7 | os: 8 | - linux 9 | - osx 10 | install: 11 | - go get github.com/mattn/goveralls 12 | script: 13 | - $GOPATH/bin/goveralls -service=travis-ci -ignore=examples/*/* 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The go-sqlite-lite Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the 13 | distribution. 14 | 15 | * Neither the name of the go-sqlite-lite project nor the names of its 16 | contributors may be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/bvinc/go-sqlite-lite/sqlite3?status.svg)](https://godoc.org/github.com/bvinc/go-sqlite-lite/sqlite3) 2 | [![Build Status](https://travis-ci.com/bvinc/go-sqlite-lite.svg?branch=master)](https://travis-ci.com/bvinc/go-sqlite-lite) 3 | [![Build status](https://ci.appveyor.com/api/projects/status/xk6fpk23wb5ppdhx?svg=true)](https://ci.appveyor.com/project/bvinc/go-sqlite-lite) 4 | [![Coverage Status](https://coveralls.io/repos/github/bvinc/go-sqlite-lite/badge.svg?branch=master)](https://coveralls.io/github/bvinc/go-sqlite-lite?branch=master) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/bvinc/go-sqlite-lite)](https://goreportcard.com/report/github.com/bvinc/go-sqlite-lite) 6 | 7 | # go-sqlite-lite 8 | 9 | go-sqlite-lite is a SQLite driver for the Go programming language. It is designed with the following goals in mind. 10 | 11 | * **Lightweight** - Most methods should be little more than a small wrapper around SQLite C functions. 12 | * **Performance** - Where possible, methods should be available to allow for the highest performance possible. 13 | * **Understandable** - You should always know what SQLite functions are being called and in what order. 14 | * **Unsurprising** - Connections, PRAGMAs, transactions, bindings, and stepping should work out of the box exactly as you would expect with SQLite. 15 | * **Debuggable** - When you encounter a SQLite error, the SQLite documentation should be relevant and relatable to the Go code. 16 | * **Ergonomic** - Where it makes sense, convenient compound methods should exist to make tasks easy and to conform to Go standard interfaces. 17 | 18 | Most database drivers include a layer to work nicely with the Go `database/sql` interface, which introduces connection pooling and behavior differences from pure SQLite. This driver does not include a `database/sql` interface. 19 | 20 | ## Releases 21 | 22 | * 2019-05-01 **v0.6.1** - Bug fixes, authorizer callback support 23 | * 2019-05-01 **v0.6.0** - SQLite version 3.28.0 24 | * 2019-02-05 **v0.5.0** - SQLite version 3.26.0 25 | * 2018-10-30 **v0.4.2** - Better error messages from SQLite 26 | * 2018-10-11 **v0.4.1** - Fixed an issue with new go 1.11 modules 27 | * 2018-09-29 **v0.4.0** - SQLite version 3.25.2. Add support for the Session extension 28 | * 2018-09-16 **v0.3.1** - Forgot to update sqlite3.h 29 | * 2018-09-16 **v0.3.0** - SQLite version 3.25.0 30 | * 2018-09-14 **v0.2.0** - Proper error and NULL handling on Column* methods. Empty blobs and empty strings are now distinct from NULL in all cases. A nil byte slice is interpreted as NULL for binding purposes as well as Column* methods. 31 | * 2018-09-01 **v0.1.2** - Added Column methods to Stmt, and WithTx methods to Conn 32 | * 2018-08-25 **v0.1.1** - Fixed linking on some Linux systems 33 | * 2018-08-21 **v0.1.0** - SQLite version 3.24.0 34 | 35 | ## Getting started 36 | 37 | ```go 38 | import "github.com/bvinc/go-sqlite-lite/sqlite3" 39 | ``` 40 | 41 | ### Acquiring a connection 42 | ```go 43 | conn, err := sqlite3.Open("mydatabase.db") 44 | if err != nil { 45 | ... 46 | } 47 | defer conn.Close() 48 | 49 | // It's always a good idea to set a busy timeout 50 | conn.BusyTimeout(5 * time.Second) 51 | ``` 52 | 53 | ### Executing SQL 54 | ```go 55 | err = conn.Exec(`CREATE TABLE student(name TEXT, age INTEGER)`) 56 | if err != nil { 57 | ... 58 | } 59 | // Exec can optionally bind parameters 60 | err = conn.Exec(`INSERT INTO student VALUES (?, ?)`, "Bob", 18) 61 | if err != nil { 62 | ... 63 | } 64 | ``` 65 | 66 | ### Using Prepared Statements 67 | ```go 68 | stmt, err := conn.Prepare(`INSERT INTO student VALUES (?, ?)`) 69 | if err != nil { 70 | ... 71 | } 72 | defer stmt.Close() 73 | 74 | // Bind the arguments 75 | err = stmt.Bind("Bill", 18) 76 | if err != nil { 77 | ... 78 | } 79 | // Step the statement 80 | hasRow, err := stmt.Step() 81 | if err != nil { 82 | ... 83 | } 84 | // Reset the statement 85 | err = stmt.Reset() 86 | if err != nil { 87 | ... 88 | } 89 | ``` 90 | 91 | ### Using Prepared Statements Conveniently 92 | ```go 93 | stmt, err := conn.Prepare(`INSERT INTO student VALUES (?, ?)`) 94 | if err != nil { 95 | ... 96 | } 97 | defer stmt.Close() 98 | 99 | // Exec binds arguments, steps the statement to completion, and always resets the statement 100 | err = stmt.Exec("John", 19) 101 | if err != nil { 102 | ... 103 | } 104 | ``` 105 | 106 | ### Using Queries Conveniently 107 | ```go 108 | // Prepare can prepare a statement and optionally also bind arguments 109 | stmt, err := conn.Prepare(`SELECT name, age FROM student WHERE age = ?`, 18) 110 | if err != nil { 111 | ... 112 | } 113 | defer stmt.Close() 114 | 115 | for { 116 | hasRow, err := stmt.Step() 117 | if err != nil { 118 | ... 119 | } 120 | if !hasRow { 121 | // The query is finished 122 | break 123 | } 124 | 125 | // Use Scan to access column data from a row 126 | var name string 127 | var age int 128 | err = stmt.Scan(&name, &age) 129 | if err != nil { 130 | ... 131 | } 132 | fmt.Println("name:", name, "age:", age) 133 | } 134 | // Remember to Reset the statement if you would like to Bind new arguments and reuse the prepared statement 135 | ``` 136 | 137 | ### Getting columns that might be NULL 138 | Scan can be convenient to use, but it doesn't handle NULL values. To get full control of column values, there are column methods for each type. 139 | ```go 140 | name, ok, err := stmt.ColumnText(0) 141 | if err != nil { 142 | // Either the column index was out of range, or SQLite failed to allocate memory 143 | ... 144 | } 145 | if !ok { 146 | // The column was NULL 147 | } 148 | 149 | age, ok, err := stmt.ColumnInt(1) 150 | if err != nil { 151 | // Can only fail if the column index is out of range 152 | ... 153 | } 154 | if !ok { 155 | // The column was NULL 156 | } 157 | ``` 158 | 159 | `ColumnBlob` returns a nil slice in the case of NULL. 160 | ```go 161 | blob, err := stmt.ColumnBlob(i) 162 | if err != nil { 163 | // Either the column index was out of range, or SQLite failed to allocate memory 164 | ... 165 | } 166 | if blob == nil { 167 | // The column was NULL 168 | } 169 | ``` 170 | 171 | 172 | 173 | ### Using Transactions 174 | ```go 175 | // Equivalent to conn.Exec("BEGIN") 176 | err := conn.Begin() 177 | if err != nil { 178 | ... 179 | } 180 | 181 | // Do some work 182 | ... 183 | 184 | // Equivalent to conn.Exec("COMMIT") 185 | err = conn.Commit() 186 | if err != nil { 187 | ... 188 | } 189 | ``` 190 | 191 | ### Using Transactions Conveniently 192 | 193 | With error handling in Go, it can be pretty inconvenient to ensure that a transaction is rolled back in the case of an error. The `WithTx` method is provided, which accepts a function of work to do inside of a transaction. `WithTx` will begin the transaction and call the function. If the function returns an error, the transaction will be rolled back. If the function succeeds, the transaction will be committed. `WithTx` can be a little awkward to use, but it's necessary. For example: 194 | 195 | ```go 196 | err := conn.WithTx(func() error { 197 | return insertStudents(conn) 198 | }) 199 | if err != nil { 200 | ... 201 | } 202 | 203 | func insertStudents(conn *sqlite3.Conn) error { 204 | ... 205 | } 206 | ``` 207 | 208 | ## Advanced Features 209 | * Binding parameters to statements using SQLite named parameters. 210 | * SQLite Blob Incremental IO API. 211 | * SQLite Online Backup API. 212 | * SQLite Session extension. 213 | * Supports setting a custom busy handler 214 | * Supports callback hooks on commit, rollback, and update. 215 | * Supports setting compile-Time authorization callbacks. 216 | * If shared cache mode is enabled and one statement receives a `SQLITE_LOCKED` error, the SQLite [unlock_notify](https://sqlite.org/unlock_notify.html) extension is used to transparently block and try again when the conflicting statement finishes. 217 | * Compiled with SQLite support for JSON1, RTREE, FTS5, GEOPOLY, STAT4, and SOUNDEX. 218 | * Compiled with SQLite support for OFFSET/LIMIT on UPDATE and DELETE statements. 219 | * RawString and RawBytes can be used to reduce copying between Go and SQLite. Please use with caution. 220 | 221 | ## Credit 222 | This project began as a fork of https://github.com/mxk/go-sqlite/ 223 | 224 | ## FAQ 225 | 226 | * **Why is there no `database/sql` interface?** 227 | 228 | If a `database/sql` interface is required, please use https://github.com/mattn/go-sqlite3 . In my experience, using a `database/sql` interface with SQLite is painful. Connection pooling causes unnecessary overhead and weirdness. Transactions using `Exec("BEGIN")` don't work as expected. Your connection does not correspond to SQLite's concept of a connection. PRAGMA commands do not work as expected. When you hit SQLite errors, such as locking or busy errors, it's difficult to discover why since you don't know which connection received which SQL and in what order. 229 | 230 | * **What are the differences between this driver and the mxk/go-sqlite driver?** 231 | 232 | This driver was forked from `mxk/go-sqlite-driver`. It hadn't been maintained in years and used an ancient version of SQLite. A large number of features were removed, reworked, and renamed. A lot of smartness and state was removed. It is now much easier to upgrade to newer versions of SQLite since the `codec` feature was removed. The behavior of methods now lines up closely with the behavior of SQLite's C API. 233 | 234 | * **What are the differences between this driver and the crawshaw/sqlite driver?** 235 | 236 | The crawshaw driver is pretty well thought out and solves a lot of the same problems as this 237 | driver. There are a few places where our philosophies differ. The crawshaw driver defaults (when flags of 0 are given) to SQLite shared cache mode and WAL mode. The default WAL synchronous mode is changed. Prepared statements are transparently cached. Connection pools are provided. I would be opposed to making most of these changes to this driver. I would like this driver to provide a default, light, and unsurprising SQLite experience. 238 | 239 | * **Are finalizers provided to automatically close connections and statements?** 240 | 241 | No finalizers are used in this driver. You are responsible for closing connections and statements. While I mostly agree with finalizers for cleaning up most accidental resource leaks, in this case, finalizers may fix errors such as locking errors while debugging only to find that the code works unreliably in production. Removing finalizers makes the behavior consistent. 242 | 243 | * **Is it thread safe?** 244 | 245 | go-sqlite-lite is as thread safe as SQLite. SQLite with this driver is compiled with `-DSQLITE_THREADSAFE=2` which is **Multi-thread** mode. In this mode, SQLite can be safely used by multiple threads provided that no single database connection is used simultaneously in two or more threads. This applies to goroutines. A single database connection should not be used simultaneously between two goroutines. 246 | 247 | It is safe to use separate connection instances concurrently, even if they are accessing the same database file. For example: 248 | ```go 249 | // ERROR (without any extra synchronization) 250 | c, _ := sqlite3.Open("sqlite.db") 251 | go use(c) 252 | go use(c) 253 | ``` 254 | ```go 255 | // OK 256 | c1, _ := sqlite3.Open("sqlite.db") 257 | c2, _ := sqlite3.Open("sqlite.db") 258 | go use(c1) 259 | go use(c2) 260 | ``` 261 | 262 | Consult the SQLite documentation for more information. 263 | 264 | https://www.sqlite.org/threadsafe.html 265 | 266 | * **How do I pool connections for handling HTTP requests?** 267 | 268 | Opening new connections is cheap and connection pooling is generally unnecessary for SQLite. I would recommend that you open a new connection for each request that you're handling. This ensures that each request is handled separately and the normal rules of SQLite database/table locking apply. 269 | 270 | If you've decided that pooling connections provides you with an advantage, it would be outside the scope of this package and something that you would need to implement and ensure works as needed. 271 | 272 | ## License 273 | This project is licensed under the BSD license. 274 | 275 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | build: off 2 | 3 | clone_folder: c:\gopath\src\github.com\bvinc\go-sqlite-lite 4 | 5 | environment: 6 | GOPATH: c:\gopath 7 | GETH_ARCH: amd64 8 | MSYS2_ARCH: x86_64 9 | MSYS2_BITS: 64 10 | MSYSTEM: MINGW64 11 | PATH: C:\msys64\mingw64\bin\;%PATH% 12 | 13 | stack: go 1.10 14 | 15 | before_test: 16 | - go vet ./... 17 | 18 | test_script: 19 | - go test ./... 20 | -------------------------------------------------------------------------------- /examples/basic/.gitignore: -------------------------------------------------------------------------------- 1 | /basic 2 | /mydatabase.db -------------------------------------------------------------------------------- /examples/basic/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bvinc/go-sqlite-lite/7ad36fb79d98bb6c4984f861f702c2b92586aeaf/examples/basic/main -------------------------------------------------------------------------------- /examples/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bvinc/go-sqlite-lite/sqlite3" 7 | ) 8 | 9 | func main() { 10 | err := main2() 11 | if err != nil { 12 | fmt.Println("Error:", err) 13 | } 14 | } 15 | 16 | func main2() error { 17 | conn, err := sqlite3.Open("mydatabase.db") 18 | if err != nil { 19 | return fmt.Errorf("failed to open database: %v", err) 20 | } 21 | defer conn.Close() 22 | 23 | err = conn.Exec(`DROP TABLE if exists student`) 24 | if err != nil { 25 | return fmt.Errorf("failed to drop students table: %v", err) 26 | } 27 | 28 | err = conn.Exec(`CREATE TABLE student(name TEXT, age INTEGER)`) 29 | if err != nil { 30 | return fmt.Errorf("failed to create students table: %v", err) 31 | } 32 | 33 | err = conn.WithTx(func() error { 34 | return insertStudents(conn) 35 | }) 36 | if err != nil { 37 | return fmt.Errorf("failed to insert students: %v", err) 38 | } 39 | 40 | err = queryStudents(conn) 41 | if err != nil { 42 | return fmt.Errorf("failed to query students: %v", err) 43 | } 44 | 45 | return nil 46 | } 47 | 48 | func insertStudents(conn *sqlite3.Conn) error { 49 | // Create a prepared statement 50 | stmt, err := conn.Prepare(`INSERT INTO student VALUES (?, ?)`) 51 | if err != nil { 52 | return fmt.Errorf("failed to prepare to insert to students table: %v", err) 53 | } 54 | defer stmt.Close() 55 | 56 | // This is how you can Bind arguments, Step the statement, then Reset it 57 | if err = stmt.Bind("Bill", 18); err != nil { 58 | return fmt.Errorf("failed to bind arguments: %v", err) 59 | } 60 | if _, err = stmt.Step(); err != nil { 61 | return fmt.Errorf("failed to step student insert: %v", err) 62 | } 63 | if err = stmt.Reset(); err != nil { 64 | return fmt.Errorf("failed to reset student insert: %v", err) 65 | } 66 | 67 | // Even more convenient, Exec will call Bind, Step as many times as needed 68 | // and always Reset the statement 69 | if err = stmt.Exec("Tom", 18); err != nil { 70 | return fmt.Errorf("failed to insert student: %v", err) 71 | } 72 | if err = stmt.Exec("John", 19); err != nil { 73 | return fmt.Errorf("failed to insert student: %v", err) 74 | } 75 | if err = stmt.Exec("Bob", 18); err != nil { 76 | return fmt.Errorf("failed to insert student: %v", err) 77 | } 78 | 79 | return nil 80 | } 81 | 82 | func queryStudents(conn *sqlite3.Conn) error { 83 | // Prepare can prepare a statement and optionally also bind arguments 84 | stmt, err := conn.Prepare(`SELECT * FROM student WHERE age = ?`, 18) 85 | if err != nil { 86 | return fmt.Errorf("failed to select from students table: %v", err) 87 | } 88 | defer stmt.Close() 89 | 90 | for { 91 | hasRow, err := stmt.Step() 92 | if err != nil { 93 | return fmt.Errorf("step failed while querying students: %v", err) 94 | } 95 | if !hasRow { 96 | break 97 | } 98 | 99 | // Use Scan to access column data from a row 100 | var name string 101 | var age int 102 | err = stmt.Scan(&name, &age) 103 | if err != nil { 104 | return fmt.Errorf("scan failed while querying students: %v", err) 105 | } 106 | 107 | fmt.Println("name:", name, "age:", age) 108 | } 109 | 110 | return nil 111 | } 112 | -------------------------------------------------------------------------------- /sqlite3/backup.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlite3 6 | 7 | /* 8 | #include "sqlite3.h" 9 | */ 10 | import "C" 11 | 12 | import ( 13 | "io" 14 | ) 15 | 16 | // Backup is a handle to an online backup operation between two databases. 17 | // https://www.sqlite.org/c3ref/backup.html 18 | type Backup struct { 19 | src *Conn 20 | dst *Conn 21 | bkup *C.sqlite3_backup 22 | } 23 | 24 | // newBackup initializes an online backup operation from src.srcName to 25 | // dst.dstName. 26 | func newBackup(src *Conn, srcName string, dst *Conn, dstName string) (*Backup, error) { 27 | srcName += "\x00" 28 | dstName += "\x00" 29 | 30 | bkup := C.sqlite3_backup_init(dst.db, cStr(dstName), src.db, cStr(srcName)) 31 | if bkup == nil { 32 | return nil, libErr(C.sqlite3_errcode(dst.db), dst.db) 33 | } 34 | 35 | b := &Backup{src, dst, bkup} 36 | return b, nil 37 | } 38 | 39 | // Close releases all resources associated with the backup operation. It is safe 40 | // to call this method prior to backup completion to abort the operation. 41 | // https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish 42 | func (b *Backup) Close() error { 43 | if bkup := b.bkup; bkup != nil { 44 | b.bkup = nil 45 | if rc := C.sqlite3_backup_finish(bkup); rc != OK { 46 | return libErr(rc, b.dst.db) 47 | } 48 | } 49 | return nil 50 | } 51 | 52 | // Conn returns the source and destination connections that are used by this 53 | // backup operation. The destination connection must not be used until the 54 | // backup operation is closed. 55 | func (b *Backup) Conn() (src, dst *Conn) { 56 | return b.src, b.dst 57 | } 58 | 59 | // Step copies up to n pages to the destination database. If n is negative, all 60 | // remaining pages are copied. io.EOF is returned upon successful backup 61 | // completion. 62 | // https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupstep 63 | func (b *Backup) Step(n int) error { 64 | if b.bkup == nil { 65 | return ErrBadBackup 66 | } 67 | if rc := C.sqlite3_backup_step(b.bkup, C.int(n)); rc != OK { 68 | // Do not close automatically since that clears the progress info 69 | if rc == DONE { 70 | return io.EOF 71 | } 72 | return libErr(rc, b.dst.db) 73 | } 74 | return nil 75 | } 76 | 77 | // Progress returns the number of pages that still need to be backed up and the 78 | // total number of pages in the source database. The values are updated after 79 | // each call to Step and are reset to 0 after the backup is closed. The total 80 | // number of pages may change if the source database is modified during the 81 | // backup operation. 82 | // https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining 83 | func (b *Backup) Progress() (remaining, total int) { 84 | if b.bkup != nil { 85 | remaining = int(C.sqlite3_backup_remaining(b.bkup)) 86 | total = int(C.sqlite3_backup_pagecount(b.bkup)) 87 | } 88 | return 89 | } 90 | -------------------------------------------------------------------------------- /sqlite3/const.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlite3 6 | 7 | /* 8 | #include "sqlite3.h" 9 | */ 10 | import "C" 11 | 12 | // Fundamental SQLite data types. These are returned by Stmt.DataTypes method. 13 | // https://www.sqlite.org/c3ref/c_blob.html 14 | const ( 15 | INTEGER = C.SQLITE_INTEGER // 1 16 | FLOAT = C.SQLITE_FLOAT // 2 17 | TEXT = C.SQLITE_TEXT // 3 18 | BLOB = C.SQLITE_BLOB // 4 19 | NULL = C.SQLITE_NULL // 5 20 | ) 21 | 22 | // Flags that can be provided to Open 23 | // https://www.sqlite.org/c3ref/open.html 24 | const ( 25 | OPEN_READONLY = C.SQLITE_OPEN_READONLY // Ok for sqlite3_open_v2() 26 | OPEN_READWRITE = C.SQLITE_OPEN_READWRITE // Ok for sqlite3_open_v2() 27 | OPEN_CREATE = C.SQLITE_OPEN_CREATE // Ok for sqlite3_open_v2() 28 | OPEN_DELETEONCLOSE = C.SQLITE_OPEN_DELETEONCLOSE // VFS only 29 | OPEN_EXCLUSIVE = C.SQLITE_OPEN_EXCLUSIVE // VFS only 30 | OPEN_AUTOPROXY = C.SQLITE_OPEN_AUTOPROXY // VFS only 31 | OPEN_URI = C.SQLITE_OPEN_URI // Ok for sqlite3_open_v2() 32 | OPEN_MEMORY = C.SQLITE_OPEN_MEMORY // Ok for sqlite3_open_v2() 33 | OPEN_MAIN_DB = C.SQLITE_OPEN_MAIN_DB // VFS only 34 | OPEN_TEMP_DB = C.SQLITE_OPEN_TEMP_DB // VFS only 35 | OPEN_TRANSIENT_DB = C.SQLITE_OPEN_TRANSIENT_DB // VFS only 36 | OPEN_MAIN_JOURNAL = C.SQLITE_OPEN_MAIN_JOURNAL // VFS only 37 | OPEN_TEMP_JOURNAL = C.SQLITE_OPEN_TEMP_JOURNAL // VFS only 38 | OPEN_SUBJOURNAL = C.SQLITE_OPEN_SUBJOURNAL // VFS only 39 | OPEN_MASTER_JOURNAL = C.SQLITE_OPEN_MASTER_JOURNAL // VFS only 40 | OPEN_NOMUTEX = C.SQLITE_OPEN_NOMUTEX // Ok for sqlite3_open_v2() 41 | OPEN_FULLMUTEX = C.SQLITE_OPEN_FULLMUTEX // Ok for sqlite3_open_v2() 42 | OPEN_SHAREDCACHE = C.SQLITE_OPEN_SHAREDCACHE // Ok for sqlite3_open_v2() 43 | OPEN_PRIVATECACHE = C.SQLITE_OPEN_PRIVATECACHE // Ok for sqlite3_open_v2() 44 | OPEN_WAL = C.SQLITE_OPEN_WAL // VFS only 45 | ) 46 | 47 | // General result codes returned by the SQLite API. When converted to an error, 48 | // OK and ROW become nil, and DONE becomes either nil or io.EOF, depending on 49 | // the context in which the statement is executed. All other codes are returned 50 | // via the Error struct. 51 | // https://www.sqlite.org/c3ref/c_abort.html 52 | const ( 53 | OK = C.SQLITE_OK // 0 = Successful result 54 | ERROR = C.SQLITE_ERROR // 1 = SQL error or missing database 55 | INTERNAL = C.SQLITE_INTERNAL // 2 = Internal logic error in SQLite 56 | PERM = C.SQLITE_PERM // 3 = Access permission denied 57 | ABORT = C.SQLITE_ABORT // 4 = Callback routine requested an abort 58 | BUSY = C.SQLITE_BUSY // 5 = The database file is locked 59 | LOCKED = C.SQLITE_LOCKED // 6 = A table in the database is locked 60 | NOMEM = C.SQLITE_NOMEM // 7 = A malloc() failed 61 | READONLY = C.SQLITE_READONLY // 8 = Attempt to write a readonly database 62 | INTERRUPT = C.SQLITE_INTERRUPT // 9 = Operation terminated by sqlite3_interrupt() 63 | IOERR = C.SQLITE_IOERR // 10 = Some kind of disk I/O error occurred 64 | CORRUPT = C.SQLITE_CORRUPT // 11 = The database disk image is malformed 65 | NOTFOUND = C.SQLITE_NOTFOUND // 12 = Unknown opcode in sqlite3_file_control() 66 | FULL = C.SQLITE_FULL // 13 = Insertion failed because database is full 67 | CANTOPEN = C.SQLITE_CANTOPEN // 14 = Unable to open the database file 68 | PROTOCOL = C.SQLITE_PROTOCOL // 15 = Database lock protocol error 69 | EMPTY = C.SQLITE_EMPTY // 16 = Database is empty 70 | SCHEMA = C.SQLITE_SCHEMA // 17 = The database schema changed 71 | TOOBIG = C.SQLITE_TOOBIG // 18 = String or BLOB exceeds size limit 72 | CONSTRAINT = C.SQLITE_CONSTRAINT // 19 = Abort due to constraint violation 73 | MISMATCH = C.SQLITE_MISMATCH // 20 = Data type mismatch 74 | MISUSE = C.SQLITE_MISUSE // 21 = Library used incorrectly 75 | NOLFS = C.SQLITE_NOLFS // 22 = Uses OS features not supported on host 76 | AUTH = C.SQLITE_AUTH // 23 = Authorization denied 77 | FORMAT = C.SQLITE_FORMAT // 24 = Auxiliary database format error 78 | RANGE = C.SQLITE_RANGE // 25 = 2nd parameter to sqlite3_bind out of range 79 | NOTADB = C.SQLITE_NOTADB // 26 = File opened that is not a database file 80 | NOTICE = C.SQLITE_NOTICE // 27 = Notifications from sqlite3_log() 81 | WARNING = C.SQLITE_WARNING // 28 = Warnings from sqlite3_log() 82 | ROW = C.SQLITE_ROW // 100 = sqlite3_step() has another row ready 83 | DONE = C.SQLITE_DONE // 101 = sqlite3_step() has finished executing 84 | ) 85 | 86 | // Extended result codes returned by the SQLite API. Extended result codes are 87 | // enabled by default for all new Conn objects. Use Error.Code()&0xFF to convert 88 | // an extended code to a general one. 89 | // https://www.sqlite.org/c3ref/c_abort_rollback.html 90 | const ( 91 | IOERR_READ = C.SQLITE_IOERR_READ // (SQLITE_IOERR | (1<<8)) 92 | IOERR_SHORT_READ = C.SQLITE_IOERR_SHORT_READ // (SQLITE_IOERR | (2<<8)) 93 | IOERR_WRITE = C.SQLITE_IOERR_WRITE // (SQLITE_IOERR | (3<<8)) 94 | IOERR_FSYNC = C.SQLITE_IOERR_FSYNC // (SQLITE_IOERR | (4<<8)) 95 | IOERR_DIR_FSYNC = C.SQLITE_IOERR_DIR_FSYNC // (SQLITE_IOERR | (5<<8)) 96 | IOERR_TRUNCATE = C.SQLITE_IOERR_TRUNCATE // (SQLITE_IOERR | (6<<8)) 97 | IOERR_FSTAT = C.SQLITE_IOERR_FSTAT // (SQLITE_IOERR | (7<<8)) 98 | IOERR_UNLOCK = C.SQLITE_IOERR_UNLOCK // (SQLITE_IOERR | (8<<8)) 99 | IOERR_RDLOCK = C.SQLITE_IOERR_RDLOCK // (SQLITE_IOERR | (9<<8)) 100 | IOERR_DELETE = C.SQLITE_IOERR_DELETE // (SQLITE_IOERR | (10<<8)) 101 | IOERR_BLOCKED = C.SQLITE_IOERR_BLOCKED // (SQLITE_IOERR | (11<<8)) 102 | IOERR_NOMEM = C.SQLITE_IOERR_NOMEM // (SQLITE_IOERR | (12<<8)) 103 | IOERR_ACCESS = C.SQLITE_IOERR_ACCESS // (SQLITE_IOERR | (13<<8)) 104 | IOERR_CHECKRESERVEDLOCK = C.SQLITE_IOERR_CHECKRESERVEDLOCK // (SQLITE_IOERR | (14<<8)) 105 | IOERR_LOCK = C.SQLITE_IOERR_LOCK // (SQLITE_IOERR | (15<<8)) 106 | IOERR_CLOSE = C.SQLITE_IOERR_CLOSE // (SQLITE_IOERR | (16<<8)) 107 | IOERR_DIR_CLOSE = C.SQLITE_IOERR_DIR_CLOSE // (SQLITE_IOERR | (17<<8)) 108 | IOERR_SHMOPEN = C.SQLITE_IOERR_SHMOPEN // (SQLITE_IOERR | (18<<8)) 109 | IOERR_SHMSIZE = C.SQLITE_IOERR_SHMSIZE // (SQLITE_IOERR | (19<<8)) 110 | IOERR_SHMLOCK = C.SQLITE_IOERR_SHMLOCK // (SQLITE_IOERR | (20<<8)) 111 | IOERR_SHMMAP = C.SQLITE_IOERR_SHMMAP // (SQLITE_IOERR | (21<<8)) 112 | IOERR_SEEK = C.SQLITE_IOERR_SEEK // (SQLITE_IOERR | (22<<8)) 113 | IOERR_DELETE_NOENT = C.SQLITE_IOERR_DELETE_NOENT // (SQLITE_IOERR | (23<<8)) 114 | IOERR_MMAP = C.SQLITE_IOERR_MMAP // (SQLITE_IOERR | (24<<8)) 115 | IOERR_GETTEMPPATH = C.SQLITE_IOERR_GETTEMPPATH // (SQLITE_IOERR | (25<<8)) 116 | IOERR_CONVPATH = C.SQLITE_IOERR_CONVPATH // (SQLITE_IOERR | (26<<8)) 117 | LOCKED_SHAREDCACHE = C.SQLITE_LOCKED_SHAREDCACHE // (SQLITE_LOCKED | (1<<8)) 118 | BUSY_RECOVERY = C.SQLITE_BUSY_RECOVERY // (SQLITE_BUSY | (1<<8)) 119 | BUSY_SNAPSHOT = C.SQLITE_BUSY_SNAPSHOT // (SQLITE_BUSY | (2<<8)) 120 | CANTOPEN_NOTEMPDIR = C.SQLITE_CANTOPEN_NOTEMPDIR // (SQLITE_CANTOPEN | (1<<8)) 121 | CANTOPEN_ISDIR = C.SQLITE_CANTOPEN_ISDIR // (SQLITE_CANTOPEN | (2<<8)) 122 | CANTOPEN_FULLPATH = C.SQLITE_CANTOPEN_FULLPATH // (SQLITE_CANTOPEN | (3<<8)) 123 | CANTOPEN_CONVPATH = C.SQLITE_CANTOPEN_CONVPATH // (SQLITE_CANTOPEN | (4<<8)) 124 | CORRUPT_VTAB = C.SQLITE_CORRUPT_VTAB // (SQLITE_CORRUPT | (1<<8)) 125 | READONLY_RECOVERY = C.SQLITE_READONLY_RECOVERY // (SQLITE_READONLY | (1<<8)) 126 | READONLY_CANTLOCK = C.SQLITE_READONLY_CANTLOCK // (SQLITE_READONLY | (2<<8)) 127 | READONLY_ROLLBACK = C.SQLITE_READONLY_ROLLBACK // (SQLITE_READONLY | (3<<8)) 128 | READONLY_DBMOVED = C.SQLITE_READONLY_DBMOVED // (SQLITE_READONLY | (4<<8)) 129 | ABORT_ROLLBACK = C.SQLITE_ABORT_ROLLBACK // (SQLITE_ABORT | (2<<8)) 130 | CONSTRAINT_CHECK = C.SQLITE_CONSTRAINT_CHECK // (SQLITE_CONSTRAINT | (1<<8)) 131 | CONSTRAINT_COMMITHOOK = C.SQLITE_CONSTRAINT_COMMITHOOK // (SQLITE_CONSTRAINT | (2<<8)) 132 | CONSTRAINT_FOREIGNKEY = C.SQLITE_CONSTRAINT_FOREIGNKEY // (SQLITE_CONSTRAINT | (3<<8)) 133 | CONSTRAINT_FUNCTION = C.SQLITE_CONSTRAINT_FUNCTION // (SQLITE_CONSTRAINT | (4<<8)) 134 | CONSTRAINT_NOTNULL = C.SQLITE_CONSTRAINT_NOTNULL // (SQLITE_CONSTRAINT | (5<<8)) 135 | CONSTRAINT_PRIMARYKEY = C.SQLITE_CONSTRAINT_PRIMARYKEY // (SQLITE_CONSTRAINT | (6<<8)) 136 | CONSTRAINT_TRIGGER = C.SQLITE_CONSTRAINT_TRIGGER // (SQLITE_CONSTRAINT | (7<<8)) 137 | CONSTRAINT_UNIQUE = C.SQLITE_CONSTRAINT_UNIQUE // (SQLITE_CONSTRAINT | (8<<8)) 138 | CONSTRAINT_VTAB = C.SQLITE_CONSTRAINT_VTAB // (SQLITE_CONSTRAINT | (9<<8)) 139 | CONSTRAINT_ROWID = C.SQLITE_CONSTRAINT_ROWID // (SQLITE_CONSTRAINT |(10<<8)) 140 | NOTICE_RECOVER_WAL = C.SQLITE_NOTICE_RECOVER_WAL // (SQLITE_NOTICE | (1<<8)) 141 | NOTICE_RECOVER_ROLLBACK = C.SQLITE_NOTICE_RECOVER_ROLLBACK // (SQLITE_NOTICE | (2<<8)) 142 | WARNING_AUTOINDEX = C.SQLITE_WARNING_AUTOINDEX // (SQLITE_WARNING | (1<<8)) 143 | ) 144 | 145 | // Codes used by SQLite to indicate the operation type when invoking authorizer 146 | // and row update callbacks. 147 | // https://www.sqlite.org/c3ref/c_alter_table.html 148 | const ( 149 | CREATE_INDEX = C.SQLITE_CREATE_INDEX // 1 150 | CREATE_TABLE = C.SQLITE_CREATE_TABLE // 2 151 | CREATE_TEMP_INDEX = C.SQLITE_CREATE_TEMP_INDEX // 3 152 | CREATE_TEMP_TABLE = C.SQLITE_CREATE_TEMP_TABLE // 4 153 | CREATE_TEMP_TRIGGER = C.SQLITE_CREATE_TEMP_TRIGGER // 5 154 | CREATE_TEMP_VIEW = C.SQLITE_CREATE_TEMP_VIEW // 6 155 | CREATE_TRIGGER = C.SQLITE_CREATE_TRIGGER // 7 156 | CREATE_VIEW = C.SQLITE_CREATE_VIEW // 8 157 | DELETE = C.SQLITE_DELETE // 9 158 | DROP_INDEX = C.SQLITE_DROP_INDEX // 10 159 | DROP_TABLE = C.SQLITE_DROP_TABLE // 11 160 | DROP_TEMP_INDEX = C.SQLITE_DROP_TEMP_INDEX // 12 161 | DROP_TEMP_TABLE = C.SQLITE_DROP_TEMP_TABLE // 13 162 | DROP_TEMP_TRIGGER = C.SQLITE_DROP_TEMP_TRIGGER // 14 163 | DROP_TEMP_VIEW = C.SQLITE_DROP_TEMP_VIEW // 15 164 | DROP_TRIGGER = C.SQLITE_DROP_TRIGGER // 16 165 | DROP_VIEW = C.SQLITE_DROP_VIEW // 17 166 | INSERT = C.SQLITE_INSERT // 18 167 | PRAGMA = C.SQLITE_PRAGMA // 19 168 | READ = C.SQLITE_READ // 20 169 | SELECT = C.SQLITE_SELECT // 21 170 | TRANSACTION = C.SQLITE_TRANSACTION // 22 171 | UPDATE = C.SQLITE_UPDATE // 23 172 | ATTACH = C.SQLITE_ATTACH // 24 173 | DETACH = C.SQLITE_DETACH // 25 174 | ALTER_TABLE = C.SQLITE_ALTER_TABLE // 26 175 | REINDEX = C.SQLITE_REINDEX // 27 176 | ANALYZE = C.SQLITE_ANALYZE // 28 177 | CREATE_VTABLE = C.SQLITE_CREATE_VTABLE // 29 178 | DROP_VTABLE = C.SQLITE_DROP_VTABLE // 30 179 | FUNCTION = C.SQLITE_FUNCTION // 31 180 | SAVEPOINT = C.SQLITE_SAVEPOINT // 32 181 | RECURSIVE = C.SQLITE_RECURSIVE // 33 182 | ) 183 | 184 | // Core SQLite performance counters that can be queried with Status. 185 | // https://www.sqlite.org/c3ref/c_status_malloc_count.html 186 | const ( 187 | STATUS_MEMORY_USED = C.SQLITE_STATUS_MEMORY_USED // 0 188 | STATUS_PAGECACHE_USED = C.SQLITE_STATUS_PAGECACHE_USED // 1 189 | STATUS_PAGECACHE_OVERFLOW = C.SQLITE_STATUS_PAGECACHE_OVERFLOW // 2 190 | STATUS_SCRATCH_USED = C.SQLITE_STATUS_SCRATCH_USED // 3 191 | STATUS_SCRATCH_OVERFLOW = C.SQLITE_STATUS_SCRATCH_OVERFLOW // 4 192 | STATUS_MALLOC_SIZE = C.SQLITE_STATUS_MALLOC_SIZE // 5 193 | STATUS_PARSER_STACK = C.SQLITE_STATUS_PARSER_STACK // 6 194 | STATUS_PAGECACHE_SIZE = C.SQLITE_STATUS_PAGECACHE_SIZE // 7 195 | STATUS_SCRATCH_SIZE = C.SQLITE_STATUS_SCRATCH_SIZE // 8 196 | STATUS_MALLOC_COUNT = C.SQLITE_STATUS_MALLOC_COUNT // 9 197 | ) 198 | 199 | // Connection performance counters that can be queried with Conn.Status. 200 | // https://www.sqlite.org/c3ref/c_dbstatus_options.html 201 | const ( 202 | DBSTATUS_LOOKASIDE_USED = C.SQLITE_DBSTATUS_LOOKASIDE_USED // 0 203 | DBSTATUS_CACHE_USED = C.SQLITE_DBSTATUS_CACHE_USED // 1 204 | DBSTATUS_SCHEMA_USED = C.SQLITE_DBSTATUS_SCHEMA_USED // 2 205 | DBSTATUS_STMT_USED = C.SQLITE_DBSTATUS_STMT_USED // 3 206 | DBSTATUS_LOOKASIDE_HIT = C.SQLITE_DBSTATUS_LOOKASIDE_HIT // 4 207 | DBSTATUS_LOOKASIDE_MISS_SIZE = C.SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE // 5 208 | DBSTATUS_LOOKASIDE_MISS_FULL = C.SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL // 6 209 | DBSTATUS_CACHE_HIT = C.SQLITE_DBSTATUS_CACHE_HIT // 7 210 | DBSTATUS_CACHE_MISS = C.SQLITE_DBSTATUS_CACHE_MISS // 8 211 | DBSTATUS_CACHE_WRITE = C.SQLITE_DBSTATUS_CACHE_WRITE // 9 212 | DBSTATUS_DEFERRED_FKS = C.SQLITE_DBSTATUS_DEFERRED_FKS // 10 213 | ) 214 | 215 | // Statement performance counters that can be queried with Stmt.Status. 216 | // https://www.sqlite.org/c3ref/c_stmtstatus_counter.html 217 | const ( 218 | STMTSTATUS_FULLSCAN_STEP = C.SQLITE_STMTSTATUS_FULLSCAN_STEP // 1 219 | STMTSTATUS_SORT = C.SQLITE_STMTSTATUS_SORT // 2 220 | STMTSTATUS_AUTOINDEX = C.SQLITE_STMTSTATUS_AUTOINDEX // 3 221 | STMTSTATUS_VM_STEP = C.SQLITE_STMTSTATUS_VM_STEP // 4 222 | ) 223 | 224 | // Per-connection limits that can be queried and changed with Conn.Limit. 225 | // https://www.sqlite.org/c3ref/c_limit_attached.html 226 | const ( 227 | LIMIT_LENGTH = C.SQLITE_LIMIT_LENGTH // 0 228 | LIMIT_SQL_LENGTH = C.SQLITE_LIMIT_SQL_LENGTH // 1 229 | LIMIT_COLUMN = C.SQLITE_LIMIT_COLUMN // 2 230 | LIMIT_EXPR_DEPTH = C.SQLITE_LIMIT_EXPR_DEPTH // 3 231 | LIMIT_COMPOUND_SELECT = C.SQLITE_LIMIT_COMPOUND_SELECT // 4 232 | LIMIT_VDBE_OP = C.SQLITE_LIMIT_VDBE_OP // 5 233 | LIMIT_FUNCTION_ARG = C.SQLITE_LIMIT_FUNCTION_ARG // 6 234 | LIMIT_ATTACHED = C.SQLITE_LIMIT_ATTACHED // 7 235 | LIMIT_LIKE_PATTERN_LENGTH = C.SQLITE_LIMIT_LIKE_PATTERN_LENGTH // 8 236 | LIMIT_VARIABLE_NUMBER = C.SQLITE_LIMIT_VARIABLE_NUMBER // 9 237 | LIMIT_TRIGGER_DEPTH = C.SQLITE_LIMIT_TRIGGER_DEPTH // 10 238 | ) 239 | -------------------------------------------------------------------------------- /sqlite3/io.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlite3 6 | 7 | /* 8 | #include "sqlite3.h" 9 | */ 10 | import "C" 11 | 12 | import ( 13 | "io" 14 | "runtime" 15 | ) 16 | 17 | // ErrBlobFull is returned by BlobIO.Write when there isn't enough space left to 18 | // write the provided bytes. 19 | var ErrBlobFull = &Error{ERROR, "incremental write failed, no space left"} 20 | 21 | // BlobIO is a handle to a single BLOB (binary large object) or TEXT value 22 | // opened for incremental I/O. This allows the value to be treated as a file for 23 | // reading and writing. The value length cannot be changed using this API; use 24 | // an UPDATE statement for that. The recommended way of allocating space for a 25 | // BLOB is to use the ZeroBlob type or the zeroblob() SQL function. 26 | // https://www.sqlite.org/c3ref/blob.html 27 | type BlobIO struct { 28 | conn *Conn 29 | blob *C.sqlite3_blob 30 | 31 | row int64 // ROWID of the row containing the BLOB/TEXT value 32 | len int // Value length in bytes 33 | off int // Current read/write offset 34 | } 35 | 36 | // newBlobIO initializes an incremental I/O operation. 37 | func newBlobIO(c *Conn, db, tbl, col string, row int64, rw bool) (*BlobIO, error) { 38 | db += "\x00" 39 | tbl += "\x00" 40 | col += "\x00" 41 | 42 | var blob *C.sqlite3_blob 43 | rc := C.sqlite3_blob_open(c.db, cStr(db), cStr(tbl), cStr(col), 44 | C.sqlite3_int64(row), cBool(rw), &blob) 45 | if rc != OK { 46 | return nil, libErr(rc, c.db) 47 | } 48 | 49 | b := &BlobIO{ 50 | conn: c, 51 | blob: blob, 52 | row: row, 53 | len: int(C.sqlite3_blob_bytes(blob)), 54 | } 55 | runtime.SetFinalizer(b, (*BlobIO).Close) 56 | return b, nil 57 | } 58 | 59 | // Close releases all resources associated with the incremental I/O operation. 60 | // It is important to check the error returned by this method, since disk I/O 61 | // and other types of errors may not be reported until the changes are actually 62 | // committed to the database. 63 | // https://www.sqlite.org/c3ref/blob_close.html 64 | func (b *BlobIO) Close() error { 65 | if blob := b.blob; blob != nil { 66 | b.blob = nil 67 | b.len = 0 68 | b.off = 0 69 | runtime.SetFinalizer(b, nil) 70 | if rc := C.sqlite3_blob_close(blob); rc != OK { 71 | return libErr(rc, b.conn.db) 72 | } 73 | } 74 | return nil 75 | } 76 | 77 | // Conn returns the connection that that created this incremental I/O operation. 78 | func (b *BlobIO) Conn() *Conn { 79 | return b.conn 80 | } 81 | 82 | // Row returns the ROWID of the row containing the BLOB/TEXT value. 83 | func (b *BlobIO) Row() int64 { 84 | return b.row 85 | } 86 | 87 | // Len returns the length of the BLOB/TEXT value in bytes. It is not possible to 88 | // read/write/seek beyond this length. The length changes to 0 if the I/O handle 89 | // expires due to an update of any column in the same row. This condition is 90 | // indicated by an ABORT error code returned from Read or Write. An expired 91 | // handle is closed automatically and cannot be reopened. Any writes that 92 | // occurred before the abort are not rolled back. 93 | // https://www.sqlite.org/c3ref/blob_bytes.html 94 | func (b *BlobIO) Len() int { 95 | return b.len 96 | } 97 | 98 | // Read implements the io.Reader interface. 99 | // https://www.sqlite.org/c3ref/blob_read.html 100 | func (b *BlobIO) Read(p []byte) (n int, err error) { 101 | if b.blob == nil { 102 | return 0, ErrBadIO 103 | } 104 | if b.off >= b.len { 105 | return 0, io.EOF 106 | } 107 | if n = b.len - b.off; len(p) < n { 108 | n = len(p) 109 | } 110 | rc := C.sqlite3_blob_read(b.blob, cBytes(p), C.int(n), C.int(b.off)) 111 | return b.io(rc, n) 112 | } 113 | 114 | // Write implements the io.Writer interface. The number of bytes written is 115 | // always either 0 or len(p). ErrBlobFull is returned if there isn't enough 116 | // space left to write all of p. 117 | // https://www.sqlite.org/c3ref/blob_write.html 118 | func (b *BlobIO) Write(p []byte) (n int, err error) { 119 | if b.blob == nil { 120 | return 0, ErrBadIO 121 | } 122 | if n = len(p); b.off+n > b.len { 123 | // Doesn't make sense to do a partial write. Better to return quickly 124 | // and let the caller reallocate the BLOB. 125 | return 0, ErrBlobFull 126 | } 127 | rc := C.sqlite3_blob_write(b.blob, cBytes(p), C.int(n), C.int(b.off)) 128 | return b.io(rc, n) 129 | } 130 | 131 | // Seek implements the io.Seeker interface. 132 | func (b *BlobIO) Seek(offset int64, whence int) (ret int64, err error) { 133 | if b.blob == nil { 134 | return 0, ErrBadIO 135 | } 136 | switch whence { 137 | case 0: 138 | case 1: 139 | offset += int64(b.off) 140 | case 2: 141 | offset += int64(b.len) 142 | default: 143 | return 0, pkgErr(MISUSE, "invalid whence for BlobIO.Seek (%d)", whence) 144 | } 145 | if offset < 0 || offset > int64(b.len) { 146 | return 0, pkgErr(MISUSE, "invalid offset for BlobIO.Seek (%d)", offset) 147 | } 148 | b.off = int(offset) 149 | return offset, nil 150 | } 151 | 152 | // Reopen closes the current value and opens another one in the same column, 153 | // specified by its ROWID. If an error is encountered, the I/O handle becomes 154 | // unusable and is automatically closed. 155 | // https://www.sqlite.org/c3ref/blob_reopen.html 156 | func (b *BlobIO) Reopen(row int64) error { 157 | if b.blob == nil { 158 | return ErrBadIO 159 | } 160 | if rc := C.sqlite3_blob_reopen(b.blob, C.sqlite3_int64(row)); rc != OK { 161 | err := libErr(rc, b.conn.db) 162 | b.Close() 163 | return err 164 | } 165 | b.row = row 166 | b.len = int(C.sqlite3_blob_bytes(b.blob)) 167 | b.off = 0 168 | return nil 169 | } 170 | 171 | // io handles the completion of a single Read/Write call. 172 | func (b *BlobIO) io(rc C.int, n int) (int, error) { 173 | if rc == OK { 174 | b.off += n 175 | return n, nil 176 | } 177 | err := libErr(rc, b.conn.db) 178 | if rc == ABORT { 179 | b.Close() 180 | } 181 | return 0, err 182 | } 183 | -------------------------------------------------------------------------------- /sqlite3/session.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // 5 | // This code was adapted from David Crawshaw's sqlite driver. 6 | // https://github.com/crawshaw/sqlite 7 | // The license to the original code is as follows: 8 | // 9 | // Copyright (c) 2018 David Crawshaw 10 | // 11 | // Permission to use, copy, modify, and distribute this software for any 12 | // purpose with or without fee is hereby granted, provided that the above 13 | // copyright notice and this permission notice appear in all copies. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | 23 | package sqlite3 24 | 25 | /* 26 | #include "sqlite3.h" 27 | 28 | extern int strm_r_tramp(void*, char*, int*); 29 | extern int strm_w_tramp(void*, char*, int); 30 | extern int xapply_conflict_tramp(void*, int, sqlite3_changeset_iter*); 31 | extern int xapply_filter_tramp(void*, char*); 32 | */ 33 | import "C" 34 | 35 | import ( 36 | "io" 37 | "unsafe" 38 | ) 39 | 40 | var strmWriterReg = newRegistry() 41 | var strmReaderReg = newRegistry() 42 | var xapplyReg = newRegistry() 43 | 44 | type Session struct { 45 | sess *C.sqlite3_session 46 | } 47 | 48 | // CreateSession creates a new session object. 49 | // https://www.sqlite.org/session/sqlite3session_create.html 50 | func (conn *Conn) CreateSession(db string) (*Session, error) { 51 | db += "\x00" 52 | s := &Session{} 53 | rc := C.sqlite3session_create(conn.db, cStr(db), &s.sess) 54 | if rc != OK { 55 | return nil, errStr(rc) 56 | } 57 | 58 | return s, nil 59 | } 60 | 61 | // Close closes a session object previously created with CreateSession. 62 | // https://www.sqlite.org/session/sqlite3session_delete.html 63 | func (s *Session) Close() { 64 | C.sqlite3session_delete(s.sess) 65 | s.sess = nil 66 | } 67 | 68 | // Enable enables recording of changes by a Session. 69 | // New Sessions start enabled. 70 | // https://www.sqlite.org/session/sqlite3session_enable.html 71 | func (s *Session) Enable() { 72 | C.sqlite3session_enable(s.sess, 1) 73 | } 74 | 75 | // Disable disables recording of changes by a Session. 76 | // https://www.sqlite.org/session/sqlite3session_enable.html 77 | func (s *Session) Disable() { 78 | C.sqlite3session_enable(s.sess, 0) 79 | } 80 | 81 | // IsEnabled queries if the session is currently enabled. 82 | // https://www.sqlite.org/session/sqlite3session_enable.html 83 | func (s *Session) IsEnabled() bool { 84 | return C.sqlite3session_enable(s.sess, -1) != 0 85 | } 86 | 87 | // https://sqlite.org/session/sqlite3session_indirect.html 88 | func (s *Session) IsIndirect() bool { 89 | return C.sqlite3session_indirect(s.sess, -1) != 0 90 | } 91 | 92 | // https://sqlite.org/session/sqlite3session_indirect.html 93 | func (s *Session) SetIndirect(indirect bool) { 94 | C.sqlite3session_indirect(s.sess, cBool(indirect)) 95 | } 96 | 97 | // https://sqlite.org/session/sqlite3session_isempty.html 98 | func (s *Session) IsEmpty() bool { 99 | return C.sqlite3session_isempty(s.sess) != 0 100 | } 101 | 102 | // Attach attaches a table to a session object. If the argument tab is equal 103 | // to "", then changes are recorded for all tables in the database. 104 | // https://www.sqlite.org/session/sqlite3session_attach.html 105 | func (s *Session) Attach(tab string) error { 106 | var zTab *C.char 107 | if tab != "" { 108 | zTab = cStr(tab + "\x00") 109 | } 110 | rc := C.sqlite3session_attach(s.sess, zTab) 111 | if rc != OK { 112 | return errStr(rc) 113 | } 114 | return nil 115 | } 116 | 117 | func (s *Session) Diff(fromDB, tbl string) error { 118 | fromDB += "\x00" 119 | tbl += "\x00" 120 | // TODO: provide a pointer to get a more accurate error message 121 | rc := C.sqlite3session_diff(s.sess, cStr(fromDB), cStr(tbl), nil) 122 | if rc != OK { 123 | return errStr(rc) 124 | } 125 | return nil 126 | } 127 | 128 | func (s *Session) Changeset(w io.Writer) error { 129 | idx := strmWriterReg.register(w) 130 | defer strmWriterReg.unregister(idx) 131 | 132 | rc := C.sqlite3session_changeset_strm(s.sess, (*[0]byte)(C.strm_w_tramp), unsafe.Pointer(&idx)) 133 | if rc != OK { 134 | return errStr(rc) 135 | } 136 | return nil 137 | } 138 | 139 | // https://www.sqlite.org/session/sqlite3session_patchset.html 140 | func (s *Session) Patchset(w io.Writer) error { 141 | idx := strmWriterReg.register(w) 142 | defer strmWriterReg.unregister(idx) 143 | 144 | rc := C.sqlite3session_patchset_strm(s.sess, (*[0]byte)(C.strm_w_tramp), unsafe.Pointer(&idx)) 145 | if rc != OK { 146 | return errStr(rc) 147 | } 148 | return nil 149 | } 150 | 151 | // https://www.sqlite.org/session/sqlite3changeset_apply.html 152 | func (conn *Conn) ChangesetApply(r io.Reader, filterFn func(tableName string) bool, conflictFn func(ConflictType, ChangesetIter) ConflictAction) error { 153 | readerIdx := strmReaderReg.register(r) 154 | defer strmReaderReg.unregister(readerIdx) 155 | 156 | x := &xapply{ 157 | conn: conn, 158 | filterFn: filterFn, 159 | conflictFn: conflictFn, 160 | } 161 | 162 | xapplyIdx := xapplyReg.register(x) 163 | defer xapplyReg.unregister(xapplyIdx) 164 | 165 | var filterTramp, conflictTramp *[0]byte 166 | if x.filterFn != nil { 167 | filterTramp = (*[0]byte)(C.xapply_filter_tramp) 168 | } 169 | if x.conflictFn != nil { 170 | conflictTramp = (*[0]byte)(C.xapply_conflict_tramp) 171 | } 172 | 173 | rc := C.sqlite3changeset_apply_strm(conn.db, (*[0]byte)(C.strm_r_tramp), unsafe.Pointer(&readerIdx), filterTramp, conflictTramp, unsafe.Pointer(&xapplyIdx)) 174 | if rc != OK { 175 | return errStr(rc) 176 | } 177 | 178 | return nil 179 | } 180 | 181 | // https://www.sqlite.org/session/sqlite3changeset_invert.html 182 | func ChangesetInvert(w io.Writer, r io.Reader) error { 183 | readerIdx := strmReaderReg.register(r) 184 | defer strmReaderReg.unregister(readerIdx) 185 | 186 | writerIdx := strmWriterReg.register(w) 187 | defer strmWriterReg.unregister(writerIdx) 188 | rc := C.sqlite3changeset_invert_strm( 189 | (*[0]byte)(C.strm_r_tramp), unsafe.Pointer(&readerIdx), 190 | (*[0]byte)(C.strm_w_tramp), unsafe.Pointer(&writerIdx), 191 | ) 192 | if rc != OK { 193 | return errStr(rc) 194 | } 195 | return nil 196 | } 197 | 198 | // https://www.sqlite.org/session/sqlite3changeset_concat.html 199 | func ChangesetConcat(w io.Writer, r1, r2 io.Reader) error { 200 | readerIdx1 := strmReaderReg.register(r1) 201 | defer strmReaderReg.unregister(readerIdx1) 202 | readerIdx2 := strmReaderReg.register(r2) 203 | defer strmReaderReg.unregister(readerIdx2) 204 | writerIdx := strmWriterReg.register(w) 205 | defer strmWriterReg.unregister(writerIdx) 206 | 207 | rc := C.sqlite3changeset_concat_strm( 208 | (*[0]byte)(C.strm_r_tramp), unsafe.Pointer(&readerIdx1), 209 | (*[0]byte)(C.strm_r_tramp), unsafe.Pointer(&readerIdx2), 210 | (*[0]byte)(C.strm_w_tramp), unsafe.Pointer(&writerIdx), 211 | ) 212 | if rc != OK { 213 | return errStr(rc) 214 | } 215 | return nil 216 | } 217 | 218 | type ChangesetIter struct { 219 | ptr *C.sqlite3_changeset_iter 220 | readerIdx *int 221 | reader io.Reader 222 | } 223 | 224 | // https://www.sqlite.org/session/sqlite3changeset_start.html 225 | func ChangesetIterStart(r io.Reader) (ChangesetIter, error) { 226 | idx := strmReaderReg.register(r) 227 | iter := ChangesetIter{ 228 | reader: r, 229 | readerIdx: new(int), 230 | } 231 | *iter.readerIdx = idx 232 | // rc := C.sqlite3changeset_start_strm(&iter.ptr, (*[0]byte)(C.strm_r_tramp), unsafe.Pointer(&iter.readerIdx)) 233 | rc := C.sqlite3changeset_start_strm(&iter.ptr, (*[0]byte)(C.strm_r_tramp), unsafe.Pointer(iter.readerIdx)) 234 | if rc != OK { 235 | return ChangesetIter{}, errStr(rc) 236 | } 237 | return iter, nil 238 | } 239 | 240 | // https://www.sqlite.org/session/sqlite3changeset_finalize.html 241 | func (iter ChangesetIter) Close() error { 242 | rc := C.sqlite3changeset_finalize(iter.ptr) 243 | iter.ptr = nil 244 | strmReaderReg.unregister(*iter.readerIdx) 245 | *iter.readerIdx = 0 246 | if rc != OK { 247 | return errStr(rc) 248 | } 249 | return nil 250 | } 251 | 252 | // https://www.sqlite.org/session/sqlite3changeset_old.html 253 | func (iter ChangesetIter) Old(col int) (v Value, err error) { 254 | rc := C.sqlite3changeset_old(iter.ptr, C.int(col), &v.ptr) 255 | if rc != OK { 256 | return Value{}, errStr(rc) 257 | } 258 | return v, nil 259 | } 260 | 261 | // https://www.sqlite.org/session/sqlite3changeset_new.html 262 | func (iter ChangesetIter) New(col int) (v Value, err error) { 263 | rc := C.sqlite3changeset_new(iter.ptr, C.int(col), &v.ptr) 264 | if rc != OK { 265 | return Value{}, errStr(rc) 266 | } 267 | return v, nil 268 | } 269 | 270 | // https://www.sqlite.org/session/sqlite3changeset_conflict.html 271 | func (iter ChangesetIter) Conflict(col int) (v Value, err error) { 272 | rc := C.sqlite3changeset_conflict(iter.ptr, C.int(col), &v.ptr) 273 | if rc != OK { 274 | return Value{}, errStr(rc) 275 | } 276 | return v, nil 277 | } 278 | 279 | func (iter ChangesetIter) Next() (rowReturned bool, err error) { 280 | rc := C.sqlite3changeset_next(iter.ptr) 281 | switch rc { 282 | case ROW: 283 | return true, nil 284 | case DONE: 285 | return false, nil 286 | default: 287 | return false, errStr(rc) 288 | } 289 | } 290 | 291 | // https://www.sqlite.org/session/sqlite3changeset_op.html 292 | func (iter ChangesetIter) Op() (table string, numCols int, opType OpType, indirect bool, err error) { 293 | var zTab *C.char 294 | var nCol, op, bIndirect C.int 295 | rc := C.sqlite3changeset_op(iter.ptr, &zTab, &nCol, &op, &bIndirect) 296 | if rc != OK { 297 | return "", 0, 0, false, errStr(rc) 298 | } 299 | table = C.GoString(zTab) 300 | numCols = int(nCol) 301 | opType = OpType(op) 302 | indirect = bIndirect != 0 303 | return table, numCols, opType, indirect, nil 304 | } 305 | 306 | // https://www.sqlite.org/session/sqlite3changeset_fk_conflicts.html 307 | func (iter ChangesetIter) FKConflicts() (int, error) { 308 | var pnOut C.int 309 | rc := C.sqlite3changeset_fk_conflicts(iter.ptr, &pnOut) 310 | if rc != OK { 311 | return 0, errStr(rc) 312 | } 313 | return int(pnOut), nil 314 | } 315 | 316 | // https://www.sqlite.org/session/sqlite3changeset_pk.html 317 | func (iter ChangesetIter) PK() ([]bool, error) { 318 | var pabPK *C.uchar 319 | var pnCol C.int 320 | rc := C.sqlite3changeset_pk(iter.ptr, &pabPK, &pnCol) 321 | if rc != OK { 322 | return nil, errStr(rc) 323 | } 324 | vals := (*[127]byte)(unsafe.Pointer(pabPK))[:pnCol:pnCol] 325 | cols := make([]bool, pnCol) 326 | for i, val := range vals { 327 | if val != 0 { 328 | cols[i] = true 329 | } 330 | } 331 | return cols, nil 332 | } 333 | 334 | type OpType int 335 | 336 | type ConflictType int 337 | 338 | const ( 339 | CHANGESET_DATA = C.SQLITE_CHANGESET_DATA 340 | CHANGESET_NOTFOUND = C.SQLITE_CHANGESET_NOTFOUND 341 | CHANGESET_CONFLICT = C.SQLITE_CHANGESET_CONFLICT 342 | CHANGESET_CONSTRAINT = C.SQLITE_CHANGESET_CONSTRAINT 343 | CHANGESET_FOREIGN_KEY = C.SQLITE_CHANGESET_FOREIGN_KEY 344 | ) 345 | 346 | type ConflictAction int 347 | 348 | const ( 349 | CHANGESET_OMIT = C.SQLITE_CHANGESET_OMIT 350 | CHANGESET_ABORT = C.SQLITE_CHANGESET_ABORT 351 | CHANGESET_REPLACE = C.SQLITE_CHANGESET_REPLACE 352 | ) 353 | 354 | type Changegroup struct { 355 | ptr *C.sqlite3_changegroup 356 | } 357 | 358 | // https://www.sqlite.org/session/sqlite3changegroup_new.html 359 | func NewChangegroup() (*Changegroup, error) { 360 | c := &Changegroup{} 361 | rc := C.sqlite3changegroup_new(&c.ptr) 362 | if rc != OK { 363 | return nil, errStr(rc) 364 | } 365 | return c, nil 366 | } 367 | 368 | // https://www.sqlite.org/session/sqlite3changegroup_add.html 369 | func (cg Changegroup) Add(r io.Reader) error { 370 | idx := strmReaderReg.register(r) 371 | defer strmReaderReg.unregister(idx) 372 | rc := C.sqlite3changegroup_add_strm(cg.ptr, (*[0]byte)(C.strm_r_tramp), unsafe.Pointer(&idx)) 373 | if rc != OK { 374 | return errStr(rc) 375 | } 376 | return nil 377 | } 378 | 379 | // Delete deletes a Changegroup. 380 | // 381 | // https://www.sqlite.org/session/sqlite3changegroup_delete.html 382 | func (cg Changegroup) Delete() { 383 | C.sqlite3changegroup_delete(cg.ptr) 384 | } 385 | 386 | // https://www.sqlite.org/session/sqlite3changegroup_output.html 387 | func (cg Changegroup) Output(w io.Writer) (err error) { 388 | idx := strmWriterReg.register(w) 389 | defer strmWriterReg.unregister(idx) 390 | 391 | rc := C.sqlite3changegroup_output_strm(cg.ptr, (*[0]byte)(C.strm_w_tramp), unsafe.Pointer(&idx)) 392 | if rc != OK { 393 | return errStr(rc) 394 | } 395 | return nil 396 | } 397 | 398 | //export strm_w_tramp 399 | func strm_w_tramp(pOut unsafe.Pointer, pData *C.char, n C.int) C.int { 400 | w := strmWriterReg.lookup(*(*int)(pOut)).(io.Writer) 401 | b := (*[1 << 30]byte)(unsafe.Pointer(pData))[:n:n] 402 | for len(b) > 0 { 403 | nw, err := w.Write(b) 404 | b = b[nw:] 405 | 406 | if err != nil { 407 | return C.SQLITE_IOERR 408 | } 409 | } 410 | return C.SQLITE_OK 411 | } 412 | 413 | //export strm_r_tramp 414 | func strm_r_tramp(pIn unsafe.Pointer, pData *C.char, pnData *C.int) C.int { 415 | r := strmReaderReg.lookup(*(*int)(pIn)).(io.Reader) 416 | b := (*[1 << 30]byte)(unsafe.Pointer(pData))[:*pnData:*pnData] 417 | 418 | var n int 419 | var err error 420 | for n == 0 && err == nil { 421 | // Technically an io.Reader is allowed to return (0, nil) 422 | // and it is not treated as the end of the stream. 423 | // 424 | // So we spin here until the io.Reader is gracious enough 425 | // to get off its butt and actually do something. 426 | n, err = r.Read(b) 427 | } 428 | 429 | *pnData = C.int(n) 430 | if err != nil && err != io.EOF { 431 | return C.SQLITE_IOERR 432 | } 433 | return C.SQLITE_OK 434 | } 435 | 436 | type xapply struct { 437 | id int 438 | conn *Conn 439 | filterFn func(string) bool 440 | conflictFn func(ConflictType, ChangesetIter) ConflictAction 441 | } 442 | 443 | //export xapply_filter_tramp 444 | func xapply_filter_tramp(pCtx unsafe.Pointer, zTab *C.char) C.int { 445 | x := xapplyReg.lookup(*(*int)(pCtx)).(*xapply) 446 | 447 | tableName := C.GoString(zTab) 448 | if x.filterFn(tableName) { 449 | return 1 450 | } 451 | return 0 452 | } 453 | 454 | //export xapply_conflict_tramp 455 | func xapply_conflict_tramp(pCtx unsafe.Pointer, eConflict C.int, p *C.sqlite3_changeset_iter) C.int { 456 | x := xapplyReg.lookup(*(*int)(pCtx)).(*xapply) 457 | 458 | action := x.conflictFn(ConflictType(eConflict), ChangesetIter{ptr: p}) 459 | return C.int(action) 460 | } 461 | -------------------------------------------------------------------------------- /sqlite3/session_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // 5 | // This code was adapted from David Crawshaw's sqlite driver. 6 | // https://github.com/crawshaw/sqlite 7 | // The license to the original code is as follows: 8 | // 9 | // Copyright (c) 2018 David Crawshaw 10 | // 11 | // Permission to use, copy, modify, and distribute this software for any 12 | // purpose with or without fee is hereby granted, provided that the above 13 | // copyright notice and this permission notice appear in all copies. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 | package sqlite3_test 23 | 24 | import ( 25 | "bytes" 26 | "reflect" 27 | "testing" 28 | 29 | "github.com/bvinc/go-sqlite-lite/sqlite3" 30 | ) 31 | 32 | func initT(t *testing.T, conn *sqlite3.Conn) { 33 | err := conn.Exec(`INSERT INTO t (c1, c2, c3) VALUES ("1", "2", "3");`) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | 38 | err = conn.Exec(`INSERT INTO t (c1, c2, c3) VALUES ("4", "5", "6");`) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | } 43 | 44 | func fillSession(t *testing.T) (*sqlite3.Conn, *sqlite3.Session) { 45 | conn, err := sqlite3.Open(":memory:") 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | 50 | err = conn.Exec("CREATE TABLE t (c1 PRIMARY KEY, c2, c3);") 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | initT(t, conn) // two rows that predate the session 55 | 56 | s, err := conn.CreateSession("main") 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | if err := s.Attach(""); err != nil { 61 | t.Fatal(err) 62 | } 63 | 64 | stmts := []string{ 65 | `UPDATE t SET c1="one" WHERE c1="1";`, 66 | `UPDATE t SET c2="two", c3="three" WHERE c1="one";`, 67 | `UPDATE t SET c1="noop" WHERE c2="2";`, 68 | `DELETE FROM t WHERE c1="4";`, 69 | `INSERT INTO t (c1, c2, c3) VALUES ("four", "five", "six");`, 70 | } 71 | 72 | for _, stmt := range stmts { 73 | err := conn.Exec(stmt) 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | } 78 | 79 | err = conn.Exec("BEGIN;") 80 | if err != nil { 81 | t.Fatal(err) 82 | } 83 | 84 | stmt, err := conn.Prepare("INSERT INTO t (c1, c2, c3) VALUES (?,?,?);") 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | defer stmt.Close() 89 | 90 | for i := int64(2); i < 100; i++ { 91 | stmt.Bind(i, "column2", "column3") 92 | if _, err := stmt.Step(); err != nil { 93 | t.Fatal(err) 94 | } 95 | stmt.Reset() 96 | } 97 | if err := conn.Exec("COMMIT;"); err != nil { 98 | t.Fatal(err) 99 | } 100 | 101 | return conn, s 102 | } 103 | 104 | func TestFillSession(t *testing.T) { 105 | conn, s := fillSession(t) 106 | s.Close() 107 | conn.Close() 108 | } 109 | 110 | func TestChangeset(t *testing.T) { 111 | conn, s := fillSession(t) 112 | defer func() { 113 | s.Close() 114 | if err := conn.Close(); err != nil { 115 | t.Error(err) 116 | } 117 | }() 118 | 119 | buf := new(bytes.Buffer) 120 | if err := s.Changeset(buf); err != nil { 121 | t.Fatal(err) 122 | } 123 | b := buf.Bytes() 124 | if len(b) == 0 { 125 | t.Errorf("changeset has no length") 126 | } 127 | 128 | iter, err := sqlite3.ChangesetIterStart(bytes.NewReader(b)) 129 | if err != nil { 130 | t.Fatal(err) 131 | } 132 | numChanges := 0 133 | num3Cols := 0 134 | opTypes := make(map[sqlite3.OpType]int) 135 | for { 136 | hasRow, err := iter.Next() 137 | if err != nil { 138 | t.Fatal(err) 139 | } 140 | if !hasRow { 141 | break 142 | } 143 | table, numCols, opType, _, err := iter.Op() 144 | if err != nil { 145 | t.Fatalf("numChanges=%d, Op err: %v", numChanges, err) 146 | } 147 | if table != "t" { 148 | t.Errorf("table=%q, want t", table) 149 | } 150 | opTypes[opType]++ 151 | if numCols == 3 { 152 | num3Cols++ 153 | } 154 | numChanges++ 155 | } 156 | if numChanges != 102 { 157 | t.Errorf("numChanges=%d, want 102", numChanges) 158 | } 159 | if num3Cols != 102 { 160 | t.Errorf("num3Cols=%d, want 102", num3Cols) 161 | } 162 | if got := opTypes[sqlite3.INSERT]; got != 100 { 163 | t.Errorf("num inserts=%d, want 100", got) 164 | } 165 | if err := iter.Close(); err != nil { 166 | t.Fatal(err) 167 | } 168 | } 169 | 170 | func TestChangesetInvert(t *testing.T) { 171 | conn, s := fillSession(t) 172 | defer func() { 173 | s.Close() 174 | if err := conn.Close(); err != nil { 175 | t.Error(err) 176 | } 177 | }() 178 | 179 | buf := new(bytes.Buffer) 180 | if err := s.Changeset(buf); err != nil { 181 | t.Fatal(err) 182 | } 183 | b := buf.Bytes() 184 | 185 | buf = new(bytes.Buffer) 186 | if err := sqlite3.ChangesetInvert(buf, bytes.NewReader(b)); err != nil { 187 | t.Fatal(err) 188 | } 189 | invB := buf.Bytes() 190 | if len(invB) == 0 { 191 | t.Error("no inverted changeset") 192 | } 193 | if bytes.Equal(b, invB) { 194 | t.Error("inverted changeset is unchanged") 195 | } 196 | 197 | buf = new(bytes.Buffer) 198 | if err := sqlite3.ChangesetInvert(buf, bytes.NewReader(invB)); err != nil { 199 | t.Fatal(err) 200 | } 201 | invinvB := buf.Bytes() 202 | if !bytes.Equal(b, invinvB) { 203 | t.Error("inv(inv(b)) != b") 204 | } 205 | } 206 | 207 | func TestChangesetApply(t *testing.T) { 208 | conn, s := fillSession(t) 209 | defer func() { 210 | s.Close() 211 | if err := conn.Close(); err != nil { 212 | t.Error(err) 213 | } 214 | }() 215 | 216 | buf := new(bytes.Buffer) 217 | if err := s.Changeset(buf); err != nil { 218 | t.Fatal(err) 219 | } 220 | b := buf.Bytes() 221 | 222 | invBuf := new(bytes.Buffer) 223 | if err := sqlite3.ChangesetInvert(invBuf, bytes.NewReader(b)); err != nil { 224 | t.Fatal(err) 225 | } 226 | 227 | // Undo the entire session. 228 | if err := conn.ChangesetApply(invBuf, nil, nil); err != nil { 229 | t.Fatal(err) 230 | } 231 | 232 | // Table t should now be equivalent to the first two statements: 233 | // INSERT INTO t (c1, c2, c3) VALUES ("1", "2", "3"); 234 | // INSERT INTO t (c1, c2, c3) VALUES ("4", "5", "6"); 235 | want := []string{"1,2,3", "4,5,6"} 236 | var got []string 237 | stmt, err := conn.Prepare("SELECT c1, c2, c3 FROM t ORDER BY c1;") 238 | if err != nil { 239 | t.Fatal(err) 240 | } 241 | defer func() { 242 | if err := stmt.Close(); err != nil { 243 | t.Error(err) 244 | } 245 | }() 246 | 247 | for { 248 | hasRow, err := stmt.Step() 249 | if err != nil { 250 | t.Fatal(err) 251 | } 252 | if !hasRow { 253 | break 254 | } 255 | 256 | t0, _, _ := stmt.ColumnText(0) 257 | t1, _, _ := stmt.ColumnText(1) 258 | t2, _, _ := stmt.ColumnText(2) 259 | got = append(got, t0+","+t1+","+t2) 260 | } 261 | 262 | if !reflect.DeepEqual(got, want) { 263 | t.Errorf("got=%v, want=%v", got, want) 264 | } 265 | } 266 | 267 | func TestPatchsetApply(t *testing.T) { 268 | conn, s := fillSession(t) 269 | defer func() { 270 | if s != nil { 271 | s.Close() 272 | } 273 | if err := conn.Close(); err != nil { 274 | t.Error(err) 275 | } 276 | }() 277 | 278 | var rowCountBefore int 279 | 280 | stmt1, err := conn.Prepare("SELECT COUNT(*) FROM t;") 281 | if err != nil { 282 | t.Fatal(err) 283 | } 284 | defer func() { 285 | if err := stmt1.Close(); err != nil { 286 | t.Error(err) 287 | } 288 | }() 289 | 290 | _, err = stmt1.Step() 291 | if err != nil { 292 | t.Fatal(err) 293 | } 294 | rowCountBefore, _, _ = stmt1.ColumnInt(0) 295 | 296 | buf := new(bytes.Buffer) 297 | if err := s.Patchset(buf); err != nil { 298 | t.Fatal(err) 299 | } 300 | b := buf.Bytes() 301 | 302 | s.Close() 303 | s = nil 304 | 305 | if err := conn.Exec("DELETE FROM t;"); err != nil { 306 | t.Fatal(err) 307 | } 308 | initT(t, conn) 309 | 310 | filterFnCalled := false 311 | filterFn := func(tableName string) bool { 312 | if tableName == "t" { 313 | filterFnCalled = true 314 | return true 315 | } else { 316 | t.Errorf("unexpected table in filter fn: %q", tableName) 317 | return false 318 | } 319 | } 320 | conflictFn := func(sqlite3.ConflictType, sqlite3.ChangesetIter) sqlite3.ConflictAction { 321 | t.Error("conflict applying patchset") 322 | return sqlite3.CHANGESET_ABORT 323 | } 324 | if err := conn.ChangesetApply(bytes.NewReader(b), filterFn, conflictFn); err != nil { 325 | t.Fatal(err) 326 | } 327 | if !filterFnCalled { 328 | t.Error("filter function not called") 329 | } 330 | 331 | var rowCountAfter int 332 | stmt2, err := conn.Prepare("SELECT COUNT(*) FROM t;") 333 | if err != nil { 334 | t.Fatal(err) 335 | } 336 | defer func() { 337 | if err := stmt2.Close(); err != nil { 338 | t.Error(err) 339 | } 340 | }() 341 | 342 | _, err = stmt2.Step() 343 | if err != nil { 344 | t.Fatal(err) 345 | } 346 | rowCountAfter, _, _ = stmt2.ColumnInt(0) 347 | 348 | if rowCountBefore != rowCountAfter { 349 | t.Errorf("row count is %d, want %d", rowCountAfter, rowCountBefore) 350 | } 351 | 352 | // Second application of patchset should fail. 353 | haveConflict := false 354 | conflictFn = func(ct sqlite3.ConflictType, iter sqlite3.ChangesetIter) sqlite3.ConflictAction { 355 | if ct == sqlite3.CHANGESET_CONFLICT { 356 | haveConflict = true 357 | } else { 358 | t.Errorf("unexpected conflict type: %v", ct) 359 | } 360 | _, _, opType, _, err := iter.Op() 361 | if err != nil { 362 | t.Errorf("conflict iter.Op() error: %v", err) 363 | } 364 | if opType != sqlite3.INSERT { 365 | t.Errorf("unexpected conflict op type: %v", opType) 366 | } 367 | return sqlite3.CHANGESET_ABORT 368 | } 369 | err = conn.ChangesetApply(bytes.NewReader(b), nil, conflictFn) 370 | sqlite_err, _ := err.(*sqlite3.Error) 371 | if code := sqlite_err.Code(); code != sqlite3.ABORT { 372 | t.Errorf("conflicting changeset Apply error is %v, want SQLITE_ABORT", err) 373 | } 374 | if !haveConflict { 375 | t.Error("no conflict found") 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /sqlite3/sqlite3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // go-sqlite-lite is a SQLite driver for the Go programming language. It is 6 | // designed to be simple, lightweight, performant, understandable, 7 | // unsurprising, debuggable, ergonomic, and fully featured. This driver does 8 | // not provide a database/sql interface. 9 | package sqlite3 10 | 11 | /* 12 | // SQLite compilation options. 13 | // https://www.sqlite.org/compile.html 14 | // https://www.sqlite.org/footprint.html 15 | #cgo CFLAGS: -std=gnu99 16 | #cgo CFLAGS: -Os 17 | #cgo CFLAGS: -DNDEBUG=1 18 | #cgo CFLAGS: -DSQLITE_CORE=1 19 | #cgo CFLAGS: -DSQLITE_ENABLE_API_ARMOR=1 20 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS3=1 21 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 22 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS4=1 23 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS5=1 24 | #cgo CFLAGS: -DSQLITE_ENABLE_GEOPOLY=1 25 | #cgo CFLAGS: -DSQLITE_ENABLE_JSON1=1 26 | #cgo CFLAGS: -DSQLITE_ENABLE_PREUPDATE_HOOK 27 | #cgo CFLAGS: -DSQLITE_ENABLE_RTREE=1 28 | #cgo CFLAGS: -DSQLITE_ENABLE_SESSION 29 | #cgo CFLAGS: -DSQLITE_ENABLE_STAT4=1 30 | #cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY 31 | #cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 32 | #cgo CFLAGS: -DSQLITE_OMIT_AUTOINIT=1 33 | #cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED=1 34 | #cgo CFLAGS: -DSQLITE_OMIT_PROGRESS_CALLBACK=1 35 | #cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION=1 36 | #cgo CFLAGS: -DSQLITE_OMIT_TRACE=1 37 | #cgo CFLAGS: -DSQLITE_OMIT_UTF16=1 38 | #cgo CFLAGS: -DSQLITE_SOUNDEX=1 39 | #cgo CFLAGS: -DSQLITE_TEMP_STORE=2 40 | #cgo CFLAGS: -DSQLITE_THREADSAFE=2 41 | #cgo CFLAGS: -DSQLITE_USE_ALLOCA=1 42 | #cgo CFLAGS: -DSQLITE_USE_URI=1 43 | #cgo linux LDFLAGS: -lm 44 | #cgo openbsd LDFLAGS: -lm 45 | 46 | #cgo linux,!android CFLAGS: -DHAVE_FDATASYNC=1 47 | #cgo linux,!android CFLAGS: -DHAVE_PREAD=1 -DHAVE_PWRITE=1 48 | #cgo darwin CFLAGS: -DHAVE_FDATASYNC=1 49 | #cgo darwin CFLAGS: -DHAVE_PREAD=1 -DHAVE_PWRITE=1 50 | 51 | #cgo windows LDFLAGS: -Wl,-Bstatic -lwinpthread -Wl,-Bdynamic 52 | 53 | // Fix for BusyTimeout on *nix systems. 54 | #cgo !windows CFLAGS: -DHAVE_USLEEP=1 55 | 56 | 57 | // Fix "_localtime32(0): not defined" linker error. 58 | #cgo windows,386 CFLAGS: -D_localtime32=localtime 59 | 60 | #include 61 | #include 62 | #include "sqlite3.h" 63 | 64 | // cgo doesn't handle variadic functions. 65 | static void set_temp_dir(const char *path) { 66 | sqlite3_temp_directory = sqlite3_mprintf("%s", path); 67 | } 68 | 69 | // cgo doesn't handle SQLITE_{STATIC,TRANSIENT} pointer constants. 70 | static int bind_text(sqlite3_stmt *s, int i, const char *p, int n, int copy) { 71 | if (n > 0) { 72 | return sqlite3_bind_text(s, i, p, n, 73 | (copy ? SQLITE_TRANSIENT : SQLITE_STATIC)); 74 | } 75 | return sqlite3_bind_text(s, i, "", 0, SQLITE_STATIC); 76 | } 77 | static int bind_blob(sqlite3_stmt *s, int i, const void *p, int n, int copy) { 78 | if (n > 0) { 79 | return sqlite3_bind_blob(s, i, p, n, 80 | (copy ? SQLITE_TRANSIENT : SQLITE_STATIC)); 81 | } 82 | return sqlite3_bind_zeroblob(s, i, 0); 83 | } 84 | 85 | // Faster retrieval of column data types (1 cgo call instead of n). 86 | static void column_types(sqlite3_stmt *s, unsigned char p[], int n) { 87 | int i = 0; 88 | for (; i < n; ++i, ++p) { 89 | *p = sqlite3_column_type(s, i); 90 | } 91 | } 92 | 93 | // Macro for creating callback setter functions. 94 | #define SET(x) \ 95 | static void set_##x(sqlite3 *db, void *data, int enable) { \ 96 | (enable ? sqlite3_##x(db, go_##x, data) : sqlite3_##x(db, 0, 0)); \ 97 | } 98 | 99 | // util.go exports. 100 | int go_busy_handler(void*,int); 101 | int go_commit_hook(void*); 102 | void go_rollback_hook(void*); 103 | void go_update_hook(void* data, int op,const char *db, const char *tbl, sqlite3_int64 row); 104 | int go_set_authorizer(void* data, int op, const char *arg1, const char *arg2, const char *db, const char *entity); 105 | 106 | SET(busy_handler) 107 | SET(commit_hook) 108 | SET(rollback_hook) 109 | SET(update_hook) 110 | SET(set_authorizer) 111 | 112 | // A pointer to an instance of this structure is passed as the user-context 113 | // pointer when registering for an unlock-notify callback. 114 | typedef struct UnlockNotification UnlockNotification; 115 | struct UnlockNotification { 116 | int fired; // True after unlock event has occurred 117 | pthread_cond_t cond; // Condition variable to wait on 118 | pthread_mutex_t mutex; // Mutex to protect structure 119 | }; 120 | 121 | // This function is an unlock-notify callback registered with SQLite. 122 | static void unlock_notify_cb(void **apArg, int nArg){ 123 | int i; 124 | for(i=0; imutex); 127 | p->fired = 1; 128 | pthread_cond_signal(&p->cond); 129 | pthread_mutex_unlock(&p->mutex); 130 | } 131 | } 132 | 133 | // This function assumes that an SQLite API call (either sqlite3_prepare_v2() 134 | // or sqlite3_step()) has just returned SQLITE_LOCKED. The argument is the 135 | // associated database connection. 136 | // 137 | // This function calls sqlite3_unlock_notify() to register for an 138 | // unlock-notify callback, then blocks until that callback is delivered 139 | // and returns SQLITE_OK. The caller should then retry the failed operation. 140 | // 141 | // Or, if sqlite3_unlock_notify() indicates that to block would deadlock 142 | // the system, then this function returns SQLITE_LOCKED immediately. In 143 | // this case the caller should not retry the operation and should roll 144 | // back the current transaction (if any). 145 | static int wait_for_unlock_notify(sqlite3 *db){ 146 | int rc; 147 | UnlockNotification un; 148 | 149 | // Initialize the UnlockNotification structure. 150 | un.fired = 0; 151 | pthread_mutex_init(&un.mutex, 0); 152 | pthread_cond_init(&un.cond, 0); 153 | 154 | // Register for an unlock-notify callback. 155 | rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un); 156 | assert( rc==SQLITE_LOCKED || rc==SQLITE_OK ); 157 | 158 | // The call to sqlite3_unlock_notify() always returns either SQLITE_LOCKED 159 | // or SQLITE_OK. 160 | // 161 | // If SQLITE_LOCKED was returned, then the system is deadlocked. In this 162 | // case this function needs to return SQLITE_LOCKED to the caller so 163 | // that the current transaction can be rolled back. Otherwise, block 164 | // until the unlock-notify callback is invoked, then return SQLITE_OK. 165 | if( rc==SQLITE_OK ){ 166 | pthread_mutex_lock(&un.mutex); 167 | if( !un.fired ){ 168 | pthread_cond_wait(&un.cond, &un.mutex); 169 | } 170 | pthread_mutex_unlock(&un.mutex); 171 | } 172 | 173 | // Destroy the mutex and condition variables. 174 | pthread_cond_destroy(&un.cond); 175 | pthread_mutex_destroy(&un.mutex); 176 | 177 | return rc; 178 | } 179 | 180 | // This function is a wrapper around the SQLite function sqlite3_step(). 181 | // It functions in the same way as step(), except that if a required 182 | // shared-cache lock cannot be obtained, this function may block waiting for 183 | // the lock to become available. In this scenario the normal API step() 184 | // function always returns SQLITE_LOCKED. 185 | // 186 | // If this function returns SQLITE_LOCKED, the caller should rollback 187 | // the current transaction (if any) and try again later. Otherwise, the 188 | // system may become deadlocked. 189 | int sqlite3_blocking_step(sqlite3 *db, sqlite3_stmt *pStmt){ 190 | int rc; 191 | for (;;) { 192 | rc = sqlite3_step(pStmt); 193 | if( rc != SQLITE_LOCKED ) { 194 | break; 195 | } 196 | if( sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE ) { 197 | break; 198 | } 199 | rc = wait_for_unlock_notify(sqlite3_db_handle(pStmt)); 200 | if( rc!=SQLITE_OK ) { 201 | break; 202 | } 203 | sqlite3_reset(pStmt); 204 | } 205 | return rc; 206 | } 207 | 208 | // This function is a wrapper around the SQLite function sqlite3_prepare_v2(). 209 | // It functions in the same way as prepare_v2(), except that if a required 210 | // shared-cache lock cannot be obtained, this function may block waiting for 211 | // the lock to become available. In this scenario the normal API prepare_v2() 212 | // function always returns SQLITE_LOCKED. 213 | // 214 | // If this function returns SQLITE_LOCKED, the caller should rollback 215 | // the current transaction (if any) and try again later. Otherwise, the 216 | // system may become deadlocked. 217 | int sqlite3_blocking_prepare_v2( 218 | sqlite3 *db, // Database handle. 219 | const char *zSql, // UTF-8 encoded SQL statement. 220 | int nSql, // Length of zSql in bytes. 221 | sqlite3_stmt **ppStmt, // OUT: A pointer to the prepared statement 222 | const char **pz // OUT: End of parsed string 223 | ){ 224 | int rc; 225 | for (;;) { 226 | rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, pz); 227 | if( rc != SQLITE_LOCKED ){ 228 | break; 229 | } 230 | if( sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE ) { 231 | break; 232 | } 233 | rc = wait_for_unlock_notify(db); 234 | if( rc!=SQLITE_OK ) { 235 | break; 236 | } 237 | } 238 | return rc; 239 | } 240 | */ 241 | import "C" 242 | 243 | import ( 244 | "fmt" 245 | "io" 246 | "os" 247 | "time" 248 | "unsafe" 249 | ) 250 | 251 | // initErr indicates a SQLite initialization error, which disables this package. 252 | var initErr error 253 | 254 | // emptyByteSlice is what we might return on ColumnRawBytes to avoid 255 | // allocation. Since they are not allowed to modify the slice, this is safe. 256 | var emptyByteSlice = []byte{} 257 | 258 | var busyRegistry = newRegistry() 259 | var commitRegistry = newRegistry() 260 | var rollbackRegistry = newRegistry() 261 | var updateRegistry = newRegistry() 262 | var authorizerRegistry = newRegistry() 263 | 264 | func init() { 265 | // Initialize SQLite (required with SQLITE_OMIT_AUTOINIT). 266 | // https://www.sqlite.org/c3ref/initialize.html 267 | if rc := C.sqlite3_initialize(); rc != OK { 268 | initErr = errStr(rc) 269 | return 270 | } 271 | 272 | // Use the same temporary directory as Go. 273 | // https://www.sqlite.org/c3ref/temp_directory.html 274 | tmp := os.TempDir() + "\x00" 275 | C.set_temp_dir(cStr(tmp)) 276 | } 277 | 278 | // Conn is a connection handle, which may have multiple databases attached to it 279 | // by using the ATTACH SQL statement. 280 | // https://www.sqlite.org/c3ref/sqlite3.html 281 | type Conn struct { 282 | db *C.sqlite3 283 | 284 | busyIdx int 285 | commitIdx int 286 | rollbackIdx int 287 | updateIdx int 288 | authorizerIdx int 289 | } 290 | 291 | // Open creates a new connection to a SQLite database. The name can be 1) a path 292 | // to a file, which is created if it does not exist, 2) a URI using the syntax 293 | // described at https://www.sqlite.org/uri.html, 3) the string ":memory:", 294 | // which creates a temporary in-memory database, or 4) an empty string, which 295 | // creates a temporary on-disk database (deleted when closed) in the directory 296 | // returned by os.TempDir(). Flags to Open can optionally be provided. If no 297 | // flags are provided, the default flags of OPEN_READWRITE|OPEN_CREATE are 298 | // used. 299 | // https://www.sqlite.org/c3ref/open.html 300 | func Open(name string, flagArgs ...int) (*Conn, error) { 301 | if len(flagArgs) > 1 { 302 | return nil, pkgErr(MISUSE, "too many arguments provided to Open") 303 | } 304 | 305 | if initErr != nil { 306 | return nil, initErr 307 | } 308 | name += "\x00" 309 | 310 | var db *C.sqlite3 311 | flags := C.SQLITE_OPEN_READWRITE | C.SQLITE_OPEN_CREATE 312 | if len(flagArgs) == 1 { 313 | flags = flagArgs[0] 314 | } 315 | rc := C.sqlite3_open_v2(cStr(name), &db, C.int(flags), nil) 316 | if rc != OK { 317 | err := libErr(rc, db) 318 | C.sqlite3_close(db) 319 | return nil, err 320 | } 321 | c := &Conn{db: db} 322 | C.sqlite3_extended_result_codes(db, 1) 323 | return c, nil 324 | } 325 | 326 | // Close releases all resources associated with the connection. If any prepared 327 | // statements, incremental I/O operations, or backup operations are still 328 | // active, the connection becomes an unusable "zombie" and is closed after all 329 | // remaining statements and operations are destroyed. A BUSY error code is 330 | // returned if the connection is left in this "zombie" status, which may 331 | // indicate a programming error where some previously allocated resource is not 332 | // properly released. 333 | // https://www.sqlite.org/c3ref/close.html 334 | func (c *Conn) Close() error { 335 | if db := c.db; db != nil { 336 | c.db = nil 337 | 338 | // Unregister all of the globally registered callbacks 339 | if c.busyIdx != 0 { 340 | busyRegistry.unregister(c.busyIdx) 341 | } 342 | if c.commitIdx != 0 { 343 | commitRegistry.unregister(c.commitIdx) 344 | } 345 | if c.rollbackIdx != 0 { 346 | rollbackRegistry.unregister(c.rollbackIdx) 347 | } 348 | if c.updateIdx != 0 { 349 | updateRegistry.unregister(c.updateIdx) 350 | } 351 | if c.authorizerIdx != 0 { 352 | authorizerRegistry.unregister(c.authorizerIdx) 353 | } 354 | 355 | if rc := C.sqlite3_close(db); rc != OK { 356 | err := libErr(rc, db) 357 | if rc == BUSY { 358 | C.sqlite3_close_v2(db) 359 | } 360 | return err 361 | } 362 | } 363 | return nil 364 | } 365 | 366 | // Prepare compiles the first statement in sql. Any remaining text after the 367 | // first statement is saved in s.Tail. This function may return a nil stmt and 368 | // a nil error, if the sql string contains nothing to do. For convenience, 369 | // this function can also bind arguments to the returned statement. If an 370 | // error occurs during binding, the statement is closed/finalized and the error 371 | // is returned. 372 | // https://www.sqlite.org/c3ref/prepare.html 373 | func (c *Conn) Prepare(sql string, args ...interface{}) (s *Stmt, err error) { 374 | zSQL := sql + "\x00" 375 | 376 | var stmt *C.sqlite3_stmt 377 | var cTail *C.char 378 | rc := C.sqlite3_blocking_prepare_v2(c.db, cStr(zSQL), -1, &stmt, &cTail) 379 | if rc != OK { 380 | return nil, libErr(rc, c.db) 381 | } 382 | 383 | if stmt == nil { 384 | return nil, nil 385 | } 386 | 387 | var tail string 388 | if cTail != nil { 389 | n := cStrOffset(zSQL, cTail) 390 | if n >= 0 && n < len(sql) { 391 | tail = sql[n:] 392 | } 393 | } 394 | 395 | s = &Stmt{stmt: stmt, db: c.db, Tail: tail} 396 | 397 | if len(args) > 0 { 398 | if err = s.Bind(args...); err != nil { 399 | s.Close() 400 | return nil, err 401 | } 402 | } 403 | 404 | return s, nil 405 | } 406 | 407 | // Exec is a convenience function that will call sqlite3_exec if no argument 408 | // are given. If arguments are given, it's simply a convenient way to 409 | // Prepare a statement, Bind arguments, Step the statement to completion, 410 | // and Close/finalize the statement. 411 | // https://www.sqlite.org/c3ref/exec.html 412 | func (c *Conn) Exec(sql string, args ...interface{}) error { 413 | // Fast path via sqlite3_exec, which doesn't support parameter binding 414 | if len(args) == 0 { 415 | sql += "\x00" 416 | return c.exec(cStr(sql)) 417 | } 418 | 419 | s, err := c.Prepare(sql) 420 | if err != nil { 421 | return err 422 | } 423 | if s == nil { 424 | return nil 425 | } 426 | defer s.Close() 427 | 428 | if err = s.Bind(args...); err != nil { 429 | return err 430 | } 431 | 432 | if err = s.StepToCompletion(); err != nil { 433 | return err 434 | } 435 | 436 | return nil 437 | } 438 | 439 | // Begin starts a new deferred transaction. This is equivalent to 440 | // c.Exec("BEGIN") 441 | // https://www.sqlite.org/lang_transaction.html 442 | func (c *Conn) Begin() error { 443 | return c.exec(cStr("BEGIN\x00")) 444 | } 445 | 446 | // BeginImmediate starts a new immediate transaction. This is equivalent to 447 | // c.Exec("BEGIN IMMEDIATE") 448 | // https://www.sqlite.org/lang_transaction.html 449 | func (c *Conn) BeginImmediate() error { 450 | return c.exec(cStr("BEGIN IMMEDIATE\x00")) 451 | } 452 | 453 | // BeginExclusive starts a new exclusive transaction. This is equivalent to 454 | // c.Exec("BEGIN EXCLUSIVE") 455 | // https://www.sqlite.org/lang_transaction.html 456 | func (c *Conn) BeginExclusive() error { 457 | return c.exec(cStr("BEGIN EXCLUSIVE\x00")) 458 | } 459 | 460 | // Commit saves all changes made within a transaction to the database. 461 | func (c *Conn) Commit() error { 462 | return c.exec(cStr("COMMIT\x00")) 463 | } 464 | 465 | // Rollback aborts the current transaction without saving any changes. 466 | func (c *Conn) Rollback() error { 467 | return c.exec(cStr("ROLLBACK\x00")) 468 | } 469 | 470 | // WithTx is a convenience method that begins a deferred transaction, calls the 471 | // function f, and will commit the transaction if f does not return an error, 472 | // and will roll back the transaction if f does return an error. 473 | func (c *Conn) WithTx(f func() error) error { 474 | if err := c.Begin(); err != nil { 475 | return fmt.Errorf("failed to begin transaction: %v", err) 476 | } 477 | 478 | // Perform work inside the transaction 479 | err := f() 480 | if err != nil { 481 | err2 := c.Rollback() 482 | if err2 == nil { 483 | return err 484 | } 485 | return fmt.Errorf("%v, additionally rolling back transaction failed: %v", err, err2) 486 | } 487 | 488 | if err = c.Commit(); err != nil { 489 | return fmt.Errorf("failed to commit transaction: %v", err) 490 | } 491 | return nil 492 | } 493 | 494 | // WithTxImmediate is a convenience method that begins an immediate 495 | // transaction, calls the function f, and will commit the transaction if f does 496 | // not return an error, and will roll back the transaction if f does return an 497 | // error. 498 | func (c *Conn) WithTxImmediate(f func() error) error { 499 | if err := c.BeginImmediate(); err != nil { 500 | return fmt.Errorf("failed to begin immediate transaction: %v", err) 501 | } 502 | 503 | // Perform work inside the transaction 504 | err := f() 505 | if err != nil { 506 | err2 := c.Rollback() 507 | if err2 == nil { 508 | return err 509 | } 510 | return fmt.Errorf("%v, additionally rolling back transaction failed: %v", err, err2) 511 | } 512 | 513 | if err = c.Commit(); err != nil { 514 | return fmt.Errorf("failed to commit transaction: %v", err) 515 | } 516 | return nil 517 | } 518 | 519 | // WithTxExclusive is a convenience method that begins a exclusive transaction, 520 | // calls the function f, and will commit the transaction if f does not return 521 | // an error, and will roll back the transaction if f does return an error. 522 | func (c *Conn) WithTxExclusive(f func() error) error { 523 | if err := c.BeginExclusive(); err != nil { 524 | return fmt.Errorf("failed to begin exclusive transaction: %v", err) 525 | } 526 | 527 | // Perform work inside the transaction 528 | err := f() 529 | if err != nil { 530 | err2 := c.Rollback() 531 | if err2 == nil { 532 | return err 533 | } 534 | return fmt.Errorf("%v, additionally rolling back transaction failed: %v", err, err2) 535 | } 536 | 537 | if err = c.Commit(); err != nil { 538 | return fmt.Errorf("failed to commit transaction: %v", err) 539 | } 540 | return nil 541 | } 542 | 543 | // Interrupt causes any pending database operation to abort and return at its 544 | // earliest opportunity. It is safe to call this method from a goroutine 545 | // different from the one that is currently running the database operation, but 546 | // it is not safe to call this method on a connection that might close before 547 | // the call returns. 548 | // https://www.sqlite.org/c3ref/interrupt.html 549 | func (c *Conn) Interrupt() { 550 | if db := c.db; db != nil { 551 | C.sqlite3_interrupt(db) 552 | } 553 | } 554 | 555 | // AutoCommit returns true if the database connection is in auto-commit mode 556 | // (i.e. outside of an explicit transaction started by BEGIN). 557 | // https://www.sqlite.org/c3ref/get_autocommit.html 558 | func (c *Conn) AutoCommit() bool { 559 | return C.sqlite3_get_autocommit(c.db) != 0 560 | } 561 | 562 | // LastInsertRowID returns the ROWID of the most recent successful INSERT 563 | // statement. 564 | // https://www.sqlite.org/c3ref/last_insert_rowid.html 565 | func (c *Conn) LastInsertRowID() int64 { 566 | return int64(C.sqlite3_last_insert_rowid(c.db)) 567 | } 568 | 569 | // Changes returns the number of rows that were changed, inserted, or deleted 570 | // by the most recent statement. Auxiliary changes caused by triggers or 571 | // foreign key actions are not counted. 572 | // https://www.sqlite.org/c3ref/changes.html 573 | func (c *Conn) Changes() int { 574 | return int(C.sqlite3_changes(c.db)) 575 | } 576 | 577 | // TotalChanges returns the number of rows that were changed, inserted, or 578 | // deleted since the database connection was opened, including changes caused by 579 | // trigger and foreign key actions. 580 | // https://www.sqlite.org/c3ref/total_changes.html 581 | func (c *Conn) TotalChanges() int { 582 | return int(C.sqlite3_total_changes(c.db)) 583 | } 584 | 585 | // Backup starts an online database backup of c.srcName into dst.dstName. 586 | // Connections c and dst must be distinct. All existing contents of the 587 | // destination database are overwritten. 588 | // 589 | // A read lock is acquired on the source database only while it is being read 590 | // during a call to Backup.Step. The source connection may be used for other 591 | // purposes between these calls. The destination connection must not be used for 592 | // anything until the backup is closed. 593 | // https://www.sqlite.org/backup.html 594 | func (c *Conn) Backup(srcName string, dst *Conn, dstName string) (*Backup, error) { 595 | if c == dst || dst == nil { 596 | return nil, ErrBadConn 597 | } 598 | return newBackup(c, srcName, dst, dstName) 599 | } 600 | 601 | // BlobIO opens a BLOB or TEXT value for incremental I/O, allowing the value to 602 | // be treated as a file for reading and/or writing. The value is located as if 603 | // by the following query: 604 | // 605 | // SELECT col FROM db.tbl WHERE rowid=row 606 | // 607 | // If rw is true, the value is opened with read-write access, otherwise it is 608 | // read-only. It is not possible to open a column that is part of an index or 609 | // primary key for writing. If foreign key constraints are enabled, it is not 610 | // possible to open a column that is part of a child key for writing. 611 | // https://www.sqlite.org/c3ref/blob_open.html 612 | func (c *Conn) BlobIO(db, tbl, col string, row int64, rw bool) (*BlobIO, error) { 613 | return newBlobIO(c, db, tbl, col, row, rw) 614 | } 615 | 616 | // BusyTimeout enables the built-in busy handler, which retries the table 617 | // locking operation for the specified duration before aborting. The busy 618 | // handler is disabled if d is negative or zero. 619 | // https://www.sqlite.org/c3ref/busy_timeout.html 620 | func (c *Conn) BusyTimeout(d time.Duration) { 621 | C.sqlite3_busy_timeout(c.db, C.int(d/time.Millisecond)) 622 | } 623 | 624 | // BusyFunc registers a function that is invoked by SQLite when it is unable to 625 | // acquire a lock on a table. The function f should return true to make another 626 | // lock acquisition attempt, or false to let the operation fail with BUSY or 627 | // IOERR_BLOCKED error code. 628 | // https://www.sqlite.org/c3ref/busy_handler.html 629 | func (c *Conn) BusyFunc(f BusyFunc) { 630 | idx := busyRegistry.register(f) 631 | c.busyIdx = idx 632 | C.set_busy_handler(c.db, unsafe.Pointer(&c.busyIdx), cBool(f != nil)) 633 | } 634 | 635 | // FileName returns the full file path of an attached database. An empty string 636 | // is returned for temporary databases. 637 | // https://www.sqlite.org/c3ref/db_filename.html 638 | func (c *Conn) FileName(db string) string { 639 | db += "\x00" 640 | if path := C.sqlite3_db_filename(c.db, cStr(db)); path != nil { 641 | return C.GoString(path) 642 | } 643 | return "" 644 | } 645 | 646 | // Status returns the current and peak values of a connection performance 647 | // counter, specified by one of the DBSTATUS constants. If reset is true, the 648 | // peak value is reset back down to the current value after retrieval. 649 | // https://www.sqlite.org/c3ref/db_status.html 650 | func (c *Conn) Status(op int, reset bool) (cur, peak int, err error) { 651 | var cCur, cPeak C.int 652 | rc := C.sqlite3_db_status(c.db, C.int(op), &cCur, &cPeak, cBool(reset)) 653 | if rc != OK { 654 | return 0, 0, pkgErr(MISUSE, "invalid connection status op (%d)", op) 655 | } 656 | return int(cCur), int(cPeak), nil 657 | } 658 | 659 | // Limit changes a per-connection resource usage or performance limit, specified 660 | // by one of the LIMIT constants, returning its previous value. If the new value 661 | // is negative, the limit is left unchanged and its current value is returned. 662 | // https://www.sqlite.org/c3ref/limit.html 663 | func (c *Conn) Limit(id, value int) (prev int) { 664 | prev = int(C.sqlite3_limit(c.db, C.int(id), C.int(value))) 665 | return 666 | } 667 | 668 | // exec calls sqlite3_exec on sql, which must be a null-terminated C string. 669 | func (c *Conn) exec(sql *C.char) error { 670 | if rc := C.sqlite3_exec(c.db, sql, nil, nil, nil); rc != OK { 671 | return libErr(rc, c.db) 672 | } 673 | return nil 674 | } 675 | 676 | // Stmt is a prepared statement handle. 677 | // https://www.sqlite.org/c3ref/stmt.html 678 | type Stmt struct { 679 | Tail string 680 | 681 | stmt *C.sqlite3_stmt 682 | db *C.sqlite3 683 | // Data type codes for all columns in the current row. This is 684 | // unfortunately absolutely necessary to keep around. Column types are 685 | // required in order to differentiate NULL from error conditions, and 686 | // sqlite3_column_type is undefined after a type conversion happens. 687 | colTypes []uint8 688 | haveColTypes bool 689 | } 690 | 691 | // Close releases all resources associated with the prepared statement. This 692 | // method can be called at any point in the statement's life cycle. 693 | // https://www.sqlite.org/c3ref/finalize.html 694 | func (s *Stmt) Close() error { 695 | rc := C.sqlite3_finalize(s.stmt) 696 | s.stmt = nil 697 | if rc != OK { 698 | return libErr(rc, s.db) 699 | } 700 | return nil 701 | } 702 | 703 | // Busy returns true if the prepared statement is in the middle of execution 704 | // with a row available for scanning. 705 | func (s *Stmt) Busy() bool { 706 | return C.sqlite3_stmt_busy(s.stmt) != 0 707 | } 708 | 709 | // ReadOnly returns true if the prepared statement makes no direct changes to 710 | // the content of the database file. 711 | // https://www.sqlite.org/c3ref/stmt_readonly.html 712 | func (s *Stmt) ReadOnly() bool { 713 | return C.sqlite3_stmt_readonly(s.stmt) != 0 714 | } 715 | 716 | // BindParameterCount returns the number of SQL parameters in the prepared 717 | // statement. 718 | // https://www.sqlite.org/c3ref/bind_parameter_count.html 719 | func (s *Stmt) BindParameterCount() int { 720 | return int(C.sqlite3_bind_parameter_count(s.stmt)) 721 | } 722 | 723 | // ColumnCount returns the number of columns produced by the prepared 724 | // statement. 725 | // https://www.sqlite.org/c3ref/column_count.html 726 | func (s *Stmt) ColumnCount() int { 727 | return int(C.sqlite3_column_count(s.stmt)) 728 | } 729 | 730 | // // Params returns the names of bound parameters in the prepared statement. Nil 731 | // // is returned if the statement does not use named parameters. 732 | // // https://www.sqlite.org/c3ref/bind_parameter_name.html 733 | // func (s *Stmt) Params() []string { 734 | // if s.varNames == nil { 735 | // var names []string 736 | // for i := 0; i < s.nVars; i++ { 737 | // name := C.sqlite3_bind_parameter_name(s.stmt, C.int(i+1)) 738 | // if name == nil { 739 | // names = unnamedVars 740 | // break 741 | // } 742 | // if names == nil { 743 | // names = make([]string, s.nVars) 744 | // } 745 | // names[i] = C.GoString(name) 746 | // } 747 | // s.varNames = names 748 | // } 749 | // if len(s.varNames) == 0 { 750 | // return nil // unnamedVars -> nil 751 | // } 752 | // return s.varNames 753 | // } 754 | 755 | // ColumnName returns the name of column produced by the prepared statement. 756 | // The leftmost column is number 0. 757 | // https://www.sqlite.org/c3ref/column_name.html 758 | func (s *Stmt) ColumnName(i int) string { 759 | return C.GoString(C.sqlite3_column_name(s.stmt, C.int(i))) 760 | } 761 | 762 | // ColumnNames returns the names of columns produced by the prepared 763 | // statement. 764 | // https://www.sqlite.org/c3ref/column_name.html 765 | func (s *Stmt) ColumnNames() []string { 766 | nCols := s.ColumnCount() 767 | names := make([]string, nCols) 768 | for i := range names { 769 | names[i] = s.ColumnName(i) 770 | } 771 | return names 772 | } 773 | 774 | // DeclType returns the type declaration of columns produced by the prepared 775 | // statement. The leftmost column is number 0. 776 | // https://www.sqlite.org/c3ref/column_decltype.html 777 | func (s *Stmt) DeclType(i int) string { 778 | return C.GoString(C.sqlite3_column_decltype(s.stmt, C.int(i))) 779 | } 780 | 781 | // DeclTypes returns the type declarations of columns produced by the prepared 782 | // statement. 783 | // https://www.sqlite.org/c3ref/column_decltype.html 784 | func (s *Stmt) DeclTypes() []string { 785 | nCols := s.ColumnCount() 786 | declTypes := make([]string, nCols) 787 | for i := range declTypes { 788 | declTypes[i] = s.DeclType(i) 789 | } 790 | return declTypes 791 | } 792 | 793 | // Exec is a convenience method that binds the given arguments to the statement 794 | // then steps the statement to completion and resets the prepared statement. No 795 | // rows are returned. Reset is always called, even in error cases. Note that 796 | // bindings are not cleared. 797 | func (s *Stmt) Exec(args ...interface{}) error { 798 | err := s.Bind(args...) 799 | if err != nil { 800 | s.Reset() 801 | return err 802 | } 803 | 804 | if err = s.StepToCompletion(); err != nil { 805 | s.Reset() 806 | return err 807 | } 808 | 809 | if err = s.Reset(); err != nil { 810 | return err 811 | } 812 | 813 | return err 814 | } 815 | 816 | // Bind binds either the named arguments or unnamed arguments depending on the 817 | // type of arguments passed 818 | func (s *Stmt) Bind(args ...interface{}) error { 819 | for i, v := range args { 820 | var rc C.int 821 | if v == nil { 822 | rc = C.sqlite3_bind_null(s.stmt, C.int(i+1)) 823 | if rc != OK { 824 | return errStr(rc) 825 | } 826 | continue 827 | } 828 | switch v := v.(type) { 829 | case int: 830 | rc = C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(v)) 831 | case int64: 832 | rc = C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(v)) 833 | case float64: 834 | rc = C.sqlite3_bind_double(s.stmt, C.int(i+1), C.double(v)) 835 | case bool: 836 | rc = C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(cBool(v))) 837 | case string: 838 | rc = C.bind_text(s.stmt, C.int(i+1), cStr(v), C.int(len(v)), 1) 839 | case []byte: 840 | // This is a strange case. nil byte arrays should be treated as inserting NULL 841 | if []byte(v) == nil { 842 | rc = C.sqlite3_bind_null(s.stmt, C.int(i+1)) 843 | } else { 844 | rc = C.bind_blob(s.stmt, C.int(i+1), cBytes(v), C.int(len(v)), 1) 845 | } 846 | case RawString: 847 | rc = C.bind_text(s.stmt, C.int(i+1), cStr(string(v)), C.int(len(v)), 0) 848 | case RawBytes: 849 | rc = C.bind_blob(s.stmt, C.int(i+1), cBytes(v), C.int(len(v)), 0) 850 | case ZeroBlob: 851 | rc = C.sqlite3_bind_zeroblob(s.stmt, C.int(i+1), C.int(v)) 852 | case NamedArgs: 853 | if i != 0 || len(args) != 1 { 854 | return pkgErr(MISUSE, "NamedArgs must be used as the only argument to Bind()") 855 | } 856 | return s.bindNamed(v) 857 | default: 858 | return pkgErr(MISUSE, "unsupported type at index %d (%T)", i, v) 859 | } 860 | if rc != OK { 861 | return errStr(rc) 862 | } 863 | } 864 | return nil 865 | } 866 | 867 | // Scan retrieves data from the current row, storing successive column values 868 | // into successive arguments. The same row may be scanned multiple times. Nil 869 | // arguments are silently skipped. 870 | // https://www.sqlite.org/c3ref/column_blob.html 871 | func (s *Stmt) Scan(dst ...interface{}) error { 872 | n := len(dst) 873 | if n == 0 { 874 | return nil 875 | } 876 | 877 | for i, v := range dst[:n] { 878 | if v != nil { 879 | if err := s.scan(i, v); err != nil { 880 | return err 881 | } 882 | } 883 | } 884 | return nil 885 | } 886 | 887 | // Reset returns the prepared statement to its initial state, ready to be 888 | // re-executed. This should be done when the remaining rows returned by a query 889 | // are not needed, which releases some resources that would otherwise persist 890 | // until the next call to Exec or Query. 891 | // https://www.sqlite.org/c3ref/reset.html 892 | func (s *Stmt) Reset() error { 893 | s.colTypes = s.colTypes[:0] 894 | s.haveColTypes = false 895 | if rc := C.sqlite3_reset(s.stmt); rc != OK { 896 | return errStr(rc) 897 | } 898 | return nil 899 | } 900 | 901 | // ClearBindings clears the bindings on a prepared statement. Reset does not 902 | // clear bindings. 903 | // https://www.sqlite.org/c3ref/clear_bindings.html 904 | func (s *Stmt) ClearBindings() error { 905 | if rc := C.sqlite3_clear_bindings(s.stmt); rc != OK { 906 | return errStr(rc) 907 | } 908 | return nil 909 | } 910 | 911 | // Status returns the current value of a statement performance counter, 912 | // specified by one of the STMTSTATUS constants. If reset is true, the value is 913 | // reset back down to 0 after retrieval. 914 | // https://www.sqlite.org/c3ref/stmt_status.html 915 | func (s *Stmt) Status(op int, reset bool) int { 916 | return int(C.sqlite3_stmt_status(s.stmt, C.int(op), cBool(reset))) 917 | } 918 | 919 | // bindNamed binds statement parameters using the name/value pairs in args. 920 | func (s *Stmt) bindNamed(args NamedArgs) error { 921 | for name, v := range args { 922 | zName := name + "\x00" 923 | i := C.sqlite3_bind_parameter_index(s.stmt, cStr(zName)) 924 | if i == 0 { 925 | // The name wasn't found in the prepared statement 926 | continue 927 | } 928 | 929 | var rc C.int 930 | if v == nil { 931 | rc = C.sqlite3_bind_null(s.stmt, i) 932 | if rc != OK { 933 | return errStr(rc) 934 | } 935 | continue 936 | } 937 | switch v := v.(type) { 938 | case int: 939 | rc = C.sqlite3_bind_int64(s.stmt, i, C.sqlite3_int64(v)) 940 | case int64: 941 | rc = C.sqlite3_bind_int64(s.stmt, i, C.sqlite3_int64(v)) 942 | case float64: 943 | rc = C.sqlite3_bind_double(s.stmt, i, C.double(v)) 944 | case bool: 945 | rc = C.sqlite3_bind_int64(s.stmt, i, C.sqlite3_int64(cBool(v))) 946 | case string: 947 | rc = C.bind_text(s.stmt, i, cStr(v), C.int(len(v)), 1) 948 | case []byte: 949 | // This is a strange case. nil byte arrays should be treated as inserting NULL 950 | if []byte(v) == nil { 951 | rc = C.sqlite3_bind_null(s.stmt, i) 952 | } else { 953 | rc = C.bind_blob(s.stmt, i, cBytes(v), C.int(len(v)), 1) 954 | } 955 | case RawString: 956 | rc = C.bind_text(s.stmt, i, cStr(string(v)), C.int(len(v)), 0) 957 | case RawBytes: 958 | rc = C.bind_blob(s.stmt, i, cBytes(v), C.int(len(v)), 0) 959 | case ZeroBlob: 960 | rc = C.sqlite3_bind_zeroblob(s.stmt, i, C.int(v)) 961 | default: 962 | return pkgErr(MISUSE, "unsupported type for %s (%T)", name, v) 963 | } 964 | if rc != OK { 965 | return errStr(rc) 966 | } 967 | } 968 | return nil 969 | } 970 | 971 | // Step evaluates the next step in the statement's program. It returns true if 972 | // if a new row of data is ready for processing. 973 | // https://www.sqlite.org/c3ref/step.html 974 | func (s *Stmt) Step() (bool, error) { 975 | s.colTypes = s.colTypes[:0] 976 | s.haveColTypes = false 977 | rc := C.sqlite3_blocking_step(s.db, s.stmt) 978 | if rc == ROW { 979 | return true, nil 980 | } 981 | if rc == DONE { 982 | return false, nil 983 | } 984 | return false, errStr(rc) 985 | } 986 | 987 | // StepToCompletion is a convenience method that repeatedly calls Step until no 988 | // more rows are returned or an error occurs. 989 | // https://www.sqlite.org/c3ref/step.html 990 | func (s *Stmt) StepToCompletion() error { 991 | s.colTypes = s.colTypes[:0] 992 | s.haveColTypes = false 993 | for { 994 | rc := C.sqlite3_blocking_step(s.db, s.stmt) 995 | if rc == ROW { 996 | continue 997 | } else if rc == DONE { 998 | break 999 | } else { 1000 | return errStr(rc) 1001 | } 1002 | } 1003 | return nil 1004 | } 1005 | 1006 | // assureColTypes asks SQLite for column types for the current row if we don't 1007 | // currently have them. We must cache the column types since they're important 1008 | // for error detection and their values are undefined after type conversions. 1009 | func (s *Stmt) assureColTypes() { 1010 | if !s.haveColTypes { 1011 | n := s.ColumnCount() 1012 | if cap(s.colTypes) < n { 1013 | s.colTypes = make([]uint8, n) 1014 | } else { 1015 | s.colTypes = s.colTypes[:n] 1016 | } 1017 | C.column_types(s.stmt, (*C.uchar)(cBytes(s.colTypes)), C.int(n)) 1018 | s.haveColTypes = true 1019 | } 1020 | } 1021 | 1022 | // ColumnType returns the data type code of column i in the current row (one of 1023 | // INTEGER, FLOAT, TEXT, BLOB, or NULL). Unlike SQLite, these values are still 1024 | // defined even after type conversion. 1025 | // https://www.sqlite.org/c3ref/column_blob.html 1026 | func (s *Stmt) ColumnType(i int) byte { 1027 | s.assureColTypes() 1028 | return s.colTypes[i] 1029 | } 1030 | 1031 | // ColumnTypes returns the data type codes of columns in the current row. 1032 | // Possible data types are INTEGER, FLOAT, TEXT, BLOB, and NULL. These 1033 | // represent the actual storage classes used by SQLite to store each value. 1034 | // Unlike SQLite, these values are still defined after type conversion. 1035 | // https://www.sqlite.org/c3ref/column_blob.html 1036 | func (s *Stmt) ColumnTypes() []byte { 1037 | s.assureColTypes() 1038 | return s.colTypes 1039 | } 1040 | 1041 | // scan scans the value of column i (starting at 0) into v. 1042 | func (s *Stmt) scan(i int, v interface{}) error { 1043 | var err error 1044 | switch v := v.(type) { 1045 | case *interface{}: 1046 | return s.scanDynamic(i, v) 1047 | case *int: 1048 | *v, _, err = s.ColumnInt(i) 1049 | case *int64: 1050 | *v, _, err = s.ColumnInt64(i) 1051 | case *float64: 1052 | *v, _, err = s.ColumnDouble(i) 1053 | case *bool: 1054 | var b int64 1055 | b, _, err = s.ColumnInt64(i) 1056 | *v = b != 0 1057 | case *string: 1058 | *v, _, err = s.ColumnText(i) 1059 | case *[]byte: 1060 | *v, err = s.ColumnBlob(i) 1061 | case *RawString: 1062 | *v, _, err = s.ColumnRawString(i) 1063 | case *RawBytes: 1064 | *v, err = s.ColumnRawBytes(i) 1065 | case io.Writer: 1066 | _, err = v.Write(blob(s.stmt, C.int(i), false)) 1067 | default: 1068 | return pkgErr(MISUSE, "unscannable type for column %d (%T)", int(i), v) 1069 | } 1070 | if err != nil { 1071 | return err 1072 | } 1073 | return nil 1074 | } 1075 | 1076 | // scanDynamic scans the value of column i (starting at 0) into v, using the 1077 | // column's data type and declaration to select an appropriate representation. 1078 | func (s *Stmt) scanDynamic(i int, v *interface{}) error { 1079 | var err error 1080 | switch typ := s.ColumnType(int(i)); typ { 1081 | case INTEGER: 1082 | *v, _, err = s.ColumnInt64(i) 1083 | case FLOAT: 1084 | *v, _, err = s.ColumnDouble(i) 1085 | case TEXT: 1086 | *v, _, err = s.ColumnText(i) 1087 | case BLOB: 1088 | *v, err = s.ColumnBlob(i) 1089 | case NULL: 1090 | *v = nil 1091 | default: 1092 | *v = nil 1093 | return pkgErr(ERROR, "unknown column type (%d)", typ) 1094 | } 1095 | if err != nil { 1096 | return err 1097 | } 1098 | return nil 1099 | } 1100 | 1101 | // ColumnBlob gets the blob value of column i (starting at 0). If the blob 1102 | // is NULL, then nil is returned. 1103 | // https://www.sqlite.org/c3ref/column_blob.html 1104 | func (s *Stmt) ColumnBlob(i int) (val []byte, err error) { 1105 | s.assureColTypes() 1106 | if i >= len(s.colTypes) { 1107 | return nil, errStr(RANGE) 1108 | } 1109 | if s.colTypes[i] == NULL { 1110 | return nil, nil 1111 | } 1112 | 1113 | n := C.sqlite3_column_bytes(s.stmt, C.int(i)) 1114 | if n == 0 { 1115 | return []byte{}, nil 1116 | } 1117 | 1118 | p := C.sqlite3_column_blob(s.stmt, C.int(i)) 1119 | if p == nil { 1120 | rc := C.sqlite3_errcode(s.db) 1121 | return nil, libErr(rc, s.db) 1122 | } 1123 | 1124 | // Copy the blob 1125 | return C.GoBytes(p, n), nil 1126 | } 1127 | 1128 | // ColumnDouble gets the double value of column i (starting at 0). If the 1129 | // value is NULL, then val is set to 0.0 and ok is set to false. 1130 | // https://www.sqlite.org/c3ref/column_blob.html 1131 | func (s *Stmt) ColumnDouble(i int) (val float64, ok bool, err error) { 1132 | s.assureColTypes() 1133 | if i >= len(s.colTypes) { 1134 | return 0.0, false, errStr(RANGE) 1135 | } 1136 | if s.colTypes[i] == NULL { 1137 | return 0.0, false, nil 1138 | } 1139 | 1140 | val = float64(C.sqlite3_column_double(s.stmt, C.int(i))) 1141 | return val, true, nil 1142 | } 1143 | 1144 | // ColumnInt gets the int value of column i (starting at 0). If the value is 1145 | // NULL, then val is set to 0 and ok is set to false. 1146 | // https://www.sqlite.org/c3ref/column_blob.html 1147 | func (s *Stmt) ColumnInt(i int) (val int, ok bool, err error) { 1148 | s.assureColTypes() 1149 | if i >= len(s.colTypes) { 1150 | return 0, false, errStr(RANGE) 1151 | } 1152 | if s.colTypes[i] == NULL { 1153 | return 0, false, nil 1154 | } 1155 | 1156 | val = int(C.sqlite3_column_int64(s.stmt, C.int(i))) 1157 | return val, true, nil 1158 | } 1159 | 1160 | // ColumnInt64 gets the int64 value of column i (starting at 0). If the 1161 | // value is NULL, then val is set to 0 and ok is set to false. 1162 | // https://www.sqlite.org/c3ref/column_blob.html 1163 | func (s *Stmt) ColumnInt64(i int) (val int64, ok bool, err error) { 1164 | s.assureColTypes() 1165 | if i >= len(s.colTypes) { 1166 | return 0, false, errStr(RANGE) 1167 | } 1168 | if s.colTypes[i] == NULL { 1169 | return 0, false, nil 1170 | } 1171 | 1172 | val = int64(C.sqlite3_column_int64(s.stmt, C.int(i))) 1173 | return val, true, nil 1174 | } 1175 | 1176 | // ColumnText gets the text value of column i (starting at 0). If the value is 1177 | // NULL, then val is set to "" and ok is set to false. 1178 | // https://www.sqlite.org/c3ref/column_blob.html 1179 | func (s *Stmt) ColumnText(i int) (val string, ok bool, err error) { 1180 | s.assureColTypes() 1181 | if i >= len(s.colTypes) { 1182 | return "", false, errStr(RANGE) 1183 | } 1184 | if s.colTypes[i] == NULL { 1185 | return "", false, nil 1186 | } 1187 | 1188 | n := C.sqlite3_column_bytes(s.stmt, C.int(i)) 1189 | if n == 0 { 1190 | return "", true, nil 1191 | } 1192 | 1193 | p := (*C.char)(unsafe.Pointer(C.sqlite3_column_text(s.stmt, C.int(i)))) 1194 | if p == nil { 1195 | rc := C.sqlite3_errcode(s.db) 1196 | return "", false, libErr(rc, s.db) 1197 | } 1198 | 1199 | // Copy the string 1200 | return C.GoStringN(p, n), true, nil 1201 | } 1202 | 1203 | // ColumnBytes gets the size of a blob or UTF-8 text in column i (starting at 1204 | // 0). 1205 | // https://www.sqlite.org/c3ref/column_blob.html 1206 | func (s *Stmt) ColumnBytes(i int) (int, error) { 1207 | s.assureColTypes() 1208 | if i >= len(s.colTypes) { 1209 | return 0, errStr(RANGE) 1210 | } 1211 | 1212 | return int(C.sqlite3_column_bytes(s.stmt, C.int(i))), nil 1213 | } 1214 | 1215 | // ColumnRawBytes gets the blob value of column i (starting at 0). CAUTION: 1216 | // The internal []byte pointer is set to reference memory belonging to SQLite. 1217 | // The memory remains valid until another method is called on the Stmt object 1218 | // and should not be modified. This is similar to ColumnBlob, except faster 1219 | // and less safe. Consider using ColumnBlob unless performance is critical. 1220 | // https://www.sqlite.org/c3ref/column_blob.html 1221 | func (s *Stmt) ColumnRawBytes(i int) (val RawBytes, err error) { 1222 | s.assureColTypes() 1223 | if i >= len(s.colTypes) { 1224 | return nil, errStr(RANGE) 1225 | } 1226 | if s.colTypes[i] == NULL { 1227 | return nil, nil 1228 | } 1229 | 1230 | n := C.sqlite3_column_bytes(s.stmt, C.int(i)) 1231 | if n == 0 { 1232 | return emptyByteSlice, nil 1233 | } 1234 | 1235 | p := C.sqlite3_column_blob(s.stmt, C.int(i)) 1236 | if p == nil { 1237 | rc := C.sqlite3_errcode(s.db) 1238 | return nil, libErr(rc, s.db) 1239 | } 1240 | 1241 | // Don't copy the blob 1242 | return goBytes(p, n), nil 1243 | } 1244 | 1245 | // ColumnRawString gets the text value of column i (starting at 0). CAUTION: 1246 | // The internal string pointer is set to reference memory belonging to SQLite. 1247 | // The memory remains valid until another method is called on the Stmt object 1248 | // and should not be modified. This is similar to ColumnText, except faster 1249 | // and less safe. Consider using ColumnText unless performance is critical. 1250 | // https://www.sqlite.org/c3ref/column_blob.html 1251 | func (s *Stmt) ColumnRawString(i int) (val RawString, ok bool, err error) { 1252 | s.assureColTypes() 1253 | if i >= len(s.colTypes) { 1254 | return "", false, errStr(RANGE) 1255 | } 1256 | if s.colTypes[i] == NULL { 1257 | return "", false, nil 1258 | } 1259 | 1260 | n := C.sqlite3_column_bytes(s.stmt, C.int(i)) 1261 | if n == 0 { 1262 | return "", true, nil 1263 | } 1264 | 1265 | p := (*C.char)(unsafe.Pointer(C.sqlite3_column_text(s.stmt, C.int(i)))) 1266 | if p == nil { 1267 | rc := C.sqlite3_errcode(s.db) 1268 | return "", false, libErr(rc, s.db) 1269 | } 1270 | 1271 | // Don't copy the string 1272 | return RawString(goStrN(p, n)), true, nil 1273 | } 1274 | 1275 | // blob returns the value of column i as a []byte. If copy is false, the []byte 1276 | // will point to memory allocated by SQLite. 1277 | func blob(stmt *C.sqlite3_stmt, i C.int, copy bool) []byte { 1278 | if p := C.sqlite3_column_blob(stmt, i); p != nil { 1279 | n := C.sqlite3_column_bytes(stmt, i) 1280 | if copy { 1281 | return C.GoBytes(p, n) 1282 | } 1283 | return goBytes(p, n) 1284 | } 1285 | return nil 1286 | } 1287 | 1288 | // CommitFunc registers a function that is invoked by SQLite before a 1289 | // transaction is committed. It returns the previous commit handler, if any. If 1290 | // the function f returns true, the transaction is rolled back instead, causing 1291 | // the rollback handler to be invoked, if one is registered. 1292 | // https://www.sqlite.org/c3ref/commit_hook.html 1293 | func (c *Conn) CommitFunc(f CommitFunc) (prev CommitFunc) { 1294 | idx := commitRegistry.register(f) 1295 | prevIdx := c.commitIdx 1296 | c.commitIdx = idx 1297 | C.set_commit_hook(c.db, unsafe.Pointer(&c.commitIdx), cBool(f != nil)) 1298 | prev, _ = commitRegistry.unregister(prevIdx).(CommitFunc) 1299 | return 1300 | } 1301 | 1302 | // RollbackFunc registers a function that is invoked by SQLite when a 1303 | // transaction is rolled back. It returns the previous rollback handler, if any. 1304 | // https://www.sqlite.org/c3ref/commit_hook.html 1305 | func (c *Conn) RollbackFunc(f RollbackFunc) (prev RollbackFunc) { 1306 | idx := rollbackRegistry.register(f) 1307 | prevIdx := c.rollbackIdx 1308 | c.rollbackIdx = idx 1309 | C.set_rollback_hook(c.db, unsafe.Pointer(&c.rollbackIdx), cBool(f != nil)) 1310 | prev, _ = rollbackRegistry.unregister(prevIdx).(RollbackFunc) 1311 | return 1312 | } 1313 | 1314 | // UpdateFunc registers a function that is invoked by SQLite when a row is 1315 | // updated, inserted, or deleted. It returns the previous update handler, if 1316 | // any. 1317 | // https://www.sqlite.org/c3ref/update_hook.html 1318 | func (c *Conn) UpdateFunc(f UpdateFunc) (prev UpdateFunc) { 1319 | idx := updateRegistry.register(f) 1320 | prevIdx := c.updateIdx 1321 | c.updateIdx = idx 1322 | C.set_update_hook(c.db, unsafe.Pointer(&c.updateIdx), cBool(f != nil)) 1323 | prev, _ = updateRegistry.unregister(prevIdx).(UpdateFunc) 1324 | return 1325 | } 1326 | 1327 | // AuthorizerFunc registers a function that is invoked by SQLite During sql 1328 | // statement compilation. Function can return sqlite3.OK to accept statement, 1329 | // sqlite3.IGNORE to disallow specyfic action, but allow further statement 1330 | // processing, or sqlite3.DENY to deny action completly and stop processing. 1331 | // https://www.sqlite.org/c3ref/set_authorizer.html 1332 | func (c *Conn) AuthorizerFunc(f AuthorizerFunc) (prev AuthorizerFunc) { 1333 | idx := authorizerRegistry.register(f) 1334 | prevIdx := c.authorizerIdx 1335 | c.authorizerIdx = idx 1336 | C.set_set_authorizer(c.db, unsafe.Pointer(&c.authorizerIdx), cBool(f != nil)) 1337 | prev, _ = authorizerRegistry.unregister(prevIdx).(AuthorizerFunc) 1338 | return 1339 | } 1340 | -------------------------------------------------------------------------------- /sqlite3/sqlite3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | package sqlite3 5 | 6 | import ( 7 | "bytes" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "math" 13 | "os" 14 | "reflect" 15 | "runtime" 16 | "strings" 17 | "testing" 18 | "time" 19 | "unsafe" 20 | ) 21 | 22 | // skip, when set to true, causes all remaining tests to be skipped. 23 | var skip = false 24 | 25 | type T struct{ *testing.T } 26 | 27 | func begin(t *testing.T) T { 28 | if skip { 29 | t.SkipNow() 30 | } 31 | return T{t} 32 | } 33 | 34 | func (t T) skipRestIfFailed() { 35 | // skip = skip || t.Failed() 36 | } 37 | 38 | func (t T) open(name string) *Conn { 39 | c, err := Open(name) 40 | if c == nil || err != nil { 41 | t.Fatalf(cl("Open(%q) unexpected error: %v"), name, err) 42 | } 43 | return c 44 | } 45 | 46 | func (t T) close(c io.Closer) { 47 | if c != nil { 48 | if db, _ := c.(*Conn); db != nil { 49 | if path := db.FileName("main"); path != "" { 50 | defer os.Remove(path) 51 | } 52 | } 53 | if err := c.Close(); err != nil { 54 | if !t.Failed() { 55 | t.Fatalf(cl("(%T).Close() unexpected error: %v"), c, err) 56 | } 57 | t.FailNow() 58 | } 59 | } 60 | } 61 | 62 | func (t T) prepare(c *Conn, args ...interface{}) (s *Stmt) { 63 | var sql string 64 | var err error 65 | sql = args[0].(string) 66 | s, err = c.Prepare(sql, args[1:]...) 67 | if s == nil || err != nil { 68 | t.Fatalf(cl("(%T).Prepare(%q) unexpected error: %v"), c, sql, err) 69 | } 70 | return 71 | } 72 | 73 | func (t T) bind(s *Stmt, args ...interface{}) { 74 | err := s.Bind(args...) 75 | if s == nil || err != nil { 76 | t.Fatalf(cl("(%T).Bind(%q) unexpected error: %v"), s, args, err) 77 | } 78 | } 79 | 80 | func (t T) exec(cs interface{}, args ...interface{}) { 81 | var sql string 82 | var err error 83 | if c, ok := cs.(*Conn); ok { 84 | sql = args[0].(string) 85 | err = c.Exec(sql, args[1:]...) 86 | } else { 87 | s := cs.(*Stmt) 88 | err = s.Exec(args...) 89 | } 90 | if err != nil { 91 | t.Fatalf(cl("(%T).Exec(%q) unexpected error: %v"), cs, sql, err) 92 | } 93 | } 94 | 95 | func (t T) reset(s *Stmt) { 96 | if err := s.Reset(); err != nil { 97 | t.Fatalf(cl("s.Reset() unexpected error: %v"), err) 98 | } 99 | } 100 | 101 | func (t T) scan(s *Stmt, dst ...interface{}) { 102 | if err := s.Scan(dst...); err != nil { 103 | t.Fatalf(cl("s.Scan() unexpected error: %v"), err) 104 | } 105 | } 106 | 107 | func (t T) step(s *Stmt, wantRow bool) { 108 | haveRow, haveErr := s.Step() 109 | if haveErr != nil { 110 | t.Fatalf(cl("s.Step() expected success; got %v"), haveErr) 111 | } 112 | if haveRow != wantRow { 113 | t.Fatalf(cl("s.Next() expected row %v; got row %v"), wantRow, haveRow) 114 | } 115 | } 116 | 117 | func (t T) stepErr(s *Stmt) { 118 | haveRow, haveErr := s.Step() 119 | if haveErr == nil { 120 | t.Fatalf(cl("s.Step() expected an error; got success")) 121 | } 122 | if haveRow { 123 | t.Fatalf(cl("s.Step() expected an error; which it got, but it also got a row")) 124 | } 125 | } 126 | 127 | func (t T) tmpFile() string { 128 | f, err := ioutil.TempFile("", "go-sqlite.db.") 129 | if err != nil { 130 | t.Fatalf(cl("tmpFile() unexpected error: %v"), err) 131 | } 132 | defer f.Close() 133 | return f.Name() 134 | } 135 | 136 | func (t T) errCode(have error, want int) { 137 | if e, ok := have.(*Error); !ok || e.Code() != want { 138 | t.Fatalf(cl("errCode() expected error code [%d]; got %v"), want, have) 139 | } 140 | } 141 | 142 | func cl(s string) string { 143 | _, thisFile, _, _ := runtime.Caller(1) 144 | _, testFile, line, ok := runtime.Caller(2) 145 | if ok && thisFile == testFile { 146 | return fmt.Sprintf("%d: %s", line, s) 147 | } 148 | return s 149 | } 150 | 151 | func sHdr(s string) *reflect.StringHeader { 152 | return (*reflect.StringHeader)(unsafe.Pointer(&s)) 153 | } 154 | 155 | func bHdr(b []byte) *reflect.SliceHeader { 156 | return (*reflect.SliceHeader)(unsafe.Pointer(&b)) 157 | } 158 | 159 | func TestLib(T *testing.T) { 160 | t := begin(T) 161 | defer t.skipRestIfFailed() 162 | 163 | if v, min := VersionNum(), 3024000; v < min { 164 | t.Errorf("VersionNum() expected >= %d; got %d", min, v) 165 | } 166 | 167 | sql := "CREATE TABLE x(a)" 168 | if Complete(sql) { 169 | t.Errorf("Complete(%q) expected false", sql) 170 | } 171 | if sql += ";"; !Complete(sql) { 172 | t.Errorf("Complete(%q) expected true", sql) 173 | } 174 | } 175 | 176 | func TestCreate(T *testing.T) { 177 | t := begin(T) 178 | defer t.skipRestIfFailed() 179 | 180 | checkPath := func(c *Conn, name, want string) { 181 | if have := c.FileName(name); have != want { 182 | t.Fatalf(cl("c.FileName() expected %q; got %q"), want, have) 183 | } 184 | } 185 | sql := "CREATE TABLE x(a); INSERT INTO x VALUES(1);" 186 | tmp := t.tmpFile() 187 | 188 | // File 189 | os.Remove(tmp) 190 | c := t.open(tmp) 191 | defer t.close(c) 192 | checkPath(c, "main", tmp) 193 | t.exec(c, sql) 194 | if err := c.Close(); err != nil { 195 | t.Fatalf("c.Close() unexpected error: %v", err) 196 | } 197 | if err := c.Exec(sql); err == nil { 198 | t.Fatalf("c.Exec() expected an error") 199 | } 200 | 201 | // URI (existing) 202 | uri := strings.NewReplacer("?", "%3f", "#", "%23").Replace(tmp) 203 | if runtime.GOOS == "windows" { 204 | uri = "/" + strings.Replace(uri, "\\", "/", -1) 205 | } 206 | c = t.open("file:" + uri) 207 | defer t.close(c) 208 | checkPath(c, "main", tmp) 209 | t.exec(c, "INSERT INTO x VALUES(2)") 210 | 211 | // Temporary (in-memory) 212 | c = t.open(":memory:") 213 | defer t.close(c) 214 | checkPath(c, "main", "") 215 | t.exec(c, sql) 216 | 217 | // Temporary (file) 218 | c = t.open("") 219 | defer t.close(c) 220 | checkPath(c, "main", "") 221 | t.exec(c, sql) 222 | } 223 | 224 | func TestPrepare(T *testing.T) { 225 | t := begin(T) 226 | defer t.skipRestIfFailed() 227 | 228 | sql := ` 229 | CREATE TABLE x(a, b, c, d, e); 230 | INSERT INTO x VALUES(NULL, 123, 1.23, 'TEXT', x'424C4F42'); 231 | ` 232 | type row struct { 233 | a interface{} 234 | b int 235 | c float64 236 | d string 237 | e []byte 238 | } 239 | want := &row{nil, 123, 1.23, "TEXT", []byte("BLOB")} 240 | have := &row{} 241 | 242 | c := t.open(":memory:") 243 | defer t.close(c) 244 | t.exec(c, sql) 245 | 246 | s := t.prepare(c, "SELECT * FROM x") 247 | defer t.close(s) 248 | t.step(s, true) 249 | t.scan(s, &have.a, &have.b, &have.c, &have.d, &have.e) 250 | if !reflect.DeepEqual(have, want) { 251 | t.Errorf("s.Scan() expected %v; got %v", want, have) 252 | } 253 | 254 | t.step(s, false) 255 | t.close(s) 256 | s.Bind() 257 | t.stepErr(s) 258 | } 259 | 260 | func TestScan(T *testing.T) { 261 | t := begin(T) 262 | defer t.skipRestIfFailed() 263 | 264 | type types struct { 265 | v interface{} 266 | int int 267 | int64 int64 268 | float64 float64 269 | bool bool 270 | string string 271 | bytes []byte 272 | RawString RawString 273 | RawBytes RawBytes 274 | Writer io.Writer 275 | } 276 | scan := func(s *Stmt, dst ...interface{}) { 277 | // Re-query to avoid interference from type conversion 278 | t.reset(s) 279 | s.ClearBindings() 280 | t.step(s, true) 281 | 282 | t.scan(s, dst...) 283 | } 284 | skipCols := make([]interface{}, 0, 16) 285 | scanNext := func(s *Stmt, have, want *types) { 286 | scan(s, append(skipCols, &have.v)...) 287 | scan(s, append(skipCols, &have.int)...) 288 | scan(s, append(skipCols, &have.int64)...) 289 | scan(s, append(skipCols, &have.float64)...) 290 | scan(s, append(skipCols, &have.bool)...) 291 | scan(s, append(skipCols, &have.string)...) 292 | scan(s, append(skipCols, &have.bytes)...) 293 | scan(s, append(skipCols, have.Writer)...) 294 | 295 | // RawString must be copied, RawBytes (last access) can be used directly 296 | scan(s, append(skipCols, &have.RawString)...) 297 | have.RawString = RawString(have.RawString.Copy()) 298 | scan(s, append(skipCols, &have.RawBytes)...) 299 | 300 | if !reflect.DeepEqual(have, want) { 301 | t.Errorf(cl("scanNext() expected\n%#v; got\n%#v"), want, have) 302 | if have.Writer != want.Writer { 303 | t.Errorf(cl("not equal: expected\n%#v; got\n%#v"), want.Writer, have.Writer) 304 | } 305 | if !reflect.DeepEqual(have.Writer, want.Writer) { 306 | t.Errorf(cl("not deep equal: expected\n%#v; got\n%#v"), want.Writer, have.Writer) 307 | } 308 | t.Fatalf(cl("Failed")) 309 | } 310 | skipCols = append(skipCols, nil) 311 | } 312 | 313 | c := t.open(":memory:") 314 | defer t.close(c) 315 | t.exec(c, ` 316 | CREATE TABLE x(a, b, c, d, e, f, g, h, i); 317 | INSERT INTO x VALUES(NULL, '', x'', 0, 0.0, 4.2, 42, '42', x'3432'); 318 | `) 319 | s := t.prepare(c, "SELECT * FROM x") 320 | defer t.close(s) 321 | 322 | t.step(s, true) 323 | 324 | // Verify data types 325 | wantT := []uint8{NULL, TEXT, BLOB, INTEGER, FLOAT, FLOAT, INTEGER, TEXT, BLOB} 326 | if haveT := s.ColumnTypes(); !reflect.DeepEqual(haveT, wantT) { 327 | t.Fatalf(cl("s.ColumnTypes() expected %v; got %v"), wantT, haveT) 328 | } 329 | 330 | // NULL 331 | have := &types{Writer: new(bytes.Buffer)} 332 | want := &types{Writer: new(bytes.Buffer)} 333 | scanNext(s, have, want) 334 | 335 | // '' 336 | want.v = "" 337 | want.bytes = []byte{} 338 | want.RawBytes = []byte{} 339 | scanNext(s, have, want) 340 | 341 | // x'' 342 | want.v = []byte{} 343 | scanNext(s, have, want) 344 | 345 | // 0 346 | want.v = int64(0) 347 | want.string = "0" 348 | want.bytes = []byte("0") 349 | want.RawString = RawString("0") 350 | want.RawBytes = RawBytes("0") 351 | want.Writer.Write([]byte("0")) 352 | scanNext(s, have, want) 353 | 354 | // 0.0 355 | want.v = 0.0 356 | want.string = "0.0" 357 | want.bytes = []byte("0.0") 358 | want.RawString = RawString("0.0") 359 | want.RawBytes = RawBytes("0.0") 360 | want.Writer.Write([]byte("0.0")) 361 | scanNext(s, have, want) 362 | 363 | // 4.2 364 | want.v = 4.2 365 | want.int = 4 366 | want.int64 = 4 367 | want.float64 = 4.2 368 | want.bool = true 369 | want.string = "4.2" 370 | want.bytes = []byte("4.2") 371 | want.RawString = RawString("4.2") 372 | want.RawBytes = RawBytes("4.2") 373 | want.Writer.Write([]byte("4.2")) 374 | scanNext(s, have, want) 375 | 376 | // 42 377 | want.v = int64(42) 378 | want.int = 42 379 | want.int64 = 42 380 | want.float64 = 42 381 | want.string = "42" 382 | want.bytes = []byte("42") 383 | want.RawString = RawString("42") 384 | want.RawBytes = RawBytes("42") 385 | want.Writer.Write([]byte("42")) 386 | scanNext(s, have, want) 387 | 388 | // '42' 389 | want.v = "42" 390 | want.Writer.Write([]byte("42")) 391 | scanNext(s, have, want) 392 | 393 | // x'3432' 394 | want.v = []byte("42") 395 | want.Writer.Write([]byte("42")) 396 | scanNext(s, have, want) 397 | 398 | // Zero destinations 399 | t.scan(s) 400 | 401 | // Unsupported type 402 | var f32 float32 403 | t.errCode(s.Scan(&f32), MISUSE) 404 | 405 | // EOF 406 | t.step(s, false) 407 | } 408 | 409 | func TestScanDynamic(T *testing.T) { 410 | t := begin(T) 411 | defer t.skipRestIfFailed() 412 | 413 | type row struct { 414 | a, b, c, d interface{} 415 | } 416 | scanNext := func(s *Stmt, have, want *row) { 417 | t.scan(s, &have.a, &have.b, &have.c, &have.d) 418 | if !reflect.DeepEqual(have, want) { 419 | t.Errorf("%s %s\n", reflect.TypeOf(want.c), reflect.TypeOf(have.c)) 420 | t.Errorf("%s %s\n", reflect.TypeOf(want.d), reflect.TypeOf(have.d)) 421 | t.Fatalf(cl("scanNext() expected\n%#v; got\n%#v"), want, have) 422 | } 423 | if haveRow, err := s.Step(); !haveRow { 424 | t.reset(s) 425 | t.step(s, true) 426 | } else if err != nil { 427 | t.Fatalf(cl("s.Next() unexpected error: %v"), err) 428 | } 429 | } 430 | 431 | c := t.open(":memory:") 432 | defer t.close(c) 433 | t.exec(c, ` 434 | -- Affinity: NONE, INTEGER, REAL, TEXT 435 | CREATE TABLE x(a, b INTEGER, c FLOAT, d TEXT); 436 | INSERT INTO x VALUES(NULL, NULL, NULL, NULL); 437 | INSERT INTO x VALUES('', '', '', ''); 438 | INSERT INTO x VALUES(x'', x'', x'', x''); 439 | INSERT INTO x VALUES(0, 0, 0, 0); 440 | INSERT INTO x VALUES(0.0, 0.0, 0.0, 0.0); 441 | INSERT INTO x VALUES(4.2, 4.2, 4.2, 4.2); 442 | INSERT INTO x VALUES(42, 42, 42, 42); 443 | INSERT INTO x VALUES('42', '42', '42', '42'); 444 | INSERT INTO x VALUES(x'3432', x'3432', x'3432', x'3432'); 445 | `) 446 | s := t.prepare(c, "SELECT * FROM x ORDER BY rowid") 447 | defer t.close(s) 448 | t.step(s, true) 449 | 450 | // NULL 451 | have, want := &row{}, &row{} 452 | scanNext(s, have, want) 453 | 454 | // '' 455 | want = &row{"", "", "", ""} 456 | scanNext(s, have, want) 457 | 458 | // x'' 459 | want = &row{[]byte{}, []byte{}, []byte{}, []byte{}} 460 | scanNext(s, have, want) 461 | 462 | // 0 463 | want = &row{int64(0), int64(0), 0.0, "0"} 464 | scanNext(s, have, want) 465 | 466 | // 0.0 467 | want = &row{0.0, int64(0), 0.0, "0.0"} 468 | scanNext(s, have, want) 469 | 470 | // 4.2 471 | want = &row{4.2, 4.2, 4.2, "4.2"} 472 | scanNext(s, have, want) 473 | 474 | // 42 475 | want = &row{int64(42), int64(42), 42.0, "42"} 476 | scanNext(s, have, want) 477 | 478 | // '42' 479 | want = &row{"42", int64(42), 42.0, "42"} 480 | scanNext(s, have, want) 481 | 482 | // x'3432' 483 | want = &row{[]byte("42"), []byte("42"), []byte("42"), []byte("42")} 484 | scanNext(s, have, want) 485 | } 486 | 487 | func TestTail(T *testing.T) { 488 | t := begin(T) 489 | defer t.skipRestIfFailed() 490 | 491 | c := t.open(":memory:") 492 | defer t.close(c) 493 | 494 | check := func(sql, tail string) { 495 | s, _ := c.Prepare(sql) 496 | if s != nil { 497 | defer t.close(s) 498 | } 499 | if s == nil { 500 | return 501 | } 502 | 503 | if s.Tail != tail { 504 | t.Errorf(cl("tail expected %q; got %q"), tail, s.Tail) 505 | } 506 | } 507 | head := "CREATE TABLE x(a);" 508 | tail := " -- comment" 509 | 510 | check("", "") 511 | check(head, "") 512 | check(head+tail, tail) 513 | check(tail, "") 514 | } 515 | 516 | func TestParams(T *testing.T) { 517 | t := begin(T) 518 | defer t.skipRestIfFailed() 519 | 520 | c := t.open(":memory:") 521 | defer t.close(c) 522 | t.exec(c, "CREATE TABLE x(a, b, c, d)") 523 | 524 | dt := func(v interface{}) uint8 { 525 | switch v.(type) { 526 | case int64: 527 | return INTEGER 528 | case float64: 529 | return FLOAT 530 | case string: 531 | return TEXT 532 | case []byte: 533 | return BLOB 534 | } 535 | return NULL 536 | } 537 | verify := func(_a, _b, _c, _d interface{}) { 538 | s := t.prepare(c, "SELECT * FROM x ORDER BY rowid LIMIT 1") 539 | defer t.close(s) 540 | 541 | t.step(s, true) 542 | 543 | wantT := []uint8{dt(_a), dt(_b), dt(_c), dt(_d)} 544 | if haveT := s.ColumnTypes(); !reflect.DeepEqual(haveT, wantT) { 545 | t.Fatalf(cl("s.ColumnTypes() expected %v; got %v"), wantT, haveT) 546 | } 547 | 548 | type row struct{ a, b, c, d interface{} } 549 | want, have := &row{_a, _b, _c, _d}, &row{} 550 | t.scan(s, &have.a, &have.b, &have.c, &have.d) 551 | if !reflect.DeepEqual(have, want) { 552 | t.Fatalf(cl("verify() expected\n%#v; got\n%#v"), want, have) 553 | } 554 | t.exec(c, "DELETE FROM x WHERE rowid=(SELECT min(rowid) FROM x)") 555 | } 556 | 557 | // Unnamed 558 | sql := "INSERT INTO x VALUES(?, ?, ?, ?)" 559 | s := t.prepare(c, sql) 560 | defer t.close(s) 561 | 562 | t.exec(s, nil, nil, nil, nil) 563 | verify(nil, nil, nil, nil) 564 | 565 | t.exec(s, int(0), int(1), int64(math.MinInt64), int64(math.MaxInt64)) 566 | verify(int64(0), int64(1), int64(math.MinInt64), int64(math.MaxInt64)) 567 | 568 | t.exec(s, 0.0, 1.0, math.SmallestNonzeroFloat64, math.MaxFloat64) 569 | verify(0.0, 1.0, math.SmallestNonzeroFloat64, math.MaxFloat64) 570 | 571 | t.exec(s, false, true, "", "x\x00y") 572 | verify(int64(0), int64(1), "", "x\x00y") 573 | 574 | t.exec(s, []byte(nil), []byte{}, []byte{0}, []byte("1")) 575 | verify(nil, []byte{}, []byte{0}, []byte("1")) 576 | 577 | t.exec(s, int64(0), int64(1), RawString(""), RawString("x")) 578 | verify(int64(0), int64(1), "", "x") 579 | 580 | t.exec(s, RawBytes(""), RawBytes("x"), ZeroBlob(0), ZeroBlob(2)) 581 | verify([]byte{}, []byte("x"), []byte{}, []byte{0, 0}) 582 | 583 | // Named 584 | s = t.prepare(c, "INSERT INTO x VALUES(:a, @B, :a, $d)") 585 | defer t.close(s) 586 | 587 | t.exec(s, NamedArgs{}) 588 | verify(nil, nil, nil, nil) 589 | 590 | t.exec(s, 0, 1, 2) 591 | verify(int64(0), int64(1), int64(0), int64(2)) 592 | 593 | t.exec(s, NamedArgs{":a": "a", "@B": "b", "$d": "d", "$c": nil}) 594 | verify("a", "b", "a", "d") 595 | s.ClearBindings() 596 | 597 | t.exec(s, NamedArgs{"@B": RawString("hello"), "$d": RawBytes("world")}) 598 | verify(nil, "hello", nil, []byte("world")) 599 | 600 | // Conn.Prepare 601 | s, err := c.Prepare(sql, 1, 2, 3, 4) 602 | if err != nil { 603 | t.Fatal("failed to insert 1,2,3,4") 604 | } 605 | defer s.Close() 606 | t.step(s, false) 607 | verify(int64(1), int64(2), int64(3), int64(4)) 608 | 609 | // Conn.Exec 610 | t.exec(c, "INSERT INTO x VALUES(?, ?, NULL, NULL)", 1, 2) 611 | t.exec(c, "INSERT INTO x VALUES(NULL, NULL, ?, ?);", 3, 4) 612 | verify(int64(1), int64(2), nil, nil) 613 | verify(nil, nil, int64(3), int64(4)) 614 | 615 | t.exec(c, "INSERT INTO x VALUES($a, $b, NULL, NULL);", NamedArgs{"$a": "a", "$b": "b"}) 616 | t.exec(c, "INSERT INTO x VALUES($a, $a, $c, $d);", NamedArgs{"$a": "a", "$b": "b", "$c": "c", "$d": "d"}) 617 | verify("a", "b", nil, nil) 618 | verify("a", "a", "c", "d") 619 | } 620 | 621 | func TestTx(T *testing.T) { 622 | t := begin(T) 623 | defer t.skipRestIfFailed() 624 | 625 | c := t.open(":memory:") 626 | defer t.close(c) 627 | t.exec(c, "CREATE TABLE x(a)") 628 | 629 | // Begin/Commit 630 | if err := c.Begin(); err != nil { 631 | t.Fatalf("c.Begin() unexpected error: %v", err) 632 | } 633 | t.exec(c, "INSERT INTO x VALUES(1)") 634 | t.exec(c, "INSERT INTO x VALUES(2)") 635 | if err := c.Commit(); err != nil { 636 | t.Fatalf("c.Commit() unexpected error: %v", err) 637 | } 638 | 639 | // Begin/Rollback 640 | if err := c.Begin(); err != nil { 641 | t.Fatalf("c.Begin() unexpected error: %v", err) 642 | } 643 | t.exec(c, "INSERT INTO x VALUES(3)") 644 | t.exec(c, "INSERT INTO x VALUES(4)") 645 | if err := c.Rollback(); err != nil { 646 | t.Fatalf("c.Rollback() unexpected error: %v", err) 647 | } 648 | 649 | // Verify 650 | s := t.prepare(c, "SELECT * FROM x ORDER BY rowid") 651 | defer t.close(s) 652 | t.step(s, true) 653 | 654 | var i int 655 | if t.scan(s, &i); i != 1 { 656 | t.Fatalf("s.Scan() expected 1; got %d", i) 657 | } 658 | t.step(s, true) 659 | if t.scan(s, &i); i != 2 { 660 | t.Fatalf("s.Scan() expected 2; got %d", i) 661 | } 662 | t.step(s, false) 663 | } 664 | 665 | func TestIO(T *testing.T) { 666 | t := begin(T) 667 | 668 | c := t.open(":memory:") 669 | defer t.close(c) 670 | t.exec(c, "CREATE TABLE x(a)") 671 | t.exec(c, "INSERT INTO x VALUES(?)", ZeroBlob(8)) 672 | t.exec(c, "INSERT INTO x VALUES(?)", "hello, world") 673 | 674 | // Open 675 | b, err := c.BlobIO("main", "x", "a", 1, true) 676 | if b == nil || err != nil { 677 | t.Fatalf("c.BlobIO() unexpected error: %v", err) 678 | } 679 | defer t.close(b) 680 | 681 | // State 682 | if b.Conn() != c { 683 | t.Fatalf("b.Conn() expected %p; got %p", c, b.Conn()) 684 | } 685 | if b.Row() != 1 { 686 | t.Fatalf("b.Row() expected 1; got %d", b.Row()) 687 | } 688 | if b.Len() != 8 { 689 | t.Fatalf("b.Len() expected 8; got %d", b.Len()) 690 | } 691 | 692 | // Write 693 | in := []byte("1234567") 694 | if n, err := b.Write(in); n != 7 || err != nil { 695 | t.Fatalf("b.Write(%q) expected 7, ; got %d, %v", in, n, err) 696 | } 697 | in = []byte("89") 698 | if n, err := b.Write(in); n != 0 || err != ErrBlobFull { 699 | t.Fatalf("b.Write(%q) expected 0, ErrBlobFull; got %d, %v", in, n, err) 700 | } 701 | 702 | // Reopen 703 | if err := b.Reopen(2); err != nil { 704 | t.Fatalf("b.Reopen(2) unexpected error: %v", err) 705 | } 706 | if b.Row() != 2 { 707 | t.Fatalf("b.Row() expected 2; got %d", b.Row()) 708 | } 709 | if b.Len() != 12 { 710 | t.Fatalf("b.Len() expected 12; got %d", b.Len()) 711 | } 712 | 713 | // Read 714 | for i := 0; i < 2; i++ { 715 | out := make([]byte, 13) 716 | if n, err := b.Read(out); n != 12 || err != nil { 717 | t.Fatalf("b.Read() #%d expected 12, ; got %d, %v", i, n, err) 718 | } 719 | have := string(out) 720 | if want := "hello, world\x00"; have != want { 721 | t.Fatalf("b.Read() #%d expected %q; got %q", i, have, want) 722 | } 723 | if p, err := b.Seek(0, 0); p != 0 || err != nil { 724 | t.Fatalf("b.Seek() #%d expected 0, ; got %d, %v", i, p, err) 725 | } 726 | } 727 | 728 | // Close 729 | t.close(b) 730 | if err := b.Reopen(1); err != ErrBadIO { 731 | t.Fatalf("b.Reopen(1) expected %v; got %v", ErrBadIO, err) 732 | } 733 | 734 | // Verify 735 | s := t.prepare(c, "SELECT * FROM x ORDER BY rowid") 736 | defer t.close(s) 737 | var have string 738 | t.step(s, true) 739 | t.scan(s, &have) 740 | if want := "1234567\x00"; have != want { 741 | t.Fatalf("s.Scan() expected %q; got %q", want, have) 742 | } 743 | t.step(s, true) 744 | t.scan(s, &have) 745 | if want := "hello, world"; have != want { 746 | t.Fatalf("s.Scan() expected %q; got %q", want, have) 747 | } 748 | t.step(s, false) 749 | } 750 | 751 | func TestBackup(T *testing.T) { 752 | t := begin(T) 753 | 754 | c1, c2 := t.open(":memory:"), t.open(":memory:") 755 | defer t.close(c1) 756 | defer t.close(c2) 757 | t.exec(c1, "CREATE TABLE x(a)") 758 | t.exec(c1, "INSERT INTO x VALUES(?)", "1234567\x00") 759 | t.exec(c1, "INSERT INTO x VALUES(?)", "hello, world") 760 | 761 | // Backup 762 | b, err := c1.Backup("main", c2, "main") 763 | if b == nil || err != nil { 764 | t.Fatalf("b.Backup() unexpected error: %v", err) 765 | } 766 | defer t.close(b) 767 | if pr, pt := b.Progress(); pr != 0 || pt != 0 { 768 | t.Fatalf("b.Progress() expected 0, 0; got %d, %d", pr, pt) 769 | } 770 | if err = b.Step(1); err != nil { 771 | t.Fatalf("b.Step(1) expected ; got %v", err) 772 | } 773 | if pr, pt := b.Progress(); pr != 1 || pt != 2 { 774 | t.Fatalf("b.Progress() expected 1, 2; got %d, %d", pr, pt) 775 | } 776 | if err = b.Step(-1); err != io.EOF { 777 | t.Fatalf("b.Step(-1) expected EOF; got %v", err) 778 | } 779 | 780 | // Close 781 | t.close(b) 782 | if err = b.Step(-1); err != ErrBadBackup { 783 | t.Fatalf("b.Step(-1) expected %v; got %v", ErrBadBackup, err) 784 | } 785 | 786 | // Verify 787 | s := t.prepare(c2, "SELECT * FROM x ORDER BY rowid") 788 | defer t.close(s) 789 | t.step(s, true) 790 | 791 | var have string 792 | t.scan(s, &have) 793 | if want := "1234567\x00"; have != want { 794 | t.Fatalf("s.Scan() expected %q; got %q", want, have) 795 | } 796 | t.step(s, true) 797 | t.scan(s, &have) 798 | if want := "hello, world"; have != want { 799 | t.Fatalf("s.Scan() expected %q; got %q", want, have) 800 | } 801 | t.step(s, false) 802 | } 803 | 804 | func TestSchema(T *testing.T) { 805 | t := begin(T) 806 | 807 | c := t.open(":memory:") 808 | defer t.close(c) 809 | t.exec(c, "CREATE TABLE x(a int)") 810 | t.exec(c, "INSERT INTO x VALUES(1)") 811 | t.exec(c, "INSERT INTO x VALUES(2)") 812 | 813 | checkCols := func(s *Stmt, want ...string) { 814 | if have := s.ColumnCount(); have != len(want) { 815 | t.Fatalf(cl("s.ColumnCount() expected %d; got %d"), len(want), have) 816 | } 817 | if have := s.ColumnNames(); !reflect.DeepEqual(have, want) { 818 | t.Fatalf(cl("s.ColumnNames() expected %v; got %v"), want, have) 819 | } 820 | } 821 | checkDecls := func(s *Stmt, want ...string) { 822 | if have := s.DeclTypes(); !reflect.DeepEqual(have, want) { 823 | t.Fatalf(cl("s.DeclTypes() expected %v; got %v"), want, have) 824 | } 825 | } 826 | s := t.prepare(c, "SELECT * FROM x ORDER BY rowid") 827 | defer t.close(s) 828 | 829 | t.step(s, true) 830 | 831 | checkCols(s, "a") 832 | checkDecls(s, "int") 833 | var a interface{} 834 | t.scan(s, &a) 835 | if a != int64(1) { 836 | t.Fatal("Expected 1, got", a) 837 | } 838 | 839 | // Schema changes do not affect running statements 840 | t.exec(c, "ALTER TABLE x ADD b text") 841 | t.step(s, true) 842 | 843 | checkCols(s, "a") 844 | checkDecls(s, "int") 845 | t.scan(s, &a) 846 | if a != int64(2) { 847 | t.Fatal("Expected 2") 848 | } 849 | t.step(s, false) 850 | 851 | checkCols(s, "a") 852 | checkDecls(s, "int") 853 | t.reset(s) 854 | t.step(s, true) 855 | 856 | checkCols(s, "a", "b") 857 | checkDecls(s, "int", "text") 858 | var b interface{} 859 | t.scan(s, &a, &b) 860 | if a != int64(1) { 861 | t.Fatal("Expected 1") 862 | } 863 | if b != nil { 864 | t.Fatal("Expected nil") 865 | } 866 | t.step(s, true) 867 | 868 | checkCols(s, "a", "b") 869 | checkDecls(s, "int", "text") 870 | t.scan(s, &a, &b) 871 | if a != int64(2) { 872 | t.Fatal("Expected 2") 873 | } 874 | if b != nil { 875 | t.Fatal("Expected nil") 876 | } 877 | t.step(s, false) 878 | } 879 | 880 | func TestUpdateDeleteLimit(T *testing.T) { 881 | t := begin(T) 882 | 883 | c := t.open(":memory:") 884 | defer t.close(c) 885 | t.exec(c, "CREATE TABLE x(a INTEGER PRIMARY KEY)") 886 | t.exec(c, "INSERT INTO x VALUES(?)", 1) 887 | t.exec(c, "INSERT INTO x VALUES(?)", 2) 888 | t.exec(c, "INSERT INTO x VALUES(?)", 3) 889 | t.exec(c, "INSERT INTO x VALUES(?)", 4) 890 | t.exec(c, "INSERT INTO x VALUES(?)", 5) 891 | t.exec(c, "UPDATE x SET a = a + 10 WHERE a >= 1 ORDER BY a LIMIT 2 OFFSET 1") 892 | t.exec(c, "DELETE FROM x WHERE a >= 10 ORDER BY a LIMIT 1 OFFSET 1") 893 | } 894 | 895 | func TestStringNull(T *testing.T) { 896 | t := begin(T) 897 | 898 | c := t.open(":memory:") 899 | defer t.close(c) 900 | t.exec(c, "CREATE TABLE x(a TEXT)") 901 | t.exec(c, "INSERT INTO x VALUES(?)", nil) 902 | s := t.prepare(c, "SELECT * from x") 903 | defer s.Close() 904 | 905 | t.step(s, true) 906 | x := "overwriteme" 907 | s.Scan(&x) 908 | if x != "" { 909 | t.Fatal("Expected empty string") 910 | } 911 | x2, ok, err := s.ColumnText(0) 912 | if x2 != "" { 913 | t.Fatal("Expected empty string") 914 | } 915 | if ok != false { 916 | t.Fatal("Expected ok==false") 917 | } 918 | if err != nil { 919 | t.Fatal("Expected err==nil") 920 | } 921 | } 922 | 923 | func TestStringNullByteSlice(T *testing.T) { 924 | t := begin(T) 925 | 926 | c := t.open(":memory:") 927 | defer t.close(c) 928 | t.exec(c, "CREATE TABLE x(a TEXT)") 929 | t.exec(c, "INSERT INTO x VALUES(?)", nil) 930 | s := t.prepare(c, "SELECT * from x") 931 | defer s.Close() 932 | 933 | t.step(s, true) 934 | var x []byte 935 | s.Scan(&x) 936 | if x != nil { 937 | t.Fatal("Expected nil byte slice") 938 | } 939 | x2, err := s.ColumnBlob(0) 940 | if x2 != nil { 941 | t.Fatal("Expected nil byte slice") 942 | } 943 | if err != nil { 944 | t.Fatal("Expected err==nil") 945 | } 946 | } 947 | 948 | func TestRawStringNull(T *testing.T) { 949 | t := begin(T) 950 | 951 | c := t.open(":memory:") 952 | defer t.close(c) 953 | t.exec(c, "CREATE TABLE x(a TEXT)") 954 | t.exec(c, "INSERT INTO x VALUES(?)", nil) 955 | } 956 | 957 | func TestTxHandler(T *testing.T) { 958 | t := begin(T) 959 | defer t.skipRestIfFailed() 960 | 961 | c := t.open(":memory:") 962 | defer t.close(c) 963 | t.exec(c, "CREATE TABLE x(a)") 964 | 965 | commit := 0 966 | rollback := 0 967 | c.CommitFunc(func() (abort bool) { commit++; return commit >= 2 }) 968 | c.RollbackFunc(func() { rollback++ }) 969 | 970 | // Allow 971 | c.Begin() 972 | t.exec(c, "INSERT INTO x VALUES(1)") 973 | t.exec(c, "INSERT INTO x VALUES(2)") 974 | if err := c.Commit(); err != nil { 975 | t.Fatalf("c.Commit() unexpected error: %v", err) 976 | } 977 | 978 | // Deny 979 | c.Begin() 980 | t.exec(c, "INSERT INTO x VALUES(3)") 981 | t.exec(c, "INSERT INTO x VALUES(4)") 982 | t.errCode(c.Commit(), CONSTRAINT_COMMITHOOK) 983 | 984 | // Verify 985 | if commit != 2 || rollback != 1 { 986 | t.Fatalf("commit/rollback expected 2/1; got %d/%d", commit, rollback) 987 | } 988 | s := t.prepare(c, "SELECT * FROM x ORDER BY rowid") 989 | defer t.close(s) 990 | var i int 991 | t.step(s, true) 992 | if t.scan(s, &i); i != 1 { 993 | t.Fatalf("s.Scan() expected 1; got %d", i) 994 | } 995 | t.step(s, true) 996 | if t.scan(s, &i); i != 2 { 997 | t.Fatalf("s.Scan() expected 2; got %d", i) 998 | } 999 | t.step(s, false) 1000 | } 1001 | 1002 | func TestUpdateHandler(T *testing.T) { 1003 | t := begin(T) 1004 | defer t.skipRestIfFailed() 1005 | 1006 | c := t.open(":memory:") 1007 | defer t.close(c) 1008 | t.exec(c, "CREATE TABLE x(a)") 1009 | 1010 | type update struct { 1011 | op int 1012 | db, tbl string 1013 | row int64 1014 | } 1015 | var have *update 1016 | verify := func(want *update) { 1017 | if !reflect.DeepEqual(have, want) { 1018 | t.Fatalf(cl("verify() expected %v; got %v"), want, have) 1019 | } 1020 | } 1021 | c.UpdateFunc(func(op int, db, tbl RawString, row int64) { 1022 | have = &update{op, db.Copy(), tbl.Copy(), row} 1023 | }) 1024 | 1025 | t.exec(c, "INSERT INTO x VALUES(1)") 1026 | verify(&update{INSERT, "main", "x", 1}) 1027 | 1028 | t.exec(c, "INSERT INTO x VALUES(2)") 1029 | verify(&update{INSERT, "main", "x", 2}) 1030 | 1031 | t.exec(c, "UPDATE x SET a=3 WHERE rowid=1") 1032 | verify(&update{UPDATE, "main", "x", 1}) 1033 | 1034 | t.exec(c, "DELETE FROM x WHERE rowid=2") 1035 | verify(&update{DELETE, "main", "x", 2}) 1036 | } 1037 | 1038 | func TestAuthorizerHandler(T *testing.T) { 1039 | t := begin(T) 1040 | defer t.skipRestIfFailed() 1041 | 1042 | c := t.open(":memory:") 1043 | defer t.close(c) 1044 | t.exec(c, "CREATE TABLE x(a)") 1045 | 1046 | type authorizer struct { 1047 | op int 1048 | arg1, arg2, db, entity string 1049 | } 1050 | var have *authorizer 1051 | verify := func(want *authorizer) { 1052 | if !reflect.DeepEqual(have, want) { 1053 | t.Fatalf(cl("verify() expected %v; got %v"), want, have) 1054 | } 1055 | } 1056 | c.AuthorizerFunc(func(op int, arg1, arg2, db, entity RawString) int { 1057 | have = &authorizer{op, arg1.Copy(), arg2.Copy(), db.Copy(), entity.Copy()} 1058 | return OK 1059 | }) 1060 | 1061 | t.exec(c, "INSERT INTO x VALUES(1)") 1062 | verify(&authorizer{INSERT, "x", "", "main", ""}) 1063 | 1064 | t.exec(c, "SELECT * FROM x") 1065 | verify(&authorizer{READ, "x", "a", "main", ""}) 1066 | } 1067 | 1068 | func TestBusyHandler(T *testing.T) { 1069 | t := begin(T) 1070 | 1071 | tmp := t.tmpFile() 1072 | c1 := t.open(tmp) 1073 | defer t.close(c1) 1074 | c2 := t.open(tmp) 1075 | defer t.close(c2) 1076 | t.exec(c1, "CREATE TABLE x(a); BEGIN; INSERT INTO x VALUES(1);") 1077 | 1078 | try := func(sql string) { 1079 | err := c2.Exec(sql) 1080 | t.errCode(err, BUSY) 1081 | } 1082 | 1083 | // Default 1084 | try("INSERT INTO x VALUES(2)") 1085 | 1086 | // Built-in 1087 | c2.BusyTimeout(100 * time.Millisecond) 1088 | try("INSERT INTO x VALUES(3)") 1089 | 1090 | // Custom 1091 | calls := 0 1092 | handler := func(count int) (retry bool) { 1093 | calls++ 1094 | time.Sleep(10 * time.Millisecond) 1095 | return calls == count+1 && calls < 10 1096 | } 1097 | c2.BusyFunc(handler) 1098 | try("INSERT INTO x VALUES(4)") 1099 | if calls != 10 { 1100 | t.Fatalf("calls expected 10; got %d", calls) 1101 | } 1102 | 1103 | // Disable 1104 | c2.BusyTimeout(0) 1105 | try("INSERT INTO x VALUES(5)") 1106 | } 1107 | 1108 | func TestLocked(T *testing.T) { 1109 | t := begin(T) 1110 | defer t.skipRestIfFailed() 1111 | 1112 | c := t.open(":memory:") 1113 | defer t.close(c) 1114 | t.exec(c, "CREATE TABLE x(a)") 1115 | 1116 | // Allow 1117 | c.Begin() 1118 | t.exec(c, "INSERT INTO x VALUES(1)") 1119 | t.exec(c, "INSERT INTO x VALUES(2)") 1120 | if err := c.Commit(); err != nil { 1121 | t.Fatalf("c.Commit() unexpected error: %v", err) 1122 | } 1123 | 1124 | s := t.prepare(c, "SELECT * FROM x ORDER BY rowid") 1125 | defer t.close(s) 1126 | t.step(s, true) 1127 | 1128 | s2 := t.prepare(c, "DROP TABLE x") 1129 | defer s2.Close() 1130 | _, err := s2.Step() 1131 | if err == nil { 1132 | t.Fatalf("expected SQLITE_LOCKED") 1133 | } 1134 | } 1135 | 1136 | func TestWithTx(T *testing.T) { 1137 | t := begin(T) 1138 | conn, err := Open(":memory:", OPEN_READWRITE) 1139 | if err != nil { 1140 | t.Fatalf("failed to open") 1141 | } 1142 | 1143 | err = conn.Exec(`CREATE TABLE student(name TEXT, age INTEGER)`) 1144 | if err != nil { 1145 | t.Fatalf("failed to create table") 1146 | } 1147 | 1148 | err = conn.WithTx(func() error { 1149 | err = conn.Exec(`INSERT INTO student VALUES (?, ?)`, "Bob", 18) 1150 | if err != nil { 1151 | return err 1152 | } 1153 | return nil 1154 | }) 1155 | if err != nil { 1156 | t.Fatalf("failed to insert student") 1157 | } 1158 | 1159 | aberr := errors.New("abort operations") 1160 | err = conn.WithTx(func() error { 1161 | return aberr 1162 | }) 1163 | if err != aberr { 1164 | t.Fatalf("WithTx hasn't returned the original error") 1165 | } 1166 | 1167 | err = conn.WithTxExclusive(func() error { 1168 | err = conn.Exec(`INSERT INTO student VALUES (?, ?)`, "Bob", 18) 1169 | if err != nil { 1170 | return err 1171 | } 1172 | return nil 1173 | }) 1174 | if err != nil { 1175 | t.Fatalf("failed to insert student") 1176 | } 1177 | 1178 | err = conn.WithTxExclusive(func() error { 1179 | return aberr 1180 | }) 1181 | if err != aberr { 1182 | t.Fatalf("WithTxExclusive hasn't returned the original error") 1183 | } 1184 | 1185 | err = conn.WithTxImmediate(func() error { 1186 | err = conn.Exec(`INSERT INTO student VALUES (?, ?)`, "Bob", 18) 1187 | if err != nil { 1188 | return err 1189 | } 1190 | return nil 1191 | }) 1192 | if err != nil { 1193 | t.Fatalf("failed to insert student") 1194 | } 1195 | 1196 | err = conn.WithTxImmediate(func() error { 1197 | return aberr 1198 | }) 1199 | if err != aberr { 1200 | t.Fatalf("WithTxImmediate hasn't returned the original error") 1201 | } 1202 | } 1203 | 1204 | func TestOutOfRange(T *testing.T) { 1205 | t := begin(T) 1206 | 1207 | c := t.open(":memory:") 1208 | defer t.close(c) 1209 | t.exec(c, "CREATE TABLE x(a TEXT)") 1210 | t.exec(c, "INSERT INTO x VALUES(?)", nil) 1211 | s := t.prepare(c, "SELECT * from x") 1212 | defer s.Close() 1213 | 1214 | t.step(s, true) 1215 | x := "overwriteme" 1216 | s.Scan(&x) 1217 | if x != "" { 1218 | t.Fatal("Expected empty string") 1219 | } 1220 | _, _, err := s.ColumnText(1) 1221 | if err == nil { 1222 | t.Fatal("Expected out of range error") 1223 | } 1224 | _, err = s.ColumnBlob(1) 1225 | if err == nil { 1226 | t.Fatal("Expected out of range error") 1227 | } 1228 | _, _, err = s.ColumnDouble(1) 1229 | if err == nil { 1230 | t.Fatal("Expected out of range error") 1231 | } 1232 | _, _, err = s.ColumnInt(1) 1233 | if err == nil { 1234 | t.Fatal("Expected out of range error") 1235 | } 1236 | _, _, err = s.ColumnText(1) 1237 | if err == nil { 1238 | t.Fatal("Expected out of range error") 1239 | } 1240 | _, err = s.ColumnBytes(1) 1241 | if err == nil { 1242 | t.Fatal("Expected out of range error") 1243 | } 1244 | _, err = s.ColumnRawBytes(1) 1245 | if err == nil { 1246 | t.Fatal("Expected out of range error") 1247 | } 1248 | _, _, err = s.ColumnRawString(1) 1249 | if err == nil { 1250 | t.Fatal("Expected out of range error") 1251 | } 1252 | } 1253 | -------------------------------------------------------------------------------- /sqlite3/upgrading.md: -------------------------------------------------------------------------------- 1 | # Upgrading sqlite 2 | 3 | We need to make our own amalgamation, since we want to enable `SQLITE_ENABLE_UPDATE_DELETE_LIMIT` during the parser generator phase. 4 | 5 | `-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1` seems to be the only important option when creating the amalgamation. 6 | 7 | ```sh 8 | wget https://www.sqlite.org/2018/sqlite-src-XXXXXXX.zip 9 | unzip sqlite-src-XXXXXXX.zip 10 | cd sqlite-src-XXXXXXX 11 | CFLAGS='-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1' ./configure 12 | make sqlite3.c 13 | cp sqlite3.c sqlite3.h ~/go/src/github.com/bvinc/go-sqlite-lite/sqlite3/ 14 | ``` 15 | -------------------------------------------------------------------------------- /sqlite3/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-sqlite-lite Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlite3 6 | 7 | /* 8 | #include "sqlite3.h" 9 | */ 10 | import "C" 11 | 12 | import ( 13 | "fmt" 14 | "reflect" 15 | "sync" 16 | "unsafe" 17 | ) 18 | 19 | // NamedArgs is a name/value map of arguments passed to a prepared statement 20 | // that uses ?NNN, :AAA, @AAA, and/or $AAA parameter formats. Name matching is 21 | // case-sensitive and the prefix character (one of [?:@$]) must be included in 22 | // the name. Names that are missing from the map are treated as NULL. Names that 23 | // are not used in the prepared statement are ignored. 24 | // 25 | // It is not possible to mix named and anonymous ("?") parameters in the same 26 | // statement. 27 | // https://www.sqlite.org/lang_expr.html#varparam 28 | type NamedArgs map[string]interface{} 29 | 30 | type ( 31 | // RawString is a special string type that may be used for database input and 32 | // output without the cost of an extra copy operation. 33 | // 34 | // When used as an argument to a statement, the contents are bound using 35 | // SQLITE_STATIC instead of SQLITE_TRANSIENT flag. This requires the contents to 36 | // remain valid and unmodified until the end of statement execution. In 37 | // particular, the caller must keep a reference to the value to prevent it from 38 | // being garbage collected. 39 | // 40 | // When used for retrieving query output, the internal string pointer is set 41 | // to reference memory belonging to SQLite. The memory remains valid until 42 | // another method is called on the Stmt object and should not be modified. 43 | RawString string 44 | // RawBytes is a special string type that may be used for database input and 45 | // output without the cost of an extra copy operation. 46 | // 47 | // When used as an argument to a statement, the contents are bound using 48 | // SQLITE_STATIC instead of SQLITE_TRANSIENT flag. This requires the contents to 49 | // remain valid and unmodified until the end of statement execution. In 50 | // particular, the caller must keep a reference to the value to prevent it from 51 | // being garbage collected. 52 | // 53 | // When used for retrieving query output, the internal []byte pointer is set 54 | // to reference memory belonging to SQLite. The memory remains valid until 55 | // another method is called on the Stmt object and should not be modified. 56 | RawBytes []byte 57 | ) 58 | 59 | // Copy returns a Go-managed copy of s. 60 | func (s RawString) Copy() string { 61 | if s == "" { 62 | return "" 63 | } 64 | h := (*reflect.StringHeader)(unsafe.Pointer(&s)) 65 | return C.GoStringN((*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len)) 66 | } 67 | 68 | // Copy returns a Go-managed copy of b. 69 | func (b RawBytes) Copy() []byte { 70 | if len(b) == 0 { 71 | if b == nil { 72 | return nil 73 | } 74 | return []byte("") 75 | } 76 | h := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 77 | return C.GoBytes(unsafe.Pointer(h.Data), C.int(h.Len)) 78 | } 79 | 80 | type Value struct { 81 | ptr *C.sqlite3_value 82 | } 83 | 84 | // ZeroBlob is a special argument type used to allocate a zero-filled BLOB of 85 | // the specified length. The BLOB can then be opened for incremental I/O to 86 | // efficiently transfer a large amount of data. The maximum BLOB size can be 87 | // queried with Conn.Limit(LIMIT_LENGTH, -1). 88 | type ZeroBlob int 89 | 90 | // BusyFunc is a callback function invoked by SQLite when it is unable to 91 | // acquire a lock on a table. Count is the number of times that the callback has 92 | // been invoked for this locking event so far. If the function returns false, 93 | // then the operation is aborted. Otherwise, the function should block for a 94 | // while before returning true and letting SQLite make another locking attempt. 95 | type BusyFunc func(count int) (retry bool) 96 | 97 | // CommitFunc is a callback function invoked by SQLite before a transaction is 98 | // committed. If the function returns true, the transaction is rolled back. 99 | type CommitFunc func() (abort bool) 100 | 101 | // RollbackFunc is a callback function invoked by SQLite when a transaction is 102 | // rolled back. 103 | type RollbackFunc func() 104 | 105 | // UpdateFunc is a callback function invoked by SQLite when a row is updated, 106 | // inserted, or deleted. 107 | type UpdateFunc func(op int, db, tbl RawString, row int64) 108 | 109 | // AuthorizerFunc is a callback function invoked by SQLite when statement is compiled. 110 | type AuthorizerFunc func(op int, arg1, arg2, db, entity RawString) int 111 | 112 | // Error is returned for all SQLite API result codes other than OK, ROW, and 113 | // DONE. 114 | type Error struct { 115 | rc int 116 | msg string 117 | } 118 | 119 | // NewError creates a new Error instance using the specified SQLite result code 120 | // and error message. 121 | func NewError(rc int, msg string) *Error { 122 | return &Error{rc, msg} 123 | } 124 | 125 | func errStr(rc C.int) error { 126 | return &Error{int(rc), C.GoString(C.sqlite3_errstr(rc))} 127 | } 128 | 129 | // libErr reports an error originating in SQLite. The error message is obtained 130 | // from the database connection when possible, which may include some additional 131 | // information. Otherwise, the result code is translated to a generic message. 132 | func libErr(rc C.int, db *C.sqlite3) error { 133 | if db != nil && rc == C.sqlite3_errcode(db) { 134 | return &Error{int(rc), C.GoString(C.sqlite3_errmsg(db))} 135 | } 136 | return &Error{int(rc), C.GoString(C.sqlite3_errstr(rc))} 137 | } 138 | 139 | // pkgErr reports an error originating in this package. 140 | func pkgErr(rc int, msg string, v ...interface{}) error { 141 | if len(v) == 0 { 142 | return &Error{rc, msg} 143 | } 144 | return &Error{rc, fmt.Sprintf(msg, v...)} 145 | } 146 | 147 | // Code returns the SQLite extended result code. 148 | func (err *Error) Code() int { 149 | return err.rc 150 | } 151 | 152 | // Error implements the error interface. 153 | func (err *Error) Error() string { 154 | return fmt.Sprintf("sqlite3: %s [%d]", err.msg, err.rc) 155 | } 156 | 157 | // Errors returned for access attempts to closed or invalid objects. 158 | var ( 159 | ErrBadConn = &Error{MISUSE, "closed or invalid connection"} 160 | ErrBadIO = &Error{MISUSE, "closed or invalid incremental I/O operation"} 161 | ErrBadBackup = &Error{MISUSE, "closed or invalid backup operation"} 162 | ) 163 | 164 | // Complete returns true if sql appears to contain a complete statement that is 165 | // ready to be parsed. This does not validate the statement syntax. 166 | // https://www.sqlite.org/c3ref/complete.html 167 | func Complete(sql string) bool { 168 | if initErr != nil { 169 | return false 170 | } 171 | sql += "\x00" 172 | return C.sqlite3_complete(cStr(sql)) == 1 173 | } 174 | 175 | // ReleaseMemory attempts to free n bytes of heap memory by deallocating 176 | // non-essential memory held by the SQLite library. It returns the number of 177 | // bytes actually freed. 178 | // 179 | // This function is currently a no-op because SQLite is not compiled with the 180 | // SQLITE_ENABLE_MEMORY_MANAGEMENT option. 181 | // https://www.sqlite.org/c3ref/release_memory.html 182 | func ReleaseMemory(n int) int { 183 | if initErr != nil { 184 | return 0 185 | } 186 | return int(C.sqlite3_release_memory(C.int(n))) 187 | } 188 | 189 | // SoftHeapLimit sets and/or queries the soft limit on the amount of heap memory 190 | // that may be allocated by SQLite. A negative value for n keeps the current 191 | // limit, while 0 removes the limit. The previous limit value is returned, with 192 | // negative values indicating an error. 193 | // https://www.sqlite.org/c3ref/soft_heap_limit64.html 194 | func SoftHeapLimit(n int64) int64 { 195 | if initErr != nil { 196 | return -1 197 | } 198 | return int64(C.sqlite3_soft_heap_limit64(C.sqlite3_int64(n))) 199 | } 200 | 201 | // SourceID returns the check-in identifier of SQLite within its configuration 202 | // management system. 203 | // https://www.sqlite.org/c3ref/c_source_id.html 204 | func SourceID() string { 205 | if initErr != nil { 206 | return "" 207 | } 208 | return C.GoString(C.sqlite3_sourceid()) 209 | } 210 | 211 | // Status returns the current and peak values of a core performance 212 | // counter, specified by one of the STATUS constants. If reset is true, the peak 213 | // value is reset back down to the current value after retrieval. 214 | // https://www.sqlite.org/c3ref/status.html 215 | func Status(op int, reset bool) (cur, peak int, err error) { 216 | if initErr != nil { 217 | return 0, 0, initErr 218 | } 219 | var cCur, cPeak C.int 220 | rc := C.sqlite3_status(C.int(op), &cCur, &cPeak, cBool(reset)) 221 | if rc != OK { 222 | return 0, 0, pkgErr(MISUSE, "invalid status op (%d)", op) 223 | } 224 | return int(cCur), int(cPeak), nil 225 | } 226 | 227 | // Version returns the SQLite version as a string in the format "X.Y.Z[.N]". 228 | // https://www.sqlite.org/c3ref/libversion.html 229 | func Version() string { 230 | if initErr != nil { 231 | return "" 232 | } 233 | return goStr(C.sqlite3_libversion()) 234 | } 235 | 236 | // VersionNum returns the SQLite version as an integer in the format X*1000000 + 237 | // Y*1000 + Z, where X is the major version, Y is the minor version, and Z is 238 | // the release number. 239 | func VersionNum() int { 240 | if initErr != nil { 241 | return 0 242 | } 243 | return int(C.sqlite3_libversion_number()) 244 | } 245 | 246 | // raw casts s to a RawString. 247 | func raw(s string) RawString { 248 | return RawString(s) 249 | } 250 | 251 | // cStr returns a pointer to the first byte in s. 252 | func cStr(s string) *C.char { 253 | h := (*reflect.StringHeader)(unsafe.Pointer(&s)) 254 | return (*C.char)(unsafe.Pointer(h.Data)) 255 | } 256 | 257 | // cStrOffset returns the offset of p in s or -1 if p doesn't point into s. 258 | func cStrOffset(s string, p *C.char) int { 259 | h := (*reflect.StringHeader)(unsafe.Pointer(&s)) 260 | if off := uintptr(unsafe.Pointer(p)) - h.Data; off < uintptr(h.Len) { 261 | return int(off) 262 | } 263 | return -1 264 | } 265 | 266 | // cBytes returns a pointer to the first byte in b. 267 | func cBytes(b []byte) unsafe.Pointer { 268 | return unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&b)).Data) 269 | } 270 | 271 | // cBool returns a C representation of a Go bool (false = 0, true = 1). 272 | func cBool(b bool) C.int { 273 | if b { 274 | return 1 275 | } 276 | return 0 277 | } 278 | 279 | // goStr returns a Go representation of a null-terminated C string. 280 | func goStr(p *C.char) (s string) { 281 | if p != nil && *p != 0 { 282 | h := (*reflect.StringHeader)(unsafe.Pointer(&s)) 283 | h.Data = uintptr(unsafe.Pointer(p)) 284 | for *p != 0 { 285 | p = (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1)) // p++ 286 | } 287 | h.Len = int(uintptr(unsafe.Pointer(p)) - h.Data) 288 | } 289 | return 290 | } 291 | 292 | // goStrN returns a Go representation of an n-byte C string. 293 | func goStrN(p *C.char, n C.int) (s string) { 294 | if n > 0 { 295 | h := (*reflect.StringHeader)(unsafe.Pointer(&s)) 296 | h.Data = uintptr(unsafe.Pointer(p)) 297 | h.Len = int(n) 298 | } 299 | return 300 | } 301 | 302 | // goBytes returns a Go representation of an n-byte C array. 303 | func goBytes(p unsafe.Pointer, n C.int) (b []byte) { 304 | if n > 0 { 305 | h := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 306 | h.Data = uintptr(p) 307 | h.Len = int(n) 308 | h.Cap = int(n) 309 | } 310 | return 311 | } 312 | 313 | type registry struct { 314 | mu *sync.Mutex 315 | index int 316 | vals map[int]interface{} 317 | } 318 | 319 | func newRegistry() *registry { 320 | return ®istry{ 321 | mu: &sync.Mutex{}, 322 | index: 0, 323 | vals: make(map[int]interface{}), 324 | } 325 | } 326 | 327 | func (r *registry) register(val interface{}) int { 328 | r.mu.Lock() 329 | defer r.mu.Unlock() 330 | r.index++ 331 | for r.vals[r.index] != nil || r.index == 0 { 332 | r.index++ 333 | } 334 | r.vals[r.index] = val 335 | return r.index 336 | } 337 | 338 | func (r *registry) lookup(i int) interface{} { 339 | r.mu.Lock() 340 | defer r.mu.Unlock() 341 | return r.vals[i] 342 | } 343 | 344 | func (r *registry) unregister(i int) interface{} { 345 | r.mu.Lock() 346 | defer r.mu.Unlock() 347 | prev := r.vals[i] 348 | delete(r.vals, i) 349 | return prev 350 | } 351 | 352 | //export go_busy_handler 353 | func go_busy_handler(data unsafe.Pointer, count C.int) (retry C.int) { 354 | idx := *(*int)(data) 355 | fn := busyRegistry.lookup(idx).(BusyFunc) 356 | return cBool(fn(int(count))) 357 | } 358 | 359 | //export go_commit_hook 360 | func go_commit_hook(data unsafe.Pointer) (abort C.int) { 361 | idx := *(*int)(data) 362 | fn := commitRegistry.lookup(idx).(CommitFunc) 363 | return cBool(fn()) 364 | } 365 | 366 | //export go_rollback_hook 367 | func go_rollback_hook(data unsafe.Pointer) { 368 | idx := *(*int)(data) 369 | fn := rollbackRegistry.lookup(idx).(RollbackFunc) 370 | fn() 371 | } 372 | 373 | //export go_update_hook 374 | func go_update_hook(data unsafe.Pointer, op C.int, db, tbl *C.char, row C.sqlite3_int64) { 375 | idx := *(*int)(data) 376 | fn := updateRegistry.lookup(idx).(UpdateFunc) 377 | fn(int(op), raw(goStr(db)), raw(goStr(tbl)), int64(row)) 378 | } 379 | 380 | //export go_set_authorizer 381 | func go_set_authorizer(data unsafe.Pointer, op C.int, arg1, arg2, db, entity *C.char) C.int { 382 | idx := *(*int)(data) 383 | fn := authorizerRegistry.lookup(idx).(AuthorizerFunc) 384 | return C.int(fn(int(op), raw(goStr(arg1)), raw(goStr(arg2)), raw(goStr(db)), raw(goStr(entity)))) 385 | } 386 | --------------------------------------------------------------------------------