├── embed.db ├── embed2.db ├── compat └── compat.go ├── norlimit.go ├── benchmark ├── util_test.go ├── go.mod ├── README.md ├── bench_test.go ├── util.go ├── bench.go └── go.sum ├── rlimit.go ├── .github ├── dependabot.yml └── workflows │ ├── tests.yml │ └── badge-sqlite-version.yml ├── rulimit.go ├── null_test.go ├── mutex.go ├── go.mod ├── sqlite_version_test.go ├── AUTHORS ├── CONTRIBUTORS ├── sqlite_go18_test.go ├── examples └── example1 │ └── main.go ├── LICENSE ├── SQLITE-LICENSE ├── addport.go ├── sqlite_go18.go ├── README.md ├── go.sum ├── functest └── func_test.go ├── sqlite.go └── all_test.go /embed.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glebarez/go-sqlite/HEAD/embed.db -------------------------------------------------------------------------------- /embed2.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glebarez/go-sqlite/HEAD/embed2.db -------------------------------------------------------------------------------- /compat/compat.go: -------------------------------------------------------------------------------- 1 | package compat 2 | 3 | import "github.com/glebarez/go-sqlite" 4 | 5 | func init() { 6 | sqlite.RegisterAsSQLITE3() 7 | } 8 | -------------------------------------------------------------------------------- /norlimit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Sqlite 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:build windows 6 | // +build windows 7 | 8 | package sqlite // import "modernc.org/sqlite" 9 | 10 | func setMaxOpenFiles(n int) error { return nil } 11 | -------------------------------------------------------------------------------- /benchmark/util_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Sqlite 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 benchmark 6 | 7 | import ( 8 | "math/rand" 9 | "testing" 10 | ) 11 | 12 | func BenchmarkPronounceNum(b *testing.B) { 13 | for i := 0; i < b.N; i++ { 14 | n := rand.Int31() 15 | pronounceNum(uint32(n)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rlimit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Sqlite 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:build freebsd 6 | // +build freebsd 7 | 8 | package sqlite // import "modernc.org/sqlite" 9 | 10 | import ( 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | func setMaxOpenFiles(n int64) error { 15 | var rLimit unix.Rlimit 16 | rLimit.Max = n 17 | rLimit.Cur = n 18 | return unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit) 19 | } 20 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: gomod 9 | directory: / 10 | schedule: 11 | interval: daily 12 | - package-ecosystem: github-actions 13 | directory: / 14 | schedule: 15 | interval: daily -------------------------------------------------------------------------------- /rulimit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Sqlite 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:build linux || darwin || netbsd || openbsd 6 | // +build linux darwin netbsd openbsd 7 | 8 | package sqlite // import "modernc.org/sqlite" 9 | 10 | import ( 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | func setMaxOpenFiles(n int64) error { 15 | var rLimit unix.Rlimit 16 | rLimit.Max = uint64(n) 17 | rLimit.Cur = uint64(n) 18 | return unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit) 19 | } 20 | -------------------------------------------------------------------------------- /null_test.go: -------------------------------------------------------------------------------- 1 | package sqlite 2 | 3 | import ( 4 | "database/sql" 5 | "testing" 6 | ) 7 | 8 | func TestNullBinding(t *testing.T) { 9 | db, err := sql.Open("sqlite", "file::memory:") 10 | if err != nil { 11 | t.Errorf("cannot open: %v", err) 12 | return 13 | } 14 | _, err = db.Exec(` 15 | CREATE TABLE table1 (field1 varchar NULL); 16 | INSERT INTO table1 (field1) VALUES (?); 17 | `, sql.NullString{}) 18 | if err != nil { 19 | t.Errorf("Error binding null: %v", err) 20 | } 21 | err = db.Close() 22 | if err != nil { 23 | t.Errorf("cannot close: %v", err) 24 | return 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /mutex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Sqlite 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 sqlite // import "modernc.org/sqlite" 6 | 7 | import ( 8 | "sync" 9 | "unsafe" 10 | 11 | "modernc.org/libc" 12 | "modernc.org/libc/sys/types" 13 | ) 14 | 15 | type mutex struct { 16 | sync.Mutex 17 | } 18 | 19 | func mutexAlloc(tls *libc.TLS) uintptr { 20 | return libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(mutex{}))) 21 | } 22 | 23 | func mutexFree(tls *libc.TLS, m uintptr) { libc.Xfree(tls, m) } 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/glebarez/go-sqlite 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd 7 | golang.org/x/sys v0.22.0 8 | modernc.org/libc v1.55.3 9 | modernc.org/mathutil v1.6.0 10 | modernc.org/sqlite v1.31.0 11 | ) 12 | 13 | require ( 14 | github.com/dustin/go-humanize v1.0.1 // indirect 15 | github.com/google/uuid v1.6.0 // indirect 16 | github.com/mattn/go-isatty v0.0.20 // indirect 17 | github.com/ncruces/go-strftime v0.1.9 // indirect 18 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 19 | modernc.org/memory v1.8.0 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /benchmark/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/glebarez/go-sqlite/benchmark 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/glebarez/go-sqlite v1.14.5 7 | github.com/klauspost/cpuid/v2 v2.0.9 8 | github.com/mattn/go-sqlite3 v1.14.9 9 | ) 10 | 11 | require ( 12 | github.com/google/uuid v1.3.0 // indirect 13 | github.com/mattn/go-isatty v0.0.12 // indirect 14 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect 15 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect 16 | modernc.org/libc v1.11.104 // indirect 17 | modernc.org/mathutil v1.4.1 // indirect 18 | modernc.org/memory v1.0.5 // indirect 19 | modernc.org/sqlite v1.14.3 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /sqlite_version_test.go: -------------------------------------------------------------------------------- 1 | package sqlite 2 | 3 | import ( 4 | "database/sql" 5 | "log" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestSQLiteVersion(t *testing.T) { 11 | 12 | db, err := sql.Open(driverName, ":memory:") 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | var ( 17 | version string 18 | sourceID string 19 | ) 20 | 21 | row := db.QueryRow("select sqlite_version(), sqlite_source_id()") 22 | if row.Scan(&version, &sourceID) != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | releaseDate, err := time.Parse(`2006-01-02`, sourceID[:10]) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | t.Logf("%s (%s)\n", version, releaseDate.Format(`02/Jan/2006`)) 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | strategy: 10 | matrix: 11 | go: ['1.19','1.20', '1.21'] 12 | platform: [ubuntu-latest, macos-latest, windows-latest] 13 | runs-on: ${{ matrix.platform }} 14 | 15 | steps: 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: ${{ matrix.go }} 20 | check-latest: true 21 | 22 | - run: go version 23 | 24 | - uses: actions/checkout@v4 25 | 26 | - name: go mod package cache 27 | uses: actions/cache@v3 28 | with: 29 | path: ~/go/pkg/mod 30 | key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('**/go.mod') }} 31 | 32 | - name: Test 33 | run: go test -v ./... 34 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This file lists authors for copyright purposes. This file is distinct from 2 | # the CONTRIBUTORS files. See the latter for an explanation. 3 | # 4 | # Names should be added to this file as: 5 | # Name or Organization 6 | # 7 | # The email address is not required for organizations. 8 | # 9 | # Please keep the list sorted. 10 | 11 | Artyom Pervukhin 12 | Dan Peterson 13 | David Walton 14 | Davsk Ltd Co 15 | Jaap Aarts 16 | Jan Mercl <0xjnml@gmail.com> 17 | Josh Bleecher Snyder 18 | Logan Snow 19 | Michael Hoffmann 20 | Ross Light 21 | Saed SayedAhmed 22 | Steffen Butzer 23 | Michael Rykov 24 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This file lists people who contributed code to this repository. The AUTHORS 2 | # file lists the copyright holders; this file lists people. 3 | # 4 | # Names should be added to this file like so: 5 | # Name 6 | # 7 | # Please keep the list sorted. 8 | 9 | Alexander Menzhinsky 10 | Artyom Pervukhin 11 | Dan Peterson 12 | David Skinner 13 | David Walton 14 | Elle Mouton 15 | FlyingOnion <731677080@qq.com> 16 | Gleb Sakhnov 17 | Jaap Aarts 18 | Jan Mercl <0xjnml@gmail.com> 19 | Josh Bleecher Snyder 20 | Logan Snow 21 | Matthew Gabeler-Lee 22 | Michael Hoffmann 23 | Ross Light 24 | Saed SayedAhmed 25 | Steffen Butzer 26 | Yaacov Akiba Slama 27 | Saed SayedAhmed 28 | Michael Rykov 29 | -------------------------------------------------------------------------------- /sqlite_go18_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Sqlite 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:build go1.8 6 | // +build go1.8 7 | 8 | package sqlite // import "modernc.org/sqlite" 9 | 10 | import ( 11 | "database/sql" 12 | "os" 13 | "reflect" 14 | "testing" 15 | ) 16 | 17 | func TestNamedParameters(t *testing.T) { 18 | dir, db := tempDB(t) 19 | defer func() { 20 | db.Close() 21 | os.RemoveAll(dir) 22 | }() 23 | 24 | _, err := db.Exec(` 25 | create table t(s1 varchar(32), s2 varchar(32), s3 varchar(32), s4 varchar(32)); 26 | insert into t values(?, @aa, $aa, @bb); 27 | `, "1", sql.Named("aa", "one"), sql.Named("bb", "two")) 28 | 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | rows, err := db.Query("select * from t") 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | 38 | rec := make([]string, 4) 39 | for rows.Next() { 40 | if err := rows.Scan(&rec[0], &rec[1], &rec[2], &rec[3]); err != nil { 41 | t.Fatal(err) 42 | } 43 | } 44 | 45 | w := []string{"1", "one", "one", "two"} 46 | if !reflect.DeepEqual(rec, w) { 47 | t.Fatal(rec, w) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/badge-sqlite-version.yml: -------------------------------------------------------------------------------- 1 | name: Badge Sqlite version 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [master] 7 | 8 | jobs: 9 | create-sqlite-version-badge: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Set up Go 13 | uses: actions/setup-go@v5 14 | with: 15 | go-version: 1.21 16 | 17 | - name: Check out code into the Go module directory 18 | uses: actions/checkout@v4 19 | 20 | - name: go mod package cache 21 | uses: actions/cache@v3 22 | with: 23 | path: ~/go/pkg/mod 24 | key: ${{ runner.os }}-${{ hashFiles('go.mod') }} 25 | 26 | - name: request sqlite_version() 27 | run: echo "sqlite_version=$(go test . -run '^TestSQLiteVersion$' -v | grep sqlite_version | tr -s ' ' | cut -d' ' -f3,4)" >> $GITHUB_ENV 28 | 29 | - name: Make version badge 30 | uses: schneegans/dynamic-badges-action@v1.7.0 31 | with: 32 | auth: ${{ secrets.GIST_SECRET }} 33 | gistID: 0fd7561eb29baf31d5362ffee1ae1702 34 | filename: badge-sqlite-version-with-date.json 35 | label: SQLite release 36 | message: "${{ env.sqlite_version }}" 37 | color: 2269d3 38 | labelColor: 25292d -------------------------------------------------------------------------------- /examples/example1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | 9 | _ "github.com/glebarez/go-sqlite" 10 | ) 11 | 12 | func main() { 13 | if err := main1(); err != nil { 14 | fmt.Println(err) 15 | os.Exit(1) 16 | } 17 | } 18 | 19 | func main1() error { 20 | dir, err := os.MkdirTemp("", "test-") 21 | if err != nil { 22 | return err 23 | } 24 | 25 | defer os.RemoveAll(dir) 26 | 27 | fn := filepath.Join(dir, "db") 28 | 29 | db, err := sql.Open("sqlite", fn) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | if _, err = db.Exec(` 35 | drop table if exists t; 36 | create table t(i); 37 | insert into t values(42), (314); 38 | `); err != nil { 39 | return err 40 | } 41 | 42 | rows, err := db.Query("select 3*i from t order by i;") 43 | if err != nil { 44 | return err 45 | } 46 | 47 | for rows.Next() { 48 | var i int 49 | if err = rows.Scan(&i); err != nil { 50 | return err 51 | } 52 | 53 | fmt.Println(i) 54 | } 55 | 56 | if err = rows.Err(); err != nil { 57 | return err 58 | } 59 | 60 | if err = db.Close(); err != nil { 61 | return err 62 | } 63 | 64 | fi, err := os.Stat(fn) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | fmt.Printf("%s size: %v\n", fn, fi.Size()) 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 The Sqlite 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 met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /SQLITE-LICENSE: -------------------------------------------------------------------------------- 1 | SQLite Is Public Domain 2 | 3 | All of the code and documentation in SQLite has been dedicated to the public 4 | domain by the authors. All code authors, and representatives of the companies 5 | they work for, have signed affidavits dedicating their contributions to the 6 | public domain and originals of those signed affidavits are stored in a firesafe 7 | at the main offices of Hwaci. Anyone is free to copy, modify, publish, use, 8 | compile, sell, or distribute the original SQLite code, either in source code 9 | form or as a compiled binary, for any purpose, commercial or non-commercial, 10 | and by any means. 11 | 12 | The previous paragraph applies to the deliverable code and documentation in 13 | SQLite - those parts of the SQLite library that you actually bundle and ship 14 | with a larger application. Some scripts used as part of the build process (for 15 | example the "configure" scripts generated by autoconf) might fall under other 16 | open-source licenses. Nothing from these build scripts ever reaches the final 17 | deliverable SQLite library, however, and so the licenses associated with those 18 | scripts should not be a factor in assessing your rights to copy and use the 19 | SQLite library. 20 | 21 | All of the deliverable code in SQLite has been written from scratch. No code 22 | has been taken from other projects or from the open internet. Every line of 23 | code can be traced back to its original author, and all of those authors have 24 | public domain dedications on file. So the SQLite code base is clean and is 25 | uncontaminated with licensed code from other projects. 26 | -------------------------------------------------------------------------------- /addport.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Sqlite 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:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | "path/filepath" 14 | "strings" 15 | ) 16 | 17 | func fail(rc int, msg string, args ...interface{}) { 18 | fmt.Fprintf(os.Stderr, msg, args...) 19 | os.Exit(rc) 20 | } 21 | 22 | func main() { 23 | if len(os.Args) != 3 { 24 | fail(1, "expected 2 args: pattern and replacement\n") 25 | } 26 | 27 | pattern := os.Args[1] 28 | replacement := os.Args[2] 29 | if err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { 30 | if err != nil { 31 | return err 32 | } 33 | 34 | if info.IsDir() { 35 | return nil 36 | } 37 | 38 | dir, file := filepath.Split(path) 39 | if x := strings.Index(file, pattern); x >= 0 { 40 | // pattern freebsd 41 | // replacement netbsd 42 | // file libc_freebsd_amd64.go 43 | // replaced libc_netbsd_amd64.go 44 | // 01234567890123456789 45 | // 1 46 | // x 5 47 | file = file[:x] + replacement + file[x+len(pattern):] 48 | dst := filepath.Join(dir, file) 49 | b, err := os.ReadFile(path) 50 | if err != nil { 51 | return fmt.Errorf("reading %s: %v", path, err) 52 | } 53 | 54 | if err := os.WriteFile(dst, b, 0640); err != nil { 55 | return fmt.Errorf("writing %s: %v", dst, err) 56 | } 57 | fmt.Printf("%s -> %s\n", path, dst) 58 | } 59 | 60 | return nil 61 | }); err != nil { 62 | fail(1, "%s", err) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sqlite_go18.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Sqlite 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:build go1.8 6 | // +build go1.8 7 | 8 | package sqlite // import "modernc.org/sqlite" 9 | 10 | import ( 11 | "context" 12 | "database/sql/driver" 13 | ) 14 | 15 | // Ping implements driver.Pinger 16 | func (c *conn) Ping(ctx context.Context) error { 17 | _, err := c.ExecContext(ctx, "select 1", nil) 18 | return err 19 | } 20 | 21 | // BeginTx implements driver.ConnBeginTx 22 | func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { 23 | return c.begin(ctx, opts) 24 | } 25 | 26 | // PrepareContext implements driver.ConnPrepareContext 27 | func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { 28 | return c.prepare(ctx, query) 29 | } 30 | 31 | // ExecContext implements driver.ExecerContext 32 | func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 33 | return c.exec(ctx, query, args) 34 | } 35 | 36 | // QueryContext implements driver.QueryerContext 37 | func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { 38 | return c.query(ctx, query, args) 39 | } 40 | 41 | // ExecContext implements driver.StmtExecContext 42 | func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { 43 | return s.exec(ctx, args) 44 | } 45 | 46 | // QueryContext implements driver.StmtQueryContext 47 | func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { 48 | return s.query(ctx, args) 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Tests](https://github.com/glebarez/go-sqlite/actions/workflows/tests.yml/badge.svg)](https://github.com/glebarez/go-sqlite/actions/workflows/tests.yml) 2 | ![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/glebarez/0fd7561eb29baf31d5362ffee1ae1702/raw/badge-sqlite-version-with-date.json) 3 | 4 | # go-sqlite 5 | This is a pure-Go SQLite driver for Golang's native [database/sql](https://pkg.go.dev/database/sql) package. 6 | The driver has [Go-based implementation of SQLite](https://gitlab.com/cznic/sqlite) embedded in itself (so, you don't need to install SQLite separately) 7 | 8 | # Usage 9 | 10 | ## Example 11 | 12 | ```go 13 | package main 14 | 15 | import ( 16 | "database/sql" 17 | "log" 18 | 19 | _ "github.com/glebarez/go-sqlite" 20 | ) 21 | 22 | func main() { 23 | // connect 24 | db, err := sql.Open("sqlite", ":memory:") 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | 29 | // get SQLite version 30 | _ := db.QueryRow("select sqlite_version()") 31 | } 32 | ``` 33 | 34 | ## Connection string examples 35 | - in-memory SQLite: ```":memory:"``` 36 | - on-disk SQLite: ```"path/to/some.db"``` 37 | - Foreign-key constraint activation: ```":memory:?_pragma=foreign_keys(1)"``` 38 | 39 | ## Settings PRAGMAs in connection string 40 | Any SQLIte pragma can be preset for a Database connection using ```_pragma``` query parameter. Examples: 41 | - [journal mode](https://www.sqlite.org/pragma.html#pragma_journal_mode): ```path/to/some.db?_pragma=journal_mode(WAL)``` 42 | - [busy timeout](https://www.sqlite.org/pragma.html#pragma_busy_timeout): ```:memory:?_pragma=busy_timeout(5000)``` 43 | 44 | Multiple PRAGMAs can be specified, e.g.:
45 | ```path/to/some.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)``` 46 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | ## Benchmarks 2 | The benchmarking is conducted against CGo implementation of SQLite driver (https://github.com/mattn/go-sqlite3). 3 | 4 | Benchmark tests are inspired by and closely repeat those described in https://www.sqlite.org/speed.html. 5 | 6 | ## Doing benchmarks 7 | Benchmarks are run by custom runner and invoked with 8 | ```console 9 | go test -v . 10 | ``` 11 | Additional command line arguments: 12 | 13 | | flag | type | default | description | 14 | | ---- | ---- | ------- | ----------------------------------------------------------------------------------------------- | 15 | | -mem | bool | false | if true: benchmarks will use in-memory SQLite instance, otherwise: on-disk instance | 16 | | -rep | uint | 1 | run each benchmark multiple times and average the results. this may provide more stable results | 17 | 18 | 19 | ## Current results 20 | ```text 21 | === RUN Test_BenchmarkSQLite 22 | 23 | goos: darwin 24 | goarch: amd64 25 | cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz 26 | repeat: 1 time(s) 27 | in-memory SQLite: false 28 | 29 | bench_create_index | 1.80x | CGo: 120.880 ms/op | Pure-Go: 217.574 ms/op 30 | bench_select_on_string_comparison | 2.25x | CGo: 19.326 ms/op | Pure-Go: 43.498 ms/op 31 | bench_select_with_index | 5.84x | CGo: 0.002 ms/op | Pure-Go: 0.014 ms/op 32 | bench_select_without_index | 1.50x | CGo: 6.071 ms/op | Pure-Go: 9.111 ms/op 33 | bench_insert | 1.17x | CGo: 0.481 ms/op | Pure-Go: 0.565 ms/op 34 | bench_insert_in_transaction | 1.78x | CGo: 0.004 ms/op | Pure-Go: 0.006 ms/op 35 | bench_insert_into_indexed | 1.62x | CGo: 0.008 ms/op | Pure-Go: 0.013 ms/op 36 | bench_insert_from_select | 1.80x | CGo: 30.409 ms/op | Pure-Go: 54.703 ms/op 37 | bench_update_text_with_index | 3.26x | CGo: 0.004 ms/op | Pure-Go: 0.013 ms/op 38 | bench_update_with_index | 4.20x | CGo: 0.003 ms/op | Pure-Go: 0.011 ms/op 39 | bench_update_without_index | 1.40x | CGo: 6.421 ms/op | Pure-Go: 9.010 ms/op 40 | bench_delete_without_index | 1.28x | CGo: 180.734 ms/op | Pure-Go: 231.105 ms/op 41 | bench_delete_with_index | 1.85x | CGo: 34.284 ms/op | Pure-Go: 63.569 ms/op 42 | --- PASS: Test_BenchmarkSQLite (171.62s) 43 | ``` -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 2 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 3 | github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= 4 | github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= 5 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 6 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 8 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 9 | github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= 10 | github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= 11 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 12 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 13 | golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= 14 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 15 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= 16 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 17 | golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= 18 | modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= 19 | modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= 20 | modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= 21 | modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= 22 | modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= 23 | modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= 24 | modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= 25 | modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= 26 | modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= 27 | modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= 28 | modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= 29 | modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= 30 | modernc.org/sqlite v1.31.0 h1:LUP3eMSMqziE2O82cucbYQOhkKWQ4m8bWkKZ7QYxA/U= 31 | modernc.org/sqlite v1.31.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= 32 | modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= 33 | modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= 34 | -------------------------------------------------------------------------------- /benchmark/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Sqlite 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 file allows to run benchmarks via go test 6 | package benchmark 7 | 8 | import ( 9 | "database/sql" 10 | "flag" 11 | "fmt" 12 | "os" 13 | "runtime" 14 | "testing" 15 | 16 | _ "github.com/glebarez/go-sqlite" 17 | "github.com/klauspost/cpuid/v2" 18 | _ "github.com/mattn/go-sqlite3" 19 | ) 20 | 21 | var ( 22 | // flag, allows to run each benchmark multiple times and average the results. this may provide more stable results between runs 23 | reps uint 24 | 25 | // flag, whether to use in-memory SQLite 26 | inMemory bool 27 | 28 | // benchmark funcs to execute 29 | funcs = []func(*testing.B, *sql.DB){ 30 | benchCreateIndex, 31 | benchSelectOnStringComparison, 32 | benchSelectWithIndex, 33 | benchSelectWithoutIndex, 34 | benchInsert, 35 | benchInsertInTransaction, 36 | benchInsertIntoIndexed, 37 | benchInsertFromSelect, 38 | benchUpdateTextWithIndex, 39 | benchUpdateWithIndex, 40 | benchUpdateWithoutIndex, 41 | benchDeleteWithoutIndex, 42 | benchDeleteWithIndex, 43 | 44 | // due to very long run of this benchmark, it is disabled 45 | // benchDropTable, 46 | } 47 | ) 48 | 49 | func TestMain(m *testing.M) { 50 | flag.UintVar(&reps, "rep", 1, "allows to run each benchmark multiple times and average the results. this may provide more stable results between runs") 51 | flag.BoolVar(&inMemory, "mem", false, "if set, use in-memory SQLite") 52 | flag.Parse() 53 | os.Exit(m.Run()) 54 | } 55 | 56 | func TestBenchmarkSQLite(t *testing.T) { 57 | // print info about CPU and OS 58 | fmt.Println() 59 | fmt.Printf("goos: %s\n", runtime.GOOS) 60 | fmt.Printf("goarch: %s\n", runtime.GOARCH) 61 | if cpu := cpuid.CPU.BrandName; cpu != "" { 62 | fmt.Printf("cpu: %s\n", cpu) 63 | } 64 | fmt.Printf("repeat: %d time(s)\n", reps) 65 | fmt.Printf("in-memory SQLite: %v\n", inMemory) 66 | fmt.Println() 67 | 68 | // loop on functions 69 | for _, f := range funcs { 70 | 71 | var ( 72 | nsPerOpCGo avgVal 73 | nsPerOpPureGo avgVal 74 | ) 75 | 76 | // run benchmark against different drivers 77 | for r := uint(0); r < reps; r++ { 78 | // -- run bench against Cgo -- 79 | db := createDB(t, inMemory, "sqlite3") 80 | br := testing.Benchmark(func(b *testing.B) { f(b, db) }) 81 | 82 | // contribue metric to average 83 | nsPerOpCGo.contribInt(br.NsPerOp()) 84 | 85 | // close DB 86 | if err := db.Close(); err != nil { 87 | t.Fatal(err) 88 | } 89 | 90 | // -- run bench against Pure-go -- 91 | db = createDB(t, inMemory, "sqlite") 92 | br = testing.Benchmark(func(b *testing.B) { f(b, db) }) 93 | 94 | // contribue metric to average 95 | nsPerOpPureGo.contribInt(br.NsPerOp()) 96 | // close DB 97 | if err := db.Close(); err != nil { 98 | t.Fatal(err) 99 | } 100 | } 101 | 102 | // print result row 103 | fmt.Printf("%-35s | %5.2fx | CGo: %7.3f ms/op | Pure-Go: %7.3f ms/op\n", 104 | toSnakeCase(getFuncName(f)), 105 | nsPerOpPureGo.val/nsPerOpCGo.val, // factor 106 | nsPerOpCGo.val/1e6, // ms/op 107 | nsPerOpPureGo.val/1e6, // ms/op 108 | ) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /benchmark/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Sqlite 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 benchmark 6 | 7 | import ( 8 | "database/sql" 9 | "fmt" 10 | "math/rand" 11 | "path" 12 | "reflect" 13 | "regexp" 14 | "runtime" 15 | "strings" 16 | "testing" 17 | ) 18 | 19 | const ( 20 | // maximum for randomly generated number 21 | maxGeneratedNum = 1000000 22 | 23 | // default row count for pre-filled test table 24 | testTableRowCount = 100000 25 | 26 | // default name for test table 27 | testTableName = "t1" 28 | ) 29 | 30 | var ( 31 | matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") 32 | matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") 33 | ) 34 | 35 | func toSnakeCase(str string) string { 36 | snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") 37 | snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") 38 | return strings.ToLower(snake) 39 | } 40 | 41 | // mustExec executes SQL statements and panic if error occurs 42 | func mustExec(db *sql.DB, statements ...string) { 43 | for _, s := range statements { 44 | if _, err := db.Exec(s); err != nil { 45 | panic(fmt.Sprintf("%s: %v", s, err)) 46 | } 47 | } 48 | } 49 | 50 | // createTestTableWithName creates new DB table 51 | // with following DDL: 52 | // 53 | // CREATE TABLE (a INTEGER, b INTEGER, c VARCHAR(100)). 54 | // 55 | // Additionaly, indicies are created for columns whose names are passed in indexedColumns. 56 | func createTestTableWithName(db *sql.DB, tableName string, indexedColumns ...string) { 57 | // define table create statements 58 | statements := []string{ 59 | fmt.Sprintf(`DROP TABLE IF EXISTS %s`, tableName), 60 | fmt.Sprintf(`CREATE TABLE %s(a INTEGER, b INTEGER, c VARCHAR(100))`, tableName), 61 | } 62 | 63 | // add index creating statements 64 | for i, indexedColumn := range indexedColumns { 65 | statements = append(statements, fmt.Sprintf(`CREATE INDEX i%d ON %s(%s)`, i+1, tableName, indexedColumn)) 66 | } 67 | 68 | // execute table creation 69 | mustExec(db, statements...) 70 | } 71 | 72 | // createTestTable is a wrapper for createTestTableWithName with default table name 73 | func createTestTable(db *sql.DB, indexedColumns ...string) { 74 | createTestTableWithName(db, testTableName, indexedColumns...) 75 | } 76 | 77 | // runInTransaction executes f() inside BEGIN/COMMIT block 78 | func runInTransaction(db *sql.DB, f func()) { 79 | if _, err := db.Exec(`BEGIN`); err != nil { 80 | panic(err) 81 | } 82 | f() 83 | if _, err := db.Exec(`COMMIT`); err != nil { 84 | panic(err) 85 | } 86 | } 87 | 88 | // fillTestTable inserts rows into test table of default name (testTableName). 89 | // the values of columns are as follows: 90 | // 91 | // a - sequence number starting from 1 92 | // 93 | // b - random number between 0 and maxGeneratedNum 94 | // 95 | // c - text with english prononciation of b 96 | // 97 | // for example, SQL statements will be similiar to following: 98 | // 99 | // INSERT INTO t1 VALUES(1,13153,'thirteen thousand one hundred fifty three'); 100 | // 101 | // INSERT INTO t1 VALUES(2,75560,'seventy five thousand five hundred sixty'); 102 | func fillTestTable(db *sql.DB, rowCount int) { 103 | // prepare statement for insertion of rows 104 | stmt, err := db.Prepare(fmt.Sprintf("INSERT INTO %s VALUES(?,?,?)", testTableName)) 105 | if err != nil { 106 | panic(err) 107 | } 108 | defer stmt.Close() 109 | 110 | // insert rows 111 | for i := 0; i < rowCount; i++ { 112 | // generate random number 113 | num := rand.Int31n(maxGeneratedNum) 114 | 115 | // get number as words 116 | numAsWords := pronounceNum(uint32(num)) 117 | 118 | // insert row 119 | if _, err := stmt.Exec(i+1, num, numAsWords); err != nil { 120 | panic(err) 121 | } 122 | } 123 | } 124 | 125 | // fillTestTableInTx calls fillTestTable inside a transaction 126 | func fillTestTableInTx(db *sql.DB, rowCount int) { 127 | runInTransaction(db, func() { 128 | fillTestTable(db, rowCount) 129 | }) 130 | } 131 | 132 | // createDB creates a new instance of sql.DB with specified SQLite driver name. 133 | // if inMemory = false, then database file is created in random temporary directory via tb.TempDir() 134 | func createDB(tb testing.TB, inMemory bool, driverName string) *sql.DB { 135 | var dsn string 136 | if inMemory { 137 | dsn = ":memory:" 138 | } else { 139 | dsn = path.Join(tb.TempDir(), "test.db") 140 | } 141 | 142 | db, err := sql.Open(driverName, dsn) 143 | if err != nil { 144 | panic(err) 145 | } 146 | db.SetMaxOpenConns(1) 147 | 148 | // when in on-disk mode - set synchronous = OFF 149 | // this turns off fsync() sys call at every record inserted out of transaction scope 150 | // thus we don't bother HDD/SSD too often during specific bechmarks 151 | if !inMemory { 152 | // disable sync 153 | _, err = db.Exec(`PRAGMA synchronous = OFF`) 154 | if err != nil { 155 | tb.Fatal(err) 156 | } 157 | } 158 | return db 159 | } 160 | 161 | // getFuncName gets function name as string, without package prefix 162 | func getFuncName(i interface{}) string { 163 | // get function name as "package.function" 164 | fn := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() 165 | 166 | // return last component 167 | comps := strings.Split(fn, ".") 168 | return comps[len(comps)-1] 169 | } 170 | 171 | // pronounceNum generates english pronounciation for a given number 172 | func pronounceNum(n uint32) string { 173 | switch { 174 | case n == 0: 175 | return `zero` 176 | case n < 10: 177 | return []string{`one`, `two`, `three`, `four`, `five`, `six`, `seven`, `eight`, `nine`}[n-1] 178 | case n < 20: 179 | return []string{`ten`, `eleven`, `twelve`, `thirteen`, `fourteen`, `fifteen`, `sixteen`, `seventeen`, `eighteen`, `nineteen`}[n-10] 180 | case n < 100: 181 | p := []string{`twenty`, `thirty`, `forty`, `fifty`, `sixty`, `seventy`, `eighty`, `ninety`}[n/10-2] 182 | if n%10 == 0 { 183 | return p 184 | } 185 | return fmt.Sprint(p, " ", pronounceNum(n%10)) 186 | default: 187 | divisors := []struct { 188 | num uint32 189 | name string 190 | }{ 191 | {num: 1000000000, name: `1e9`}, 192 | {num: 1000000, name: `million`}, 193 | {num: 1000, name: `thousand`}, 194 | {num: 100, name: `hundred`}, 195 | } 196 | 197 | for _, div := range divisors { 198 | if n >= div.num { 199 | p := fmt.Sprint(pronounceNum(n/div.num), " ", div.name) 200 | if n%div.num == 0 { 201 | return p 202 | } 203 | return fmt.Sprint(p, " ", pronounceNum(n%div.num)) 204 | } 205 | } 206 | } 207 | panic("must have returned already") 208 | } 209 | 210 | // AvgVal provides average value with value contributions on-the-fly 211 | type avgVal struct { 212 | // the current average value 213 | val float64 214 | 215 | // number of contribuitons for involved in current average 216 | numContributions int 217 | } 218 | 219 | func (a *avgVal) contribFloat(v float64) { 220 | nContrib := float64(a.numContributions) 221 | a.val = (a.val*nContrib + v) / (nContrib + 1.) 222 | a.numContributions++ 223 | } 224 | 225 | func (a *avgVal) contribInt(v int64) { 226 | a.contribFloat(float64(v)) 227 | } 228 | -------------------------------------------------------------------------------- /functest/func_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The Sqlite 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 functest // modernc.org/sqlite/functest 6 | 7 | import ( 8 | "bytes" 9 | "crypto/md5" 10 | "database/sql" 11 | "database/sql/driver" 12 | "encoding/hex" 13 | "errors" 14 | "fmt" 15 | "strings" 16 | "testing" 17 | "time" 18 | 19 | sqlite3 "github.com/glebarez/go-sqlite" 20 | ) 21 | 22 | func init() { 23 | sqlite3.MustRegisterDeterministicScalarFunction( 24 | "test_int64", 25 | 0, 26 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 27 | return int64(42), nil 28 | }, 29 | ) 30 | 31 | sqlite3.MustRegisterDeterministicScalarFunction( 32 | "test_float64", 33 | 0, 34 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 35 | return float64(1e-2), nil 36 | }, 37 | ) 38 | 39 | sqlite3.MustRegisterDeterministicScalarFunction( 40 | "test_null", 41 | 0, 42 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 43 | return nil, nil 44 | }, 45 | ) 46 | 47 | sqlite3.MustRegisterDeterministicScalarFunction( 48 | "test_error", 49 | 0, 50 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 51 | return nil, errors.New("boom") 52 | }, 53 | ) 54 | 55 | sqlite3.MustRegisterDeterministicScalarFunction( 56 | "test_empty_byte_slice", 57 | 0, 58 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 59 | return []byte{}, nil 60 | }, 61 | ) 62 | 63 | sqlite3.MustRegisterDeterministicScalarFunction( 64 | "test_nonempty_byte_slice", 65 | 0, 66 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 67 | return []byte("abcdefg"), nil 68 | }, 69 | ) 70 | 71 | sqlite3.MustRegisterDeterministicScalarFunction( 72 | "test_empty_string", 73 | 0, 74 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 75 | return "", nil 76 | }, 77 | ) 78 | 79 | sqlite3.MustRegisterDeterministicScalarFunction( 80 | "test_nonempty_string", 81 | 0, 82 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 83 | return "abcdefg", nil 84 | }, 85 | ) 86 | 87 | sqlite3.MustRegisterDeterministicScalarFunction( 88 | "yesterday", 89 | 1, 90 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 91 | var arg time.Time 92 | switch argTyped := args[0].(type) { 93 | case int64: 94 | arg = time.Unix(argTyped, 0) 95 | default: 96 | fmt.Println(argTyped) 97 | return nil, fmt.Errorf("expected argument to be int64, got: %T", argTyped) 98 | } 99 | return arg.Add(-24 * time.Hour), nil 100 | }, 101 | ) 102 | 103 | sqlite3.MustRegisterDeterministicScalarFunction( 104 | "md5", 105 | 1, 106 | func(ctx *sqlite3.FunctionContext, args []driver.Value) (driver.Value, error) { 107 | var arg *bytes.Buffer 108 | switch argTyped := args[0].(type) { 109 | case string: 110 | arg = bytes.NewBuffer([]byte(argTyped)) 111 | case []byte: 112 | arg = bytes.NewBuffer(argTyped) 113 | default: 114 | return nil, fmt.Errorf("expected argument to be a string, got: %T", argTyped) 115 | } 116 | w := md5.New() 117 | if _, err := arg.WriteTo(w); err != nil { 118 | return nil, fmt.Errorf("unable to compute md5 checksum: %s", err) 119 | } 120 | return hex.EncodeToString(w.Sum(nil)), nil 121 | }, 122 | ) 123 | } 124 | 125 | func TestRegisteredFunctions(t *testing.T) { 126 | withDB := func(test func(db *sql.DB)) { 127 | db, err := sql.Open("sqlite", "file::memory:") 128 | if err != nil { 129 | t.Fatalf("failed to open database: %v", err) 130 | } 131 | defer db.Close() 132 | 133 | test(db) 134 | } 135 | 136 | t.Run("int64", func(tt *testing.T) { 137 | withDB(func(db *sql.DB) { 138 | row := db.QueryRow("select test_int64()") 139 | 140 | var a int 141 | if err := row.Scan(&a); err != nil { 142 | tt.Fatal(err) 143 | } 144 | if g, e := a, 42; g != e { 145 | tt.Fatal(g, e) 146 | } 147 | 148 | }) 149 | }) 150 | 151 | t.Run("float64", func(tt *testing.T) { 152 | withDB(func(db *sql.DB) { 153 | row := db.QueryRow("select test_float64()") 154 | 155 | var a float64 156 | if err := row.Scan(&a); err != nil { 157 | tt.Fatal(err) 158 | } 159 | if g, e := a, 1e-2; g != e { 160 | tt.Fatal(g, e) 161 | } 162 | 163 | }) 164 | }) 165 | 166 | t.Run("error", func(tt *testing.T) { 167 | withDB(func(db *sql.DB) { 168 | err := db.QueryRow("select test_error()").Scan() 169 | if err == nil { 170 | tt.Fatal("expected error, got none") 171 | } 172 | if !strings.Contains(err.Error(), "boom") { 173 | tt.Fatal(err) 174 | } 175 | }) 176 | }) 177 | 178 | t.Run("empty_byte_slice", func(tt *testing.T) { 179 | withDB(func(db *sql.DB) { 180 | row := db.QueryRow("select test_empty_byte_slice()") 181 | 182 | var a []byte 183 | if err := row.Scan(&a); err != nil { 184 | tt.Fatal(err) 185 | } 186 | if len(a) > 0 { 187 | tt.Fatal("expected empty byte slice") 188 | } 189 | }) 190 | }) 191 | 192 | t.Run("nonempty_byte_slice", func(tt *testing.T) { 193 | withDB(func(db *sql.DB) { 194 | row := db.QueryRow("select test_nonempty_byte_slice()") 195 | 196 | var a []byte 197 | if err := row.Scan(&a); err != nil { 198 | tt.Fatal(err) 199 | } 200 | if g, e := a, []byte("abcdefg"); !bytes.Equal(g, e) { 201 | tt.Fatal(string(g), string(e)) 202 | } 203 | }) 204 | }) 205 | 206 | t.Run("empty_string", func(tt *testing.T) { 207 | withDB(func(db *sql.DB) { 208 | row := db.QueryRow("select test_empty_string()") 209 | 210 | var a string 211 | if err := row.Scan(&a); err != nil { 212 | tt.Fatal(err) 213 | } 214 | if len(a) > 0 { 215 | tt.Fatal("expected empty string") 216 | } 217 | }) 218 | }) 219 | 220 | t.Run("nonempty_string", func(tt *testing.T) { 221 | withDB(func(db *sql.DB) { 222 | row := db.QueryRow("select test_nonempty_string()") 223 | 224 | var a string 225 | if err := row.Scan(&a); err != nil { 226 | tt.Fatal(err) 227 | } 228 | if g, e := a, "abcdefg"; g != e { 229 | tt.Fatal(g, e) 230 | } 231 | }) 232 | }) 233 | 234 | t.Run("null", func(tt *testing.T) { 235 | withDB(func(db *sql.DB) { 236 | row := db.QueryRow("select test_null()") 237 | 238 | var a interface{} 239 | if err := row.Scan(&a); err != nil { 240 | tt.Fatal(err) 241 | } 242 | if a != nil { 243 | tt.Fatal("expected nil") 244 | } 245 | }) 246 | }) 247 | 248 | t.Run("dates", func(tt *testing.T) { 249 | withDB(func(db *sql.DB) { 250 | row := db.QueryRow("select yesterday(unixepoch('2018-11-01'))") 251 | 252 | var a int64 253 | if err := row.Scan(&a); err != nil { 254 | tt.Fatal(err) 255 | } 256 | if g, e := time.Unix(a, 0), time.Date(2018, time.October, 31, 0, 0, 0, 0, time.UTC); !g.Equal(e) { 257 | tt.Fatal(g, e) 258 | } 259 | }) 260 | }) 261 | 262 | t.Run("md5", func(tt *testing.T) { 263 | withDB(func(db *sql.DB) { 264 | row := db.QueryRow("select md5('abcdefg')") 265 | 266 | var a string 267 | if err := row.Scan(&a); err != nil { 268 | tt.Fatal(err) 269 | } 270 | if g, e := a, "7ac66c0f148de9519b8bd264312c4d64"; g != e { 271 | tt.Fatal(g, e) 272 | } 273 | }) 274 | }) 275 | 276 | t.Run("md5 with blob input", func(tt *testing.T) { 277 | withDB(func(db *sql.DB) { 278 | if _, err := db.Exec("create table t(b blob); insert into t values (?)", []byte("abcdefg")); err != nil { 279 | tt.Fatal(err) 280 | } 281 | row := db.QueryRow("select md5(b) from t") 282 | 283 | var a []byte 284 | if err := row.Scan(&a); err != nil { 285 | tt.Fatal(err) 286 | } 287 | if g, e := a, []byte("7ac66c0f148de9519b8bd264312c4d64"); !bytes.Equal(g, e) { 288 | tt.Fatal(string(g), string(e)) 289 | } 290 | }) 291 | }) 292 | } 293 | -------------------------------------------------------------------------------- /benchmark/bench.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Sqlite 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 benchmark 5 | 6 | /* 7 | this file contains benchmarks inspired by https://www.sqlite.org/speed.html 8 | */ 9 | 10 | import ( 11 | "database/sql" 12 | "fmt" 13 | "math/rand" 14 | "testing" 15 | ) 16 | 17 | // corresponds to Test 1 from https://www.sqlite.org/speed.html 18 | func benchInsert(b *testing.B, db *sql.DB) { 19 | // create test table 20 | createTestTable(db) 21 | 22 | // measure from here 23 | b.ResetTimer() 24 | 25 | fillTestTable(db, b.N) 26 | } 27 | 28 | // corresponds to Test 2 from https://www.sqlite.org/speed.html 29 | func benchInsertInTransaction(b *testing.B, db *sql.DB) { 30 | // create test table 31 | createTestTable(db) 32 | 33 | // measure from here 34 | b.ResetTimer() 35 | 36 | fillTestTableInTx(db, b.N) 37 | } 38 | 39 | // corresponds to Test 3 from https://www.sqlite.org/speed.html 40 | func benchInsertIntoIndexed(b *testing.B, db *sql.DB) { 41 | // create test table with indexed column 42 | createTestTable(db, `c`) 43 | 44 | b.ResetTimer() 45 | 46 | fillTestTableInTx(db, b.N) 47 | } 48 | 49 | // corresponds to Test 4 from https://www.sqlite.org/speed.html 50 | func benchSelectWithoutIndex(b *testing.B, db *sql.DB) { 51 | // create test table 52 | createTestTable(db) 53 | 54 | // fill test table 55 | fillTestTableInTx(db, testTableRowCount) 56 | 57 | // prepare statement for selection 58 | stmt, err := db.Prepare(fmt.Sprintf(`SELECT count(*), avg(b) FROM %s WHERE b>=? AND b=0 AND b<1000; 69 | // SELECT count(*), avg(b) FROM t2 WHERE b>=100 AND b<1100; 70 | // SELECT count(*), avg(b) FROM t2 WHERE b>=200 AND b<1200; 71 | // ... 72 | for i := 0; i < b.N; i++ { 73 | b := (i * 100) % maxGeneratedNum 74 | if _, err := stmt.Exec(b, b+1000); err != nil { 75 | panic(err) 76 | } 77 | } 78 | }) 79 | } 80 | 81 | // corresponds to Test 5 from https://www.sqlite.org/speed.html 82 | func benchSelectOnStringComparison(b *testing.B, db *sql.DB) { 83 | // create test table 84 | createTestTable(db) 85 | 86 | // fill test table 87 | fillTestTableInTx(db, testTableRowCount) 88 | 89 | // prepare statement for selection 90 | stmt, err := db.Prepare(fmt.Sprintf(`SELECT count(*), avg(b) FROM %s WHERE c LIKE ?`, testTableName)) 91 | if err != nil { 92 | panic(err) 93 | } 94 | defer stmt.Close() 95 | 96 | b.ResetTimer() 97 | 98 | runInTransaction(db, func() { 99 | // exec many selects 100 | // SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%one%'; 101 | // SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%two%'; 102 | // ... 103 | // SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%ninety nine%'; 104 | for i := 1; i <= b.N; i++ { 105 | // e.g. following will produce "... WHERE c LIKE '%seventy eight%' " 106 | likeCond := pronounceNum(uint32(i) % 100) 107 | if _, err := stmt.Exec(`%` + likeCond + `%`); err != nil { 108 | panic(err) 109 | } 110 | } 111 | }) 112 | } 113 | 114 | // corresponds to Test 6 from https://www.sqlite.org/speed.html 115 | func benchCreateIndex(b *testing.B, db *sql.DB) { 116 | // create test table 117 | createTestTable(db) 118 | 119 | // fill test table 120 | fillTestTableInTx(db, testTableRowCount) 121 | 122 | var ( 123 | nameIdxA = "iA" 124 | nameIdxB = "iB" 125 | createIndStmtA = fmt.Sprintf(`CREATE INDEX %s ON %s(a)`, nameIdxA, testTableName) 126 | createIndStmtB = fmt.Sprintf(`CREATE INDEX %s ON %s(c)`, nameIdxB, testTableName) 127 | dropIndStmtA = fmt.Sprintf(`DROP INDEX IF EXISTS %s`, nameIdxA) 128 | dropIndStmtB = fmt.Sprintf(`DROP INDEX IF EXISTS %s`, nameIdxB) 129 | ) 130 | 131 | b.ResetTimer() 132 | for i := 1; i <= b.N; i++ { 133 | // drop indexes from previous iteration (if any) 134 | b.StopTimer() 135 | mustExec(db, dropIndStmtA, dropIndStmtB) 136 | b.StartTimer() 137 | 138 | // create indexes 139 | runInTransaction(db, func() { 140 | mustExec(db, createIndStmtA, createIndStmtB) 141 | }) 142 | } 143 | } 144 | 145 | // corresponds to Test 7 from https://www.sqlite.org/speed.html 146 | func benchSelectWithIndex(b *testing.B, db *sql.DB) { 147 | // create test table with indexed field 148 | createTestTable(db, `b`) 149 | 150 | // fill test table 151 | fillTestTableInTx(db, testTableRowCount) 152 | 153 | // prepare statement for selection 154 | stmt, err := db.Prepare(fmt.Sprintf(`SELECT count(*), avg(b) FROM %s WHERE b>=? AND b=0 AND b<100; 165 | // SELECT count(*), avg(b) FROM t2 WHERE b>=100 AND b<200; 166 | // SELECT count(*), avg(b) FROM t2 WHERE b>=200 AND b<300; 167 | for i := 0; i < b.N; i++ { 168 | b := (i * 100) % maxGeneratedNum 169 | if _, err := stmt.Exec(b, b+100); err != nil { 170 | panic(err) 171 | } 172 | 173 | } 174 | }) 175 | 176 | } 177 | 178 | // corresponds to Test 8 from https://www.sqlite.org/speed.html 179 | func benchUpdateWithoutIndex(b *testing.B, db *sql.DB) { 180 | // create test table 181 | createTestTable(db) 182 | 183 | // fill test table 184 | fillTestTableInTx(db, testTableRowCount) 185 | 186 | // prepare statement 187 | stmt, err := db.Prepare(fmt.Sprintf(`UPDATE %s SET b=b*2 WHERE a>=? AND a=0 AND a<10; 198 | // UPDATE t1 SET b=b*2 WHERE a>=10 AND a<20; 199 | // UPDATE t1 SET b=b*2 WHERE a>=20 AND a<30; 200 | for i := 0; i < b.N; i++ { 201 | a := (i * 10) % testTableRowCount 202 | if _, err := stmt.Exec(a, a+10); err != nil { 203 | panic(err) 204 | } 205 | } 206 | }) 207 | 208 | } 209 | 210 | // corresponds to Test 9 from https://www.sqlite.org/speed.html 211 | func benchUpdateWithIndex(b *testing.B, db *sql.DB) { 212 | // create test table 213 | createTestTable(db, `a`) 214 | 215 | // fill test table 216 | fillTestTableInTx(db, testTableRowCount) 217 | 218 | // prepare statement 219 | stmt, err := db.Prepare(fmt.Sprintf(`UPDATE %s SET b=? WHERE a=?`, testTableName)) 220 | if err != nil { 221 | panic(err) 222 | } 223 | defer stmt.Close() 224 | 225 | b.ResetTimer() 226 | 227 | runInTransaction(db, func() { 228 | // exec many 229 | // UPDATE t2 SET b=468026 WHERE a=1; 230 | // UPDATE t2 SET b=121928 WHERE a=2; 231 | // ... 232 | for i := 0; i < b.N; i++ { 233 | if _, err := stmt.Exec( 234 | rand.Uint32(), // b = ? 235 | i%testTableRowCount+1, // WHERE a=? 236 | ); err != nil { 237 | panic(err) 238 | } 239 | } 240 | }) 241 | } 242 | 243 | // corresponds to Test 10 from https://www.sqlite.org/speed.html 244 | func benchUpdateTextWithIndex(b *testing.B, db *sql.DB) { 245 | // create test table 246 | createTestTable(db, `a`) 247 | 248 | // fill test table 249 | fillTestTableInTx(db, testTableRowCount) 250 | 251 | // prepare statement 252 | stmt, err := db.Prepare(fmt.Sprintf(`UPDATE %s SET c=? WHERE a=?`, testTableName)) 253 | if err != nil { 254 | panic(err) 255 | } 256 | defer stmt.Close() 257 | 258 | b.ResetTimer() 259 | 260 | runInTransaction(db, func() { 261 | // exec many 262 | // UPDATE t2 SET c='one hundred forty eight thousand three hundred eighty two' WHERE a=1; 263 | // UPDATE t2 SET c='three hundred sixty six thousand five hundred two' WHERE a=2; 264 | for i := 0; i < b.N; i++ { 265 | // generate new random number-as-words for c 266 | if _, err := stmt.Exec( 267 | pronounceNum(uint32(rand.Int31n(maxGeneratedNum))), // SET c=? 268 | i%testTableRowCount+1, // WHERE a=? 269 | ); err != nil { 270 | panic(err) 271 | } 272 | } 273 | }) 274 | } 275 | 276 | // corresponds to Test 11 from https://www.sqlite.org/speed.html 277 | func benchInsertFromSelect(b *testing.B, db *sql.DB) { 278 | // create source table 279 | createTestTable(db) 280 | fillTestTableInTx(db, testTableRowCount) 281 | 282 | // create name for target table 283 | targetTableName := fmt.Sprintf("%s_copy", testTableName) 284 | 285 | // need to create table here, otherwise prepared statement will give error 286 | createTestTableWithName(db, targetTableName) 287 | 288 | // prepare statement 289 | stmt, err := db.Prepare(fmt.Sprintf(`INSERT INTO %s SELECT b,a,c FROM %s`, targetTableName, testTableName)) 290 | if err != nil { 291 | panic(err) 292 | } 293 | defer stmt.Close() 294 | 295 | b.ResetTimer() 296 | for i := 0; i < b.N; i++ { 297 | // drop/create target table 298 | b.StopTimer() 299 | createTestTableWithName(db, targetTableName) 300 | b.StartTimer() 301 | 302 | runInTransaction(db, func() { 303 | if _, err := stmt.Exec(); err != nil { 304 | panic(err) 305 | } 306 | }) 307 | } 308 | } 309 | 310 | // corresponds to Test 12 from https://www.sqlite.org/speed.html 311 | func benchDeleteWithoutIndex(b *testing.B, db *sql.DB) { 312 | // create test table 313 | createTestTable(db) 314 | 315 | // prepare statement for deletion 316 | stmt, err := db.Prepare(fmt.Sprintf(`DELETE FROM %s WHERE c LIKE '%%fifty%%'`, testTableName)) 317 | if err != nil { 318 | panic(err) 319 | } 320 | defer stmt.Close() 321 | 322 | b.ResetTimer() 323 | for i := 0; i < b.N; i++ { 324 | // drop/create target table 325 | b.StopTimer() 326 | fillTestTableInTx(db, testTableRowCount) 327 | b.StartTimer() 328 | 329 | runInTransaction(db, func() { 330 | if _, err := stmt.Exec(); err != nil { 331 | panic(err) 332 | } 333 | }) 334 | } 335 | } 336 | 337 | // corresponds to Test 13 from https://www.sqlite.org/speed.html 338 | func benchDeleteWithIndex(b *testing.B, db *sql.DB) { 339 | // create test table with indexed column 340 | createTestTable(db, `a`) 341 | 342 | // prepare statement for BIG deletion (nearly half of table rows) 343 | stmt, err := db.Prepare(fmt.Sprintf(`DELETE FROM %s WHERE a>%d AND a<%d`, testTableName, 10, testTableRowCount/2)) 344 | if err != nil { 345 | panic(err) 346 | } 347 | defer stmt.Close() 348 | 349 | b.ResetTimer() 350 | for i := 0; i < b.N; i++ { 351 | // drop/create target table 352 | b.StopTimer() 353 | fillTestTableInTx(db, testTableRowCount) 354 | b.StartTimer() 355 | 356 | runInTransaction(db, func() { 357 | if _, err := stmt.Exec(); err != nil { 358 | panic(err) 359 | } 360 | }) 361 | } 362 | } 363 | 364 | // corresponds to Test 16 from https://www.sqlite.org/speed.html 365 | func benchDropTable(b *testing.B, db *sql.DB) { 366 | for i := 0; i < b.N; i++ { 367 | b.StopTimer() 368 | createTestTable(db) 369 | fillTestTableInTx(db, testTableRowCount) 370 | b.StartTimer() 371 | 372 | if _, err := db.Exec(fmt.Sprintf("DROP TABLE %s", testTableName)); err != nil { 373 | panic(err) 374 | } 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /benchmark/go.sum: -------------------------------------------------------------------------------- 1 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 2 | github.com/glebarez/go-sqlite v1.14.5 h1:kSjlUZFt/Uy4ZzJG/FjzNVfHfFqqUKTHVy+J6EKQKhk= 3 | github.com/glebarez/go-sqlite v1.14.5/go.mod h1:uw5Rp1iEmkACQBl+djShb9eBdoLR9nRRqpCpbdt+1pM= 4 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 5 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 6 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 7 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 8 | github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= 9 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 10 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 11 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 12 | github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= 13 | github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= 16 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 17 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 18 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 19 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 20 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 21 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 22 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 23 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 24 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 25 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 26 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 27 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 28 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 29 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 30 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 31 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 32 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 33 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 34 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 35 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 36 | golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 37 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 38 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 39 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 40 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 41 | lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= 42 | modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 43 | modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 44 | modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 45 | modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 46 | modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 47 | modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 48 | modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 49 | modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 50 | modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 51 | modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 52 | modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 53 | modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 54 | modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 55 | modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 56 | modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 57 | modernc.org/cc/v3 v3.35.22 h1:BzShpwCAP7TWzFppM4k2t03RhXhgYqaibROWkrWq7lE= 58 | modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= 59 | modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= 60 | modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= 61 | modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= 62 | modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= 63 | modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= 64 | modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= 65 | modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= 66 | modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= 67 | modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= 68 | modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= 69 | modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= 70 | modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= 71 | modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= 72 | modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= 73 | modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= 74 | modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= 75 | modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= 76 | modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= 77 | modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= 78 | modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= 79 | modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= 80 | modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= 81 | modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= 82 | modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= 83 | modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= 84 | modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= 85 | modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= 86 | modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= 87 | modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= 88 | modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= 89 | modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= 90 | modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= 91 | modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= 92 | modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= 93 | modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= 94 | modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko= 95 | modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA= 96 | modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8= 97 | modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= 98 | modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= 99 | modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= 100 | modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= 101 | modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= 102 | modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= 103 | modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= 104 | modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= 105 | modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= 106 | modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= 107 | modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= 108 | modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= 109 | modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= 110 | modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= 111 | modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= 112 | modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= 113 | modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= 114 | modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= 115 | modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= 116 | modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= 117 | modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= 118 | modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= 119 | modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= 120 | modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= 121 | modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= 122 | modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= 123 | modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= 124 | modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= 125 | modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= 126 | modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= 127 | modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= 128 | modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= 129 | modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= 130 | modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= 131 | modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= 132 | modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= 133 | modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= 134 | modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c= 135 | modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI= 136 | modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ= 137 | modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= 138 | modernc.org/libc v1.13.2 h1:GCFjY9bmwDZ/TJC4OZOUWaNgxIxwb104C/QZrqpcVEA= 139 | modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= 140 | modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 141 | modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 142 | modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 143 | modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= 144 | modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= 145 | modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= 146 | modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 147 | modernc.org/sqlite v1.14.3 h1:psrTwgpEujgWEP3FNdsC9yNh5tSeA77U0GeWhHH4XmQ= 148 | modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y= 149 | modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= 150 | modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg= 151 | modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 152 | modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo= 153 | -------------------------------------------------------------------------------- /sqlite.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Sqlite 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:generate go run generator.go -full-path-comments 6 | 7 | package sqlite // import "modernc.org/sqlite" 8 | 9 | import ( 10 | "context" 11 | "database/sql" 12 | "database/sql/driver" 13 | "fmt" 14 | "io" 15 | "log" 16 | "math" 17 | "net/url" 18 | "reflect" 19 | "strconv" 20 | "strings" 21 | "sync" 22 | "sync/atomic" 23 | "time" 24 | "unsafe" 25 | 26 | "modernc.org/libc" 27 | "modernc.org/libc/sys/types" 28 | sqlite3 "modernc.org/sqlite/lib" 29 | ) 30 | 31 | var ( 32 | _ driver.Conn = (*conn)(nil) 33 | _ driver.Driver = (*Driver)(nil) 34 | //lint:ignore SA1019 TODO implement ExecerContext 35 | _ driver.Execer = (*conn)(nil) 36 | //lint:ignore SA1019 TODO implement QueryerContext 37 | _ driver.Queryer = (*conn)(nil) 38 | _ driver.Result = (*result)(nil) 39 | _ driver.Rows = (*rows)(nil) 40 | _ driver.RowsColumnTypeDatabaseTypeName = (*rows)(nil) 41 | _ driver.RowsColumnTypeLength = (*rows)(nil) 42 | _ driver.RowsColumnTypeNullable = (*rows)(nil) 43 | _ driver.RowsColumnTypePrecisionScale = (*rows)(nil) 44 | _ driver.RowsColumnTypeScanType = (*rows)(nil) 45 | _ driver.Stmt = (*stmt)(nil) 46 | _ driver.Tx = (*tx)(nil) 47 | _ error = (*Error)(nil) 48 | ) 49 | 50 | // LogSqlStatements - if true when Open() is called, all SQL statements will be logged to standard output 51 | var LogSqlStatements bool 52 | 53 | const ( 54 | driverName = "sqlite" 55 | ptrSize = unsafe.Sizeof(uintptr(0)) 56 | sqliteLockedSharedcache = sqlite3.SQLITE_LOCKED | (1 << 8) 57 | ) 58 | 59 | // Error represents sqlite library error code. 60 | type Error struct { 61 | msg string 62 | code int 63 | } 64 | 65 | // Error implements error. 66 | func (e *Error) Error() string { return e.msg } 67 | 68 | // Code returns the sqlite result code for this error. 69 | func (e *Error) Code() int { return e.code } 70 | 71 | var ( 72 | // ErrorCodeString maps Error.Code() to its string representation. 73 | ErrorCodeString = map[int]string{ 74 | sqlite3.SQLITE_ABORT: "Callback routine requested an abort (SQLITE_ABORT)", 75 | sqlite3.SQLITE_AUTH: "Authorization denied (SQLITE_AUTH)", 76 | sqlite3.SQLITE_BUSY: "The database file is locked (SQLITE_BUSY)", 77 | sqlite3.SQLITE_CANTOPEN: "Unable to open the database file (SQLITE_CANTOPEN)", 78 | sqlite3.SQLITE_CONSTRAINT: "Abort due to constraint violation (SQLITE_CONSTRAINT)", 79 | sqlite3.SQLITE_CORRUPT: "The database disk image is malformed (SQLITE_CORRUPT)", 80 | sqlite3.SQLITE_DONE: "sqlite3_step() has finished executing (SQLITE_DONE)", 81 | sqlite3.SQLITE_EMPTY: "Internal use only (SQLITE_EMPTY)", 82 | sqlite3.SQLITE_ERROR: "Generic error (SQLITE_ERROR)", 83 | sqlite3.SQLITE_FORMAT: "Not used (SQLITE_FORMAT)", 84 | sqlite3.SQLITE_FULL: "Insertion failed because database is full (SQLITE_FULL)", 85 | sqlite3.SQLITE_INTERNAL: "Internal logic error in SQLite (SQLITE_INTERNAL)", 86 | sqlite3.SQLITE_INTERRUPT: "Operation terminated by sqlite3_interrupt()(SQLITE_INTERRUPT)", 87 | sqlite3.SQLITE_IOERR | (1 << 8): "(SQLITE_IOERR_READ)", 88 | sqlite3.SQLITE_IOERR | (10 << 8): "(SQLITE_IOERR_DELETE)", 89 | sqlite3.SQLITE_IOERR | (11 << 8): "(SQLITE_IOERR_BLOCKED)", 90 | sqlite3.SQLITE_IOERR | (12 << 8): "(SQLITE_IOERR_NOMEM)", 91 | sqlite3.SQLITE_IOERR | (13 << 8): "(SQLITE_IOERR_ACCESS)", 92 | sqlite3.SQLITE_IOERR | (14 << 8): "(SQLITE_IOERR_CHECKRESERVEDLOCK)", 93 | sqlite3.SQLITE_IOERR | (15 << 8): "(SQLITE_IOERR_LOCK)", 94 | sqlite3.SQLITE_IOERR | (16 << 8): "(SQLITE_IOERR_CLOSE)", 95 | sqlite3.SQLITE_IOERR | (17 << 8): "(SQLITE_IOERR_DIR_CLOSE)", 96 | sqlite3.SQLITE_IOERR | (2 << 8): "(SQLITE_IOERR_SHORT_READ)", 97 | sqlite3.SQLITE_IOERR | (3 << 8): "(SQLITE_IOERR_WRITE)", 98 | sqlite3.SQLITE_IOERR | (4 << 8): "(SQLITE_IOERR_FSYNC)", 99 | sqlite3.SQLITE_IOERR | (5 << 8): "(SQLITE_IOERR_DIR_FSYNC)", 100 | sqlite3.SQLITE_IOERR | (6 << 8): "(SQLITE_IOERR_TRUNCATE)", 101 | sqlite3.SQLITE_IOERR | (7 << 8): "(SQLITE_IOERR_FSTAT)", 102 | sqlite3.SQLITE_IOERR | (8 << 8): "(SQLITE_IOERR_UNLOCK)", 103 | sqlite3.SQLITE_IOERR | (9 << 8): "(SQLITE_IOERR_RDLOCK)", 104 | sqlite3.SQLITE_IOERR: "Some kind of disk I/O error occurred (SQLITE_IOERR)", 105 | sqlite3.SQLITE_LOCKED | (1 << 8): "(SQLITE_LOCKED_SHAREDCACHE)", 106 | sqlite3.SQLITE_LOCKED: "A table in the database is locked (SQLITE_LOCKED)", 107 | sqlite3.SQLITE_MISMATCH: "Data type mismatch (SQLITE_MISMATCH)", 108 | sqlite3.SQLITE_MISUSE: "Library used incorrectly (SQLITE_MISUSE)", 109 | sqlite3.SQLITE_NOLFS: "Uses OS features not supported on host (SQLITE_NOLFS)", 110 | sqlite3.SQLITE_NOMEM: "A malloc() failed (SQLITE_NOMEM)", 111 | sqlite3.SQLITE_NOTADB: "File opened that is not a database file (SQLITE_NOTADB)", 112 | sqlite3.SQLITE_NOTFOUND: "Unknown opcode in sqlite3_file_control() (SQLITE_NOTFOUND)", 113 | sqlite3.SQLITE_NOTICE: "Notifications from sqlite3_log() (SQLITE_NOTICE)", 114 | sqlite3.SQLITE_PERM: "Access permission denied (SQLITE_PERM)", 115 | sqlite3.SQLITE_PROTOCOL: "Database lock protocol error (SQLITE_PROTOCOL)", 116 | sqlite3.SQLITE_RANGE: "2nd parameter to sqlite3_bind out of range (SQLITE_RANGE)", 117 | sqlite3.SQLITE_READONLY: "Attempt to write a readonly database (SQLITE_READONLY)", 118 | sqlite3.SQLITE_ROW: "sqlite3_step() has another row ready (SQLITE_ROW)", 119 | sqlite3.SQLITE_SCHEMA: "The database schema changed (SQLITE_SCHEMA)", 120 | sqlite3.SQLITE_TOOBIG: "String or BLOB exceeds size limit (SQLITE_TOOBIG)", 121 | sqlite3.SQLITE_WARNING: "Warnings from sqlite3_log() (SQLITE_WARNING)", 122 | } 123 | ) 124 | 125 | func init() { 126 | sql.Register(driverName, newDriver()) 127 | } 128 | 129 | type result struct { 130 | lastInsertID int64 131 | rowsAffected int 132 | } 133 | 134 | func newResult(c *conn) (_ *result, err error) { 135 | r := &result{} 136 | if r.rowsAffected, err = c.changes(); err != nil { 137 | return nil, err 138 | } 139 | 140 | if r.lastInsertID, err = c.lastInsertRowID(); err != nil { 141 | return nil, err 142 | } 143 | 144 | return r, nil 145 | } 146 | 147 | // LastInsertId returns the database's auto-generated ID after, for example, an 148 | // INSERT into a table with primary key. 149 | func (r *result) LastInsertId() (int64, error) { 150 | if r == nil { 151 | return 0, nil 152 | } 153 | 154 | return r.lastInsertID, nil 155 | } 156 | 157 | // RowsAffected returns the number of rows affected by the query. 158 | func (r *result) RowsAffected() (int64, error) { 159 | if r == nil { 160 | return 0, nil 161 | } 162 | 163 | return int64(r.rowsAffected), nil 164 | } 165 | 166 | type rows struct { 167 | allocs []uintptr // allocations made for this prepared statement (to be freed) 168 | c *conn // connection 169 | columns []string // column names 170 | pstmt uintptr // correspodning prepared statement 171 | } 172 | 173 | func newRows(c *conn, pstmt uintptr, allocs []uintptr) (r *rows, err error) { 174 | r = &rows{c: c, pstmt: pstmt, allocs: allocs} 175 | 176 | // deferred close if anything goes wrong 177 | defer func() { 178 | if err != nil { 179 | r.Close() 180 | r = nil 181 | } 182 | }() 183 | 184 | // get columns count 185 | n, err := c.columnCount(pstmt) 186 | if err != nil { 187 | return nil, err 188 | } 189 | 190 | // get column names 191 | r.columns = make([]string, n) 192 | for i := range r.columns { 193 | if r.columns[i], err = r.c.columnName(pstmt, i); err != nil { 194 | return nil, err 195 | } 196 | } 197 | 198 | return r, nil 199 | } 200 | 201 | // Close closes the rows iterator. 202 | func (r *rows) Close() (err error) { 203 | // free all allocations made for this rows 204 | for _, v := range r.allocs { 205 | r.c.free(v) 206 | } 207 | r.allocs = nil 208 | 209 | // finalize prepared statement 210 | return r.c.finalize(r.pstmt) 211 | } 212 | 213 | // Columns returns the names of the columns. The number of columns of the 214 | // result is inferred from the length of the slice. If a particular column name 215 | // isn't known, an empty string should be returned for that entry. 216 | func (r *rows) Columns() (c []string) { 217 | return r.columns 218 | } 219 | 220 | // Next is called to populate the next row of data into the provided slice. The 221 | // provided slice will be the same size as the Columns() are wide. 222 | // 223 | // Next should return io.EOF when there are no more rows. 224 | func (r *rows) Next(dest []driver.Value) error { 225 | // yet another step 226 | rc, err := r.c.step(r.pstmt) 227 | if err != nil { 228 | return err 229 | } 230 | 231 | // analyze error code 232 | switch rc { 233 | case sqlite3.SQLITE_ROW: 234 | if g, e := len(dest), len(r.columns); g != e { 235 | return fmt.Errorf("sqlite: Next: have %v destination values, expected %v", g, e) 236 | } 237 | 238 | for i := range dest { 239 | ct, err := r.c.columnType(r.pstmt, i) 240 | if err != nil { 241 | return err 242 | } 243 | 244 | switch ct { 245 | case sqlite3.SQLITE_INTEGER: 246 | v, err := r.c.columnInt64(r.pstmt, i) 247 | if err != nil { 248 | return err 249 | } 250 | 251 | dest[i] = v 252 | case sqlite3.SQLITE_FLOAT: 253 | v, err := r.c.columnDouble(r.pstmt, i) 254 | if err != nil { 255 | return err 256 | } 257 | 258 | dest[i] = v 259 | case sqlite3.SQLITE_TEXT: 260 | v, err := r.c.columnText(r.pstmt, i) 261 | if err != nil { 262 | return err 263 | } 264 | 265 | switch r.ColumnTypeDatabaseTypeName(i) { 266 | case "DATE", "DATETIME", "TIMESTAMP": 267 | dest[i], _ = r.c.parseTime(v) 268 | default: 269 | dest[i] = v 270 | } 271 | case sqlite3.SQLITE_BLOB: 272 | v, err := r.c.columnBlob(r.pstmt, i) 273 | if err != nil { 274 | return err 275 | } 276 | 277 | dest[i] = v 278 | case sqlite3.SQLITE_NULL: 279 | dest[i] = nil 280 | default: 281 | return fmt.Errorf("internal error: rc %d", rc) 282 | } 283 | } 284 | return nil 285 | case sqlite3.SQLITE_DONE: 286 | return io.EOF 287 | default: 288 | return r.c.errstr(int32(rc)) 289 | } 290 | } 291 | 292 | // Inspired by mattn/go-sqlite3: https://github.com/mattn/go-sqlite3/blob/ab91e934/sqlite3.go#L210-L226 293 | // 294 | // These time.Parse formats handle formats 1 through 7 listed at https://www.sqlite.org/lang_datefunc.html. 295 | var parseTimeFormats = []string{ 296 | "2006-01-02 15:04:05.999999999-07:00", 297 | "2006-01-02T15:04:05.999999999-07:00", 298 | "2006-01-02 15:04:05.999999999", 299 | "2006-01-02T15:04:05.999999999", 300 | "2006-01-02 15:04:05", 301 | "2006-01-02T15:04:05", 302 | "2006-01-02 15:04", 303 | "2006-01-02T15:04", 304 | "2006-01-02", 305 | } 306 | 307 | // Attempt to parse s as a time. Return (s, false) if s is not 308 | // recognized as a valid time encoding. 309 | func (c *conn) parseTime(s string) (interface{}, bool) { 310 | if v, ok := c.parseTimeString(s, strings.Index(s, "m=")); ok { 311 | return v, true 312 | } 313 | 314 | ts := strings.TrimSuffix(s, "Z") 315 | 316 | for _, f := range parseTimeFormats { 317 | t, err := time.ParseInLocation(f, ts, time.UTC) 318 | if err == nil { 319 | return t, true 320 | } 321 | } 322 | 323 | return s, false 324 | } 325 | 326 | // Attempt to parse s as a time string produced by t.String(). If x > 0 it's 327 | // the index of substring "m=" within s. Return (s, false) if s is 328 | // not recognized as a valid time encoding. 329 | func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) { 330 | s := s0 331 | if x > 0 { 332 | s = s[:x] // "2006-01-02 15:04:05.999999999 -0700 MST m=+9999" -> "2006-01-02 15:04:05.999999999 -0700 MST " 333 | } 334 | s = strings.TrimSpace(s) 335 | if t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", s); err == nil { 336 | return t, true 337 | } 338 | 339 | return s0, false 340 | } 341 | 342 | // writeTimeFormats are the names and formats supported 343 | // by the `_time_format` DSN query param. 344 | var writeTimeFormats = map[string]string{ 345 | "sqlite": parseTimeFormats[0], 346 | } 347 | 348 | func (c *conn) formatTime(t time.Time) string { 349 | // default format is the first element from parseTimeFormats slice 350 | // this is inspired by https://github.com/mattn/go-sqlite3/blob/85436841b33e86c07dce0fa2e88c31a97c96a22f/sqlite3.go#L1893 351 | if c.writeTimeFormat == "" { 352 | return t.Format(parseTimeFormats[0]) 353 | } 354 | return t.Format(c.writeTimeFormat) 355 | } 356 | 357 | // RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return 358 | // the database system type name without the length. Type names should be 359 | // uppercase. Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", 360 | // "CHAR", "TEXT", "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", 361 | // "JSONB", "XML", "TIMESTAMP". 362 | func (r *rows) ColumnTypeDatabaseTypeName(index int) string { 363 | return strings.ToUpper(r.c.columnDeclType(r.pstmt, index)) 364 | } 365 | 366 | // RowsColumnTypeLength may be implemented by Rows. It should return the length 367 | // of the column type if the column is a variable length type. If the column is 368 | // not a variable length type ok should return false. If length is not limited 369 | // other than system limits, it should return math.MaxInt64. The following are 370 | // examples of returned values for various types: 371 | // 372 | // TEXT (math.MaxInt64, true) 373 | // varchar(10) (10, true) 374 | // nvarchar(10) (10, true) 375 | // decimal (0, false) 376 | // int (0, false) 377 | // bytea(30) (30, true) 378 | func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) { 379 | t, err := r.c.columnType(r.pstmt, index) 380 | if err != nil { 381 | return 0, false 382 | } 383 | 384 | switch t { 385 | case sqlite3.SQLITE_INTEGER: 386 | return 0, false 387 | case sqlite3.SQLITE_FLOAT: 388 | return 0, false 389 | case sqlite3.SQLITE_TEXT: 390 | return math.MaxInt64, true 391 | case sqlite3.SQLITE_BLOB: 392 | return math.MaxInt64, true 393 | case sqlite3.SQLITE_NULL: 394 | return 0, false 395 | default: 396 | return 0, false 397 | } 398 | } 399 | 400 | // RowsColumnTypeNullable may be implemented by Rows. The nullable value should 401 | // be true if it is known the column may be null, or false if the column is 402 | // known to be not nullable. If the column nullability is unknown, ok should be 403 | // false. 404 | func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) { 405 | return true, true 406 | } 407 | 408 | // RowsColumnTypePrecisionScale may be implemented by Rows. It should return 409 | // the precision and scale for decimal types. If not applicable, ok should be 410 | // false. The following are examples of returned values for various types: 411 | // 412 | // decimal(38, 4) (38, 4, true) 413 | // int (0, 0, false) 414 | // decimal (math.MaxInt64, math.MaxInt64, true) 415 | func (r *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { 416 | return 0, 0, false 417 | } 418 | 419 | // RowsColumnTypeScanType may be implemented by Rows. It should return the 420 | // value type that can be used to scan types into. For example, the database 421 | // column type "bigint" this should return "reflect.TypeOf(int64(0))". 422 | func (r *rows) ColumnTypeScanType(index int) reflect.Type { 423 | t, err := r.c.columnType(r.pstmt, index) 424 | if err != nil { 425 | return reflect.TypeOf("") 426 | } 427 | 428 | declType := strings.ToLower(r.c.columnDeclType(r.pstmt, index)) 429 | 430 | switch t { 431 | case sqlite3.SQLITE_INTEGER: 432 | if declType == "boolean" { 433 | // SQLite does not have a separate Boolean storage class. Instead, Boolean values are stored as integers 0 (false) and 1 (true). 434 | return reflect.TypeOf(false) 435 | } else { 436 | return reflect.TypeOf(int64(0)) 437 | } 438 | case sqlite3.SQLITE_FLOAT: 439 | return reflect.TypeOf(float64(0)) 440 | case sqlite3.SQLITE_TEXT: 441 | // SQLite does not have a storage class set aside for storing dates and/or times. 442 | // Instead, the built-in Date And Time Functions of SQLite are capable of storing 443 | // dates and times as TEXT, REAL, or INTEGER values 444 | switch declType { 445 | case "date", "datetime", "time", "timestamp": 446 | return reflect.TypeOf(time.Time{}) 447 | default: 448 | return reflect.TypeOf("") 449 | } 450 | case sqlite3.SQLITE_BLOB: 451 | return reflect.SliceOf(reflect.TypeOf([]byte{})) 452 | case sqlite3.SQLITE_NULL: 453 | return reflect.TypeOf(nil) 454 | default: 455 | return reflect.TypeOf("") 456 | } 457 | } 458 | 459 | type stmt struct { 460 | c *conn 461 | psql uintptr 462 | } 463 | 464 | func newStmt(c *conn, sql string) (*stmt, error) { 465 | p, err := libc.CString(sql) 466 | if err != nil { 467 | return nil, err 468 | } 469 | stm := stmt{c: c, psql: p} 470 | 471 | return &stm, nil 472 | } 473 | 474 | // Close closes the statement. 475 | // 476 | // As of Go 1.1, a Stmt will not be closed if it's in use by any queries. 477 | func (s *stmt) Close() (err error) { 478 | s.c.free(s.psql) 479 | s.psql = 0 480 | return nil 481 | } 482 | 483 | // Exec executes a query that doesn't return rows, such as an INSERT or UPDATE. 484 | // 485 | // Deprecated: Drivers should implement StmtExecContext instead (or 486 | // additionally). 487 | func (s *stmt) Exec(args []driver.Value) (driver.Result, error) { //TODO StmtExecContext 488 | return s.exec(context.Background(), toNamedValues(args)) 489 | } 490 | 491 | // toNamedValues converts []driver.Value to []driver.NamedValue 492 | func toNamedValues(vals []driver.Value) (r []driver.NamedValue) { 493 | r = make([]driver.NamedValue, len(vals)) 494 | for i, val := range vals { 495 | r[i] = driver.NamedValue{Value: val, Ordinal: i + 1} 496 | } 497 | return r 498 | } 499 | 500 | func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { 501 | var pstmt uintptr 502 | var done int32 503 | if ctx != nil && ctx.Done() != nil { 504 | defer interruptOnDone(ctx, s.c, &done)() 505 | } 506 | 507 | for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0 && atomic.LoadInt32(&done) == 0; { 508 | if pstmt, err = s.c.prepareV2(&psql); err != nil { 509 | return nil, err 510 | } 511 | 512 | if pstmt == 0 { 513 | continue 514 | } 515 | err = func() (err error) { 516 | n, err := s.c.bindParameterCount(pstmt) 517 | if err != nil { 518 | return err 519 | } 520 | 521 | if n != 0 { 522 | allocs, err := s.c.bind(pstmt, n, args) 523 | if err != nil { 524 | return err 525 | } 526 | 527 | if len(allocs) != 0 { 528 | defer func() { 529 | for _, v := range allocs { 530 | s.c.free(v) 531 | } 532 | }() 533 | } 534 | } 535 | 536 | rc, err := s.c.step(pstmt) 537 | if err != nil { 538 | return err 539 | } 540 | 541 | switch rc & 0xff { 542 | case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW: 543 | // nop 544 | default: 545 | return s.c.errstr(int32(rc)) 546 | } 547 | 548 | return nil 549 | }() 550 | 551 | if e := s.c.finalize(pstmt); e != nil && err == nil { 552 | err = e 553 | } 554 | 555 | if err != nil { 556 | return nil, err 557 | } 558 | } 559 | return newResult(s.c) 560 | } 561 | 562 | // NumInput returns the number of placeholder parameters. 563 | // 564 | // If NumInput returns >= 0, the sql package will sanity check argument counts 565 | // from callers and return errors to the caller before the statement's Exec or 566 | // Query methods are called. 567 | // 568 | // NumInput may also return -1, if the driver doesn't know its number of 569 | // placeholders. In that case, the sql package will not sanity check Exec or 570 | // Query argument counts. 571 | func (s *stmt) NumInput() (n int) { 572 | return -1 573 | } 574 | 575 | // Query executes a query that may return rows, such as a 576 | // SELECT. 577 | // 578 | // Deprecated: Drivers should implement StmtQueryContext instead (or 579 | // additionally). 580 | func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { //TODO StmtQueryContext 581 | return s.query(context.Background(), toNamedValues(args)) 582 | } 583 | 584 | func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { 585 | var pstmt uintptr // C-pointer to prepared statement 586 | var done int32 // done indicator (atomic usage) 587 | 588 | // context honoring 589 | if ctx != nil && ctx.Done() != nil { 590 | defer interruptOnDone(ctx, s.c, &done)() 591 | } 592 | 593 | // generally, query may contain multiple SQL statements 594 | // here we execute every but the last statement 595 | // we then create rows instance for deferred execution of the last statement 596 | 597 | // loop on all but last statements 598 | for pzTail := s.psql; ; { 599 | // honor the context 600 | if atomic.LoadInt32(&done) != 0 { 601 | return nil, ctx.Err() 602 | } 603 | 604 | // prepare yet another portion of SQL string 605 | if pstmt, err = s.c.prepareV2(&pzTail); err != nil { 606 | return nil, err 607 | } 608 | 609 | // *pzTail is left pointing to what remains uncompiled 610 | // so we can check if it was the last statement (if pzTail is NULL) 611 | if *(*byte)(unsafe.Pointer(pzTail)) == 0 { 612 | // it is the last statement, leave the prepared statement for the rows{} instance 613 | break 614 | } 615 | 616 | // If the input text contains no SQL (if the input is an empty string or a comment) then *ppStmt is set to NULL 617 | if pstmt == 0 { 618 | // we can safely skip it 619 | continue 620 | } 621 | 622 | // This routine can be used to find the number of SQL parameters in a prepared statement 623 | nParams, err := s.c.bindParameterCount(pstmt) 624 | if err != nil { 625 | return nil, err 626 | } 627 | 628 | if nParams > 0 { 629 | // bind the required portion of args 630 | if allocs, err := s.c.bind(pstmt, nParams, args); err != nil { 631 | return nil, err 632 | } else { 633 | // defer free allocated data 634 | defer func() { 635 | for _, v := range allocs { 636 | s.c.free(v) 637 | } 638 | }() 639 | } 640 | 641 | // shift the args to what has left after binding 642 | args = args[nParams:] 643 | for i := range args { 644 | args[i].Ordinal = i + 1 645 | } 646 | } 647 | 648 | // execute the statement 649 | rc, err := s.c.step(pstmt) 650 | if err != nil { 651 | return nil, err 652 | } 653 | 654 | // inspect return code 655 | switch rc & 0xff { 656 | case sqlite3.SQLITE_ROW, sqlite3.SQLITE_DONE: 657 | // we actually don't care if it is ROW or DONE 658 | // we ain't going to read the results anyway 659 | default: 660 | // other RCs considered error 661 | return nil, s.c.errstr(int32(rc)) 662 | } 663 | 664 | // The application must finalize every prepared statement in order to avoid resource leaks. 665 | if err := s.c.finalize(pstmt); err != nil { 666 | return nil, err 667 | } 668 | } 669 | 670 | // OK, at this point, we've executed all but last statements in SQL query 671 | // let's create rows{} object with prepared statement of the last statement in SQL query 672 | 673 | // This routine can be used to find the number of SQL parameters in a prepared statement 674 | nParams, err := s.c.bindParameterCount(pstmt) 675 | if err != nil { 676 | return nil, err 677 | } 678 | 679 | // bind the required portion of args 680 | var allocs []uintptr 681 | allocs, err = s.c.bind(pstmt, nParams, args) 682 | if err != nil { 683 | return nil, err 684 | } 685 | 686 | // create rows 687 | r, err = newRows(s.c, pstmt, allocs) 688 | if err != nil { 689 | return nil, err 690 | } 691 | return r, nil 692 | } 693 | 694 | type tx struct { 695 | c *conn 696 | } 697 | 698 | func newTx(c *conn, opts driver.TxOptions) (*tx, error) { 699 | r := &tx{c: c} 700 | 701 | sql := "begin" 702 | if !opts.ReadOnly && c.beginMode != "" { 703 | sql = "begin " + c.beginMode 704 | } 705 | 706 | if err := r.exec(context.Background(), sql); err != nil { 707 | return nil, err 708 | } 709 | 710 | return r, nil 711 | } 712 | 713 | // Commit implements driver.Tx. 714 | func (t *tx) Commit() (err error) { 715 | return t.exec(context.Background(), "commit") 716 | } 717 | 718 | // Rollback implements driver.Tx. 719 | func (t *tx) Rollback() (err error) { 720 | return t.exec(context.Background(), "rollback") 721 | } 722 | 723 | func (t *tx) exec(ctx context.Context, sql string) (err error) { 724 | psql, err := libc.CString(sql) 725 | if err != nil { 726 | return err 727 | } 728 | 729 | defer t.c.free(psql) 730 | //TODO use t.conn.ExecContext() instead 731 | 732 | if ctx != nil && ctx.Done() != nil { 733 | defer interruptOnDone(ctx, t.c, nil)() 734 | } 735 | 736 | if rc := sqlite3.Xsqlite3_exec(t.c.tls, t.c.db, psql, 0, 0, 0); rc != sqlite3.SQLITE_OK { 737 | return t.c.errstr(rc) 738 | } 739 | 740 | return nil 741 | } 742 | 743 | // interruptOnDone sets up a goroutine to interrupt the provided db when the 744 | // context is canceled, and returns a function the caller must defer so it 745 | // doesn't interrupt after the caller finishes. 746 | func interruptOnDone( 747 | ctx context.Context, 748 | c *conn, 749 | done *int32, 750 | ) func() { 751 | if done == nil { 752 | var d int32 753 | done = &d 754 | } 755 | 756 | donech := make(chan struct{}) 757 | 758 | go func() { 759 | select { 760 | case <-ctx.Done(): 761 | // don't call interrupt if we were already done: it indicates that this 762 | // call to exec is no longer running and we would be interrupting 763 | // nothing, or even possibly an unrelated later call to exec. 764 | if atomic.AddInt32(done, 1) == 1 { 765 | c.interrupt(c.db) 766 | } 767 | case <-donech: 768 | } 769 | }() 770 | 771 | // the caller is expected to defer this function 772 | return func() { 773 | // set the done flag so that a context cancellation right after the caller 774 | // returns doesn't trigger a call to interrupt for some other statement. 775 | atomic.AddInt32(done, 1) 776 | close(donech) 777 | } 778 | } 779 | 780 | type conn struct { 781 | db uintptr // *sqlite3.Xsqlite3 782 | tls *libc.TLS 783 | 784 | // Context handling can cause conn.Close and conn.interrupt to be invoked 785 | // concurrently. 786 | sync.Mutex 787 | 788 | writeTimeFormat string 789 | beginMode string 790 | } 791 | 792 | func newConn(dsn string) (*conn, error) { 793 | var query, vfsName string 794 | 795 | // Parse the query parameters from the dsn and them from the dsn if not prefixed by file: 796 | // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1046 797 | // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1383 798 | pos := strings.IndexRune(dsn, '?') 799 | if pos >= 1 { 800 | query = dsn[pos+1:] 801 | var err error 802 | vfsName, err = getVFSName(query) 803 | if err != nil { 804 | return nil, err 805 | } 806 | 807 | if !strings.HasPrefix(dsn, "file:") { 808 | dsn = dsn[:pos] 809 | } 810 | } 811 | 812 | c := &conn{tls: libc.NewTLS()} 813 | db, err := c.openV2( 814 | dsn, 815 | vfsName, 816 | sqlite3.SQLITE_OPEN_READWRITE|sqlite3.SQLITE_OPEN_CREATE| 817 | sqlite3.SQLITE_OPEN_FULLMUTEX| 818 | sqlite3.SQLITE_OPEN_URI, 819 | ) 820 | if err != nil { 821 | return nil, err 822 | } 823 | 824 | // register statement logger 825 | if LogSqlStatements { 826 | if sqlite3.Xsqlite3_trace_v2(c.tls, db, sqlite3.SQLITE_TRACE_STMT, *(*uintptr)(unsafe.Pointer(&struct { 827 | f func(*libc.TLS, uint32, uintptr, uintptr, uintptr) int32 828 | }{stmtLog})), 0) != 0 { 829 | log.Fatal("failed to register tracing handler") 830 | } 831 | } 832 | 833 | c.db = db 834 | if err = c.extendedResultCodes(true); err != nil { 835 | c.Close() 836 | return nil, err 837 | } 838 | 839 | if err = applyQueryParams(c, query); err != nil { 840 | c.Close() 841 | return nil, err 842 | } 843 | 844 | return c, nil 845 | } 846 | 847 | func stmtLog(tls *libc.TLS, type1 uint32, cd uintptr, pd uintptr, xd uintptr) int32 { /* tclsqlite.c:661:12: */ 848 | if type1 == uint32(sqlite3.SQLITE_TRACE_STMT) { 849 | // get SQL string 850 | stmtEx := libc.GoString(sqlite3.Xsqlite3_expanded_sql(tls, pd)) 851 | log.Println(strings.Trim(stmtEx, "\r\n\t ")) 852 | } 853 | return sqlite3.SQLITE_OK 854 | } 855 | 856 | func getVFSName(query string) (r string, err error) { 857 | q, err := url.ParseQuery(query) 858 | if err != nil { 859 | return "", err 860 | } 861 | 862 | for _, v := range q["vfs"] { 863 | if r != "" && r != v { 864 | return "", fmt.Errorf("conflicting vfs query parameters: %v", q["vfs"]) 865 | } 866 | 867 | r = v 868 | } 869 | 870 | return r, nil 871 | } 872 | 873 | func applyQueryParams(c *conn, query string) error { 874 | q, err := url.ParseQuery(query) 875 | if err != nil { 876 | return err 877 | } 878 | 879 | // set default BUSY_TIMEOUT, just like mattn/go-sqlite3 does. 880 | _, err = c.exec(context.Background(), `pragma BUSY_TIMEOUT(5000)`, nil) 881 | if err != nil { 882 | return err 883 | } 884 | 885 | for _, v := range q["_pragma"] { 886 | cmd := "pragma " + v 887 | _, err := c.exec(context.Background(), cmd, nil) 888 | if err != nil { 889 | return err 890 | } 891 | } 892 | 893 | if v := q.Get("_time_format"); v != "" { 894 | f, ok := writeTimeFormats[v] 895 | if !ok { 896 | return fmt.Errorf("unknown _time_format %q", v) 897 | } 898 | c.writeTimeFormat = f 899 | return nil 900 | } 901 | 902 | if v := q.Get("_txlock"); v != "" { 903 | lower := strings.ToLower(v) 904 | if lower != "deferred" && lower != "immediate" && lower != "exclusive" { 905 | return fmt.Errorf("unknown _txlock %q", v) 906 | } 907 | c.beginMode = v 908 | } 909 | 910 | return nil 911 | } 912 | 913 | // const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); 914 | func (c *conn) columnBlob(pstmt uintptr, iCol int) (v []byte, err error) { 915 | p := sqlite3.Xsqlite3_column_blob(c.tls, pstmt, int32(iCol)) 916 | len, err := c.columnBytes(pstmt, iCol) 917 | if err != nil { 918 | return nil, err 919 | } 920 | 921 | if p == 0 || len == 0 { 922 | return nil, nil 923 | } 924 | 925 | v = make([]byte, len) 926 | copy(v, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) 927 | return v, nil 928 | } 929 | 930 | // int sqlite3_column_bytes(sqlite3_stmt*, int iCol); 931 | func (c *conn) columnBytes(pstmt uintptr, iCol int) (_ int, err error) { 932 | v := sqlite3.Xsqlite3_column_bytes(c.tls, pstmt, int32(iCol)) 933 | return int(v), nil 934 | } 935 | 936 | // const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); 937 | func (c *conn) columnText(pstmt uintptr, iCol int) (v string, err error) { 938 | p := sqlite3.Xsqlite3_column_text(c.tls, pstmt, int32(iCol)) 939 | len, err := c.columnBytes(pstmt, iCol) 940 | if err != nil { 941 | return "", err 942 | } 943 | 944 | if p == 0 || len == 0 { 945 | return "", nil 946 | } 947 | 948 | b := make([]byte, len) 949 | copy(b, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) 950 | return string(b), nil 951 | } 952 | 953 | // double sqlite3_column_double(sqlite3_stmt*, int iCol); 954 | func (c *conn) columnDouble(pstmt uintptr, iCol int) (v float64, err error) { 955 | v = sqlite3.Xsqlite3_column_double(c.tls, pstmt, int32(iCol)) 956 | return v, nil 957 | } 958 | 959 | // sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); 960 | func (c *conn) columnInt64(pstmt uintptr, iCol int) (v int64, err error) { 961 | v = sqlite3.Xsqlite3_column_int64(c.tls, pstmt, int32(iCol)) 962 | return v, nil 963 | } 964 | 965 | // int sqlite3_column_type(sqlite3_stmt*, int iCol); 966 | func (c *conn) columnType(pstmt uintptr, iCol int) (_ int, err error) { 967 | v := sqlite3.Xsqlite3_column_type(c.tls, pstmt, int32(iCol)) 968 | return int(v), nil 969 | } 970 | 971 | // const char *sqlite3_column_decltype(sqlite3_stmt*,int); 972 | func (c *conn) columnDeclType(pstmt uintptr, iCol int) string { 973 | return libc.GoString(sqlite3.Xsqlite3_column_decltype(c.tls, pstmt, int32(iCol))) 974 | } 975 | 976 | // const char *sqlite3_column_name(sqlite3_stmt*, int N); 977 | func (c *conn) columnName(pstmt uintptr, n int) (string, error) { 978 | p := sqlite3.Xsqlite3_column_name(c.tls, pstmt, int32(n)) 979 | return libc.GoString(p), nil 980 | } 981 | 982 | // int sqlite3_column_count(sqlite3_stmt *pStmt); 983 | func (c *conn) columnCount(pstmt uintptr) (_ int, err error) { 984 | v := sqlite3.Xsqlite3_column_count(c.tls, pstmt) 985 | return int(v), nil 986 | } 987 | 988 | // sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); 989 | func (c *conn) lastInsertRowID() (v int64, _ error) { 990 | return sqlite3.Xsqlite3_last_insert_rowid(c.tls, c.db), nil 991 | } 992 | 993 | // int sqlite3_changes(sqlite3*); 994 | func (c *conn) changes() (int, error) { 995 | v := sqlite3.Xsqlite3_changes(c.tls, c.db) 996 | return int(v), nil 997 | } 998 | 999 | // int sqlite3_step(sqlite3_stmt*); 1000 | func (c *conn) step(pstmt uintptr) (int, error) { 1001 | for { 1002 | switch rc := sqlite3.Xsqlite3_step(c.tls, pstmt); rc { 1003 | case sqliteLockedSharedcache: 1004 | if err := c.retry(pstmt); err != nil { 1005 | return sqlite3.SQLITE_LOCKED, err 1006 | } 1007 | case 1008 | sqlite3.SQLITE_DONE, 1009 | sqlite3.SQLITE_ROW: 1010 | 1011 | return int(rc), nil 1012 | default: 1013 | return int(rc), c.errstr(rc) 1014 | } 1015 | } 1016 | } 1017 | 1018 | func (c *conn) retry(pstmt uintptr) error { 1019 | mu := mutexAlloc(c.tls) 1020 | (*mutex)(unsafe.Pointer(mu)).Lock() 1021 | rc := sqlite3.Xsqlite3_unlock_notify( 1022 | c.tls, 1023 | c.db, 1024 | *(*uintptr)(unsafe.Pointer(&struct { 1025 | f func(*libc.TLS, uintptr, int32) 1026 | }{unlockNotify})), 1027 | mu, 1028 | ) 1029 | if rc == sqlite3.SQLITE_LOCKED { // Deadlock, see https://www.sqlite.org/c3ref/unlock_notify.html 1030 | (*mutex)(unsafe.Pointer(mu)).Unlock() 1031 | mutexFree(c.tls, mu) 1032 | return c.errstr(rc) 1033 | } 1034 | 1035 | (*mutex)(unsafe.Pointer(mu)).Lock() 1036 | (*mutex)(unsafe.Pointer(mu)).Unlock() 1037 | mutexFree(c.tls, mu) 1038 | if pstmt != 0 { 1039 | sqlite3.Xsqlite3_reset(c.tls, pstmt) 1040 | } 1041 | return nil 1042 | } 1043 | 1044 | func unlockNotify(t *libc.TLS, ppArg uintptr, nArg int32) { 1045 | for i := int32(0); i < nArg; i++ { 1046 | mu := *(*uintptr)(unsafe.Pointer(ppArg)) 1047 | (*mutex)(unsafe.Pointer(mu)).Unlock() 1048 | ppArg += ptrSize 1049 | } 1050 | } 1051 | 1052 | func (c *conn) bind(pstmt uintptr, n int, args []driver.NamedValue) (allocs []uintptr, err error) { 1053 | defer func() { 1054 | if err == nil { 1055 | return 1056 | } 1057 | 1058 | for _, v := range allocs { 1059 | c.free(v) 1060 | } 1061 | allocs = nil 1062 | }() 1063 | 1064 | for i := 1; i <= n; i++ { 1065 | name, err := c.bindParameterName(pstmt, i) 1066 | if err != nil { 1067 | return allocs, err 1068 | } 1069 | 1070 | var found bool 1071 | var v driver.NamedValue 1072 | for _, v = range args { 1073 | if name != "" { 1074 | // For ?NNN and $NNN params, match if NNN == v.Ordinal. 1075 | // 1076 | // Supporting this for $NNN is a special case that makes eg 1077 | // `select $1, $2, $3 ...` work without needing to use 1078 | // sql.Named. 1079 | if (name[0] == '?' || name[0] == '$') && name[1:] == strconv.Itoa(v.Ordinal) { 1080 | found = true 1081 | break 1082 | } 1083 | 1084 | // sqlite supports '$', '@' and ':' prefixes for string 1085 | // identifiers and '?' for numeric, so we cannot 1086 | // combine different prefixes with the same name 1087 | // because `database/sql` requires variable names 1088 | // to start with a letter 1089 | if name[1:] == v.Name[:] { 1090 | found = true 1091 | break 1092 | } 1093 | } else { 1094 | if v.Ordinal == i { 1095 | found = true 1096 | break 1097 | } 1098 | } 1099 | } 1100 | 1101 | if !found { 1102 | if name != "" { 1103 | return allocs, fmt.Errorf("missing named argument %q", name[1:]) 1104 | } 1105 | 1106 | return allocs, fmt.Errorf("missing argument with index %d", i) 1107 | } 1108 | 1109 | var p uintptr 1110 | switch x := v.Value.(type) { 1111 | case int64: 1112 | if err := c.bindInt64(pstmt, i, x); err != nil { 1113 | return allocs, err 1114 | } 1115 | case float64: 1116 | if err := c.bindDouble(pstmt, i, x); err != nil { 1117 | return allocs, err 1118 | } 1119 | case bool: 1120 | v := 0 1121 | if x { 1122 | v = 1 1123 | } 1124 | if err := c.bindInt(pstmt, i, v); err != nil { 1125 | return allocs, err 1126 | } 1127 | case []byte: 1128 | if p, err = c.bindBlob(pstmt, i, x); err != nil { 1129 | return allocs, err 1130 | } 1131 | case string: 1132 | if p, err = c.bindText(pstmt, i, x); err != nil { 1133 | return allocs, err 1134 | } 1135 | case time.Time: 1136 | if p, err = c.bindText(pstmt, i, c.formatTime(x)); err != nil { 1137 | return allocs, err 1138 | } 1139 | case nil: 1140 | if p, err = c.bindNull(pstmt, i); err != nil { 1141 | return allocs, err 1142 | } 1143 | default: 1144 | return allocs, fmt.Errorf("sqlite: invalid driver.Value type %T", x) 1145 | } 1146 | if p != 0 { 1147 | allocs = append(allocs, p) 1148 | } 1149 | } 1150 | return allocs, nil 1151 | } 1152 | 1153 | // int sqlite3_bind_null(sqlite3_stmt*, int); 1154 | func (c *conn) bindNull(pstmt uintptr, idx1 int) (uintptr, error) { 1155 | if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK { 1156 | return 0, c.errstr(rc) 1157 | } 1158 | 1159 | return 0, nil 1160 | } 1161 | 1162 | // int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); 1163 | func (c *conn) bindText(pstmt uintptr, idx1 int, value string) (uintptr, error) { 1164 | p, err := libc.CString(value) 1165 | if err != nil { 1166 | return 0, err 1167 | } 1168 | 1169 | if rc := sqlite3.Xsqlite3_bind_text(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { 1170 | c.free(p) 1171 | return 0, c.errstr(rc) 1172 | } 1173 | 1174 | return p, nil 1175 | } 1176 | 1177 | // int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); 1178 | func (c *conn) bindBlob(pstmt uintptr, idx1 int, value []byte) (uintptr, error) { 1179 | if value != nil && len(value) == 0 { 1180 | if rc := sqlite3.Xsqlite3_bind_zeroblob(c.tls, pstmt, int32(idx1), 0); rc != sqlite3.SQLITE_OK { 1181 | return 0, c.errstr(rc) 1182 | } 1183 | return 0, nil 1184 | } 1185 | 1186 | p, err := c.malloc(len(value)) 1187 | if err != nil { 1188 | return 0, err 1189 | } 1190 | if len(value) != 0 { 1191 | copy((*libc.RawMem)(unsafe.Pointer(p))[:len(value):len(value)], value) 1192 | } 1193 | if rc := sqlite3.Xsqlite3_bind_blob(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { 1194 | c.free(p) 1195 | return 0, c.errstr(rc) 1196 | } 1197 | 1198 | return p, nil 1199 | } 1200 | 1201 | // int sqlite3_bind_int(sqlite3_stmt*, int, int); 1202 | func (c *conn) bindInt(pstmt uintptr, idx1, value int) (err error) { 1203 | if rc := sqlite3.Xsqlite3_bind_int(c.tls, pstmt, int32(idx1), int32(value)); rc != sqlite3.SQLITE_OK { 1204 | return c.errstr(rc) 1205 | } 1206 | 1207 | return nil 1208 | } 1209 | 1210 | // int sqlite3_bind_double(sqlite3_stmt*, int, double); 1211 | func (c *conn) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) { 1212 | if rc := sqlite3.Xsqlite3_bind_double(c.tls, pstmt, int32(idx1), value); rc != 0 { 1213 | return c.errstr(rc) 1214 | } 1215 | 1216 | return nil 1217 | } 1218 | 1219 | // int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); 1220 | func (c *conn) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) { 1221 | if rc := sqlite3.Xsqlite3_bind_int64(c.tls, pstmt, int32(idx1), value); rc != sqlite3.SQLITE_OK { 1222 | return c.errstr(rc) 1223 | } 1224 | 1225 | return nil 1226 | } 1227 | 1228 | // const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); 1229 | func (c *conn) bindParameterName(pstmt uintptr, i int) (string, error) { 1230 | p := sqlite3.Xsqlite3_bind_parameter_name(c.tls, pstmt, int32(i)) 1231 | return libc.GoString(p), nil 1232 | } 1233 | 1234 | // int sqlite3_bind_parameter_count(sqlite3_stmt*); 1235 | func (c *conn) bindParameterCount(pstmt uintptr) (_ int, err error) { 1236 | r := sqlite3.Xsqlite3_bind_parameter_count(c.tls, pstmt) 1237 | return int(r), nil 1238 | } 1239 | 1240 | // int sqlite3_finalize(sqlite3_stmt *pStmt); 1241 | func (c *conn) finalize(pstmt uintptr) error { 1242 | if rc := sqlite3.Xsqlite3_finalize(c.tls, pstmt); rc != sqlite3.SQLITE_OK { 1243 | return c.errstr(rc) 1244 | } 1245 | 1246 | return nil 1247 | } 1248 | 1249 | // int sqlite3_prepare_v2( 1250 | // 1251 | // sqlite3 *db, /* Database handle */ 1252 | // const char *zSql, /* SQL statement, UTF-8 encoded */ 1253 | // int nByte, /* Maximum length of zSql in bytes. */ 1254 | // sqlite3_stmt **ppStmt, /* OUT: Statement handle */ 1255 | // const char **pzTail /* OUT: Pointer to unused portion of zSql */ 1256 | // 1257 | // ); 1258 | func (c *conn) prepareV2(zSQL *uintptr) (pstmt uintptr, err error) { 1259 | var ppstmt, pptail uintptr 1260 | 1261 | defer func() { 1262 | c.free(ppstmt) 1263 | c.free(pptail) 1264 | }() 1265 | 1266 | if ppstmt, err = c.malloc(int(ptrSize)); err != nil { 1267 | return 0, err 1268 | } 1269 | 1270 | if pptail, err = c.malloc(int(ptrSize)); err != nil { 1271 | return 0, err 1272 | } 1273 | 1274 | for { 1275 | switch rc := sqlite3.Xsqlite3_prepare_v2(c.tls, c.db, *zSQL, -1, ppstmt, pptail); rc { 1276 | case sqlite3.SQLITE_OK: 1277 | *zSQL = *(*uintptr)(unsafe.Pointer(pptail)) 1278 | return *(*uintptr)(unsafe.Pointer(ppstmt)), nil 1279 | case sqliteLockedSharedcache: 1280 | if err := c.retry(0); err != nil { 1281 | return 0, err 1282 | } 1283 | default: 1284 | return 0, c.errstr(rc) 1285 | } 1286 | } 1287 | } 1288 | 1289 | // void sqlite3_interrupt(sqlite3*); 1290 | func (c *conn) interrupt(pdb uintptr) (err error) { 1291 | c.Lock() // Defend against race with .Close invoked by context handling. 1292 | 1293 | defer c.Unlock() 1294 | 1295 | if c.tls != nil { 1296 | sqlite3.Xsqlite3_interrupt(c.tls, pdb) 1297 | } 1298 | return nil 1299 | } 1300 | 1301 | // int sqlite3_extended_result_codes(sqlite3*, int onoff); 1302 | func (c *conn) extendedResultCodes(on bool) error { 1303 | if rc := sqlite3.Xsqlite3_extended_result_codes(c.tls, c.db, libc.Bool32(on)); rc != sqlite3.SQLITE_OK { 1304 | return c.errstr(rc) 1305 | } 1306 | 1307 | return nil 1308 | } 1309 | 1310 | // int sqlite3_open_v2( 1311 | // 1312 | // const char *filename, /* Database filename (UTF-8) */ 1313 | // sqlite3 **ppDb, /* OUT: SQLite db handle */ 1314 | // int flags, /* Flags */ 1315 | // const char *zVfs /* Name of VFS module to use */ 1316 | // 1317 | // ); 1318 | func (c *conn) openV2(name, vfsName string, flags int32) (uintptr, error) { 1319 | var p, s, vfs uintptr 1320 | 1321 | defer func() { 1322 | if p != 0 { 1323 | c.free(p) 1324 | } 1325 | if s != 0 { 1326 | c.free(s) 1327 | } 1328 | if vfs != 0 { 1329 | c.free(vfs) 1330 | } 1331 | }() 1332 | 1333 | p, err := c.malloc(int(ptrSize)) 1334 | if err != nil { 1335 | return 0, err 1336 | } 1337 | 1338 | if s, err = libc.CString(name); err != nil { 1339 | return 0, err 1340 | } 1341 | 1342 | if vfsName != "" { 1343 | if vfs, err = libc.CString(vfsName); err != nil { 1344 | return 0, err 1345 | } 1346 | } 1347 | 1348 | if rc := sqlite3.Xsqlite3_open_v2(c.tls, s, p, flags, vfs); rc != sqlite3.SQLITE_OK { 1349 | return 0, c.errstr(rc) 1350 | } 1351 | 1352 | return *(*uintptr)(unsafe.Pointer(p)), nil 1353 | } 1354 | 1355 | func (c *conn) malloc(n int) (uintptr, error) { 1356 | if p := libc.Xmalloc(c.tls, types.Size_t(n)); p != 0 || n == 0 { 1357 | return p, nil 1358 | } 1359 | 1360 | return 0, fmt.Errorf("sqlite: cannot allocate %d bytes of memory", n) 1361 | } 1362 | 1363 | func (c *conn) free(p uintptr) { 1364 | if p != 0 { 1365 | libc.Xfree(c.tls, p) 1366 | } 1367 | } 1368 | 1369 | // const char *sqlite3_errstr(int); 1370 | func (c *conn) errstr(rc int32) error { 1371 | p := sqlite3.Xsqlite3_errstr(c.tls, rc) 1372 | str := libc.GoString(p) 1373 | p = sqlite3.Xsqlite3_errmsg(c.tls, c.db) 1374 | var s string 1375 | if rc == sqlite3.SQLITE_BUSY { 1376 | s = " (SQLITE_BUSY)" 1377 | } 1378 | switch msg := libc.GoString(p); { 1379 | case msg == str: 1380 | return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)} 1381 | default: 1382 | return &Error{msg: fmt.Sprintf("%s: %s (%v)%s", str, msg, rc, s), code: int(rc)} 1383 | } 1384 | } 1385 | 1386 | // Begin starts a transaction. 1387 | // 1388 | // Deprecated: Drivers should implement ConnBeginTx instead (or additionally). 1389 | func (c *conn) Begin() (driver.Tx, error) { 1390 | return c.begin(context.Background(), driver.TxOptions{}) 1391 | } 1392 | 1393 | func (c *conn) begin(ctx context.Context, opts driver.TxOptions) (t driver.Tx, err error) { 1394 | return newTx(c, opts) 1395 | } 1396 | 1397 | // Close invalidates and potentially stops any current prepared statements and 1398 | // transactions, marking this connection as no longer in use. 1399 | // 1400 | // Because the sql package maintains a free pool of connections and only calls 1401 | // Close when there's a surplus of idle connections, it shouldn't be necessary 1402 | // for drivers to do their own connection caching. 1403 | func (c *conn) Close() error { 1404 | c.Lock() // Defend against race with .interrupt invoked by context handling. 1405 | 1406 | defer c.Unlock() 1407 | 1408 | if c.db != 0 { 1409 | if err := c.closeV2(c.db); err != nil { 1410 | return err 1411 | } 1412 | 1413 | c.db = 0 1414 | } 1415 | 1416 | if c.tls != nil { 1417 | c.tls.Close() 1418 | c.tls = nil 1419 | } 1420 | return nil 1421 | } 1422 | 1423 | // int sqlite3_close_v2(sqlite3*); 1424 | func (c *conn) closeV2(db uintptr) error { 1425 | if rc := sqlite3.Xsqlite3_close_v2(c.tls, db); rc != sqlite3.SQLITE_OK { 1426 | return c.errstr(rc) 1427 | } 1428 | 1429 | return nil 1430 | } 1431 | 1432 | type userDefinedFunction struct { 1433 | zFuncName uintptr 1434 | nArg int32 1435 | eTextRep int32 1436 | xFunc func(*libc.TLS, uintptr, int32, uintptr) 1437 | 1438 | freeOnce sync.Once 1439 | } 1440 | 1441 | func (c *conn) createFunctionInternal(fun *userDefinedFunction) error { 1442 | if rc := sqlite3.Xsqlite3_create_function( 1443 | c.tls, 1444 | c.db, 1445 | fun.zFuncName, 1446 | fun.nArg, 1447 | fun.eTextRep, 1448 | 0, 1449 | *(*uintptr)(unsafe.Pointer(&fun.xFunc)), 1450 | 0, 1451 | 0, 1452 | ); rc != sqlite3.SQLITE_OK { 1453 | return c.errstr(rc) 1454 | } 1455 | return nil 1456 | } 1457 | 1458 | // Execer is an optional interface that may be implemented by a Conn. 1459 | // 1460 | // If a Conn does not implement Execer, the sql package's DB.Exec will first 1461 | // prepare a query, execute the statement, and then close the statement. 1462 | // 1463 | // Exec may return ErrSkip. 1464 | // 1465 | // Deprecated: Drivers should implement ExecerContext instead. 1466 | func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) { 1467 | return c.exec(context.Background(), query, toNamedValues(args)) 1468 | } 1469 | 1470 | func (c *conn) exec(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { 1471 | s, err := c.prepare(ctx, query) 1472 | if err != nil { 1473 | return nil, err 1474 | } 1475 | 1476 | defer func() { 1477 | if err2 := s.Close(); err2 != nil && err == nil { 1478 | err = err2 1479 | } 1480 | }() 1481 | 1482 | return s.(*stmt).exec(ctx, args) 1483 | } 1484 | 1485 | // Prepare returns a prepared statement, bound to this connection. 1486 | func (c *conn) Prepare(query string) (driver.Stmt, error) { 1487 | return c.prepare(context.Background(), query) 1488 | } 1489 | 1490 | func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) { 1491 | //TODO use ctx 1492 | return newStmt(c, query) 1493 | } 1494 | 1495 | // Queryer is an optional interface that may be implemented by a Conn. 1496 | // 1497 | // If a Conn does not implement Queryer, the sql package's DB.Query will first 1498 | // prepare a query, execute the statement, and then close the statement. 1499 | // 1500 | // Query may return ErrSkip. 1501 | // 1502 | // Deprecated: Drivers should implement QueryerContext instead. 1503 | func (c *conn) Query(query string, args []driver.Value) (driver.Rows, error) { 1504 | return c.query(context.Background(), query, toNamedValues(args)) 1505 | } 1506 | 1507 | func (c *conn) query(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { 1508 | s, err := c.prepare(ctx, query) 1509 | if err != nil { 1510 | return nil, err 1511 | } 1512 | 1513 | defer func() { 1514 | if err2 := s.Close(); err2 != nil && err == nil { 1515 | err = err2 1516 | } 1517 | }() 1518 | 1519 | return s.(*stmt).query(ctx, args) 1520 | } 1521 | 1522 | // Driver implements database/sql/driver.Driver. 1523 | type Driver struct { 1524 | // user defined functions that are added to every new connection on Open 1525 | udfs map[string]*userDefinedFunction 1526 | } 1527 | 1528 | var d = &Driver{udfs: make(map[string]*userDefinedFunction)} 1529 | 1530 | func newDriver() *Driver { return d } 1531 | 1532 | // Open returns a new connection to the database. The name is a string in a 1533 | // driver-specific format. 1534 | // 1535 | // Open may return a cached connection (one previously closed), but doing so is 1536 | // unnecessary; the sql package maintains a pool of idle connections for 1537 | // efficient re-use. 1538 | // 1539 | // The returned connection is only used by one goroutine at a time. 1540 | // 1541 | // If name contains a '?', what follows is treated as a query string. This 1542 | // driver supports the following query parameters: 1543 | // 1544 | // _pragma: Each value will be run as a "PRAGMA ..." statement (with the PRAGMA 1545 | // keyword added for you). May be specified more than once. Example: 1546 | // "_pragma=foreign_keys(1)" will enable foreign key enforcement. More 1547 | // information on supported PRAGMAs is available from the SQLite documentation: 1548 | // https://www.sqlite.org/pragma.html 1549 | // 1550 | // _time_format: The name of a format to use when writing time values to the 1551 | // database. Currently the only supported value is "sqlite", which corresponds 1552 | // to format 7 from https://www.sqlite.org/lang_datefunc.html#time_values, 1553 | // including the timezone specifier. If this parameter is not specified, then 1554 | // the default String() format will be used. 1555 | // 1556 | // _txlock: The locking behavior to use when beginning a transaction. May be 1557 | // "deferred", "immediate", or "exclusive" (case insensitive). The default is to 1558 | // not specify one, which SQLite maps to "deferred". More information is 1559 | // available at 1560 | // https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions 1561 | func (d *Driver) Open(name string) (driver.Conn, error) { 1562 | c, err := newConn(name) 1563 | if err != nil { 1564 | return nil, err 1565 | } 1566 | 1567 | for _, udf := range d.udfs { 1568 | if err = c.createFunctionInternal(udf); err != nil { 1569 | c.Close() 1570 | return nil, err 1571 | } 1572 | } 1573 | 1574 | if LogSqlStatements { 1575 | log.Println("new connection") 1576 | } 1577 | 1578 | return c, nil 1579 | } 1580 | 1581 | // FunctionContext represents the context user defined functions execute in. 1582 | // Fields and/or methods of this type may get addedd in the future. 1583 | type FunctionContext struct{} 1584 | 1585 | const sqliteValPtrSize = unsafe.Sizeof(&sqlite3.Sqlite3_value{}) 1586 | 1587 | // RegisterScalarFunction registers a scalar function named zFuncName with nArg 1588 | // arguments. Passing -1 for nArg indicates the function is variadic. 1589 | // 1590 | // The new function will be available to all new connections opened after 1591 | // executing RegisterScalarFunction. 1592 | func RegisterScalarFunction( 1593 | zFuncName string, 1594 | nArg int32, 1595 | xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error), 1596 | ) error { 1597 | return registerScalarFunction(zFuncName, nArg, sqlite3.SQLITE_UTF8, xFunc) 1598 | } 1599 | 1600 | // MustRegisterScalarFunction is like RegisterScalarFunction but panics on 1601 | // error. 1602 | func MustRegisterScalarFunction( 1603 | zFuncName string, 1604 | nArg int32, 1605 | xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error), 1606 | ) { 1607 | if err := RegisterScalarFunction(zFuncName, nArg, xFunc); err != nil { 1608 | panic(err) 1609 | } 1610 | } 1611 | 1612 | // MustRegisterDeterministicScalarFunction is like 1613 | // RegisterDeterministicScalarFunction but panics on error. 1614 | func MustRegisterDeterministicScalarFunction( 1615 | zFuncName string, 1616 | nArg int32, 1617 | xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error), 1618 | ) { 1619 | if err := RegisterDeterministicScalarFunction(zFuncName, nArg, xFunc); err != nil { 1620 | panic(err) 1621 | } 1622 | } 1623 | 1624 | // RegisterDeterministicScalarFunction registers a deterministic scalar 1625 | // function named zFuncName with nArg arguments. Passing -1 for nArg indicates 1626 | // the function is variadic. A deterministic function means that the function 1627 | // always gives the same output when the input parameters are the same. 1628 | // 1629 | // The new function will be available to all new connections opened after 1630 | // executing RegisterDeterministicScalarFunction. 1631 | func RegisterDeterministicScalarFunction( 1632 | zFuncName string, 1633 | nArg int32, 1634 | xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error), 1635 | ) error { 1636 | return registerScalarFunction(zFuncName, nArg, sqlite3.SQLITE_UTF8|sqlite3.SQLITE_DETERMINISTIC, xFunc) 1637 | } 1638 | 1639 | func registerScalarFunction( 1640 | zFuncName string, 1641 | nArg int32, 1642 | eTextRep int32, 1643 | xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error), 1644 | ) error { 1645 | 1646 | if _, ok := d.udfs[zFuncName]; ok { 1647 | return fmt.Errorf("a function named %q is already registered", zFuncName) 1648 | } 1649 | 1650 | // dont free, functions registered on the driver live as long as the program 1651 | name, err := libc.CString(zFuncName) 1652 | if err != nil { 1653 | return err 1654 | } 1655 | 1656 | udf := &userDefinedFunction{ 1657 | zFuncName: name, 1658 | nArg: nArg, 1659 | eTextRep: eTextRep, 1660 | xFunc: func(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { 1661 | setErrorResult := func(res error) { 1662 | errmsg, cerr := libc.CString(res.Error()) 1663 | if cerr != nil { 1664 | panic(cerr) 1665 | } 1666 | defer libc.Xfree(tls, errmsg) 1667 | sqlite3.Xsqlite3_result_error(tls, ctx, errmsg, -1) 1668 | sqlite3.Xsqlite3_result_error_code(tls, ctx, sqlite3.SQLITE_ERROR) 1669 | } 1670 | 1671 | args := make([]driver.Value, argc) 1672 | for i := int32(0); i < argc; i++ { 1673 | valPtr := *(*uintptr)(unsafe.Pointer(argv + uintptr(i)*sqliteValPtrSize)) 1674 | 1675 | switch valType := sqlite3.Xsqlite3_value_type(tls, valPtr); valType { 1676 | case sqlite3.SQLITE_TEXT: 1677 | args[i] = libc.GoString(sqlite3.Xsqlite3_value_text(tls, valPtr)) 1678 | case sqlite3.SQLITE_INTEGER: 1679 | args[i] = sqlite3.Xsqlite3_value_int64(tls, valPtr) 1680 | case sqlite3.SQLITE_FLOAT: 1681 | args[i] = sqlite3.Xsqlite3_value_double(tls, valPtr) 1682 | case sqlite3.SQLITE_NULL: 1683 | args[i] = nil 1684 | case sqlite3.SQLITE_BLOB: 1685 | size := sqlite3.Xsqlite3_value_bytes(tls, valPtr) 1686 | blobPtr := sqlite3.Xsqlite3_value_blob(tls, valPtr) 1687 | v := make([]byte, size) 1688 | copy(v, (*libc.RawMem)(unsafe.Pointer(blobPtr))[:size:size]) 1689 | args[i] = v 1690 | default: 1691 | panic(fmt.Sprintf("unexpected argument type %q passed by sqlite", valType)) 1692 | } 1693 | } 1694 | 1695 | res, err := xFunc(&FunctionContext{}, args) 1696 | if err != nil { 1697 | setErrorResult(err) 1698 | return 1699 | } 1700 | 1701 | switch resTyped := res.(type) { 1702 | case nil: 1703 | sqlite3.Xsqlite3_result_null(tls, ctx) 1704 | case int64: 1705 | sqlite3.Xsqlite3_result_int64(tls, ctx, resTyped) 1706 | case float64: 1707 | sqlite3.Xsqlite3_result_double(tls, ctx, resTyped) 1708 | case bool: 1709 | sqlite3.Xsqlite3_result_int(tls, ctx, libc.Bool32(resTyped)) 1710 | case time.Time: 1711 | sqlite3.Xsqlite3_result_int64(tls, ctx, resTyped.Unix()) 1712 | case string: 1713 | size := int32(len(resTyped)) 1714 | cstr, err := libc.CString(resTyped) 1715 | if err != nil { 1716 | panic(err) 1717 | } 1718 | defer libc.Xfree(tls, cstr) 1719 | sqlite3.Xsqlite3_result_text(tls, ctx, cstr, size, sqlite3.SQLITE_TRANSIENT) 1720 | case []byte: 1721 | size := int32(len(resTyped)) 1722 | if size == 0 { 1723 | sqlite3.Xsqlite3_result_zeroblob(tls, ctx, 0) 1724 | return 1725 | } 1726 | p := libc.Xmalloc(tls, types.Size_t(size)) 1727 | if p == 0 { 1728 | panic(fmt.Sprintf("unable to allocate space for blob: %d", size)) 1729 | } 1730 | defer libc.Xfree(tls, p) 1731 | copy((*libc.RawMem)(unsafe.Pointer(p))[:size:size], resTyped) 1732 | 1733 | sqlite3.Xsqlite3_result_blob(tls, ctx, p, size, sqlite3.SQLITE_TRANSIENT) 1734 | default: 1735 | setErrorResult(fmt.Errorf("function did not return a valid driver.Value: %T", resTyped)) 1736 | return 1737 | } 1738 | }, 1739 | } 1740 | d.udfs[zFuncName] = udf 1741 | 1742 | return nil 1743 | } 1744 | 1745 | func RegisterAsSQLITE3() { 1746 | sql.Register("sqlite3", newDriver()) 1747 | } 1748 | -------------------------------------------------------------------------------- /all_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Sqlite 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 sqlite // import "modernc.org/sqlite" 6 | 7 | import ( 8 | "bytes" 9 | "context" 10 | "database/sql" 11 | "database/sql/driver" 12 | "embed" 13 | "errors" 14 | "flag" 15 | "fmt" 16 | "io" 17 | "math/rand" 18 | "net/url" 19 | "os" 20 | "os/exec" 21 | "path" 22 | "path/filepath" 23 | "reflect" 24 | "regexp" 25 | "runtime" 26 | "runtime/debug" 27 | "runtime/pprof" 28 | "strconv" 29 | "strings" 30 | "sync" 31 | "sync/atomic" 32 | "testing" 33 | "time" 34 | "unsafe" 35 | 36 | "github.com/google/pprof/profile" 37 | "modernc.org/libc" 38 | "modernc.org/mathutil" 39 | sqlite3 "modernc.org/sqlite/lib" 40 | "modernc.org/sqlite/vfs" 41 | ) 42 | 43 | func caller(s string, va ...interface{}) { 44 | if s == "" { 45 | s = strings.Repeat("%v ", len(va)) 46 | } 47 | _, fn, fl, _ := runtime.Caller(2) 48 | fmt.Fprintf(os.Stderr, "# caller: %s:%d: ", path.Base(fn), fl) 49 | fmt.Fprintf(os.Stderr, s, va...) 50 | fmt.Fprintln(os.Stderr) 51 | _, fn, fl, _ = runtime.Caller(1) 52 | fmt.Fprintf(os.Stderr, "# \tcallee: %s:%d: ", path.Base(fn), fl) 53 | fmt.Fprintln(os.Stderr) 54 | os.Stderr.Sync() 55 | } 56 | 57 | func dbg(s string, va ...interface{}) { 58 | if s == "" { 59 | s = strings.Repeat("%v ", len(va)) 60 | } 61 | _, fn, fl, _ := runtime.Caller(1) 62 | fmt.Fprintf(os.Stderr, "# dbg %s:%d: ", path.Base(fn), fl) 63 | fmt.Fprintf(os.Stderr, s, va...) 64 | fmt.Fprintln(os.Stderr) 65 | os.Stderr.Sync() 66 | } 67 | 68 | func stack() string { return string(debug.Stack()) } 69 | 70 | func use(...interface{}) {} 71 | 72 | func init() { 73 | use(caller, dbg, stack, todo, trc) //TODOOK 74 | } 75 | 76 | func origin(skip int) string { 77 | pc, fn, fl, _ := runtime.Caller(skip) 78 | f := runtime.FuncForPC(pc) 79 | var fns string 80 | if f != nil { 81 | fns = f.Name() 82 | if x := strings.LastIndex(fns, "."); x > 0 { 83 | fns = fns[x+1:] 84 | } 85 | } 86 | return fmt.Sprintf("%s:%d:%s", fn, fl, fns) 87 | } 88 | 89 | func todo(s string, args ...interface{}) string { //TODO- 90 | switch { 91 | case s == "": 92 | s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) 93 | default: 94 | s = fmt.Sprintf(s, args...) 95 | } 96 | r := fmt.Sprintf("%s: TODOTODO %s", origin(2), s) //TODOOK 97 | fmt.Fprintf(os.Stdout, "%s\n", r) 98 | os.Stdout.Sync() 99 | return r 100 | } 101 | 102 | func trc(s string, args ...interface{}) string { //TODO- 103 | switch { 104 | case s == "": 105 | s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) 106 | default: 107 | s = fmt.Sprintf(s, args...) 108 | } 109 | r := fmt.Sprintf("\n%s: TRC %s", origin(2), s) 110 | fmt.Fprintf(os.Stdout, "%s\n", r) 111 | os.Stdout.Sync() 112 | return r 113 | } 114 | 115 | // ============================================================================ 116 | 117 | var ( 118 | oRecsPerSec = flag.Bool("recs_per_sec_as_mbps", false, "Show records per second as MB/s.") 119 | oXTags = flag.String("xtags", "", "passed to go build of testfixture in TestTclTest") 120 | tempDir string 121 | ) 122 | 123 | func TestMain(m *testing.M) { 124 | fmt.Printf("test binary compiled for %s/%s\n", runtime.GOOS, runtime.GOARCH) 125 | flag.Parse() 126 | libc.MemAuditStart() 127 | os.Exit(testMain(m)) 128 | } 129 | 130 | func testMain(m *testing.M) int { 131 | var err error 132 | tempDir, err = os.MkdirTemp("", "sqlite-test-") 133 | if err != nil { 134 | panic(err) //TODOOK 135 | } 136 | 137 | defer os.RemoveAll(tempDir) 138 | 139 | return m.Run() 140 | } 141 | 142 | func tempDB(t testing.TB) (string, *sql.DB) { 143 | dir, err := os.MkdirTemp("", "sqlite-test-") 144 | if err != nil { 145 | t.Fatal(err) 146 | } 147 | 148 | db, err := sql.Open(driverName, filepath.Join(dir, "tmp.db")) 149 | if err != nil { 150 | os.RemoveAll(dir) 151 | t.Fatal(err) 152 | } 153 | 154 | return dir, db 155 | } 156 | 157 | // https://gitlab.com/cznic/sqlite/issues/118 158 | func TestIssue118(t *testing.T) { 159 | // Many iterations generate enough objects to ensure pprof 160 | // profile captures the samples that we are seeking below 161 | for i := 0; i < 10000; i++ { 162 | func() { 163 | db, err := sql.Open("sqlite", ":memory:") 164 | if err != nil { 165 | t.Fatal(err) 166 | } 167 | defer db.Close() 168 | if _, err := db.Exec(`CREATE TABLE t1(v TEXT)`); err != nil { 169 | t.Fatal(err) 170 | } 171 | var val []byte 172 | if _, err := db.Exec(`INSERT INTO t1(v) VALUES(?)`, val); err != nil { 173 | t.Fatal(err) 174 | } 175 | var count int 176 | err = db.QueryRow("SELECT MAX(_ROWID_) FROM t1").Scan(&count) 177 | if err != nil || count <= 0 { 178 | t.Fatalf("Query failure: %d, %s", count, err) 179 | } 180 | }() 181 | } 182 | 183 | // Dump & read heap sample 184 | var buf bytes.Buffer 185 | if err := pprof.Lookup("heap").WriteTo(&buf, 0); err != nil { 186 | t.Fatalf("Error dumping heap profile: %s", err) 187 | } 188 | heapProfile, err := profile.Parse(&buf) 189 | if err != nil { 190 | t.Fatalf("Error parsing heap profile: %s", err) 191 | } 192 | 193 | // Profile.SampleType indexes map into Sample.Values below. We are 194 | // looking for "inuse_*" values, and skip the "alloc_*" ones 195 | inUseIndexes := make([]int, 0, 2) 196 | for i, t := range heapProfile.SampleType { 197 | if strings.HasPrefix(t.Type, "inuse_") { 198 | inUseIndexes = append(inUseIndexes, i) 199 | } 200 | } 201 | 202 | // Look for samples from "libc.NewTLS" and insure that they have nothing in-use 203 | for _, sample := range heapProfile.Sample { 204 | isInUse := false 205 | for _, idx := range inUseIndexes { 206 | isInUse = isInUse || sample.Value[idx] > 0 207 | } 208 | if !isInUse { 209 | continue 210 | } 211 | 212 | isNewTLS := false 213 | sampleStack := []string{} 214 | for _, location := range sample.Location { 215 | for _, line := range location.Line { 216 | sampleStack = append(sampleStack, fmt.Sprintf("%s (%s:%d)", line.Function.Name, line.Function.Filename, line.Line)) 217 | isNewTLS = isNewTLS || strings.Contains(line.Function.Name, "libc.NewTLS") 218 | } 219 | } 220 | if isNewTLS { 221 | t.Errorf("Memory leak via libc.NewTLS:\n%s\n", strings.Join(sampleStack, "\n")) 222 | } 223 | } 224 | } 225 | 226 | // https://gitlab.com/cznic/sqlite/issues/100 227 | func TestIssue100(t *testing.T) { 228 | db, err := sql.Open("sqlite", ":memory:") 229 | if err != nil { 230 | t.Fatal(err) 231 | } 232 | defer db.Close() 233 | if _, err := db.Exec(`CREATE TABLE t1(v TEXT)`); err != nil { 234 | t.Fatal(err) 235 | } 236 | var val []byte 237 | if _, err := db.Exec(`INSERT INTO t1(v) VALUES(?)`, val); err != nil { 238 | t.Fatal(err) 239 | } 240 | var res sql.NullString 241 | if err = db.QueryRow(`SELECT v FROM t1 LIMIT 1`).Scan(&res); err != nil { 242 | t.Fatal(err) 243 | } 244 | if res.Valid { 245 | t.Fatalf("got non-NULL result: %+v", res) 246 | } 247 | 248 | if _, err := db.Exec(`CREATE TABLE t2( 249 | v TEXT check(v is NULL OR(json_valid(v) AND json_type(v)='array')) 250 | )`); err != nil { 251 | t.Fatal(err) 252 | } 253 | for _, val := range [...][]byte{nil, []byte(`["a"]`)} { 254 | if _, err := db.Exec(`INSERT INTO t2(v) VALUES(?)`, val); err != nil { 255 | t.Fatalf("inserting value %v (%[1]q): %v", val, err) 256 | } 257 | } 258 | } 259 | 260 | // https://gitlab.com/cznic/sqlite/issues/98 261 | func TestIssue98(t *testing.T) { 262 | dir, db := tempDB(t) 263 | 264 | defer func() { 265 | db.Close() 266 | os.RemoveAll(dir) 267 | }() 268 | 269 | if _, err := db.Exec("create table t(b mediumblob not null)"); err != nil { 270 | t.Fatal(err) 271 | } 272 | if _, err := db.Exec("insert into t values (?)", []byte{}); err != nil { 273 | t.Fatal(err) 274 | } 275 | if _, err := db.Exec("insert into t values (?)", nil); err == nil { 276 | t.Fatal("expected statement to fail") 277 | } 278 | } 279 | 280 | // https://gitlab.com/cznic/sqlite/issues/97 281 | func TestIssue97(t *testing.T) { 282 | name := filepath.Join(t.TempDir(), "tmp.db") 283 | 284 | db, err := sql.Open(driverName, fmt.Sprintf("file:%s", name)) 285 | if err != nil { 286 | t.Fatal(err) 287 | } 288 | defer db.Close() 289 | 290 | if _, err := db.Exec("create table t(b int)"); err != nil { 291 | t.Fatal(err) 292 | } 293 | 294 | rodb, err := sql.Open(driverName, fmt.Sprintf("file:%s?mode=ro", name)) 295 | if err != nil { 296 | t.Fatal(err) 297 | } 298 | defer rodb.Close() 299 | 300 | _, err = rodb.Exec("drop table t") 301 | if err == nil { 302 | t.Fatal("expected drop table statement to fail on a read only database") 303 | } else if err.Error() != "attempt to write a readonly database (8)" { 304 | t.Fatal("expected drop table statement to fail because its a readonly database") 305 | } 306 | } 307 | 308 | func TestScalar(t *testing.T) { 309 | dir, db := tempDB(t) 310 | 311 | defer func() { 312 | db.Close() 313 | os.RemoveAll(dir) 314 | }() 315 | 316 | t1 := time.Date(2017, 4, 20, 1, 2, 3, 56789, time.UTC) 317 | t2 := time.Date(2018, 5, 21, 2, 3, 4, 98765, time.UTC) 318 | r, err := db.Exec(` 319 | create table t(i int, f double, b bool, s text, t time); 320 | insert into t values(12, 3.14, ?, 'foo', ?), (34, 2.78, ?, 'bar', ?); 321 | `, 322 | true, t1, 323 | false, t2, 324 | ) 325 | if err != nil { 326 | t.Fatal(err) 327 | } 328 | 329 | n, err := r.RowsAffected() 330 | if err != nil { 331 | t.Fatal(err) 332 | } 333 | 334 | if g, e := n, int64(2); g != e { 335 | t.Fatal(g, e) 336 | } 337 | 338 | rows, err := db.Query("select * from t") 339 | if err != nil { 340 | t.Fatal(err) 341 | } 342 | 343 | type rec struct { 344 | i int 345 | f float64 346 | b bool 347 | s string 348 | t string 349 | } 350 | var a []rec 351 | for rows.Next() { 352 | var r rec 353 | if err := rows.Scan(&r.i, &r.f, &r.b, &r.s, &r.t); err != nil { 354 | t.Fatal(err) 355 | } 356 | 357 | a = append(a, r) 358 | } 359 | if err := rows.Err(); err != nil { 360 | t.Fatal(err) 361 | } 362 | 363 | if g, e := len(a), 2; g != e { 364 | t.Fatal(g, e) 365 | } 366 | 367 | if g, e := a[0], (rec{12, 3.14, true, "foo", t1.Format(parseTimeFormats[0])}); g != e { 368 | t.Fatal(g, e) 369 | } 370 | 371 | if g, e := a[1], (rec{34, 2.78, false, "bar", t2.Format(parseTimeFormats[0])}); g != e { 372 | t.Fatal(g, e) 373 | } 374 | } 375 | 376 | func TestRedefineUserDefinedFunction(t *testing.T) { 377 | dir, db := tempDB(t) 378 | ctx := context.Background() 379 | 380 | defer func() { 381 | db.Close() 382 | os.RemoveAll(dir) 383 | }() 384 | 385 | connection, err := db.Conn(context.Background()) 386 | if err != nil { 387 | t.Fatal(err) 388 | } 389 | 390 | var r int 391 | funName := "test" 392 | 393 | if err = connection.Raw(func(driverConn interface{}) error { 394 | c := driverConn.(*conn) 395 | 396 | name, err := libc.CString(funName) 397 | if err != nil { 398 | return err 399 | } 400 | 401 | return c.createFunctionInternal(&userDefinedFunction{ 402 | zFuncName: name, 403 | nArg: 0, 404 | eTextRep: sqlite3.SQLITE_UTF8 | sqlite3.SQLITE_DETERMINISTIC, 405 | xFunc: func(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { 406 | sqlite3.Xsqlite3_result_int(tls, ctx, 1) 407 | }, 408 | }) 409 | }); err != nil { 410 | t.Fatal(err) 411 | } 412 | row := connection.QueryRowContext(ctx, "select test()") 413 | 414 | if err := row.Scan(&r); err != nil { 415 | t.Fatal(err) 416 | } 417 | 418 | if g, e := r, 1; g != e { 419 | t.Fatal(g, e) 420 | } 421 | 422 | if err = connection.Raw(func(driverConn interface{}) error { 423 | c := driverConn.(*conn) 424 | 425 | name, err := libc.CString(funName) 426 | if err != nil { 427 | return err 428 | } 429 | 430 | return c.createFunctionInternal(&userDefinedFunction{ 431 | zFuncName: name, 432 | nArg: 0, 433 | eTextRep: sqlite3.SQLITE_UTF8 | sqlite3.SQLITE_DETERMINISTIC, 434 | xFunc: func(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { 435 | sqlite3.Xsqlite3_result_int(tls, ctx, 2) 436 | }, 437 | }) 438 | }); err != nil { 439 | t.Fatal(err) 440 | } 441 | row = connection.QueryRowContext(ctx, "select test()") 442 | 443 | if err := row.Scan(&r); err != nil { 444 | t.Fatal(err) 445 | } 446 | 447 | if g, e := r, 2; g != e { 448 | t.Fatal(g, e) 449 | } 450 | } 451 | 452 | func TestRegexpUserDefinedFunction(t *testing.T) { 453 | dir, db := tempDB(t) 454 | ctx := context.Background() 455 | 456 | defer func() { 457 | db.Close() 458 | os.RemoveAll(dir) 459 | }() 460 | 461 | connection, err := db.Conn(context.Background()) 462 | if err != nil { 463 | t.Fatal(err) 464 | } 465 | 466 | if err = connection.Raw(func(driverConn interface{}) error { 467 | c := driverConn.(*conn) 468 | 469 | name, err := libc.CString("regexp") 470 | if err != nil { 471 | return err 472 | } 473 | 474 | return c.createFunctionInternal(&userDefinedFunction{ 475 | zFuncName: name, 476 | nArg: 2, 477 | eTextRep: sqlite3.SQLITE_UTF8 | sqlite3.SQLITE_DETERMINISTIC, 478 | xFunc: func(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { 479 | const sqliteValPtrSize = unsafe.Sizeof(&sqlite3.Sqlite3_value{}) 480 | 481 | argvv := make([]uintptr, argc) 482 | for i := int32(0); i < argc; i++ { 483 | argvv[i] = *(*uintptr)(unsafe.Pointer(argv + uintptr(i)*sqliteValPtrSize)) 484 | } 485 | 486 | setErrorResult := func(res error) { 487 | errmsg, cerr := libc.CString(res.Error()) 488 | if cerr != nil { 489 | panic(cerr) 490 | } 491 | defer libc.Xfree(tls, errmsg) 492 | sqlite3.Xsqlite3_result_error(tls, ctx, errmsg, -1) 493 | sqlite3.Xsqlite3_result_error_code(tls, ctx, sqlite3.SQLITE_ERROR) 494 | } 495 | 496 | var s1 string 497 | switch sqlite3.Xsqlite3_value_type(tls, argvv[0]) { 498 | case sqlite3.SQLITE_TEXT: 499 | s1 = libc.GoString(sqlite3.Xsqlite3_value_text(tls, argvv[0])) 500 | default: 501 | setErrorResult(errors.New("expected argv[0] to be text")) 502 | return 503 | } 504 | 505 | var s2 string 506 | switch sqlite3.Xsqlite3_value_type(tls, argvv[1]) { 507 | case sqlite3.SQLITE_TEXT: 508 | s2 = libc.GoString(sqlite3.Xsqlite3_value_text(tls, argvv[1])) 509 | default: 510 | setErrorResult(errors.New("expected argv[1] to be text")) 511 | return 512 | } 513 | 514 | matched, err := regexp.MatchString(s1, s2) 515 | if err != nil { 516 | setErrorResult(fmt.Errorf("bad regular expression: %q", err)) 517 | return 518 | } 519 | sqlite3.Xsqlite3_result_int(tls, ctx, libc.Bool32(matched)) 520 | }, 521 | }) 522 | }); err != nil { 523 | t.Fatal(err) 524 | } 525 | 526 | t.Run("regexp filter", func(tt *testing.T) { 527 | t1 := "seafood" 528 | t2 := "fruit" 529 | 530 | connection.ExecContext(ctx, ` 531 | create table t(b text); 532 | insert into t values(?), (?); 533 | `, t1, t2) 534 | 535 | rows, err := connection.QueryContext(ctx, "select * from t where b regexp 'foo.*'") 536 | if err != nil { 537 | tt.Fatal(err) 538 | } 539 | 540 | type rec struct { 541 | b string 542 | } 543 | var a []rec 544 | for rows.Next() { 545 | var r rec 546 | if err := rows.Scan(&r.b); err != nil { 547 | tt.Fatal(err) 548 | } 549 | 550 | a = append(a, r) 551 | } 552 | if err := rows.Err(); err != nil { 553 | tt.Fatal(err) 554 | } 555 | 556 | if g, e := len(a), 1; g != e { 557 | tt.Fatal(g, e) 558 | } 559 | 560 | if g, e := a[0].b, t1; g != e { 561 | tt.Fatal(g, e) 562 | } 563 | }) 564 | 565 | t.Run("regexp matches", func(tt *testing.T) { 566 | row := connection.QueryRowContext(ctx, "select 'seafood' regexp 'foo.*'") 567 | 568 | var r int 569 | if err := row.Scan(&r); err != nil { 570 | tt.Fatal(err) 571 | } 572 | 573 | if g, e := r, 1; g != e { 574 | tt.Fatal(g, e) 575 | } 576 | }) 577 | 578 | t.Run("regexp does not match", func(tt *testing.T) { 579 | row := connection.QueryRowContext(ctx, "select 'fruit' regexp 'foo.*'") 580 | 581 | var r int 582 | if err := row.Scan(&r); err != nil { 583 | tt.Fatal(err) 584 | } 585 | 586 | if g, e := r, 0; g != e { 587 | tt.Fatal(g, e) 588 | } 589 | }) 590 | 591 | t.Run("errors on bad regexp", func(tt *testing.T) { 592 | err := connection.QueryRowContext(ctx, "select 'seafood' regexp 'a(b'").Scan() 593 | if err == nil { 594 | tt.Fatal(errors.New("expected error, got none")) 595 | } 596 | }) 597 | 598 | t.Run("errors on bad first argument", func(tt *testing.T) { 599 | err := connection.QueryRowContext(ctx, "SELECT 1 REGEXP 'a(b'").Scan() 600 | if err == nil { 601 | tt.Fatal(errors.New("expected error, got none")) 602 | } 603 | }) 604 | 605 | t.Run("errors on bad second argument", func(tt *testing.T) { 606 | err := connection.QueryRowContext(ctx, "SELECT 'seafood' REGEXP 1").Scan() 607 | if err == nil { 608 | tt.Fatal(errors.New("expected error, got none")) 609 | } 610 | }) 611 | } 612 | 613 | func TestBlob(t *testing.T) { 614 | dir, db := tempDB(t) 615 | 616 | defer func() { 617 | db.Close() 618 | os.RemoveAll(dir) 619 | }() 620 | 621 | b1 := []byte(time.Now().String()) 622 | b2 := []byte("\x00foo\x00bar\x00") 623 | if _, err := db.Exec(` 624 | create table t(b blob); 625 | insert into t values(?), (?); 626 | `, b1, b2, 627 | ); err != nil { 628 | t.Fatal(err) 629 | } 630 | 631 | rows, err := db.Query("select * from t") 632 | if err != nil { 633 | t.Fatal(err) 634 | } 635 | 636 | type rec struct { 637 | b []byte 638 | } 639 | var a []rec 640 | for rows.Next() { 641 | var r rec 642 | if err := rows.Scan(&r.b); err != nil { 643 | t.Fatal(err) 644 | } 645 | 646 | a = append(a, r) 647 | } 648 | if err := rows.Err(); err != nil { 649 | t.Fatal(err) 650 | } 651 | 652 | if g, e := len(a), 2; g != e { 653 | t.Fatal(g, e) 654 | } 655 | 656 | if g, e := a[0].b, b1; !bytes.Equal(g, e) { 657 | t.Fatal(g, e) 658 | } 659 | 660 | if g, e := a[1].b, b2; !bytes.Equal(g, e) { 661 | t.Fatal(g, e) 662 | } 663 | } 664 | 665 | func benchmarkInsertMemory(b *testing.B, n int) { 666 | db, err := sql.Open(driverName, "file::memory:") 667 | if err != nil { 668 | b.Fatal(err) 669 | } 670 | 671 | defer func() { 672 | db.Close() 673 | }() 674 | 675 | b.ReportAllocs() 676 | b.ResetTimer() 677 | for i := 0; i < b.N; i++ { 678 | b.StopTimer() 679 | if _, err := db.Exec(` 680 | drop table if exists t; 681 | create table t(i int); 682 | begin; 683 | `); err != nil { 684 | b.Fatal(err) 685 | } 686 | 687 | s, err := db.Prepare("insert into t values(?)") 688 | if err != nil { 689 | b.Fatal(err) 690 | } 691 | 692 | b.StartTimer() 693 | for i := 0; i < n; i++ { 694 | if _, err := s.Exec(int64(i)); err != nil { 695 | b.Fatal(err) 696 | } 697 | } 698 | b.StopTimer() 699 | if _, err := db.Exec(`commit;`); err != nil { 700 | b.Fatal(err) 701 | } 702 | } 703 | if *oRecsPerSec { 704 | b.SetBytes(1e6 * int64(n)) 705 | } 706 | } 707 | 708 | func BenchmarkInsertMemory(b *testing.B) { 709 | for i, n := range []int{1e1, 1e2, 1e3, 1e4, 1e5, 1e6} { 710 | b.Run(fmt.Sprintf("1e%d", i+1), func(b *testing.B) { benchmarkInsertMemory(b, n) }) 711 | } 712 | } 713 | 714 | var staticInt int 715 | 716 | func benchmarkNextMemory(b *testing.B, n int) { 717 | db, err := sql.Open(driverName, "file::memory:") 718 | if err != nil { 719 | b.Fatal(err) 720 | } 721 | 722 | defer func() { 723 | db.Close() 724 | }() 725 | 726 | if _, err := db.Exec(` 727 | create table t(i int); 728 | begin; 729 | `); err != nil { 730 | b.Fatal(err) 731 | } 732 | 733 | s, err := db.Prepare("insert into t values(?)") 734 | if err != nil { 735 | b.Fatal(err) 736 | } 737 | 738 | for i := 0; i < n; i++ { 739 | if _, err := s.Exec(int64(i)); err != nil { 740 | b.Fatal(err) 741 | } 742 | } 743 | if _, err := db.Exec(`commit;`); err != nil { 744 | b.Fatal(err) 745 | } 746 | 747 | b.ReportAllocs() 748 | b.ResetTimer() 749 | for i := 0; i < b.N; i++ { 750 | b.StopTimer() 751 | r, err := db.Query("select * from t") 752 | if err != nil { 753 | b.Fatal(err) 754 | } 755 | 756 | b.StartTimer() 757 | for i := 0; i < n; i++ { 758 | if !r.Next() { 759 | b.Fatal(err) 760 | } 761 | if err := r.Scan(&staticInt); err != nil { 762 | b.Fatal(err) 763 | } 764 | } 765 | b.StopTimer() 766 | if err := r.Err(); err != nil { 767 | b.Fatal(err) 768 | } 769 | 770 | r.Close() 771 | } 772 | if *oRecsPerSec { 773 | b.SetBytes(1e6 * int64(n)) 774 | } 775 | } 776 | 777 | func BenchmarkNextMemory(b *testing.B) { 778 | for i, n := range []int{1e1, 1e2, 1e3, 1e4, 1e5, 1e6} { 779 | b.Run(fmt.Sprintf("1e%d", i+1), func(b *testing.B) { benchmarkNextMemory(b, n) }) 780 | } 781 | } 782 | 783 | // https://gitlab.com/cznic/sqlite/issues/11 784 | func TestIssue11(t *testing.T) { 785 | const N = 6570 786 | dir, db := tempDB(t) 787 | 788 | defer func() { 789 | db.Close() 790 | os.RemoveAll(dir) 791 | }() 792 | 793 | if _, err := db.Exec(` 794 | CREATE TABLE t1 (t INT); 795 | BEGIN; 796 | `, 797 | ); err != nil { 798 | t.Fatal(err) 799 | } 800 | 801 | for i := 0; i < N; i++ { 802 | if _, err := db.Exec("INSERT INTO t1 (t) VALUES (?)", i); err != nil { 803 | t.Fatalf("#%v: %v", i, err) 804 | } 805 | } 806 | if _, err := db.Exec("COMMIT;"); err != nil { 807 | t.Fatal(err) 808 | } 809 | } 810 | 811 | // https://gitlab.com/cznic/sqlite/issues/12 812 | func TestMemDB(t *testing.T) { 813 | // Verify we can create out-of-the heap memory DB instance. 814 | db, err := sql.Open(driverName, "file::memory:") 815 | if err != nil { 816 | t.Fatal(err) 817 | } 818 | 819 | defer func() { 820 | db.Close() 821 | }() 822 | 823 | v := strings.Repeat("a", 1024) 824 | if _, err := db.Exec(` 825 | create table t(s string); 826 | begin; 827 | `); err != nil { 828 | t.Fatal(err) 829 | } 830 | 831 | s, err := db.Prepare("insert into t values(?)") 832 | if err != nil { 833 | t.Fatal(err) 834 | } 835 | 836 | // Heap used to be fixed at 32MB. 837 | for i := 0; i < (64<<20)/len(v); i++ { 838 | if _, err := s.Exec(v); err != nil { 839 | t.Fatalf("%v * %v= %v: %v", i, len(v), i*len(v), err) 840 | } 841 | } 842 | if _, err := db.Exec(`commit;`); err != nil { 843 | t.Fatal(err) 844 | } 845 | } 846 | 847 | func TestConcurrentGoroutines(t *testing.T) { 848 | const ( 849 | ngoroutines = 8 850 | nrows = 5000 851 | ) 852 | 853 | dir, err := os.MkdirTemp("", "sqlite-test-") 854 | if err != nil { 855 | t.Fatal(err) 856 | } 857 | 858 | defer func() { 859 | os.RemoveAll(dir) 860 | }() 861 | 862 | db, err := sql.Open(driverName, filepath.Join(dir, "test.db")) 863 | if err != nil { 864 | t.Fatal(err) 865 | } 866 | 867 | defer db.Close() 868 | 869 | tx, err := db.BeginTx(context.Background(), nil) 870 | if err != nil { 871 | t.Fatal(err) 872 | } 873 | 874 | if _, err := tx.Exec("create table t(i)"); err != nil { 875 | t.Fatal(err) 876 | } 877 | 878 | prep, err := tx.Prepare("insert into t values(?)") 879 | if err != nil { 880 | t.Fatal(err) 881 | } 882 | 883 | rnd := make(chan int, 100) 884 | go func() { 885 | lim := ngoroutines * nrows 886 | rng, err := mathutil.NewFC32(0, lim-1, false) 887 | if err != nil { 888 | panic(fmt.Errorf("internal error: %v", err)) 889 | } 890 | 891 | for i := 0; i < lim; i++ { 892 | rnd <- rng.Next() 893 | } 894 | }() 895 | 896 | start := make(chan int) 897 | var wg sync.WaitGroup 898 | for i := 0; i < ngoroutines; i++ { 899 | wg.Add(1) 900 | 901 | go func(id int) { 902 | 903 | defer wg.Done() 904 | 905 | next: 906 | for i := 0; i < nrows; i++ { 907 | n := <-rnd 908 | var err error 909 | for j := 0; j < 10; j++ { 910 | if _, err := prep.Exec(n); err == nil { 911 | continue next 912 | } 913 | } 914 | 915 | t.Errorf("id %d, seq %d: %v", id, i, err) 916 | return 917 | } 918 | }(i) 919 | } 920 | t0 := time.Now() 921 | close(start) 922 | wg.Wait() 923 | if err := tx.Commit(); err != nil { 924 | t.Fatal(err) 925 | } 926 | 927 | d := time.Since(t0) 928 | rows, err := db.Query("select * from t order by i") 929 | if err != nil { 930 | t.Fatal(err) 931 | } 932 | 933 | var i int 934 | for ; rows.Next(); i++ { 935 | var j int 936 | if err := rows.Scan(&j); err != nil { 937 | t.Fatalf("seq %d: %v", i, err) 938 | } 939 | 940 | if g, e := j, i; g != e { 941 | t.Fatalf("seq %d: got %d, exp %d", i, g, e) 942 | } 943 | } 944 | if err := rows.Err(); err != nil { 945 | t.Fatal(err) 946 | } 947 | 948 | if g, e := i, ngoroutines*nrows; g != e { 949 | t.Fatalf("got %d rows, expected %d", g, e) 950 | } 951 | 952 | t.Logf("%d goroutines concurrently inserted %d rows in %v", ngoroutines, ngoroutines*nrows, d) 953 | } 954 | 955 | func TestConcurrentProcesses(t *testing.T) { 956 | if testing.Short() { 957 | t.Skip("skipping test in short mode") 958 | } 959 | 960 | dir, err := os.MkdirTemp("", "sqlite-test-") 961 | if err != nil { 962 | t.Fatal(err) 963 | } 964 | 965 | defer func() { 966 | os.RemoveAll(dir) 967 | }() 968 | 969 | m, err := filepath.Glob(filepath.FromSlash("internal/mptest/*")) 970 | if err != nil { 971 | t.Fatal(err) 972 | } 973 | 974 | for _, v := range m { 975 | if s := filepath.Ext(v); s != ".test" && s != ".subtest" { 976 | continue 977 | } 978 | 979 | b, err := os.ReadFile(v) 980 | if err != nil { 981 | t.Fatal(err) 982 | } 983 | 984 | if runtime.GOOS == "windows" { 985 | // reference tests are in *nix format -- 986 | // but git on windows does line-ending xlation by default 987 | // if someone has it 'off' this has no impact. 988 | // '\r\n' --> '\n' 989 | b = bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n")) 990 | } 991 | 992 | if err := os.WriteFile(filepath.Join(dir, filepath.Base(v)), b, 0666); err != nil { 993 | t.Fatal(err) 994 | } 995 | } 996 | 997 | bin := "./mptest" 998 | if runtime.GOOS == "windows" { 999 | bin += "mptest.exe" 1000 | } 1001 | args := []string{"build", "-o", filepath.Join(dir, bin)} 1002 | if s := *oXTags; s != "" { 1003 | args = append(args, "-tags", s) 1004 | } 1005 | args = append(args, "modernc.org/sqlite/internal/mptest") 1006 | out, err := exec.Command("go", args...).CombinedOutput() 1007 | if err != nil { 1008 | t.Fatalf("%s\n%v", out, err) 1009 | } 1010 | 1011 | wd, err := os.Getwd() 1012 | if err != nil { 1013 | t.Fatal(err) 1014 | } 1015 | 1016 | defer os.Chdir(wd) 1017 | 1018 | if err := os.Chdir(dir); err != nil { 1019 | t.Fatal(err) 1020 | } 1021 | 1022 | outer: 1023 | for _, script := range m { 1024 | script = filepath.Base(script) 1025 | if filepath.Ext(script) != ".test" { 1026 | continue 1027 | } 1028 | 1029 | fmt.Printf("exec: %s db %s\n", filepath.FromSlash(bin), script) 1030 | out, err := exec.Command(filepath.FromSlash(bin), "db", "--timeout", "6000000", script).CombinedOutput() 1031 | if err != nil { 1032 | t.Fatalf("%s\n%v", out, err) 1033 | } 1034 | 1035 | // just remove it so we don't get a 1036 | // file busy race-condition 1037 | // when we spin up the next script 1038 | if runtime.GOOS == "windows" { 1039 | _ = os.Remove("db") 1040 | } 1041 | 1042 | a := strings.Split(string(out), "\n") 1043 | for _, v := range a { 1044 | if strings.HasPrefix(v, "Summary:") { 1045 | b := strings.Fields(v) 1046 | if len(b) < 2 { 1047 | t.Fatalf("unexpected format of %q", v) 1048 | } 1049 | 1050 | n, err := strconv.Atoi(b[1]) 1051 | if err != nil { 1052 | t.Fatalf("unexpected format of %q", v) 1053 | } 1054 | 1055 | if n != 0 { 1056 | t.Errorf("%s", out) 1057 | } 1058 | 1059 | t.Logf("%v: %v", script, v) 1060 | continue outer 1061 | } 1062 | 1063 | } 1064 | t.Fatalf("%s\nerror: summary line not found", out) 1065 | } 1066 | } 1067 | 1068 | // https://gitlab.com/cznic/sqlite/issues/19 1069 | func TestIssue19(t *testing.T) { 1070 | const ( 1071 | drop = ` 1072 | drop table if exists products; 1073 | ` 1074 | 1075 | up = ` 1076 | CREATE TABLE IF NOT EXISTS "products" ( 1077 | "id" VARCHAR(255), 1078 | "user_id" VARCHAR(255), 1079 | "name" VARCHAR(255), 1080 | "description" VARCHAR(255), 1081 | "created_at" BIGINT, 1082 | "credits_price" BIGINT, 1083 | "enabled" BOOLEAN, 1084 | PRIMARY KEY("id") 1085 | ); 1086 | ` 1087 | 1088 | productInsert = ` 1089 | INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('9be4398c-d527-4efb-93a4-fc532cbaf804', '16935690-348b-41a6-bb20-f8bb16011015', 'dqdwqdwqdwqwqdwqd', 'qwdwqwqdwqdwqdwqd', '1577448686', '1', '0'); 1090 | INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('759f10bd-9e1d-4ec7-b764-0868758d7b85', '16935690-348b-41a6-bb20-f8bb16011015', 'qdqwqwdwqdwqdwqwqd', 'wqdwqdwqdwqdwqdwq', '1577448692', '1', '1'); 1091 | INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('512956e7-224d-4b2a-9153-b83a52c4aa38', '16935690-348b-41a6-bb20-f8bb16011015', 'qwdwqwdqwdqdwqwqd', 'wqdwdqwqdwqdwqdwqdwqdqw', '1577448699', '2', '1'); 1092 | INSERT INTO "products" ("id", "user_id", "name", "description", "created_at", "credits_price", "enabled") VALUES ('02cd138f-6fa6-4909-9db7-a9d0eca4a7b7', '16935690-348b-41a6-bb20-f8bb16011015', 'qdwqdwqdwqwqdwdq', 'wqddwqwqdwqdwdqwdqwq', '1577448706', '3', '1'); 1093 | ` 1094 | ) 1095 | 1096 | dir, err := os.MkdirTemp("", "sqlite-test-") 1097 | if err != nil { 1098 | t.Fatal(err) 1099 | } 1100 | 1101 | defer func() { 1102 | os.RemoveAll(dir) 1103 | }() 1104 | 1105 | wd, err := os.Getwd() 1106 | if err != nil { 1107 | t.Fatal(err) 1108 | } 1109 | 1110 | defer os.Chdir(wd) 1111 | 1112 | if err := os.Chdir(dir); err != nil { 1113 | t.Fatal(err) 1114 | } 1115 | 1116 | db, err := sql.Open("sqlite", "test.db") 1117 | if err != nil { 1118 | t.Fatal("failed to connect database") 1119 | } 1120 | 1121 | defer db.Close() 1122 | 1123 | db.SetMaxOpenConns(1) 1124 | 1125 | if _, err = db.Exec(drop); err != nil { 1126 | t.Fatal(err) 1127 | } 1128 | 1129 | if _, err = db.Exec(up); err != nil { 1130 | t.Fatal(err) 1131 | } 1132 | 1133 | if _, err = db.Exec(productInsert); err != nil { 1134 | t.Fatal(err) 1135 | } 1136 | 1137 | var count int64 1138 | if err = db.QueryRow("select count(*) from products where user_id = ?", "16935690-348b-41a6-bb20-f8bb16011015").Scan(&count); err != nil { 1139 | t.Fatal(err) 1140 | } 1141 | 1142 | if count != 4 { 1143 | t.Fatalf("expected result for the count query %d, we received %d\n", 4, count) 1144 | } 1145 | 1146 | rows, err := db.Query("select * from products where user_id = ?", "16935690-348b-41a6-bb20-f8bb16011015") 1147 | if err != nil { 1148 | t.Fatal(err) 1149 | } 1150 | 1151 | count = 0 1152 | for rows.Next() { 1153 | count++ 1154 | } 1155 | if err := rows.Err(); err != nil { 1156 | t.Fatal(err) 1157 | } 1158 | 1159 | if count != 4 { 1160 | t.Fatalf("expected result for the select query %d, we received %d\n", 4, count) 1161 | } 1162 | 1163 | rows, err = db.Query("select * from products where enabled = ?", true) 1164 | if err != nil { 1165 | t.Fatal(err) 1166 | } 1167 | 1168 | count = 0 1169 | for rows.Next() { 1170 | count++ 1171 | } 1172 | if err := rows.Err(); err != nil { 1173 | t.Fatal(err) 1174 | } 1175 | 1176 | if count != 3 { 1177 | t.Fatalf("expected result for the enabled select query %d, we received %d\n", 3, count) 1178 | } 1179 | } 1180 | 1181 | func mustExec(t *testing.T, db *sql.DB, sql string, args ...interface{}) sql.Result { 1182 | res, err := db.Exec(sql, args...) 1183 | if err != nil { 1184 | t.Fatalf("Error running %q: %v", sql, err) 1185 | } 1186 | 1187 | return res 1188 | } 1189 | 1190 | // https://gitlab.com/cznic/sqlite/issues/20 1191 | func TestIssue20(t *testing.T) { 1192 | const TablePrefix = "gosqltest_" 1193 | 1194 | tempDir, err := os.MkdirTemp("", "") 1195 | if err != nil { 1196 | t.Fatal(err) 1197 | } 1198 | 1199 | defer func() { 1200 | os.RemoveAll(tempDir) 1201 | }() 1202 | 1203 | // go1.20rc1, linux/ppc64le VM 1204 | // 10000 FAIL 1205 | // 20000 FAIL 1206 | // 40000 PASS 1207 | // 30000 PASS 1208 | // 25000 PASS 1209 | db, err := sql.Open("sqlite", filepath.Join(tempDir, "foo.db")+"?_pragma=busy_timeout%3d50000") 1210 | if err != nil { 1211 | t.Fatalf("foo.db open fail: %v", err) 1212 | } 1213 | 1214 | defer db.Close() 1215 | 1216 | mustExec(t, db, "CREATE TABLE "+TablePrefix+"t (count INT)") 1217 | sel, err := db.PrepareContext(context.Background(), "SELECT count FROM "+TablePrefix+"t ORDER BY count DESC") 1218 | if err != nil { 1219 | t.Fatalf("prepare 1: %v", err) 1220 | } 1221 | 1222 | ins, err := db.PrepareContext(context.Background(), "INSERT INTO "+TablePrefix+"t (count) VALUES (?)") 1223 | if err != nil { 1224 | t.Fatalf("prepare 2: %v", err) 1225 | } 1226 | 1227 | for n := 1; n <= 3; n++ { 1228 | if _, err := ins.Exec(n); err != nil { 1229 | t.Fatalf("insert(%d) = %v", n, err) 1230 | } 1231 | } 1232 | 1233 | const nRuns = 10 1234 | ch := make(chan bool) 1235 | for i := 0; i < nRuns; i++ { 1236 | go func() { 1237 | defer func() { 1238 | ch <- true 1239 | }() 1240 | for j := 0; j < 10; j++ { 1241 | count := 0 1242 | if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows { 1243 | t.Errorf("Query: %v", err) 1244 | return 1245 | } 1246 | 1247 | if _, err := ins.Exec(rand.Intn(100)); err != nil { 1248 | t.Errorf("Insert: %v", err) 1249 | return 1250 | } 1251 | } 1252 | }() 1253 | } 1254 | for i := 0; i < nRuns; i++ { 1255 | <-ch 1256 | } 1257 | } 1258 | 1259 | func TestNoRows(t *testing.T) { 1260 | tempDir, err := os.MkdirTemp("", "") 1261 | if err != nil { 1262 | t.Fatal(err) 1263 | } 1264 | 1265 | defer func() { 1266 | os.RemoveAll(tempDir) 1267 | }() 1268 | 1269 | db, err := sql.Open("sqlite", filepath.Join(tempDir, "foo.db")) 1270 | if err != nil { 1271 | t.Fatalf("foo.db open fail: %v", err) 1272 | } 1273 | 1274 | defer func() { 1275 | db.Close() 1276 | }() 1277 | 1278 | stmt, err := db.Prepare("create table t(i);") 1279 | if err != nil { 1280 | t.Fatal(err) 1281 | } 1282 | 1283 | defer stmt.Close() 1284 | 1285 | if _, err := stmt.Query(); err != nil { 1286 | t.Fatal(err) 1287 | } 1288 | } 1289 | 1290 | func TestColumns(t *testing.T) { 1291 | db, err := sql.Open("sqlite", "file::memory:") 1292 | if err != nil { 1293 | t.Fatal(err) 1294 | } 1295 | defer db.Close() 1296 | 1297 | if _, err := db.Exec("create table t1(a integer, b text, c blob)"); err != nil { 1298 | t.Fatal(err) 1299 | } 1300 | 1301 | if _, err := db.Exec("insert into t1 (a) values (1)"); err != nil { 1302 | t.Fatal(err) 1303 | } 1304 | 1305 | rows, err := db.Query("select * from t1") 1306 | if err != nil { 1307 | t.Fatal(err) 1308 | } 1309 | defer rows.Close() 1310 | 1311 | got, err := rows.Columns() 1312 | if err != nil { 1313 | t.Fatal(err) 1314 | } 1315 | 1316 | want := []string{"a", "b", "c"} 1317 | if !reflect.DeepEqual(got, want) { 1318 | t.Errorf("got columns %v, want %v", got, want) 1319 | } 1320 | } 1321 | 1322 | // https://gitlab.com/cznic/sqlite/-/issues/32 1323 | func TestColumnsNoRows(t *testing.T) { 1324 | db, err := sql.Open("sqlite", "file::memory:") 1325 | if err != nil { 1326 | t.Fatal(err) 1327 | } 1328 | defer db.Close() 1329 | 1330 | if _, err := db.Exec("create table t1(a integer, b text, c blob)"); err != nil { 1331 | t.Fatal(err) 1332 | } 1333 | 1334 | rows, err := db.Query("select * from t1") 1335 | if err != nil { 1336 | t.Fatal(err) 1337 | } 1338 | defer rows.Close() 1339 | 1340 | got, err := rows.Columns() 1341 | if err != nil { 1342 | t.Fatal(err) 1343 | } 1344 | 1345 | want := []string{"a", "b", "c"} 1346 | if !reflect.DeepEqual(got, want) { 1347 | t.Errorf("got columns %v, want %v", got, want) 1348 | } 1349 | } 1350 | 1351 | // https://gitlab.com/cznic/sqlite/-/issues/28 1352 | func TestIssue28(t *testing.T) { 1353 | tempDir, err := os.MkdirTemp("", "") 1354 | if err != nil { 1355 | t.Fatal(err) 1356 | } 1357 | 1358 | defer func() { 1359 | os.RemoveAll(tempDir) 1360 | }() 1361 | 1362 | db, err := sql.Open("sqlite", filepath.Join(tempDir, "test.db")) 1363 | if err != nil { 1364 | t.Fatalf("test.db open fail: %v", err) 1365 | } 1366 | 1367 | defer db.Close() 1368 | 1369 | if _, err := db.Exec(`CREATE TABLE test (foo TEXT)`); err != nil { 1370 | t.Fatal(err) 1371 | } 1372 | 1373 | row := db.QueryRow(`SELECT foo FROM test`) 1374 | var foo string 1375 | if err = row.Scan(&foo); err != sql.ErrNoRows { 1376 | t.Fatalf("got %T(%[1]v), expected %T(%[2]v)", err, sql.ErrNoRows) 1377 | } 1378 | } 1379 | 1380 | // https://gitlab.com/cznic/sqlite/-/issues/30 1381 | func TestColumnTypes(t *testing.T) { 1382 | tempDir, err := os.MkdirTemp("", "") 1383 | if err != nil { 1384 | t.Fatal(err) 1385 | } 1386 | 1387 | defer func() { 1388 | os.RemoveAll(tempDir) 1389 | }() 1390 | 1391 | db, err := sql.Open("sqlite", filepath.Join(tempDir, "test.db")) 1392 | if err != nil { 1393 | t.Fatalf("test.db open fail: %v", err) 1394 | } 1395 | 1396 | defer db.Close() 1397 | 1398 | _, err = db.Exec("CREATE TABLE IF NOT EXISTS `userinfo` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT,`username` VARCHAR(64) NULL, `departname` VARCHAR(64) NULL, `created` DATE NULL);") 1399 | if err != nil { 1400 | t.Fatal(err) 1401 | } 1402 | 1403 | insertStatement := `INSERT INTO userinfo(username, departname, created) values("astaxie", "研发部门", "2012-12-09")` 1404 | _, err = db.Exec(insertStatement) 1405 | if err != nil { 1406 | t.Fatal(err) 1407 | } 1408 | 1409 | rows2, err := db.Query("SELECT * FROM userinfo") 1410 | if err != nil { 1411 | t.Fatal(err) 1412 | } 1413 | rows2.Next() // trigger statement execution 1414 | defer rows2.Close() 1415 | 1416 | columnTypes, err := rows2.ColumnTypes() 1417 | if err != nil { 1418 | t.Fatal(err) 1419 | } 1420 | 1421 | var b strings.Builder 1422 | for index, value := range columnTypes { 1423 | precision, scale, precisionOk := value.DecimalSize() 1424 | length, lengthOk := value.Length() 1425 | nullable, nullableOk := value.Nullable() 1426 | fmt.Fprintf(&b, "Col %d: DatabaseTypeName %q, DecimalSize %v %v %v, Length %v %v, Name %q, Nullable %v %v, ScanType %q\n", 1427 | index, 1428 | value.DatabaseTypeName(), 1429 | precision, scale, precisionOk, 1430 | length, lengthOk, 1431 | value.Name(), 1432 | nullable, nullableOk, 1433 | value.ScanType(), 1434 | ) 1435 | } 1436 | if err := rows2.Err(); err != nil { 1437 | t.Fatal(err) 1438 | } 1439 | 1440 | if g, e := b.String(), `Col 0: DatabaseTypeName "INTEGER", DecimalSize 0 0 false, Length 0 false, Name "uid", Nullable true true, ScanType "int64" 1441 | Col 1: DatabaseTypeName "VARCHAR(64)", DecimalSize 0 0 false, Length 9223372036854775807 true, Name "username", Nullable true true, ScanType "string" 1442 | Col 2: DatabaseTypeName "VARCHAR(64)", DecimalSize 0 0 false, Length 9223372036854775807 true, Name "departname", Nullable true true, ScanType "string" 1443 | Col 3: DatabaseTypeName "DATE", DecimalSize 0 0 false, Length 9223372036854775807 true, Name "created", Nullable true true, ScanType "time.Time" 1444 | `; g != e { 1445 | t.Fatalf("---- got\n%s\n----expected\n%s", g, e) 1446 | } 1447 | t.Log(b.String()) 1448 | } 1449 | 1450 | // https://gitlab.com/cznic/sqlite/-/issues/32 1451 | func TestColumnTypesNoRows(t *testing.T) { 1452 | tempDir, err := os.MkdirTemp("", "") 1453 | if err != nil { 1454 | t.Fatal(err) 1455 | } 1456 | 1457 | defer func() { 1458 | os.RemoveAll(tempDir) 1459 | }() 1460 | 1461 | db, err := sql.Open("sqlite", filepath.Join(tempDir, "test.db")) 1462 | if err != nil { 1463 | t.Fatalf("test.db open fail: %v", err) 1464 | } 1465 | 1466 | defer db.Close() 1467 | 1468 | _, err = db.Exec("CREATE TABLE IF NOT EXISTS `userinfo` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT,`username` VARCHAR(64) NULL, `departname` VARCHAR(64) NULL, `created` DATE NULL);") 1469 | if err != nil { 1470 | t.Fatal(err) 1471 | } 1472 | 1473 | rows2, err := db.Query("SELECT * FROM userinfo") 1474 | if err != nil { 1475 | t.Fatal(err) 1476 | } 1477 | defer rows2.Close() 1478 | 1479 | columnTypes, err := rows2.ColumnTypes() 1480 | if err != nil { 1481 | t.Fatal(err) 1482 | } 1483 | 1484 | var b strings.Builder 1485 | for index, value := range columnTypes { 1486 | precision, scale, precisionOk := value.DecimalSize() 1487 | length, lengthOk := value.Length() 1488 | nullable, nullableOk := value.Nullable() 1489 | fmt.Fprintf(&b, "Col %d: DatabaseTypeName %q, DecimalSize %v %v %v, Length %v %v, Name %q, Nullable %v %v, ScanType %q\n", 1490 | index, 1491 | value.DatabaseTypeName(), 1492 | precision, scale, precisionOk, 1493 | length, lengthOk, 1494 | value.Name(), 1495 | nullable, nullableOk, 1496 | value.ScanType(), 1497 | ) 1498 | } 1499 | if err := rows2.Err(); err != nil { 1500 | t.Fatal(err) 1501 | } 1502 | 1503 | if g, e := b.String(), `Col 0: DatabaseTypeName "INTEGER", DecimalSize 0 0 false, Length 0 false, Name "uid", Nullable true true, ScanType %!q() 1504 | Col 1: DatabaseTypeName "VARCHAR(64)", DecimalSize 0 0 false, Length 0 false, Name "username", Nullable true true, ScanType %!q() 1505 | Col 2: DatabaseTypeName "VARCHAR(64)", DecimalSize 0 0 false, Length 0 false, Name "departname", Nullable true true, ScanType %!q() 1506 | Col 3: DatabaseTypeName "DATE", DecimalSize 0 0 false, Length 0 false, Name "created", Nullable true true, ScanType %!q() 1507 | `; g != e { 1508 | t.Fatalf("---- got\n%s\n----expected\n%s", g, e) 1509 | } 1510 | t.Log(b.String()) 1511 | } 1512 | 1513 | // https://gitlab.com/cznic/sqlite/-/issues/35 1514 | func TestTime(t *testing.T) { 1515 | types := []string{ 1516 | "DATE", 1517 | "DATETIME", 1518 | "Date", 1519 | "DateTime", 1520 | "TIMESTAMP", 1521 | "TimeStamp", 1522 | "date", 1523 | "datetime", 1524 | "timestamp", 1525 | } 1526 | db, err := sql.Open(driverName, "file::memory:") 1527 | if err != nil { 1528 | t.Fatal(err) 1529 | } 1530 | 1531 | defer func() { 1532 | db.Close() 1533 | }() 1534 | 1535 | for _, typ := range types { 1536 | if _, err := db.Exec(fmt.Sprintf(` 1537 | drop table if exists mg; 1538 | create table mg (applied_at %s); 1539 | `, typ)); err != nil { 1540 | t.Fatal(err) 1541 | } 1542 | 1543 | now := time.Now() 1544 | _, err = db.Exec(`INSERT INTO mg (applied_at) VALUES (?)`, &now) 1545 | if err != nil { 1546 | t.Fatal(err) 1547 | } 1548 | 1549 | var appliedAt time.Time 1550 | err = db.QueryRow("SELECT applied_at FROM mg").Scan(&appliedAt) 1551 | if err != nil { 1552 | t.Fatal(err) 1553 | } 1554 | 1555 | if g, e := appliedAt, now; !g.Equal(e) { 1556 | t.Fatal(g, e) 1557 | } 1558 | } 1559 | } 1560 | 1561 | // https://gitlab.com/cznic/sqlite/-/issues/46 1562 | func TestTimeScan(t *testing.T) { 1563 | ref := time.Date(2021, 1, 2, 16, 39, 17, 123456789, time.UTC) 1564 | 1565 | cases := []struct { 1566 | s string 1567 | w time.Time 1568 | }{ 1569 | {s: "2021-01-02 12:39:17 -0400 ADT m=+00000", w: ref.Truncate(time.Second)}, 1570 | {s: "2021-01-02 16:39:17 +0000 UTC m=+0.000000001", w: ref.Truncate(time.Second)}, 1571 | {s: "2021-01-02 12:39:17.123456 -0400 ADT m=+00000", w: ref.Truncate(time.Microsecond)}, 1572 | {s: "2021-01-02 16:39:17.123456 +0000 UTC m=+0.000000001", w: ref.Truncate(time.Microsecond)}, 1573 | {s: "2021-01-02 16:39:17Z", w: ref.Truncate(time.Second)}, 1574 | {s: "2021-01-02 16:39:17+00:00", w: ref.Truncate(time.Second)}, 1575 | {s: "2021-01-02T16:39:17.123456+00:00", w: ref.Truncate(time.Microsecond)}, 1576 | {s: "2021-01-02 16:39:17.123456+00:00", w: ref.Truncate(time.Microsecond)}, 1577 | {s: "2021-01-02 16:39:17.123456Z", w: ref.Truncate(time.Microsecond)}, 1578 | {s: "2021-01-02 12:39:17-04:00", w: ref.Truncate(time.Second)}, 1579 | {s: "2021-01-02 16:39:17", w: ref.Truncate(time.Second)}, 1580 | {s: "2021-01-02T16:39:17", w: ref.Truncate(time.Second)}, 1581 | {s: "2021-01-02 16:39", w: ref.Truncate(time.Minute)}, 1582 | {s: "2021-01-02T16:39", w: ref.Truncate(time.Minute)}, 1583 | {s: "2021-01-02", w: ref.Truncate(24 * time.Hour)}, 1584 | } 1585 | 1586 | db, err := sql.Open(driverName, "file::memory:") 1587 | if err != nil { 1588 | t.Fatal(err) 1589 | } 1590 | defer db.Close() 1591 | 1592 | for _, colType := range []string{"DATE", "DATETIME", "TIMESTAMP"} { 1593 | for _, tc := range cases { 1594 | if _, err := db.Exec("drop table if exists x; create table x (y " + colType + ")"); err != nil { 1595 | t.Fatal(err) 1596 | } 1597 | if _, err := db.Exec("insert into x (y) values (?)", tc.s); err != nil { 1598 | t.Fatal(err) 1599 | } 1600 | 1601 | var got time.Time 1602 | if err := db.QueryRow("select y from x").Scan(&got); err != nil { 1603 | t.Fatal(err) 1604 | } 1605 | if !got.Equal(tc.w) { 1606 | t.Errorf("scan(%q as %q) = %s, want %s", tc.s, colType, got, tc.w) 1607 | } 1608 | } 1609 | } 1610 | } 1611 | 1612 | // https://gitlab.com/cznic/sqlite/-/issues/49 1613 | func TestTimeLocaltime(t *testing.T) { 1614 | db, err := sql.Open(driverName, "file::memory:") 1615 | if err != nil { 1616 | t.Fatal(err) 1617 | } 1618 | defer db.Close() 1619 | 1620 | if _, err := db.Exec("select datetime('now', 'localtime')"); err != nil { 1621 | t.Fatal(err) 1622 | } 1623 | } 1624 | 1625 | func TestTimeFormat(t *testing.T) { 1626 | ref := time.Date(2021, 1, 2, 16, 39, 17, 123456789, time.UTC) 1627 | 1628 | cases := []struct { 1629 | f string 1630 | w string 1631 | }{ 1632 | {f: "", w: "2021-01-02 16:39:17.123456789+00:00"}, 1633 | {f: "sqlite", w: "2021-01-02 16:39:17.123456789+00:00"}, 1634 | } 1635 | for _, c := range cases { 1636 | t.Run("", func(t *testing.T) { 1637 | dsn := "file::memory:" 1638 | if c.f != "" { 1639 | q := make(url.Values) 1640 | q.Set("_time_format", c.f) 1641 | dsn += "?" + q.Encode() 1642 | } 1643 | db, err := sql.Open(driverName, dsn) 1644 | if err != nil { 1645 | t.Fatal(err) 1646 | } 1647 | defer db.Close() 1648 | 1649 | if _, err := db.Exec("drop table if exists x; create table x (y text)"); err != nil { 1650 | t.Fatal(err) 1651 | } 1652 | 1653 | if _, err := db.Exec(`insert into x values (?)`, ref); err != nil { 1654 | t.Fatal(err) 1655 | } 1656 | 1657 | var got string 1658 | if err := db.QueryRow(`select y from x`).Scan(&got); err != nil { 1659 | t.Fatal(err) 1660 | } 1661 | 1662 | if got != c.w { 1663 | t.Fatal(got, c.w) 1664 | } 1665 | }) 1666 | } 1667 | } 1668 | 1669 | func TestTimeFormatBad(t *testing.T) { 1670 | db, err := sql.Open(driverName, "file::memory:?_time_format=bogus") 1671 | if err != nil { 1672 | t.Fatal(err) 1673 | } 1674 | defer db.Close() 1675 | 1676 | // Error doesn't appear until a connection is opened. 1677 | _, err = db.Exec("select 1") 1678 | if err == nil { 1679 | t.Fatal("wanted error") 1680 | } 1681 | 1682 | want := `unknown _time_format "bogus"` 1683 | if got := err.Error(); got != want { 1684 | t.Fatalf("got error %q, want %q", got, want) 1685 | } 1686 | } 1687 | 1688 | // https://sqlite.org/lang_expr.html#varparam 1689 | // https://gitlab.com/cznic/sqlite/-/issues/42 1690 | func TestBinding(t *testing.T) { 1691 | t.Run("DB", func(t *testing.T) { 1692 | testBinding(t, func(db *sql.DB, query string, args ...interface{}) (*sql.Row, func()) { 1693 | return db.QueryRow(query, args...), func() {} 1694 | }) 1695 | }) 1696 | 1697 | t.Run("Prepare", func(t *testing.T) { 1698 | testBinding(t, func(db *sql.DB, query string, args ...interface{}) (*sql.Row, func()) { 1699 | stmt, err := db.Prepare(query) 1700 | if err != nil { 1701 | t.Fatal(err) 1702 | } 1703 | return stmt.QueryRow(args...), func() { stmt.Close() } 1704 | }) 1705 | }) 1706 | } 1707 | 1708 | func testBinding(t *testing.T, query func(db *sql.DB, query string, args ...interface{}) (*sql.Row, func())) { 1709 | db, err := sql.Open(driverName, "file::memory:") 1710 | if err != nil { 1711 | t.Fatal(err) 1712 | } 1713 | defer db.Close() 1714 | 1715 | for _, tc := range []struct { 1716 | q string 1717 | in []interface{} 1718 | w []int 1719 | }{ 1720 | { 1721 | q: "?, ?, ?", 1722 | in: []interface{}{1, 2, 3}, 1723 | w: []int{1, 2, 3}, 1724 | }, 1725 | { 1726 | q: "?1, ?2, ?3", 1727 | in: []interface{}{1, 2, 3}, 1728 | w: []int{1, 2, 3}, 1729 | }, 1730 | { 1731 | q: "?1, ?, ?3", 1732 | in: []interface{}{1, 2, 3}, 1733 | w: []int{1, 2, 3}, 1734 | }, 1735 | { 1736 | q: "?3, ?2, ?1", 1737 | in: []interface{}{1, 2, 3}, 1738 | w: []int{3, 2, 1}, 1739 | }, 1740 | { 1741 | q: "?1, ?1, ?2", 1742 | in: []interface{}{1, 2}, 1743 | w: []int{1, 1, 2}, 1744 | }, 1745 | { 1746 | q: ":one, :two, :three", 1747 | in: []interface{}{sql.Named("one", 1), sql.Named("two", 2), sql.Named("three", 3)}, 1748 | w: []int{1, 2, 3}, 1749 | }, 1750 | { 1751 | q: ":one, :one, :two", 1752 | in: []interface{}{sql.Named("one", 1), sql.Named("two", 2)}, 1753 | w: []int{1, 1, 2}, 1754 | }, 1755 | { 1756 | q: "@one, @two, @three", 1757 | in: []interface{}{sql.Named("one", 1), sql.Named("two", 2), sql.Named("three", 3)}, 1758 | w: []int{1, 2, 3}, 1759 | }, 1760 | { 1761 | q: "@one, @one, @two", 1762 | in: []interface{}{sql.Named("one", 1), sql.Named("two", 2)}, 1763 | w: []int{1, 1, 2}, 1764 | }, 1765 | { 1766 | q: "$one, $two, $three", 1767 | in: []interface{}{sql.Named("one", 1), sql.Named("two", 2), sql.Named("three", 3)}, 1768 | w: []int{1, 2, 3}, 1769 | }, 1770 | { 1771 | // A common usage that should technically require sql.Named but 1772 | // does not. 1773 | q: "$1, $2, $3", 1774 | in: []interface{}{1, 2, 3}, 1775 | w: []int{1, 2, 3}, 1776 | }, 1777 | { 1778 | q: "$one, $one, $two", 1779 | in: []interface{}{sql.Named("one", 1), sql.Named("two", 2)}, 1780 | w: []int{1, 1, 2}, 1781 | }, 1782 | { 1783 | q: ":one, @one, $one", 1784 | in: []interface{}{sql.Named("one", 1)}, 1785 | w: []int{1, 1, 1}, 1786 | }, 1787 | } { 1788 | got := make([]int, len(tc.w)) 1789 | ptrs := make([]interface{}, len(got)) 1790 | for i := range got { 1791 | ptrs[i] = &got[i] 1792 | } 1793 | 1794 | row, cleanup := query(db, "select "+tc.q, tc.in...) 1795 | defer cleanup() 1796 | 1797 | if err := row.Scan(ptrs...); err != nil { 1798 | t.Errorf("query(%q, %+v) = %s", tc.q, tc.in, err) 1799 | continue 1800 | } 1801 | 1802 | if !reflect.DeepEqual(got, tc.w) { 1803 | t.Errorf("query(%q, %+v) = %#+v, want %#+v", tc.q, tc.in, got, tc.w) 1804 | } 1805 | } 1806 | } 1807 | 1808 | func TestBindingError(t *testing.T) { 1809 | t.Run("DB", func(t *testing.T) { 1810 | testBindingError(t, func(db *sql.DB, query string, args ...interface{}) (*sql.Row, func()) { 1811 | return db.QueryRow(query, args...), func() {} 1812 | }) 1813 | }) 1814 | 1815 | t.Run("Prepare", func(t *testing.T) { 1816 | testBindingError(t, func(db *sql.DB, query string, args ...interface{}) (*sql.Row, func()) { 1817 | stmt, err := db.Prepare(query) 1818 | if err != nil { 1819 | t.Fatal(err) 1820 | } 1821 | return stmt.QueryRow(args...), func() { stmt.Close() } 1822 | }) 1823 | }) 1824 | } 1825 | 1826 | func testBindingError(t *testing.T, query func(db *sql.DB, query string, args ...interface{}) (*sql.Row, func())) { 1827 | db, err := sql.Open(driverName, "file::memory:") 1828 | if err != nil { 1829 | t.Fatal(err) 1830 | } 1831 | defer db.Close() 1832 | 1833 | for _, tc := range []struct { 1834 | q string 1835 | in []interface{} 1836 | }{ 1837 | { 1838 | q: "?", 1839 | in: []interface{}{}, 1840 | }, 1841 | { 1842 | q: "?500, ?", 1843 | in: []interface{}{1, 2}, 1844 | }, 1845 | { 1846 | q: ":one", 1847 | in: []interface{}{1}, 1848 | }, 1849 | { 1850 | q: "@one", 1851 | in: []interface{}{1}, 1852 | }, 1853 | { 1854 | q: "$one", 1855 | in: []interface{}{1}, 1856 | }, 1857 | } { 1858 | got := make([]int, 2) 1859 | ptrs := make([]interface{}, len(got)) 1860 | for i := range got { 1861 | ptrs[i] = &got[i] 1862 | } 1863 | 1864 | row, cleanup := query(db, "select "+tc.q, tc.in...) 1865 | defer cleanup() 1866 | 1867 | err := row.Scan(ptrs...) 1868 | if err == nil || (!strings.Contains(err.Error(), "missing argument with index") && !strings.Contains(err.Error(), "missing named argument")) { 1869 | t.Errorf("query(%q, %+v) unexpected error %+v", tc.q, tc.in, err) 1870 | } 1871 | } 1872 | } 1873 | 1874 | // https://gitlab.com/cznic/sqlite/-/issues/51 1875 | func TestIssue51(t *testing.T) { 1876 | if testing.Short() { 1877 | t.Skip("skipping test in short mode") 1878 | } 1879 | 1880 | tempDir, err := os.MkdirTemp("", "") 1881 | if err != nil { 1882 | t.Fatal(err) 1883 | } 1884 | 1885 | defer func() { 1886 | os.RemoveAll(tempDir) 1887 | }() 1888 | 1889 | fn := filepath.Join(tempDir, "test_issue51.db") 1890 | db, err := sql.Open(driverName, fn) 1891 | if err != nil { 1892 | t.Fatal(err) 1893 | } 1894 | 1895 | defer func() { 1896 | db.Close() 1897 | }() 1898 | 1899 | if _, err := db.Exec(` 1900 | CREATE TABLE fileHash ( 1901 | "hash" TEXT NOT NULL PRIMARY KEY, 1902 | "filename" TEXT, 1903 | "lastChecked" INTEGER 1904 | );`); err != nil { 1905 | t.Fatal(err) 1906 | } 1907 | 1908 | t0 := time.Now() 1909 | n := 0 1910 | for time.Since(t0) < time.Minute { 1911 | hash := randomString() 1912 | if _, err = lookupHash(fn, hash); err != nil { 1913 | t.Fatal(err) 1914 | } 1915 | 1916 | if err = saveHash(fn, hash, hash+".temp"); err != nil { 1917 | t.Error(err) 1918 | break 1919 | } 1920 | n++ 1921 | } 1922 | t.Logf("cycles: %v", n) 1923 | row := db.QueryRow("select count(*) from fileHash") 1924 | if err := row.Scan(&n); err != nil { 1925 | t.Fatal(err) 1926 | } 1927 | 1928 | t.Logf("DB records: %v", n) 1929 | } 1930 | 1931 | func saveHash(dbFile string, hash string, fileName string) (err error) { 1932 | db, err := sql.Open("sqlite", dbFile) 1933 | if err != nil { 1934 | return fmt.Errorf("could not open database: %v", err) 1935 | } 1936 | 1937 | defer func() { 1938 | if err2 := db.Close(); err2 != nil && err == nil { 1939 | err = fmt.Errorf("could not close the database: %s", err2) 1940 | } 1941 | }() 1942 | 1943 | query := `INSERT OR REPLACE INTO fileHash(hash, fileName, lastChecked) 1944 | VALUES(?, ?, ?);` 1945 | rows, err := executeSQL(db, query, hash, fileName, time.Now().Unix()) 1946 | if err != nil { 1947 | return fmt.Errorf("error saving hash to database: %v", err) 1948 | } 1949 | defer rows.Close() 1950 | 1951 | return nil 1952 | } 1953 | 1954 | func executeSQL(db *sql.DB, query string, values ...interface{}) (*sql.Rows, error) { 1955 | statement, err := db.Prepare(query) 1956 | if err != nil { 1957 | return nil, fmt.Errorf("could not prepare statement: %v", err) 1958 | } 1959 | defer statement.Close() 1960 | 1961 | return statement.Query(values...) 1962 | } 1963 | 1964 | func lookupHash(dbFile string, hash string) (ok bool, err error) { 1965 | db, err := sql.Open("sqlite", dbFile) 1966 | if err != nil { 1967 | return false, fmt.Errorf("could not open database: %n", err) 1968 | } 1969 | 1970 | defer func() { 1971 | if err2 := db.Close(); err2 != nil && err == nil { 1972 | err = fmt.Errorf("could not close the database: %v", err2) 1973 | } 1974 | }() 1975 | 1976 | query := `SELECT hash, fileName, lastChecked 1977 | FROM fileHash 1978 | WHERE hash=?;` 1979 | rows, err := executeSQL(db, query, hash) 1980 | if err != nil { 1981 | return false, fmt.Errorf("error checking database for hash: %n", err) 1982 | } 1983 | 1984 | defer func() { 1985 | if err2 := rows.Close(); err2 != nil && err == nil { 1986 | err = fmt.Errorf("could not close DB rows: %v", err2) 1987 | } 1988 | }() 1989 | 1990 | var ( 1991 | dbHash string 1992 | fileName string 1993 | lastChecked int64 1994 | ) 1995 | for rows.Next() { 1996 | err = rows.Scan(&dbHash, &fileName, &lastChecked) 1997 | if err != nil { 1998 | return false, fmt.Errorf("could not read DB row: %v", err) 1999 | } 2000 | } 2001 | return false, rows.Err() 2002 | } 2003 | 2004 | func randomString() string { 2005 | b := make([]byte, 32) 2006 | for i := range b { 2007 | b[i] = charset[seededRand.Intn(len(charset))] 2008 | } 2009 | return string(b) 2010 | } 2011 | 2012 | var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) 2013 | 2014 | const charset = "abcdefghijklmnopqrstuvwxyz" + 2015 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 2016 | 2017 | // https://gitlab.com/cznic/sqlite/-/issues/53 2018 | func TestIssue53(t *testing.T) { 2019 | tempDir, err := os.MkdirTemp("", "") 2020 | if err != nil { 2021 | t.Fatal(err) 2022 | } 2023 | 2024 | defer func() { 2025 | os.RemoveAll(tempDir) 2026 | }() 2027 | 2028 | wd, err := os.Getwd() 2029 | if err != nil { 2030 | t.Fatal(err) 2031 | } 2032 | 2033 | defer os.Chdir(wd) 2034 | 2035 | if err := os.Chdir(tempDir); err != nil { 2036 | t.Fatal(err) 2037 | } 2038 | 2039 | const fn = "testissue53.sqlite" 2040 | 2041 | db, err := sql.Open(driverName, fn) 2042 | if err != nil { 2043 | t.Fatal(err) 2044 | } 2045 | 2046 | defer func() { 2047 | db.Close() 2048 | }() 2049 | 2050 | if _, err := db.Exec(` 2051 | CREATE TABLE IF NOT EXISTS loginst ( 2052 | instid INTEGER PRIMARY KEY, 2053 | name VARCHAR UNIQUE 2054 | ); 2055 | `); err != nil { 2056 | t.Fatal(err) 2057 | } 2058 | 2059 | tx, err := db.Begin() 2060 | if err != nil { 2061 | t.Fatal(err) 2062 | } 2063 | 2064 | for i := 0; i < 5000; i++ { 2065 | x := fmt.Sprintf("foo%d", i) 2066 | var id int 2067 | if err := tx.QueryRow("INSERT OR IGNORE INTO loginst (name) VALUES (?); SELECT instid FROM loginst WHERE name = ?", x, x).Scan(&id); err != nil { 2068 | t.Fatal(err) 2069 | } 2070 | } 2071 | 2072 | } 2073 | 2074 | // https://gitlab.com/cznic/sqlite/-/issues/37 2075 | func TestPersistPragma(t *testing.T) { 2076 | tempDir, err := os.MkdirTemp("", "") 2077 | if err != nil { 2078 | t.Fatal(err) 2079 | } 2080 | 2081 | defer func() { 2082 | os.RemoveAll(tempDir) 2083 | }() 2084 | 2085 | wd, err := os.Getwd() 2086 | if err != nil { 2087 | t.Fatal(err) 2088 | } 2089 | 2090 | defer os.Chdir(wd) 2091 | 2092 | if err := os.Chdir(tempDir); err != nil { 2093 | t.Fatal(err) 2094 | } 2095 | 2096 | pragmas := []pragmaCfg{ 2097 | {"foreign_keys", "on", int64(1)}, 2098 | {"analysis_limit", "1000", int64(1000)}, 2099 | {"application_id", "214", int64(214)}, 2100 | {"encoding", "'UTF-16le'", "UTF-16le"}} 2101 | 2102 | if err := testPragmas("testpersistpragma.sqlite", "testpersistpragma.sqlite", pragmas); err != nil { 2103 | t.Fatal(err) 2104 | } 2105 | if err := testPragmas("file::memory:", "", pragmas); err != nil { 2106 | t.Fatal(err) 2107 | } 2108 | if err := testPragmas(":memory:", "", pragmas); err != nil { 2109 | t.Fatal(err) 2110 | } 2111 | } 2112 | 2113 | type pragmaCfg struct { 2114 | name string 2115 | value string 2116 | expected interface{} 2117 | } 2118 | 2119 | func testPragmas(name, diskFile string, pragmas []pragmaCfg) error { 2120 | if diskFile != "" { 2121 | os.Remove(diskFile) 2122 | } 2123 | 2124 | q := url.Values{} 2125 | for _, pragma := range pragmas { 2126 | q.Add("_pragma", pragma.name+"="+pragma.value) 2127 | } 2128 | 2129 | dsn := name + "?" + q.Encode() 2130 | db, err := sql.Open(driverName, dsn) 2131 | if err != nil { 2132 | return err 2133 | } 2134 | 2135 | db.SetMaxOpenConns(1) 2136 | 2137 | if err := checkPragmas(db, pragmas); err != nil { 2138 | return err 2139 | } 2140 | 2141 | c, err := db.Conn(context.Background()) 2142 | if err != nil { 2143 | return err 2144 | } 2145 | 2146 | // Kill the connection to spawn a new one. Pragma configs should persist 2147 | c.Raw(func(interface{}) error { return driver.ErrBadConn }) 2148 | 2149 | if err := checkPragmas(db, pragmas); err != nil { 2150 | return err 2151 | } 2152 | 2153 | if diskFile == "" { 2154 | // Make sure in memory databases aren't being written to disk 2155 | return testInMemory(db) 2156 | } 2157 | 2158 | return nil 2159 | } 2160 | 2161 | func checkPragmas(db *sql.DB, pragmas []pragmaCfg) error { 2162 | for _, pragma := range pragmas { 2163 | row := db.QueryRow(`PRAGMA ` + pragma.name) 2164 | 2165 | var result interface{} 2166 | if err := row.Scan(&result); err != nil { 2167 | return err 2168 | } 2169 | if result != pragma.expected { 2170 | return fmt.Errorf("expected PRAGMA %s to return %v but got %v", pragma.name, pragma.expected, result) 2171 | } 2172 | } 2173 | return nil 2174 | } 2175 | 2176 | func TestInMemory(t *testing.T) { 2177 | tempDir, err := os.MkdirTemp("", "") 2178 | if err != nil { 2179 | t.Fatal(err) 2180 | } 2181 | 2182 | defer func() { 2183 | os.RemoveAll(tempDir) 2184 | }() 2185 | 2186 | wd, err := os.Getwd() 2187 | if err != nil { 2188 | t.Fatal(err) 2189 | } 2190 | 2191 | defer os.Chdir(wd) 2192 | 2193 | if err := os.Chdir(tempDir); err != nil { 2194 | t.Fatal(err) 2195 | } 2196 | 2197 | if err := testMemoryPath(":memory:"); err != nil { 2198 | t.Fatal(err) 2199 | } 2200 | if err := testMemoryPath("file::memory:"); err != nil { 2201 | t.Fatal(err) 2202 | } 2203 | 2204 | // This parameter should be ignored 2205 | q := url.Values{} 2206 | q.Add("mode", "readonly") 2207 | if err := testMemoryPath(":memory:?" + q.Encode()); err != nil { 2208 | t.Fatal(err) 2209 | } 2210 | } 2211 | 2212 | func testMemoryPath(mPath string) error { 2213 | db, err := sql.Open(driverName, mPath) 2214 | if err != nil { 2215 | return err 2216 | } 2217 | defer db.Close() 2218 | 2219 | return testInMemory(db) 2220 | } 2221 | 2222 | func testInMemory(db *sql.DB) error { 2223 | _, err := db.Exec(` 2224 | create table in_memory_test(i int, f double); 2225 | insert into in_memory_test values(12, 3.14); 2226 | `) 2227 | if err != nil { 2228 | return err 2229 | } 2230 | 2231 | dirEntries, err := os.ReadDir("./") 2232 | if err != nil { 2233 | return err 2234 | } 2235 | 2236 | for _, dirEntry := range dirEntries { 2237 | if strings.Contains(dirEntry.Name(), "memory") { 2238 | return fmt.Errorf("file was created for in memory database") 2239 | } 2240 | } 2241 | 2242 | return nil 2243 | } 2244 | 2245 | func emptyDir(s string) error { 2246 | m, err := filepath.Glob(filepath.FromSlash(s + "/*")) 2247 | if err != nil { 2248 | return err 2249 | } 2250 | 2251 | for _, v := range m { 2252 | fi, err := os.Stat(v) 2253 | if err != nil { 2254 | return err 2255 | } 2256 | 2257 | switch { 2258 | case fi.IsDir(): 2259 | if err = os.RemoveAll(v); err != nil { 2260 | return err 2261 | } 2262 | default: 2263 | if err = os.Remove(v); err != nil { 2264 | return err 2265 | } 2266 | } 2267 | } 2268 | return nil 2269 | } 2270 | 2271 | // https://gitlab.com/cznic/sqlite/-/issues/70 2272 | func TestIssue70(t *testing.T) { 2273 | db, err := sql.Open(driverName, "file::memory:") 2274 | if _, err = db.Exec(`create table t (foo)`); err != nil { 2275 | t.Fatalf("create: %v", err) 2276 | } 2277 | 2278 | defer func() { 2279 | if err := db.Close(); err != nil { 2280 | t.Errorf("conn close: %v", err) 2281 | } 2282 | }() 2283 | 2284 | r, err := db.Query("select * from t") 2285 | if err != nil { 2286 | t.Errorf("select a: %v", err) 2287 | return 2288 | } 2289 | 2290 | if err := r.Close(); err != nil { 2291 | t.Errorf("rows close: %v", err) 2292 | return 2293 | } 2294 | 2295 | if _, err := db.Query("select * from t"); err != nil { 2296 | t.Errorf("select b: %v", err) 2297 | } 2298 | } 2299 | 2300 | // https://gitlab.com/cznic/sqlite/-/issues/66 2301 | func TestIssue66(t *testing.T) { 2302 | tempDir, err := os.MkdirTemp("", "") 2303 | if err != nil { 2304 | t.Fatal(err) 2305 | } 2306 | 2307 | defer func() { 2308 | os.RemoveAll(tempDir) 2309 | }() 2310 | 2311 | fn := filepath.Join(tempDir, "testissue66.db") 2312 | db, err := sql.Open(driverName, fn) 2313 | 2314 | defer func() { 2315 | if err := db.Close(); err != nil { 2316 | t.Errorf("conn close: %v", err) 2317 | } 2318 | }() 2319 | 2320 | if _, err = db.Exec(`CREATE TABLE IF NOT EXISTS verdictcache (sha1 text);`); err != nil { 2321 | t.Fatalf("create: %v", err) 2322 | } 2323 | 2324 | // ab 2325 | // 00 ok 2326 | // 01 ok 2327 | // 10 ok 2328 | // 11 hangs with old implementation of conn.step(). 2329 | 2330 | // a 2331 | if _, err = db.Exec("INSERT OR REPLACE INTO verdictcache (sha1) VALUES ($1)", "a"); err != nil { 2332 | t.Fatalf("insert: %v", err) 2333 | } 2334 | 2335 | // b 2336 | if _, err := db.Query("SELECT * FROM verdictcache WHERE sha1=$1", "a"); err != nil { 2337 | t.Fatalf("select: %v", err) 2338 | } 2339 | 2340 | // c 2341 | if _, err = db.Exec("INSERT OR REPLACE INTO verdictcache (sha1) VALUES ($1)", "b"); err != nil { 2342 | 2343 | // https://www.sqlite.org/rescode.html#busy 2344 | // ---------------------------------------------------------------------------- 2345 | // The SQLITE_BUSY result code indicates that the database file could not be 2346 | // written (or in some cases read) because of concurrent activity by some other 2347 | // database connection, usually a database connection in a separate process. 2348 | // ---------------------------------------------------------------------------- 2349 | // 2350 | // The SQLITE_BUSY error is _expected_. 2351 | // 2352 | // According to the above, performing c after b's result was not yet 2353 | // consumed/closed is not possible. Mattn's driver seems to resort to 2354 | // autoclosing the driver.Rows returned by b in this situation, but I don't 2355 | // think that's correct (jnml). 2356 | 2357 | t.Logf("insert 2: %v", err) 2358 | if !strings.Contains(err.Error(), "database is locked (5) (SQLITE_BUSY)") { 2359 | t.Fatalf("insert 2: %v", err) 2360 | } 2361 | } 2362 | } 2363 | 2364 | // https://gitlab.com/cznic/sqlite/-/issues/65 2365 | func TestIssue65(t *testing.T) { 2366 | tempDir, err := os.MkdirTemp("", "") 2367 | if err != nil { 2368 | t.Fatal(err) 2369 | } 2370 | 2371 | defer func() { 2372 | os.RemoveAll(tempDir) 2373 | }() 2374 | 2375 | db, err := sql.Open("sqlite", filepath.Join(tempDir, "testissue65.sqlite")) 2376 | if err != nil { 2377 | t.Fatalf("Failed to open database: %v", err) 2378 | } 2379 | 2380 | testIssue65(t, db, true) 2381 | 2382 | // go1.20rc1, linux/ppc64le VM 2383 | // 10000 FAIL 2384 | // 20000 PASS, FAIL 2385 | // 40000 FAIL 2386 | // 80000 PASS, PASS 2387 | if db, err = sql.Open("sqlite", filepath.Join(tempDir, "testissue65b.sqlite")+"?_pragma=busy_timeout%3d80000"); err != nil { 2388 | t.Fatalf("Failed to open database: %v", err) 2389 | } 2390 | 2391 | testIssue65(t, db, false) 2392 | } 2393 | 2394 | func testIssue65(t *testing.T, db *sql.DB, canFail bool) { 2395 | defer db.Close() 2396 | 2397 | ctx := context.Background() 2398 | 2399 | if _, err := db.Exec("CREATE TABLE foo (department INTEGER, profits INTEGER)"); err != nil { 2400 | t.Fatal("Failed to create table:", err) 2401 | } 2402 | 2403 | if _, err := db.Exec("INSERT INTO foo VALUES (1, 10), (1, 20), (1, 45), (2, 42), (2, 115)"); err != nil { 2404 | t.Fatal("Failed to insert records:", err) 2405 | } 2406 | 2407 | readFunc := func(ctx context.Context) error { 2408 | tx, err := db.BeginTx(ctx, nil) 2409 | if err != nil { 2410 | return fmt.Errorf("read error: %v", err) 2411 | } 2412 | 2413 | defer tx.Rollback() 2414 | 2415 | var dept, count int64 2416 | if err := tx.QueryRowContext(ctx, "SELECT department, COUNT(*) FROM foo GROUP BY department").Scan( 2417 | &dept, 2418 | &count, 2419 | ); err != nil { 2420 | return fmt.Errorf("read error: %v", err) 2421 | } 2422 | 2423 | return nil 2424 | } 2425 | 2426 | writeFunc := func(ctx context.Context) error { 2427 | tx, err := db.BeginTx(ctx, nil) 2428 | if err != nil { 2429 | return fmt.Errorf("write error: %v", err) 2430 | } 2431 | 2432 | defer tx.Rollback() 2433 | 2434 | if _, err := tx.ExecContext( 2435 | ctx, 2436 | "INSERT INTO foo(department, profits) VALUES (@department, @profits)", 2437 | sql.Named("department", rand.Int()), 2438 | sql.Named("profits", rand.Int()), 2439 | ); err != nil { 2440 | return fmt.Errorf("write error: %v", err) 2441 | } 2442 | 2443 | return tx.Commit() 2444 | } 2445 | 2446 | var wg sync.WaitGroup 2447 | wg.Add(2) 2448 | 2449 | const cycles = 100 2450 | 2451 | errCh := make(chan error, 2) 2452 | 2453 | go func() { 2454 | defer wg.Done() 2455 | 2456 | for i := 0; i < cycles; i++ { 2457 | if err := readFunc(ctx); err != nil { 2458 | err = fmt.Errorf("readFunc(%v): %v", canFail, err) 2459 | t.Log(err) 2460 | if !canFail { 2461 | errCh <- err 2462 | } 2463 | return 2464 | } 2465 | } 2466 | }() 2467 | 2468 | go func() { 2469 | defer wg.Done() 2470 | 2471 | for i := 0; i < cycles; i++ { 2472 | if err := writeFunc(ctx); err != nil { 2473 | err = fmt.Errorf("writeFunc(%v): %v", canFail, err) 2474 | t.Log(err) 2475 | if !canFail { 2476 | errCh <- err 2477 | } 2478 | return 2479 | } 2480 | } 2481 | }() 2482 | 2483 | wg.Wait() 2484 | for { 2485 | select { 2486 | case err := <-errCh: 2487 | t.Error(err) 2488 | default: 2489 | return 2490 | } 2491 | } 2492 | } 2493 | 2494 | // https://gitlab.com/cznic/sqlite/-/issues/73 2495 | func TestConstraintPrimaryKeyError(t *testing.T) { 2496 | db, err := sql.Open(driverName, "file::memory:") 2497 | if err != nil { 2498 | t.Fatal(err) 2499 | } 2500 | defer db.Close() 2501 | 2502 | _, err = db.Exec(`CREATE TABLE IF NOT EXISTS hash (hashval TEXT PRIMARY KEY NOT NULL)`) 2503 | if err != nil { 2504 | t.Fatal(err) 2505 | } 2506 | 2507 | _, err = db.Exec("INSERT INTO hash (hashval) VALUES (?)", "somehashval") 2508 | if err != nil { 2509 | t.Fatal(err) 2510 | } 2511 | 2512 | _, err = db.Exec("INSERT INTO hash (hashval) VALUES (?)", "somehashval") 2513 | if err == nil { 2514 | t.Fatal("wanted error") 2515 | } 2516 | 2517 | if errs, want := err.Error(), "constraint failed: UNIQUE constraint failed: hash.hashval (1555)"; errs != want { 2518 | t.Fatalf("got error string %q, want %q", errs, want) 2519 | } 2520 | } 2521 | 2522 | func TestConstraintUniqueError(t *testing.T) { 2523 | db, err := sql.Open(driverName, "file::memory:") 2524 | if err != nil { 2525 | t.Fatal(err) 2526 | } 2527 | defer db.Close() 2528 | 2529 | _, err = db.Exec(`CREATE TABLE IF NOT EXISTS hash (hashval TEXT UNIQUE)`) 2530 | if err != nil { 2531 | t.Fatal(err) 2532 | } 2533 | 2534 | _, err = db.Exec("INSERT INTO hash (hashval) VALUES (?)", "somehashval") 2535 | if err != nil { 2536 | t.Fatal(err) 2537 | } 2538 | 2539 | _, err = db.Exec("INSERT INTO hash (hashval) VALUES (?)", "somehashval") 2540 | if err == nil { 2541 | t.Fatal("wanted error") 2542 | } 2543 | 2544 | if errs, want := err.Error(), "constraint failed: UNIQUE constraint failed: hash.hashval (2067)"; errs != want { 2545 | t.Fatalf("got error string %q, want %q", errs, want) 2546 | } 2547 | } 2548 | 2549 | // https://gitlab.com/cznic/sqlite/-/issues/92 2550 | func TestBeginMode(t *testing.T) { 2551 | tempDir, err := os.MkdirTemp("", "") 2552 | if err != nil { 2553 | t.Fatal(err) 2554 | } 2555 | 2556 | defer func() { 2557 | os.RemoveAll(tempDir) 2558 | }() 2559 | 2560 | tests := []struct { 2561 | mode string 2562 | want int32 2563 | }{ 2564 | {"deferred", sqlite3.SQLITE_TXN_NONE}, 2565 | {"immediate", sqlite3.SQLITE_TXN_WRITE}, 2566 | // TODO: how to verify "exclusive" is working differently from immediate, 2567 | // short of concurrently trying to open the database again? This is only 2568 | // different in non-WAL journal modes. 2569 | {"exclusive", sqlite3.SQLITE_TXN_WRITE}, 2570 | } 2571 | 2572 | for _, tt := range tests { 2573 | tt := tt 2574 | for _, jm := range []string{"delete", "wal"} { 2575 | jm := jm 2576 | t.Run(jm+"/"+tt.mode, func(t *testing.T) { 2577 | // t.Parallel() 2578 | 2579 | qs := fmt.Sprintf("?_txlock=%s&_pragma=journal_mode(%s)", tt.mode, jm) 2580 | db, err := sql.Open("sqlite", filepath.Join(tempDir, fmt.Sprintf("testbeginmode-%s.sqlite", tt.mode))+qs) 2581 | if err != nil { 2582 | t.Fatalf("Failed to open database: %v", err) 2583 | } 2584 | defer db.Close() 2585 | connection, err := db.Conn(context.Background()) 2586 | if err != nil { 2587 | t.Fatalf("Failed to open connection: %v", err) 2588 | } 2589 | 2590 | tx, err := connection.BeginTx(context.Background(), nil) 2591 | if err != nil { 2592 | t.Fatalf("Failed to begin transaction: %v", err) 2593 | } 2594 | defer tx.Rollback() 2595 | if err := connection.Raw(func(driverConn interface{}) error { 2596 | p, err := libc.CString("main") 2597 | if err != nil { 2598 | return err 2599 | } 2600 | c := driverConn.(*conn) 2601 | defer c.free(p) 2602 | got := sqlite3.Xsqlite3_txn_state(c.tls, c.db, p) 2603 | if got != tt.want { 2604 | return fmt.Errorf("in mode %s, got txn state %d, want %d", tt.mode, got, tt.want) 2605 | } 2606 | return nil 2607 | }); err != nil { 2608 | t.Fatalf("Failed to check txn state: %v", err) 2609 | } 2610 | }) 2611 | } 2612 | } 2613 | } 2614 | 2615 | // https://gitlab.com/cznic/sqlite/-/issues/94 2616 | func TestCancelRace(t *testing.T) { 2617 | tempDir, err := os.MkdirTemp("", "") 2618 | if err != nil { 2619 | t.Fatal(err) 2620 | } 2621 | 2622 | defer func() { 2623 | os.RemoveAll(tempDir) 2624 | }() 2625 | 2626 | db, err := sql.Open("sqlite", filepath.Join(tempDir, "testcancelrace.sqlite")) 2627 | if err != nil { 2628 | t.Fatalf("Failed to open database: %v", err) 2629 | } 2630 | defer db.Close() 2631 | 2632 | tests := []struct { 2633 | name string 2634 | f func(context.Context, *sql.DB) error 2635 | }{ 2636 | { 2637 | "db.ExecContext", 2638 | func(ctx context.Context, d *sql.DB) error { 2639 | _, err := db.ExecContext(ctx, "select 1") 2640 | return err 2641 | }, 2642 | }, 2643 | { 2644 | "db.QueryContext", 2645 | func(ctx context.Context, d *sql.DB) error { 2646 | _, err := db.QueryContext(ctx, "select 1") 2647 | return err 2648 | }, 2649 | }, 2650 | { 2651 | "tx.ExecContext", 2652 | func(ctx context.Context, d *sql.DB) error { 2653 | tx, err := db.BeginTx(ctx, &sql.TxOptions{}) 2654 | if err != nil { 2655 | return err 2656 | } 2657 | defer tx.Rollback() 2658 | if _, err := tx.ExecContext(ctx, "select 1"); err != nil { 2659 | return err 2660 | } 2661 | return tx.Rollback() 2662 | }, 2663 | }, 2664 | { 2665 | "tx.QueryContext", 2666 | func(ctx context.Context, d *sql.DB) error { 2667 | tx, err := db.BeginTx(ctx, &sql.TxOptions{}) 2668 | if err != nil { 2669 | return err 2670 | } 2671 | defer tx.Rollback() 2672 | if _, err := tx.QueryContext(ctx, "select 1"); err != nil { 2673 | return err 2674 | } 2675 | return tx.Rollback() 2676 | }, 2677 | }, 2678 | } 2679 | 2680 | for _, tt := range tests { 2681 | t.Run(tt.name, func(t *testing.T) { 2682 | // this is a race condition, so it's not guaranteed to fail on any given run, 2683 | // but with a moderate number of iterations it will eventually catch it 2684 | iterations := 100 2685 | for i := 0; i < iterations; i++ { 2686 | // none of these iterations should ever fail, because we never cancel their 2687 | // context until after they complete 2688 | ctx, cancel := context.WithCancel(context.Background()) 2689 | if err := tt.f(ctx, db); err != nil { 2690 | t.Fatalf("Failed to run test query on iteration %d: %v", i, err) 2691 | } 2692 | cancel() 2693 | } 2694 | }) 2695 | } 2696 | } 2697 | 2698 | //go:embed embed.db 2699 | var fs embed.FS 2700 | 2701 | //go:embed embed2.db 2702 | var fs2 embed.FS 2703 | 2704 | func TestVFS(t *testing.T) { 2705 | fn, f, err := vfs.New(fs) 2706 | if err != nil { 2707 | t.Fatal(err) 2708 | } 2709 | 2710 | defer func() { 2711 | if err := f.Close(); err != nil { 2712 | t.Error(err) 2713 | } 2714 | }() 2715 | 2716 | f2n, f2, err := vfs.New(fs2) 2717 | if err != nil { 2718 | t.Fatal(err) 2719 | } 2720 | 2721 | defer func() { 2722 | if err := f2.Close(); err != nil { 2723 | t.Error(err) 2724 | } 2725 | }() 2726 | 2727 | db, err := sql.Open("sqlite", "file:embed.db?vfs="+fn) 2728 | if err != nil { 2729 | t.Fatal(err) 2730 | } 2731 | 2732 | defer db.Close() 2733 | 2734 | db2, err := sql.Open("sqlite", "file:embed2.db?vfs="+f2n) 2735 | if err != nil { 2736 | t.Fatal(err) 2737 | } 2738 | 2739 | defer db2.Close() 2740 | 2741 | rows, err := db.Query("select * from t order by i;") 2742 | if err != nil { 2743 | t.Fatal(err) 2744 | } 2745 | 2746 | var a []int 2747 | for rows.Next() { 2748 | var i, j, k int 2749 | if err := rows.Scan(&i, &j, &k); err != nil { 2750 | t.Fatal(err) 2751 | } 2752 | 2753 | a = append(a, i, j, k) 2754 | } 2755 | if err := rows.Err(); err != nil { 2756 | t.Fatal(err) 2757 | } 2758 | 2759 | t.Log(a) 2760 | if g, e := fmt.Sprint(a), "[1 2 3 40 50 60]"; g != e { 2761 | t.Fatalf("got %q, expected %q", g, e) 2762 | } 2763 | 2764 | if rows, err = db2.Query("select * from u order by s;"); err != nil { 2765 | t.Fatal(err) 2766 | } 2767 | 2768 | var b []string 2769 | for rows.Next() { 2770 | var x, y string 2771 | if err := rows.Scan(&x, &y); err != nil { 2772 | t.Fatal(err) 2773 | } 2774 | 2775 | b = append(b, x, y) 2776 | } 2777 | if err := rows.Err(); err != nil { 2778 | t.Fatal(err) 2779 | } 2780 | 2781 | t.Log(b) 2782 | if g, e := fmt.Sprint(b), "[123 xyz abc def]"; g != e { 2783 | t.Fatalf("got %q, expected %q", g, e) 2784 | } 2785 | } 2786 | 2787 | // y = 2^n, except for n < 0 y = 0. 2788 | func exp(n int) int { 2789 | if n < 0 { 2790 | return 0 2791 | } 2792 | 2793 | return 1 << n 2794 | } 2795 | 2796 | func BenchmarkConcurrent(b *testing.B) { 2797 | benchmarkConcurrent(b, "sqlite", []string{"sql", "drv"}) 2798 | } 2799 | 2800 | func benchmarkConcurrent(b *testing.B, drv string, modes []string) { 2801 | for _, mode := range modes { 2802 | for _, measurement := range []string{"reads", "writes"} { 2803 | for _, writers := range []int{0, 1, 10, 100, 100} { 2804 | for _, readers := range []int{0, 1, 10, 100, 100} { 2805 | if measurement == "reads" && readers == 0 || measurement == "writes" && writers == 0 { 2806 | continue 2807 | } 2808 | 2809 | tag := fmt.Sprintf("%s %s readers %d writers %d %s", mode, measurement, readers, writers, drv) 2810 | b.Run(tag, func(b *testing.B) { c := &concurrentBenchmark{}; c.run(b, readers, writers, drv, measurement, mode) }) 2811 | } 2812 | } 2813 | } 2814 | } 2815 | } 2816 | 2817 | // The code for concurrentBenchmark is derived from/heavily inspired by 2818 | // original code available at 2819 | // 2820 | // https://github.com/kalafut/go-sqlite-bench 2821 | // 2822 | // # MIT License 2823 | // 2824 | // # Copyright (c) 2022 Jim Kalafut 2825 | // 2826 | // Permission is hereby granted, free of charge, to any person obtaining a copy 2827 | // of this software and associated documentation files (the "Software"), to deal 2828 | // in the Software without restriction, including without limitation the rights 2829 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 2830 | // copies of the Software, and to permit persons to whom the Software is 2831 | // furnished to do so, subject to the following conditions: 2832 | // 2833 | // The above copyright notice and this permission notice shall be included in all 2834 | // copies or substantial portions of the Software. 2835 | // 2836 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2837 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2838 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 2839 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2840 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2841 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 2842 | // SOFTWARE. 2843 | type concurrentBenchmark struct { 2844 | b *testing.B 2845 | drv string 2846 | fn string 2847 | start chan struct{} 2848 | stop chan struct{} 2849 | wg sync.WaitGroup 2850 | 2851 | reads int32 2852 | records int32 2853 | writes int32 2854 | } 2855 | 2856 | func (c *concurrentBenchmark) run(b *testing.B, readers, writers int, drv, measurement, mode string) { 2857 | c.b = b 2858 | c.drv = drv 2859 | b.ReportAllocs() 2860 | dir := b.TempDir() 2861 | fn := filepath.Join(dir, "test.db") 2862 | sqlite3.MutexCounters.Disable() 2863 | sqlite3.MutexEnterCallers.Disable() 2864 | c.makeDB(fn) 2865 | b.ResetTimer() 2866 | for i := 0; i < b.N; i++ { 2867 | b.StopTimer() 2868 | c.start = make(chan struct{}) 2869 | c.stop = make(chan struct{}) 2870 | sqlite3.MutexCounters.Disable() 2871 | sqlite3.MutexEnterCallers.Disable() 2872 | c.makeReaders(readers, mode) 2873 | c.makeWriters(writers, mode) 2874 | sqlite3.MutexCounters.Clear() 2875 | sqlite3.MutexCounters.Enable() 2876 | sqlite3.MutexEnterCallers.Clear() 2877 | //sqlite3.MutexEnterCallers.Enable() 2878 | time.AfterFunc(time.Second, func() { close(c.stop) }) 2879 | b.StartTimer() 2880 | close(c.start) 2881 | c.wg.Wait() 2882 | } 2883 | switch measurement { 2884 | case "reads": 2885 | b.ReportMetric(float64(c.reads), "reads/s") 2886 | case "writes": 2887 | b.ReportMetric(float64(c.writes), "writes/s") 2888 | } 2889 | // b.Log(sqlite3.MutexCounters) 2890 | // b.Log(sqlite3.MutexEnterCallers) 2891 | } 2892 | 2893 | func (c *concurrentBenchmark) randString(n int) string { 2894 | b := make([]byte, n) 2895 | for i := range b { 2896 | b[i] = byte(65 + rand.Intn(26)) 2897 | } 2898 | return string(b) 2899 | } 2900 | 2901 | func (c *concurrentBenchmark) mustExecSQL(db *sql.DB, sql string) { 2902 | var err error 2903 | for i := 0; i < 100; i++ { 2904 | if _, err = db.Exec(sql); err != nil { 2905 | if c.retry(err) { 2906 | continue 2907 | } 2908 | 2909 | c.b.Fatalf("%s: %v", sql, err) 2910 | } 2911 | 2912 | return 2913 | } 2914 | c.b.Fatalf("%s: %v", sql, err) 2915 | } 2916 | 2917 | func (c *concurrentBenchmark) mustExecDrv(db driver.Conn, sql string) { 2918 | var err error 2919 | for i := 0; i < 100; i++ { 2920 | if _, err = db.(driver.Execer).Exec(sql, nil); err != nil { 2921 | if c.retry(err) { 2922 | continue 2923 | } 2924 | 2925 | c.b.Fatalf("%s: %v", sql, err) 2926 | } 2927 | 2928 | return 2929 | } 2930 | c.b.Fatalf("%s: %v", sql, err) 2931 | } 2932 | 2933 | func (c *concurrentBenchmark) makeDB(fn string) { 2934 | const quota = 1e6 2935 | c.fn = fn 2936 | db := c.makeSQLConn() 2937 | 2938 | defer db.Close() 2939 | 2940 | c.mustExecSQL(db, "CREATE TABLE foo (id INTEGER NOT NULL PRIMARY KEY, name TEXT)") 2941 | tx, err := db.Begin() 2942 | if err != nil { 2943 | c.b.Fatal(err) 2944 | } 2945 | 2946 | stmt, err := tx.Prepare("INSERT INTO FOO(name) VALUES($1)") 2947 | if err != nil { 2948 | c.b.Fatal(err) 2949 | } 2950 | 2951 | for i := int32(0); i < quota; i++ { 2952 | if _, err = stmt.Exec(c.randString(30)); err != nil { 2953 | c.b.Fatal(err) 2954 | } 2955 | } 2956 | 2957 | if err := tx.Commit(); err != nil { 2958 | c.b.Fatal(err) 2959 | } 2960 | 2961 | c.records = quota 2962 | 2963 | // Warm the cache. 2964 | rows, err := db.Query("SELECT * FROM foo") 2965 | if err != nil { 2966 | c.b.Fatal(err) 2967 | } 2968 | 2969 | for rows.Next() { 2970 | var id int 2971 | var name string 2972 | err = rows.Scan(&id, &name) 2973 | if err != nil { 2974 | c.b.Fatal(err) 2975 | } 2976 | } 2977 | } 2978 | 2979 | func (c *concurrentBenchmark) makeSQLConn() *sql.DB { 2980 | db, err := sql.Open(c.drv, c.fn) 2981 | if err != nil { 2982 | c.b.Fatal(err) 2983 | } 2984 | 2985 | db.SetMaxOpenConns(0) 2986 | c.mustExecSQL(db, "PRAGMA busy_timeout=10000") 2987 | c.mustExecSQL(db, "PRAGMA synchronous=NORMAL") 2988 | c.mustExecSQL(db, "PRAGMA journal_mode=WAL") 2989 | return db 2990 | } 2991 | 2992 | func (c *concurrentBenchmark) makeDrvConn() driver.Conn { 2993 | db, err := sql.Open(c.drv, c.fn) 2994 | if err != nil { 2995 | c.b.Fatal(err) 2996 | } 2997 | 2998 | drv := db.Driver() 2999 | if err := db.Close(); err != nil { 3000 | c.b.Fatal(err) 3001 | } 3002 | 3003 | conn, err := drv.Open(c.fn) 3004 | if err != nil { 3005 | c.b.Fatal(err) 3006 | } 3007 | 3008 | c.mustExecDrv(conn, "PRAGMA busy_timeout=10000") 3009 | c.mustExecDrv(conn, "PRAGMA synchronous=NORMAL") 3010 | c.mustExecDrv(conn, "PRAGMA journal_mode=WAL") 3011 | return conn 3012 | } 3013 | 3014 | func (c *concurrentBenchmark) retry(err error) bool { 3015 | s := strings.ToLower(err.Error()) 3016 | return strings.Contains(s, "lock") || strings.Contains(s, "busy") 3017 | } 3018 | 3019 | func (c *concurrentBenchmark) makeReaders(n int, mode string) { 3020 | var wait sync.WaitGroup 3021 | wait.Add(n) 3022 | c.wg.Add(n) 3023 | for i := 0; i < n; i++ { 3024 | switch mode { 3025 | case "sql": 3026 | go func() { 3027 | db := c.makeSQLConn() 3028 | 3029 | defer func() { 3030 | db.Close() 3031 | c.wg.Done() 3032 | }() 3033 | 3034 | wait.Done() 3035 | <-c.start 3036 | 3037 | for i := 1; ; i++ { 3038 | select { 3039 | case <-c.stop: 3040 | return 3041 | default: 3042 | } 3043 | 3044 | recs := atomic.LoadInt32(&c.records) 3045 | id := recs * int32(i) % recs 3046 | rows, err := db.Query("SELECT * FROM foo WHERE id=$1", id) 3047 | if err != nil { 3048 | if c.retry(err) { 3049 | continue 3050 | } 3051 | 3052 | c.b.Fatal(err) 3053 | } 3054 | 3055 | for rows.Next() { 3056 | var id int 3057 | var name string 3058 | err = rows.Scan(&id, &name) 3059 | if err != nil { 3060 | c.b.Fatal(err) 3061 | } 3062 | } 3063 | if err := rows.Close(); err != nil { 3064 | c.b.Fatal(err) 3065 | } 3066 | 3067 | atomic.AddInt32(&c.reads, 1) 3068 | } 3069 | 3070 | }() 3071 | case "drv": 3072 | go func() { 3073 | conn := c.makeDrvConn() 3074 | 3075 | defer func() { 3076 | conn.Close() 3077 | c.wg.Done() 3078 | }() 3079 | 3080 | q := conn.(driver.Queryer) 3081 | wait.Done() 3082 | <-c.start 3083 | 3084 | for i := 1; ; i++ { 3085 | select { 3086 | case <-c.stop: 3087 | return 3088 | default: 3089 | } 3090 | 3091 | recs := atomic.LoadInt32(&c.records) 3092 | id := recs * int32(i) % recs 3093 | rows, err := q.Query("SELECT * FROM foo WHERE id=$1", []driver.Value{int64(id)}) 3094 | if err != nil { 3095 | if c.retry(err) { 3096 | continue 3097 | } 3098 | 3099 | c.b.Fatal(err) 3100 | } 3101 | 3102 | var dest [2]driver.Value 3103 | for { 3104 | if err := rows.Next(dest[:]); err != nil { 3105 | if err != io.EOF { 3106 | c.b.Fatal(err) 3107 | } 3108 | break 3109 | } 3110 | } 3111 | 3112 | if err := rows.Close(); err != nil { 3113 | c.b.Fatal(err) 3114 | } 3115 | 3116 | atomic.AddInt32(&c.reads, 1) 3117 | } 3118 | 3119 | }() 3120 | default: 3121 | panic(todo("")) 3122 | } 3123 | } 3124 | wait.Wait() 3125 | } 3126 | 3127 | func (c *concurrentBenchmark) makeWriters(n int, mode string) { 3128 | var wait sync.WaitGroup 3129 | wait.Add(n) 3130 | c.wg.Add(n) 3131 | for i := 0; i < n; i++ { 3132 | switch mode { 3133 | case "sql": 3134 | go func() { 3135 | db := c.makeSQLConn() 3136 | 3137 | defer func() { 3138 | db.Close() 3139 | c.wg.Done() 3140 | }() 3141 | 3142 | wait.Done() 3143 | <-c.start 3144 | 3145 | for { 3146 | select { 3147 | case <-c.stop: 3148 | return 3149 | default: 3150 | } 3151 | 3152 | if _, err := db.Exec("INSERT INTO FOO(name) VALUES($1)", c.randString(30)); err != nil { 3153 | if c.retry(err) { 3154 | continue 3155 | } 3156 | 3157 | c.b.Fatal(err) 3158 | } 3159 | 3160 | atomic.AddInt32(&c.records, 1) 3161 | atomic.AddInt32(&c.writes, 1) 3162 | } 3163 | 3164 | }() 3165 | case "drv": 3166 | go func() { 3167 | conn := c.makeDrvConn() 3168 | 3169 | defer func() { 3170 | conn.Close() 3171 | c.wg.Done() 3172 | }() 3173 | 3174 | e := conn.(driver.Execer) 3175 | wait.Done() 3176 | <-c.start 3177 | 3178 | for { 3179 | select { 3180 | case <-c.stop: 3181 | return 3182 | default: 3183 | } 3184 | 3185 | if _, err := e.Exec("INSERT INTO FOO(name) VALUES($1)", []driver.Value{c.randString(30)}); err != nil { 3186 | if c.retry(err) { 3187 | continue 3188 | } 3189 | 3190 | c.b.Fatal(err) 3191 | } 3192 | 3193 | atomic.AddInt32(&c.records, 1) 3194 | atomic.AddInt32(&c.writes, 1) 3195 | } 3196 | 3197 | }() 3198 | default: 3199 | panic(todo("")) 3200 | } 3201 | } 3202 | wait.Wait() 3203 | } 3204 | --------------------------------------------------------------------------------