├── .codecov.yml ├── .github ├── FUNDING.yml └── workflows │ ├── cifuzz.yaml │ ├── docker.yaml │ └── go.yaml ├── .gitignore ├── LICENSE ├── README.md ├── _example ├── custom_driver_name │ ├── Makefile │ └── main.go ├── custom_func │ └── main.go ├── fuzz │ └── fuzz_openexec.go ├── hook │ └── hook.go ├── json │ └── json.go ├── limit │ └── limit.go ├── mod_regexp │ ├── Makefile │ ├── extension.go │ └── sqlite3_mod_regexp.c ├── mod_vtable │ ├── Makefile │ ├── extension.go │ ├── picojson.h │ └── sqlite3_mod_vtable.cc ├── simple │ ├── Dockerfile │ └── simple.go ├── trace │ └── main.go ├── vtable │ ├── main.go │ └── vtable.go └── vtable_eponymous_only │ ├── main.go │ └── vtable.go ├── backup.go ├── backup_test.go ├── callback.go ├── callback_test.go ├── convert.go ├── doc.go ├── error.go ├── error_test.go ├── go.mod ├── go.sum ├── sqlite3-binding.c ├── sqlite3-binding.h ├── sqlite3.go ├── sqlite3_context.go ├── sqlite3_func_crypt.go ├── sqlite3_func_crypt_test.go ├── sqlite3_go113_test.go ├── sqlite3_go18.go ├── sqlite3_go18_test.go ├── sqlite3_libsqlite3.go ├── sqlite3_load_extension.go ├── sqlite3_load_extension_omit.go ├── sqlite3_load_extension_test.go ├── sqlite3_opt_allow_uri_authority.go ├── sqlite3_opt_app_armor.go ├── sqlite3_opt_column_metadata.go ├── sqlite3_opt_column_metadata_test.go ├── sqlite3_opt_foreign_keys.go ├── sqlite3_opt_fts3_test.go ├── sqlite3_opt_fts5.go ├── sqlite3_opt_icu.go ├── sqlite3_opt_introspect.go ├── sqlite3_opt_math_functions.go ├── sqlite3_opt_math_functions_test.go ├── sqlite3_opt_os_trace.go ├── sqlite3_opt_preupdate.go ├── sqlite3_opt_preupdate_hook.go ├── sqlite3_opt_preupdate_hook_test.go ├── sqlite3_opt_preupdate_omit.go ├── sqlite3_opt_secure_delete.go ├── sqlite3_opt_secure_delete_fast.go ├── sqlite3_opt_serialize.go ├── sqlite3_opt_serialize_omit.go ├── sqlite3_opt_serialize_test.go ├── sqlite3_opt_stat4.go ├── sqlite3_opt_unlock_notify.c ├── sqlite3_opt_unlock_notify.go ├── sqlite3_opt_unlock_notify_test.go ├── sqlite3_opt_userauth.go ├── sqlite3_opt_userauth_omit.go ├── sqlite3_opt_userauth_test.go ├── sqlite3_opt_vacuum_full.go ├── sqlite3_opt_vacuum_incr.go ├── sqlite3_opt_vtable.go ├── sqlite3_opt_vtable_test.go ├── sqlite3_other.go ├── sqlite3_solaris.go ├── sqlite3_test.go ├── sqlite3_trace.go ├── sqlite3_type.go ├── sqlite3_usleep_windows.go ├── sqlite3_windows.go ├── sqlite3ext.h ├── static_mock.go └── upgrade ├── go.mod ├── go.sum ├── package.go └── upgrade.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: off 4 | patch: off 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: mattn # Replace with a single Patreon username 5 | open_collective: mattn # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | custom: # Replace with a single custom sponsorship URL 9 | -------------------------------------------------------------------------------- /.github/workflows/cifuzz.yaml: -------------------------------------------------------------------------------- 1 | name: CIFuzz 2 | on: [pull_request] 3 | jobs: 4 | Fuzzing: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | sanitizer: [address] 10 | steps: 11 | - name: Build Fuzzers (${{ matrix.sanitizer }}) 12 | uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master 13 | with: 14 | oss-fuzz-project-name: 'go-sqlite3' 15 | dry-run: false 16 | sanitizer: ${{ matrix.sanitizer }} 17 | - name: Run Fuzzers (${{ matrix.sanitizer }}) 18 | uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master 19 | with: 20 | oss-fuzz-project-name: 'go-sqlite3' 21 | fuzz-seconds: 600 22 | dry-run: false 23 | sanitizer: ${{ matrix.sanitizer }} 24 | - name: Upload Crash 25 | uses: actions/upload-artifact@v4 26 | if: failure() 27 | with: 28 | name: ${{ matrix.sanitizer }}-artifacts 29 | path: ./out/artifacts 30 | -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: dockerfile 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - 'v*' 8 | pull_request: 9 | branches: [ master ] 10 | 11 | jobs: 12 | dockerfile: 13 | name: Run Dockerfiles in examples 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Run example - simple 19 | run: | 20 | docker build -t simple -f ./_example/simple/Dockerfile . 21 | docker run simple | grep 99\ こんにちは世界099 22 | -------------------------------------------------------------------------------- /.github/workflows/go.yaml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | test: 8 | name: Test 9 | runs-on: ${{ matrix.os }} 10 | defaults: 11 | run: 12 | shell: bash 13 | 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, macos-latest] 17 | go: ['1.19', '1.20', '1.21'] 18 | fail-fast: false 19 | env: 20 | OS: ${{ matrix.os }} 21 | GO: ${{ matrix.go }} 22 | steps: 23 | - if: startsWith(matrix.os, 'macos') 24 | run: brew update 25 | 26 | - uses: actions/setup-go@v2 27 | with: 28 | go-version: ${{ matrix.go }} 29 | 30 | - name: Get Build Tools 31 | run: | 32 | GO111MODULE=on go install github.com/ory/go-acc@latest 33 | 34 | - name: Add $GOPATH/bin to $PATH 35 | run: | 36 | echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" 37 | 38 | - uses: actions/checkout@v2 39 | 40 | - name: 'Tags: default' 41 | run: go-acc . -- -race -v -tags "" 42 | 43 | - name: 'Tags: libsqlite3' 44 | run: go-acc . -- -race -v -tags "libsqlite3" 45 | 46 | - name: 'Tags: full' 47 | run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_os_trace sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable" 48 | 49 | - name: 'Tags: vacuum' 50 | run: go-acc . -- -race -v -tags "sqlite_vacuum_full" 51 | 52 | - name: Upload coverage to Codecov 53 | uses: codecov/codecov-action@v1 54 | with: 55 | env_vars: OS,GO 56 | file: coverage.txt 57 | 58 | test-windows: 59 | name: Test for Windows 60 | runs-on: windows-latest 61 | defaults: 62 | run: 63 | shell: bash 64 | 65 | strategy: 66 | matrix: 67 | go: ['1.19', '1.20', '1.21'] 68 | fail-fast: false 69 | env: 70 | OS: windows-latest 71 | GO: ${{ matrix.go }} 72 | steps: 73 | - uses: msys2/setup-msys2@v2 74 | with: 75 | update: true 76 | install: mingw-w64-x86_64-toolchain mingw-w64-x86_64-sqlite3 77 | msystem: MINGW64 78 | path-type: inherit 79 | 80 | - uses: actions/setup-go@v2 81 | with: 82 | go-version: ${{ matrix.go }} 83 | 84 | - name: Add $GOPATH/bin to $PATH 85 | run: | 86 | echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" 87 | shell: msys2 {0} 88 | 89 | - uses: actions/checkout@v2 90 | 91 | - name: 'Tags: default' 92 | run: go build -race -v -tags "" 93 | shell: msys2 {0} 94 | 95 | - name: 'Tags: libsqlite3' 96 | run: go build -race -v -tags "libsqlite3" 97 | shell: msys2 {0} 98 | 99 | - name: 'Tags: full' 100 | run: | 101 | echo 'skip this test' 102 | echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable" 103 | shell: msys2 {0} 104 | 105 | - name: 'Tags: vacuum' 106 | run: go build -race -v -tags "sqlite_vacuum_full" 107 | shell: msys2 {0} 108 | 109 | - name: Upload coverage to Codecov 110 | uses: codecov/codecov-action@v2 111 | with: 112 | env_vars: OS,GO 113 | file: coverage.txt 114 | 115 | # based on: github.com/koron-go/_skeleton/.github/workflows/go.yml 116 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.exe 3 | *.dll 4 | *.o 5 | 6 | # VSCode 7 | .vscode 8 | 9 | # Exclude from upgrade 10 | upgrade/*.c 11 | upgrade/*.h 12 | 13 | # Exclude upgrade binary 14 | upgrade/upgrade 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Yasuhiro Matsumoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_example/custom_driver_name/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = custom_driver_name 2 | ifeq ($(OS),Windows_NT) 3 | TARGET := $(TARGET).exe 4 | endif 5 | 6 | all : $(TARGET) 7 | 8 | $(TARGET) : main.go 9 | go build -ldflags="-X 'github.com/mattn/go-sqlite3.driverName=my-sqlite3'" 10 | 11 | clean : 12 | rm -f $(TARGET) 13 | -------------------------------------------------------------------------------- /_example/custom_driver_name/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | 6 | _ "github.com/mattn/go-sqlite3" 7 | ) 8 | 9 | func main() { 10 | for _, driver := range sql.Drivers() { 11 | println(driver) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /_example/custom_func/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "math" 8 | "math/rand" 9 | 10 | sqlite "github.com/mattn/go-sqlite3" 11 | ) 12 | 13 | // Computes x^y 14 | func pow(x, y int64) int64 { 15 | return int64(math.Pow(float64(x), float64(y))) 16 | } 17 | 18 | // Computes the bitwise exclusive-or of all its arguments 19 | func xor(xs ...int64) int64 { 20 | var ret int64 21 | for _, x := range xs { 22 | ret ^= x 23 | } 24 | return ret 25 | } 26 | 27 | // Returns a random number. It's actually deterministic here because 28 | // we don't seed the RNG, but it's an example of a non-pure function 29 | // from SQLite's POV. 30 | func getrand() int64 { 31 | return rand.Int63() 32 | } 33 | 34 | // Computes the standard deviation of a GROUPed BY set of values 35 | type stddev struct { 36 | xs []int64 37 | // Running average calculation 38 | sum int64 39 | n int64 40 | } 41 | 42 | func newStddev() *stddev { return &stddev{} } 43 | 44 | func (s *stddev) Step(x int64) { 45 | s.xs = append(s.xs, x) 46 | s.sum += x 47 | s.n++ 48 | } 49 | 50 | func (s *stddev) Done() float64 { 51 | mean := float64(s.sum) / float64(s.n) 52 | var sqDiff []float64 53 | for _, x := range s.xs { 54 | sqDiff = append(sqDiff, math.Pow(float64(x)-mean, 2)) 55 | } 56 | var dev float64 57 | for _, x := range sqDiff { 58 | dev += x 59 | } 60 | dev /= float64(len(sqDiff)) 61 | return math.Sqrt(dev) 62 | } 63 | 64 | func main() { 65 | sql.Register("sqlite3_custom", &sqlite.SQLiteDriver{ 66 | ConnectHook: func(conn *sqlite.SQLiteConn) error { 67 | if err := conn.RegisterFunc("pow", pow, true); err != nil { 68 | return err 69 | } 70 | if err := conn.RegisterFunc("xor", xor, true); err != nil { 71 | return err 72 | } 73 | if err := conn.RegisterFunc("rand", getrand, false); err != nil { 74 | return err 75 | } 76 | if err := conn.RegisterAggregator("stddev", newStddev, true); err != nil { 77 | return err 78 | } 79 | return nil 80 | }, 81 | }) 82 | 83 | db, err := sql.Open("sqlite3_custom", ":memory:") 84 | if err != nil { 85 | log.Fatal("Failed to open database:", err) 86 | } 87 | defer db.Close() 88 | 89 | var i int64 90 | err = db.QueryRow("SELECT pow(2,3)").Scan(&i) 91 | if err != nil { 92 | log.Fatal("POW query error:", err) 93 | } 94 | fmt.Println("pow(2,3) =", i) // 8 95 | 96 | err = db.QueryRow("SELECT xor(1,2,3,4,5,6)").Scan(&i) 97 | if err != nil { 98 | log.Fatal("XOR query error:", err) 99 | } 100 | fmt.Println("xor(1,2,3,4,5) =", i) // 7 101 | 102 | err = db.QueryRow("SELECT rand()").Scan(&i) 103 | if err != nil { 104 | log.Fatal("RAND query error:", err) 105 | } 106 | fmt.Println("rand() =", i) // pseudorandom 107 | 108 | _, err = db.Exec("create table foo (department integer, profits integer)") 109 | if err != nil { 110 | log.Fatal("Failed to create table:", err) 111 | } 112 | _, err = db.Exec("insert into foo values (1, 10), (1, 20), (1, 45), (2, 42), (2, 115)") 113 | if err != nil { 114 | log.Fatal("Failed to insert records:", err) 115 | } 116 | 117 | rows, err := db.Query("select department, stddev(profits) from foo group by department") 118 | if err != nil { 119 | log.Fatal("STDDEV query error:", err) 120 | } 121 | defer rows.Close() 122 | for rows.Next() { 123 | var dept int64 124 | var dev float64 125 | if err := rows.Scan(&dept, &dev); err != nil { 126 | log.Fatal(err) 127 | } 128 | fmt.Printf("dept=%d stddev=%f\n", dept, dev) 129 | } 130 | if err := rows.Err(); err != nil { 131 | log.Fatal(err) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /_example/fuzz/fuzz_openexec.go: -------------------------------------------------------------------------------- 1 | package sqlite3_fuzz 2 | 3 | import ( 4 | "bytes" 5 | "database/sql" 6 | "io/ioutil" 7 | 8 | _ "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | func FuzzOpenExec(data []byte) int { 12 | sep := bytes.IndexByte(data, 0) 13 | if sep <= 0 { 14 | return 0 15 | } 16 | err := ioutil.WriteFile("/tmp/fuzz.db", data[sep+1:], 0644) 17 | if err != nil { 18 | return 0 19 | } 20 | db, err := sql.Open("sqlite3", "/tmp/fuzz.db") 21 | if err != nil { 22 | return 0 23 | } 24 | defer db.Close() 25 | _, err = db.Exec(string(data[:sep-1])) 26 | if err != nil { 27 | return 0 28 | } 29 | return 1 30 | } 31 | -------------------------------------------------------------------------------- /_example/hook/hook.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "log" 6 | "os" 7 | 8 | "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | func main() { 12 | sqlite3conn := []*sqlite3.SQLiteConn{} 13 | sql.Register("sqlite3_with_hook_example", 14 | &sqlite3.SQLiteDriver{ 15 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 16 | sqlite3conn = append(sqlite3conn, conn) 17 | conn.RegisterUpdateHook(func(op int, db string, table string, rowid int64) { 18 | switch op { 19 | case sqlite3.SQLITE_INSERT: 20 | log.Println("Notified of insert on db", db, "table", table, "rowid", rowid) 21 | } 22 | }) 23 | return nil 24 | }, 25 | }) 26 | os.Remove("./foo.db") 27 | os.Remove("./bar.db") 28 | 29 | srcDb, err := sql.Open("sqlite3_with_hook_example", "./foo.db") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | defer srcDb.Close() 34 | srcDb.Ping() 35 | 36 | _, err = srcDb.Exec("create table foo(id int, value text)") 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | _, err = srcDb.Exec("insert into foo values(1, 'foo')") 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | _, err = srcDb.Exec("insert into foo values(2, 'bar')") 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | _, err = srcDb.Query("select * from foo") 49 | if err != nil { 50 | log.Fatal(err) 51 | } 52 | destDb, err := sql.Open("sqlite3_with_hook_example", "./bar.db") 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | defer destDb.Close() 57 | destDb.Ping() 58 | 59 | bk, err := sqlite3conn[1].Backup("main", sqlite3conn[0], "main") 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | 64 | _, err = bk.Step(-1) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | _, err = destDb.Query("select * from foo") 69 | if err != nil { 70 | log.Fatal(err) 71 | } 72 | _, err = destDb.Exec("insert into foo values(3, 'bar')") 73 | if err != nil { 74 | log.Fatal(err) 75 | } 76 | 77 | bk.Finish() 78 | } 79 | -------------------------------------------------------------------------------- /_example/json/json.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "database/sql/driver" 6 | "encoding/json" 7 | "fmt" 8 | _ "github.com/mattn/go-sqlite3" 9 | "log" 10 | "os" 11 | ) 12 | 13 | type Tag struct { 14 | Name string `json:"name"` 15 | Country string `json:"country"` 16 | } 17 | 18 | func (t *Tag) Scan(value interface{}) error { 19 | return json.Unmarshal([]byte(value.(string)), t) 20 | } 21 | 22 | func (t *Tag) Value() (driver.Value, error) { 23 | b, err := json.Marshal(t) 24 | return string(b), err 25 | } 26 | 27 | func main() { 28 | os.Remove("./foo.db") 29 | 30 | db, err := sql.Open("sqlite3", "./foo.db") 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | defer db.Close() 35 | 36 | _, err = db.Exec(`create table foo (tag jsonb)`) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | 41 | stmt, err := db.Prepare("insert into foo(tag) values(?)") 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | defer stmt.Close() 46 | _, err = stmt.Exec(`{"name": "mattn", "country": "japan"}`) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | _, err = stmt.Exec(`{"name": "michael", "country": "usa"}`) 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | 55 | var country string 56 | err = db.QueryRow("select tag->>'country' from foo where tag->>'name' = 'mattn'").Scan(&country) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | fmt.Println(country) 61 | 62 | var tag Tag 63 | err = db.QueryRow("select tag from foo where tag->>'name' = 'mattn'").Scan(&tag) 64 | if err != nil { 65 | log.Fatal(err) 66 | } 67 | 68 | fmt.Println(tag.Name) 69 | 70 | tag.Country = "日本" 71 | _, err = db.Exec(`update foo set tag = ? where tag->>'name' == 'mattn'`, &tag) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | 76 | err = db.QueryRow("select tag->>'country' from foo where tag->>'name' = 'mattn'").Scan(&country) 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | fmt.Println(country) 81 | } 82 | -------------------------------------------------------------------------------- /_example/limit/limit.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "os" 8 | "strings" 9 | 10 | "github.com/mattn/go-sqlite3" 11 | ) 12 | 13 | func createBulkInsertQuery(n int, start int) (query string, args []any) { 14 | values := make([]string, n) 15 | args = make([]any, n*2) 16 | pos := 0 17 | for i := 0; i < n; i++ { 18 | values[i] = "(?, ?)" 19 | args[pos] = start + i 20 | args[pos+1] = fmt.Sprintf("こんにちは世界%03d", i) 21 | pos += 2 22 | } 23 | query = fmt.Sprintf( 24 | "insert into foo(id, name) values %s", 25 | strings.Join(values, ", "), 26 | ) 27 | return 28 | } 29 | 30 | func bulkInsert(db *sql.DB, query string, args []any) (err error) { 31 | stmt, err := db.Prepare(query) 32 | if err != nil { 33 | return 34 | } 35 | 36 | _, err = stmt.Exec(args...) 37 | if err != nil { 38 | return 39 | } 40 | 41 | return 42 | } 43 | 44 | func main() { 45 | var sqlite3conn *sqlite3.SQLiteConn 46 | sql.Register("sqlite3_with_limit", &sqlite3.SQLiteDriver{ 47 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 48 | sqlite3conn = conn 49 | return nil 50 | }, 51 | }) 52 | 53 | os.Remove("./foo.db") 54 | db, err := sql.Open("sqlite3_with_limit", "./foo.db") 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | defer db.Close() 59 | 60 | sqlStmt := ` 61 | create table foo (id integer not null primary key, name text); 62 | delete from foo; 63 | ` 64 | _, err = db.Exec(sqlStmt) 65 | if err != nil { 66 | log.Printf("%q: %s\n", err, sqlStmt) 67 | return 68 | } 69 | 70 | if sqlite3conn == nil { 71 | log.Fatal("not set sqlite3 connection") 72 | } 73 | 74 | limitVariableNumber := sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER) 75 | log.Printf("default SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) 76 | 77 | num := 400 78 | query, args := createBulkInsertQuery(num, 0) 79 | err = bulkInsert(db, query, args) 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | 84 | smallLimitVariableNumber := 100 85 | sqlite3conn.SetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER, smallLimitVariableNumber) 86 | 87 | limitVariableNumber = sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER) 88 | log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) 89 | 90 | query, args = createBulkInsertQuery(num, num) 91 | err = bulkInsert(db, query, args) 92 | if err != nil { 93 | if err != nil { 94 | log.Printf("expect failed since SQLITE_LIMIT_VARIABLE_NUMBER is too small: %v", err) 95 | } 96 | } 97 | 98 | bigLimitVariableNumber := 999999 99 | sqlite3conn.SetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER, bigLimitVariableNumber) 100 | limitVariableNumber = sqlite3conn.GetLimit(sqlite3.SQLITE_LIMIT_VARIABLE_NUMBER) 101 | log.Printf("set SQLITE_LIMIT_VARIABLE_NUMBER: %d", bigLimitVariableNumber) 102 | log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) 103 | 104 | query, args = createBulkInsertQuery(500, num+num) 105 | err = bulkInsert(db, query, args) 106 | if err != nil { 107 | if err != nil { 108 | log.Fatal(err) 109 | } 110 | } 111 | 112 | log.Println("no error if SQLITE_LIMIT_VARIABLE_NUMBER > 999") 113 | } 114 | -------------------------------------------------------------------------------- /_example/mod_regexp/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | EXE=extension.exe 3 | LIB_EXT=dll 4 | RM=cmd /c del 5 | LDFLAG= 6 | else 7 | EXE=extension 8 | ifeq ($(shell uname -s),Darwin) 9 | LIB_EXT=dylib 10 | else 11 | LIB_EXT=so 12 | endif 13 | RM=rm -f 14 | LDFLAG=-fPIC 15 | endif 16 | LIB=sqlite3_mod_regexp.$(LIB_EXT) 17 | 18 | all : $(EXE) $(LIB) 19 | 20 | $(EXE) : extension.go 21 | go build $< 22 | 23 | $(LIB) : sqlite3_mod_regexp.c 24 | gcc $(LDFLAG) -shared -o $@ $< -lsqlite3 -lpcre 25 | 26 | clean : 27 | @-$(RM) $(EXE) $(LIB) 28 | -------------------------------------------------------------------------------- /_example/mod_regexp/extension.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "github.com/mattn/go-sqlite3" 7 | "log" 8 | ) 9 | 10 | func main() { 11 | sql.Register("sqlite3_with_extensions", 12 | &sqlite3.SQLiteDriver{ 13 | Extensions: []string{ 14 | "sqlite3_mod_regexp", 15 | }, 16 | }) 17 | 18 | db, err := sql.Open("sqlite3_with_extensions", ":memory:") 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer db.Close() 23 | 24 | // Force db to make a new connection in pool 25 | // by putting the original in a transaction 26 | tx, err := db.Begin() 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | defer tx.Commit() 31 | 32 | // New connection works (hopefully!) 33 | rows, err := db.Query("select 'hello world' where 'hello world' regexp '^hello.*d$'") 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | defer rows.Close() 38 | for rows.Next() { 39 | var helloworld string 40 | rows.Scan(&helloworld) 41 | fmt.Println(helloworld) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /_example/mod_regexp/sqlite3_mod_regexp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | SQLITE_EXTENSION_INIT1 7 | static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) { 8 | if (argc >= 2) { 9 | const char *target = (const char *)sqlite3_value_text(argv[1]); 10 | const char *pattern = (const char *)sqlite3_value_text(argv[0]); 11 | const char* errstr = NULL; 12 | int erroff = 0; 13 | int vec[500]; 14 | int n, rc; 15 | pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); 16 | if (!re) { 17 | sqlite3_result_error(context, errstr, 0); 18 | return; 19 | } 20 | rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); 21 | if (rc <= 0) { 22 | sqlite3_result_int(context, 0); 23 | return; 24 | } 25 | sqlite3_result_int(context, 1); 26 | } 27 | } 28 | 29 | #ifdef _WIN32 30 | __declspec(dllexport) 31 | #endif 32 | int sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) { 33 | SQLITE_EXTENSION_INIT2(api); 34 | return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, (void*)db, regexp_func, NULL, NULL); 35 | } 36 | -------------------------------------------------------------------------------- /_example/mod_vtable/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | EXE=extension.exe 3 | LIB_EXT=dll 4 | RM=cmd /c del 5 | LIBCURL=-lcurldll 6 | LDFLAG= 7 | else 8 | EXE=extension 9 | ifeq ($(shell uname -s),Darwin) 10 | LIB_EXT=dylib 11 | else 12 | LIB_EXT=so 13 | endif 14 | RM=rm -f 15 | LDFLAG=-fPIC 16 | LIBCURL=-lcurl 17 | endif 18 | LIB=sqlite3_mod_vtable.$(LIB_EXT) 19 | 20 | all : $(EXE) $(LIB) 21 | 22 | $(EXE) : extension.go 23 | go build $< 24 | 25 | $(LIB) : sqlite3_mod_vtable.cc 26 | g++ $(LDFLAG) -shared -o $@ $< -lsqlite3 $(LIBCURL) 27 | 28 | clean : 29 | @-$(RM) $(EXE) $(LIB) 30 | -------------------------------------------------------------------------------- /_example/mod_vtable/extension.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | func main() { 12 | sql.Register("sqlite3_with_extensions", 13 | &sqlite3.SQLiteDriver{ 14 | Extensions: []string{ 15 | "sqlite3_mod_vtable", 16 | }, 17 | }) 18 | 19 | db, err := sql.Open("sqlite3_with_extensions", ":memory:") 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | defer db.Close() 24 | 25 | db.Exec("create virtual table repo using github(id, full_name, description, html_url)") 26 | 27 | rows, err := db.Query("select id, full_name, description, html_url from repo") 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | defer rows.Close() 32 | for rows.Next() { 33 | var id, fullName, description, htmlURL string 34 | rows.Scan(&id, &fullName, &description, &htmlURL) 35 | fmt.Printf("%s: %s\n\t%s\n\t%s\n\n", id, fullName, description, htmlURL) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /_example/mod_vtable/sqlite3_mod_vtable.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "picojson.h" 7 | 8 | #ifdef _WIN32 9 | # define EXPORT __declspec(dllexport) 10 | #else 11 | # define EXPORT 12 | #endif 13 | 14 | SQLITE_EXTENSION_INIT1; 15 | 16 | typedef struct { 17 | char* data; // response data from server 18 | size_t size; // response size of data 19 | } MEMFILE; 20 | 21 | MEMFILE* 22 | memfopen() { 23 | MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE)); 24 | if (mf) { 25 | mf->data = NULL; 26 | mf->size = 0; 27 | } 28 | return mf; 29 | } 30 | 31 | void 32 | memfclose(MEMFILE* mf) { 33 | if (mf->data) free(mf->data); 34 | free(mf); 35 | } 36 | 37 | size_t 38 | memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) { 39 | MEMFILE* mf = (MEMFILE*) stream; 40 | int block = size * nmemb; 41 | if (!mf) return block; // through 42 | if (!mf->data) 43 | mf->data = (char*) malloc(block); 44 | else 45 | mf->data = (char*) realloc(mf->data, mf->size + block); 46 | if (mf->data) { 47 | memcpy(mf->data + mf->size, ptr, block); 48 | mf->size += block; 49 | } 50 | return block; 51 | } 52 | 53 | char* 54 | memfstrdup(MEMFILE* mf) { 55 | char* buf; 56 | if (mf->size == 0) return NULL; 57 | buf = (char*) malloc(mf->size + 1); 58 | memcpy(buf, mf->data, mf->size); 59 | buf[mf->size] = 0; 60 | return buf; 61 | } 62 | 63 | static int 64 | my_connect(sqlite3 *db, void *pAux, int argc, const char * const *argv, sqlite3_vtab **ppVTab, char **c) { 65 | std::stringstream ss; 66 | ss << "CREATE TABLE " << argv[0] 67 | << "(id int, full_name text, description text, html_url text)"; 68 | int rc = sqlite3_declare_vtab(db, ss.str().c_str()); 69 | *ppVTab = (sqlite3_vtab *) sqlite3_malloc(sizeof(sqlite3_vtab)); 70 | memset(*ppVTab, 0, sizeof(sqlite3_vtab)); 71 | return rc; 72 | } 73 | 74 | static int 75 | my_create(sqlite3 *db, void *pAux, int argc, const char * const * argv, sqlite3_vtab **ppVTab, char **c) { 76 | return my_connect(db, pAux, argc, argv, ppVTab, c); 77 | } 78 | 79 | static int my_disconnect(sqlite3_vtab *pVTab) { 80 | sqlite3_free(pVTab); 81 | return SQLITE_OK; 82 | } 83 | 84 | static int 85 | my_destroy(sqlite3_vtab *pVTab) { 86 | sqlite3_free(pVTab); 87 | return SQLITE_OK; 88 | } 89 | 90 | typedef struct { 91 | sqlite3_vtab_cursor base; 92 | int index; 93 | picojson::value* rows; 94 | } cursor; 95 | 96 | static int 97 | my_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { 98 | MEMFILE* mf; 99 | CURL* curl; 100 | char* json; 101 | CURLcode res = CURLE_OK; 102 | char error[CURL_ERROR_SIZE] = {0}; 103 | char* cert_file = getenv("SSL_CERT_FILE"); 104 | 105 | mf = memfopen(); 106 | curl = curl_easy_init(); 107 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); 108 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); 109 | curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.29.0"); 110 | curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repositories"); 111 | if (cert_file) 112 | curl_easy_setopt(curl, CURLOPT_CAINFO, cert_file); 113 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); 114 | curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error); 115 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf); 116 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); 117 | res = curl_easy_perform(curl); 118 | curl_easy_cleanup(curl); 119 | if (res != CURLE_OK) { 120 | std::cerr << error << std::endl; 121 | return SQLITE_FAIL; 122 | } 123 | 124 | picojson::value* v = new picojson::value; 125 | std::string err; 126 | picojson::parse(*v, mf->data, mf->data + mf->size, &err); 127 | memfclose(mf); 128 | 129 | if (!err.empty()) { 130 | delete v; 131 | std::cerr << err << std::endl; 132 | return SQLITE_FAIL; 133 | } 134 | 135 | cursor *c = (cursor *)sqlite3_malloc(sizeof(cursor)); 136 | c->rows = v; 137 | c->index = 0; 138 | *ppCursor = &c->base; 139 | return SQLITE_OK; 140 | } 141 | 142 | static int 143 | my_close(cursor *c) { 144 | delete c->rows; 145 | sqlite3_free(c); 146 | return SQLITE_OK; 147 | } 148 | 149 | static int 150 | my_filter(cursor *c, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { 151 | c->index = 0; 152 | return SQLITE_OK; 153 | } 154 | 155 | static int 156 | my_next(cursor *c) { 157 | c->index++; 158 | return SQLITE_OK; 159 | } 160 | 161 | static int 162 | my_eof(cursor *c) { 163 | return c->index >= c->rows->get().size() ? 1 : 0; 164 | } 165 | 166 | static int 167 | my_column(cursor *c, sqlite3_context *ctxt, int i) { 168 | picojson::value v = c->rows->get()[c->index]; 169 | picojson::object row = v.get(); 170 | const char* p = NULL; 171 | switch (i) { 172 | case 0: 173 | p = row["id"].to_str().c_str(); 174 | break; 175 | case 1: 176 | p = row["full_name"].to_str().c_str(); 177 | break; 178 | case 2: 179 | p = row["description"].to_str().c_str(); 180 | break; 181 | case 3: 182 | p = row["html_url"].to_str().c_str(); 183 | break; 184 | } 185 | sqlite3_result_text(ctxt, strdup(p), strlen(p), free); 186 | return SQLITE_OK; 187 | } 188 | 189 | static int 190 | my_rowid(cursor *c, sqlite3_int64 *pRowid) { 191 | *pRowid = c->index; 192 | return SQLITE_OK; 193 | } 194 | 195 | static int 196 | my_bestindex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) { 197 | return SQLITE_OK; 198 | } 199 | 200 | static const sqlite3_module module = { 201 | 0, 202 | my_create, 203 | my_connect, 204 | my_bestindex, 205 | my_disconnect, 206 | my_destroy, 207 | my_open, 208 | (int (*)(sqlite3_vtab_cursor *)) my_close, 209 | (int (*)(sqlite3_vtab_cursor *, int, char const *, int, sqlite3_value **)) my_filter, 210 | (int (*)(sqlite3_vtab_cursor *)) my_next, 211 | (int (*)(sqlite3_vtab_cursor *)) my_eof, 212 | (int (*)(sqlite3_vtab_cursor *, sqlite3_context *, int)) my_column, 213 | (int (*)(sqlite3_vtab_cursor *, sqlite3_int64 *)) my_rowid, 214 | NULL, // my_update 215 | NULL, // my_begin 216 | NULL, // my_sync 217 | NULL, // my_commit 218 | NULL, // my_rollback 219 | NULL, // my_findfunction 220 | NULL, // my_rename 221 | }; 222 | 223 | static void 224 | destructor(void *arg) { 225 | return; 226 | } 227 | 228 | 229 | extern "C" { 230 | 231 | EXPORT int 232 | sqlite3_extension_init(sqlite3 *db, char **errmsg, const sqlite3_api_routines *api) { 233 | SQLITE_EXTENSION_INIT2(api); 234 | sqlite3_create_module_v2(db, "github", &module, NULL, destructor); 235 | return 0; 236 | } 237 | 238 | } 239 | -------------------------------------------------------------------------------- /_example/simple/Dockerfile: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # Multi-stage Dockerfile Example 3 | # ============================================================================= 4 | # This is a simple Dockerfile that will build an image of scratch-base image. 5 | # Usage: 6 | # docker build -t simple:local . && docker run --rm simple:local 7 | # ============================================================================= 8 | 9 | # ----------------------------------------------------------------------------- 10 | # Build Stage 11 | # ----------------------------------------------------------------------------- 12 | FROM golang:alpine3.18 AS build 13 | 14 | # Important: 15 | # Because this is a CGO enabled package, you are required to set it as 1. 16 | ENV CGO_ENABLED=1 17 | 18 | RUN apk add --no-cache \ 19 | # Important: required for go-sqlite3 20 | gcc \ 21 | # Required for Alpine 22 | musl-dev 23 | 24 | WORKDIR /workspace 25 | 26 | COPY . /workspace/ 27 | 28 | RUN \ 29 | cd _example/simple && \ 30 | go mod init github.com/mattn/sample && \ 31 | go mod edit -replace=github.com/mattn/go-sqlite3=../.. && \ 32 | go mod tidy && \ 33 | go install -ldflags='-s -w -extldflags "-static"' ./simple.go 34 | 35 | RUN \ 36 | # Smoke test 37 | set -o pipefail; \ 38 | /go/bin/simple | grep 99\ こんにちは世界099 39 | 40 | # ----------------------------------------------------------------------------- 41 | # Main Stage 42 | # ----------------------------------------------------------------------------- 43 | FROM scratch 44 | 45 | COPY --from=build /go/bin/simple /usr/local/bin/simple 46 | 47 | ENTRYPOINT [ "/usr/local/bin/simple" ] 48 | -------------------------------------------------------------------------------- /_example/simple/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | _ "github.com/mattn/go-sqlite3" 7 | "log" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | os.Remove("./foo.db") 13 | 14 | db, err := sql.Open("sqlite3", "./foo.db") 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | defer db.Close() 19 | 20 | sqlStmt := ` 21 | create table foo (id integer not null primary key, name text); 22 | delete from foo; 23 | ` 24 | _, err = db.Exec(sqlStmt) 25 | if err != nil { 26 | log.Printf("%q: %s\n", err, sqlStmt) 27 | return 28 | } 29 | 30 | tx, err := db.Begin() 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)") 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | defer stmt.Close() 39 | for i := 0; i < 100; i++ { 40 | _, err = stmt.Exec(i, fmt.Sprintf("こんにちは世界%03d", i)) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | } 45 | err = tx.Commit() 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | rows, err := db.Query("select id, name from foo") 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | defer rows.Close() 55 | for rows.Next() { 56 | var id int 57 | var name string 58 | err = rows.Scan(&id, &name) 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | fmt.Println(id, name) 63 | } 64 | err = rows.Err() 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | 69 | stmt, err = db.Prepare("select name from foo where id = ?") 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | defer stmt.Close() 74 | var name string 75 | err = stmt.QueryRow("3").Scan(&name) 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | fmt.Println(name) 80 | 81 | _, err = db.Exec("delete from foo") 82 | if err != nil { 83 | log.Fatal(err) 84 | } 85 | 86 | _, err = db.Exec("insert into foo(id, name) values(1, 'foo'), (2, 'bar'), (3, 'baz')") 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | 91 | rows, err = db.Query("select id, name from foo") 92 | if err != nil { 93 | log.Fatal(err) 94 | } 95 | defer rows.Close() 96 | for rows.Next() { 97 | var id int 98 | var name string 99 | err = rows.Scan(&id, &name) 100 | if err != nil { 101 | log.Fatal(err) 102 | } 103 | fmt.Println(id, name) 104 | } 105 | err = rows.Err() 106 | if err != nil { 107 | log.Fatal(err) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /_example/trace/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | sqlite3 "github.com/mattn/go-sqlite3" 10 | ) 11 | 12 | func traceCallback(info sqlite3.TraceInfo) int { 13 | // Not very readable but may be useful; uncomment next line in case of doubt: 14 | //fmt.Printf("Trace: %#v\n", info) 15 | 16 | var dbErrText string 17 | if info.DBError.Code != 0 || info.DBError.ExtendedCode != 0 { 18 | dbErrText = fmt.Sprintf("; DB error: %#v", info.DBError) 19 | } else { 20 | dbErrText = "." 21 | } 22 | 23 | // Show the Statement-or-Trigger text in curly braces ('{', '}') 24 | // since from the *paired* ASCII characters they are 25 | // the least used in SQL syntax, therefore better visual delimiters. 26 | // Maybe show 'ExpandedSQL' the same way as 'StmtOrTrigger'. 27 | // 28 | // A known use of curly braces (outside strings) is 29 | // for ODBC escape sequences. Not likely to appear here. 30 | // 31 | // Template languages, etc. don't matter, we should see their *result* 32 | // at *this* level. 33 | // Strange curly braces in SQL code that reached the database driver 34 | // suggest that there is a bug in the application. 35 | // The braces are likely to be either template syntax or 36 | // a programming language's string interpolation syntax. 37 | 38 | var expandedText string 39 | if info.ExpandedSQL != "" { 40 | if info.ExpandedSQL == info.StmtOrTrigger { 41 | expandedText = " = exp" 42 | } else { 43 | expandedText = fmt.Sprintf(" expanded {%q}", info.ExpandedSQL) 44 | } 45 | } else { 46 | expandedText = "" 47 | } 48 | 49 | // SQLite docs as of September 6, 2016: Tracing and Profiling Functions 50 | // https://www.sqlite.org/c3ref/profile.html 51 | // 52 | // The profile callback time is in units of nanoseconds, however 53 | // the current implementation is only capable of millisecond resolution 54 | // so the six least significant digits in the time are meaningless. 55 | // Future versions of SQLite might provide greater resolution on the profiler callback. 56 | 57 | var runTimeText string 58 | if info.RunTimeNanosec == 0 { 59 | if info.EventCode == sqlite3.TraceProfile { 60 | //runTimeText = "; no time" // seems confusing 61 | runTimeText = "; time 0" // no measurement unit 62 | } else { 63 | //runTimeText = "; no time" // seems useless and confusing 64 | } 65 | } else { 66 | const nanosPerMillisec = 1000000 67 | if info.RunTimeNanosec%nanosPerMillisec == 0 { 68 | runTimeText = fmt.Sprintf("; time %d ms", info.RunTimeNanosec/nanosPerMillisec) 69 | } else { 70 | // unexpected: better than millisecond resolution 71 | runTimeText = fmt.Sprintf("; time %d ns!!!", info.RunTimeNanosec) 72 | } 73 | } 74 | 75 | var modeText string 76 | if info.AutoCommit { 77 | modeText = "-AC-" 78 | } else { 79 | modeText = "+Tx+" 80 | } 81 | 82 | fmt.Printf("Trace: ev %d %s conn 0x%x, stmt 0x%x {%q}%s%s%s\n", 83 | info.EventCode, modeText, info.ConnHandle, info.StmtHandle, 84 | info.StmtOrTrigger, expandedText, 85 | runTimeText, 86 | dbErrText) 87 | return 0 88 | } 89 | 90 | func main() { 91 | eventMask := sqlite3.TraceStmt | sqlite3.TraceProfile | sqlite3.TraceRow | sqlite3.TraceClose 92 | 93 | sql.Register("sqlite3_tracing", 94 | &sqlite3.SQLiteDriver{ 95 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 96 | err := conn.SetTrace(&sqlite3.TraceConfig{ 97 | Callback: traceCallback, 98 | EventMask: eventMask, 99 | WantExpandedSQL: true, 100 | }) 101 | return err 102 | }, 103 | }) 104 | 105 | os.Exit(dbMain()) 106 | } 107 | 108 | // Harder to do DB work in main(). 109 | // It's better with a separate function because 110 | // 'defer' and 'os.Exit' don't go well together. 111 | // 112 | // DO NOT use 'log.Fatal...' below: remember that it's equivalent to 113 | // Print() followed by a call to os.Exit(1) --- and 114 | // we want to avoid Exit() so 'defer' can do cleanup. 115 | // Use 'log.Panic...' instead. 116 | 117 | func dbMain() int { 118 | db, err := sql.Open("sqlite3_tracing", ":memory:") 119 | if err != nil { 120 | fmt.Printf("Failed to open database: %#+v\n", err) 121 | return 1 122 | } 123 | defer db.Close() 124 | 125 | err = db.Ping() 126 | if err != nil { 127 | log.Panic(err) 128 | } 129 | 130 | dbSetup(db) 131 | 132 | dbDoInsert(db) 133 | dbDoInsertPrepared(db) 134 | dbDoSelect(db) 135 | dbDoSelectPrepared(db) 136 | 137 | return 0 138 | } 139 | 140 | // 'DDL' stands for "Data Definition Language": 141 | 142 | // Note: "INTEGER PRIMARY KEY NOT NULL AUTOINCREMENT" causes the error 143 | // 'near "AUTOINCREMENT": syntax error'; without "NOT NULL" it works. 144 | const tableDDL = `CREATE TABLE t1 ( 145 | id INTEGER PRIMARY KEY AUTOINCREMENT, 146 | note VARCHAR NOT NULL 147 | )` 148 | 149 | // 'DML' stands for "Data Manipulation Language": 150 | 151 | const insertDML = "INSERT INTO t1 (note) VALUES (?)" 152 | const selectDML = "SELECT id, note FROM t1 WHERE note LIKE ?" 153 | 154 | const textPrefix = "bla-1234567890-" 155 | const noteTextPattern = "%Prep%" 156 | 157 | const nGenRows = 4 // Number of Rows to Generate (for *each* approach tested) 158 | 159 | func dbSetup(db *sql.DB) { 160 | var err error 161 | 162 | _, err = db.Exec("DROP TABLE IF EXISTS t1") 163 | if err != nil { 164 | log.Panic(err) 165 | } 166 | _, err = db.Exec(tableDDL) 167 | if err != nil { 168 | log.Panic(err) 169 | } 170 | } 171 | 172 | func dbDoInsert(db *sql.DB) { 173 | const Descr = "DB-Exec" 174 | for i := 0; i < nGenRows; i++ { 175 | result, err := db.Exec(insertDML, textPrefix+Descr) 176 | if err != nil { 177 | log.Panic(err) 178 | } 179 | 180 | resultDoCheck(result, Descr, i) 181 | } 182 | } 183 | 184 | func dbDoInsertPrepared(db *sql.DB) { 185 | const Descr = "DB-Prepare" 186 | 187 | stmt, err := db.Prepare(insertDML) 188 | if err != nil { 189 | log.Panic(err) 190 | } 191 | defer stmt.Close() 192 | 193 | for i := 0; i < nGenRows; i++ { 194 | result, err := stmt.Exec(textPrefix + Descr) 195 | if err != nil { 196 | log.Panic(err) 197 | } 198 | 199 | resultDoCheck(result, Descr, i) 200 | } 201 | } 202 | 203 | func resultDoCheck(result sql.Result, callerDescr string, callIndex int) { 204 | lastID, err := result.LastInsertId() 205 | if err != nil { 206 | log.Panic(err) 207 | } 208 | nAffected, err := result.RowsAffected() 209 | if err != nil { 210 | log.Panic(err) 211 | } 212 | 213 | log.Printf("Exec result for %s (%d): ID = %d, affected = %d\n", callerDescr, callIndex, lastID, nAffected) 214 | } 215 | 216 | func dbDoSelect(db *sql.DB) { 217 | const Descr = "DB-Query" 218 | 219 | rows, err := db.Query(selectDML, noteTextPattern) 220 | if err != nil { 221 | log.Panic(err) 222 | } 223 | defer rows.Close() 224 | 225 | rowsDoFetch(rows, Descr) 226 | } 227 | 228 | func dbDoSelectPrepared(db *sql.DB) { 229 | const Descr = "DB-Prepare" 230 | 231 | stmt, err := db.Prepare(selectDML) 232 | if err != nil { 233 | log.Panic(err) 234 | } 235 | defer stmt.Close() 236 | 237 | rows, err := stmt.Query(noteTextPattern) 238 | if err != nil { 239 | log.Panic(err) 240 | } 241 | defer rows.Close() 242 | 243 | rowsDoFetch(rows, Descr) 244 | } 245 | 246 | func rowsDoFetch(rows *sql.Rows, callerDescr string) { 247 | var nRows int 248 | var id int64 249 | var note string 250 | 251 | for rows.Next() { 252 | err := rows.Scan(&id, ¬e) 253 | if err != nil { 254 | log.Panic(err) 255 | } 256 | log.Printf("Row for %s (%d): id=%d, note=%q\n", 257 | callerDescr, nRows, id, note) 258 | nRows++ 259 | } 260 | if err := rows.Err(); err != nil { 261 | log.Panic(err) 262 | } 263 | log.Printf("Total %d rows for %s.\n", nRows, callerDescr) 264 | } 265 | -------------------------------------------------------------------------------- /_example/vtable/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | func main() { 12 | sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{ 13 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 14 | return conn.CreateModule("github", &githubModule{}) 15 | }, 16 | }) 17 | db, err := sql.Open("sqlite3_with_extensions", ":memory:") 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | defer db.Close() 22 | 23 | _, err = db.Exec("create virtual table repo using github(id, full_name, description, html_url)") 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | rows, err := db.Query("select id, full_name, description, html_url from repo") 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | defer rows.Close() 33 | for rows.Next() { 34 | var id, fullName, description, htmlURL string 35 | rows.Scan(&id, &fullName, &description, &htmlURL) 36 | fmt.Printf("%s: %s\n\t%s\n\t%s\n\n", id, fullName, description, htmlURL) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /_example/vtable/vtable.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | 9 | "github.com/mattn/go-sqlite3" 10 | ) 11 | 12 | type githubRepo struct { 13 | ID int `json:"id"` 14 | FullName string `json:"full_name"` 15 | Description string `json:"description"` 16 | HTMLURL string `json:"html_url"` 17 | } 18 | 19 | type githubModule struct { 20 | } 21 | 22 | func (m *githubModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) { 23 | err := c.DeclareVTab(fmt.Sprintf(` 24 | CREATE TABLE %s ( 25 | id INT, 26 | full_name TEXT, 27 | description TEXT, 28 | html_url TEXT 29 | )`, args[0])) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return &ghRepoTable{}, nil 34 | } 35 | 36 | func (m *githubModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) { 37 | return m.Create(c, args) 38 | } 39 | 40 | func (m *githubModule) DestroyModule() {} 41 | 42 | type ghRepoTable struct { 43 | repos []githubRepo 44 | } 45 | 46 | func (v *ghRepoTable) Open() (sqlite3.VTabCursor, error) { 47 | resp, err := http.Get("https://api.github.com/repositories") 48 | if err != nil { 49 | return nil, err 50 | } 51 | defer resp.Body.Close() 52 | 53 | body, err := ioutil.ReadAll(resp.Body) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | var repos []githubRepo 59 | if err := json.Unmarshal(body, &repos); err != nil { 60 | return nil, err 61 | } 62 | return &ghRepoCursor{0, repos}, nil 63 | } 64 | 65 | func (v *ghRepoTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) { 66 | used := make([]bool, len(csts)) 67 | return &sqlite3.IndexResult{ 68 | IdxNum: 0, 69 | IdxStr: "default", 70 | Used: used, 71 | }, nil 72 | } 73 | 74 | func (v *ghRepoTable) Disconnect() error { return nil } 75 | func (v *ghRepoTable) Destroy() error { return nil } 76 | 77 | type ghRepoCursor struct { 78 | index int 79 | repos []githubRepo 80 | } 81 | 82 | func (vc *ghRepoCursor) Column(c *sqlite3.SQLiteContext, col int) error { 83 | switch col { 84 | case 0: 85 | c.ResultInt(vc.repos[vc.index].ID) 86 | case 1: 87 | c.ResultText(vc.repos[vc.index].FullName) 88 | case 2: 89 | c.ResultText(vc.repos[vc.index].Description) 90 | case 3: 91 | c.ResultText(vc.repos[vc.index].HTMLURL) 92 | } 93 | return nil 94 | } 95 | 96 | func (vc *ghRepoCursor) Filter(idxNum int, idxStr string, vals []any) error { 97 | vc.index = 0 98 | return nil 99 | } 100 | 101 | func (vc *ghRepoCursor) Next() error { 102 | vc.index++ 103 | return nil 104 | } 105 | 106 | func (vc *ghRepoCursor) EOF() bool { 107 | return vc.index >= len(vc.repos) 108 | } 109 | 110 | func (vc *ghRepoCursor) Rowid() (int64, error) { 111 | return int64(vc.index), nil 112 | } 113 | 114 | func (vc *ghRepoCursor) Close() error { 115 | return nil 116 | } 117 | -------------------------------------------------------------------------------- /_example/vtable_eponymous_only/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/mattn/go-sqlite3" 9 | ) 10 | 11 | func main() { 12 | sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{ 13 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 14 | return conn.CreateModule("series", &seriesModule{}) 15 | }, 16 | }) 17 | db, err := sql.Open("sqlite3_with_extensions", ":memory:") 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | defer db.Close() 22 | 23 | rows, err := db.Query("select * from series") 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | defer rows.Close() 28 | for rows.Next() { 29 | var value int 30 | rows.Scan(&value) 31 | fmt.Printf("value: %d\n", value) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /_example/vtable_eponymous_only/vtable.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mattn/go-sqlite3" 7 | ) 8 | 9 | type seriesModule struct{} 10 | 11 | func (m *seriesModule) EponymousOnlyModule() {} 12 | 13 | func (m *seriesModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) { 14 | err := c.DeclareVTab(fmt.Sprintf(` 15 | CREATE TABLE %s ( 16 | value INT, 17 | start HIDDEN, 18 | stop HIDDEN, 19 | step HIDDEN 20 | )`, args[0])) 21 | if err != nil { 22 | return nil, err 23 | } 24 | return &seriesTable{0, 0, 1}, nil 25 | } 26 | 27 | func (m *seriesModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) { 28 | return m.Create(c, args) 29 | } 30 | 31 | func (m *seriesModule) DestroyModule() {} 32 | 33 | type seriesTable struct { 34 | start int64 35 | stop int64 36 | step int64 37 | } 38 | 39 | func (v *seriesTable) Open() (sqlite3.VTabCursor, error) { 40 | return &seriesCursor{v, 0}, nil 41 | } 42 | 43 | func (v *seriesTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) { 44 | used := make([]bool, len(csts)) 45 | for c, cst := range csts { 46 | if cst.Usable && cst.Op == sqlite3.OpEQ { 47 | used[c] = true 48 | } 49 | } 50 | 51 | return &sqlite3.IndexResult{ 52 | IdxNum: 0, 53 | IdxStr: "default", 54 | Used: used, 55 | }, nil 56 | } 57 | 58 | func (v *seriesTable) Disconnect() error { return nil } 59 | func (v *seriesTable) Destroy() error { return nil } 60 | 61 | type seriesCursor struct { 62 | *seriesTable 63 | value int64 64 | } 65 | 66 | func (vc *seriesCursor) Column(c *sqlite3.SQLiteContext, col int) error { 67 | switch col { 68 | case 0: 69 | c.ResultInt64(vc.value) 70 | case 1: 71 | c.ResultInt64(vc.seriesTable.start) 72 | case 2: 73 | c.ResultInt64(vc.seriesTable.stop) 74 | case 3: 75 | c.ResultInt64(vc.seriesTable.step) 76 | } 77 | return nil 78 | } 79 | 80 | func (vc *seriesCursor) Filter(idxNum int, idxStr string, vals []any) error { 81 | switch { 82 | case len(vals) < 1: 83 | vc.seriesTable.start = 0 84 | vc.seriesTable.stop = 1000 85 | vc.value = vc.seriesTable.start 86 | case len(vals) < 2: 87 | vc.seriesTable.start = vals[0].(int64) 88 | vc.seriesTable.stop = 1000 89 | vc.value = vc.seriesTable.start 90 | case len(vals) < 3: 91 | vc.seriesTable.start = vals[0].(int64) 92 | vc.seriesTable.stop = vals[1].(int64) 93 | vc.value = vc.seriesTable.start 94 | case len(vals) < 4: 95 | vc.seriesTable.start = vals[0].(int64) 96 | vc.seriesTable.stop = vals[1].(int64) 97 | vc.seriesTable.step = vals[2].(int64) 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (vc *seriesCursor) Next() error { 104 | vc.value += vc.step 105 | return nil 106 | } 107 | 108 | func (vc *seriesCursor) EOF() bool { 109 | return vc.value > vc.stop 110 | } 111 | 112 | func (vc *seriesCursor) Rowid() (int64, error) { 113 | return int64(vc.value), nil 114 | } 115 | 116 | func (vc *seriesCursor) Close() error { 117 | return nil 118 | } 119 | -------------------------------------------------------------------------------- /backup.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | /* 9 | #ifndef USE_LIBSQLITE3 10 | #include "sqlite3-binding.h" 11 | #else 12 | #include 13 | #endif 14 | #include 15 | */ 16 | import "C" 17 | import ( 18 | "runtime" 19 | "unsafe" 20 | ) 21 | 22 | // SQLiteBackup implement interface of Backup. 23 | type SQLiteBackup struct { 24 | b *C.sqlite3_backup 25 | } 26 | 27 | // Backup make backup from src to dest. 28 | func (destConn *SQLiteConn) Backup(dest string, srcConn *SQLiteConn, src string) (*SQLiteBackup, error) { 29 | destptr := C.CString(dest) 30 | defer C.free(unsafe.Pointer(destptr)) 31 | srcptr := C.CString(src) 32 | defer C.free(unsafe.Pointer(srcptr)) 33 | 34 | if b := C.sqlite3_backup_init(destConn.db, destptr, srcConn.db, srcptr); b != nil { 35 | bb := &SQLiteBackup{b: b} 36 | runtime.SetFinalizer(bb, (*SQLiteBackup).Finish) 37 | return bb, nil 38 | } 39 | return nil, destConn.lastError() 40 | } 41 | 42 | // Step to backs up for one step. Calls the underlying `sqlite3_backup_step` 43 | // function. This function returns a boolean indicating if the backup is done 44 | // and an error signalling any other error. Done is returned if the underlying 45 | // C function returns SQLITE_DONE (Code 101) 46 | func (b *SQLiteBackup) Step(p int) (bool, error) { 47 | ret := C.sqlite3_backup_step(b.b, C.int(p)) 48 | if ret == C.SQLITE_DONE { 49 | return true, nil 50 | } else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY { 51 | return false, Error{Code: ErrNo(ret)} 52 | } 53 | return false, nil 54 | } 55 | 56 | // Remaining return whether have the rest for backup. 57 | func (b *SQLiteBackup) Remaining() int { 58 | return int(C.sqlite3_backup_remaining(b.b)) 59 | } 60 | 61 | // PageCount return count of pages. 62 | func (b *SQLiteBackup) PageCount() int { 63 | return int(C.sqlite3_backup_pagecount(b.b)) 64 | } 65 | 66 | // Finish close backup. 67 | func (b *SQLiteBackup) Finish() error { 68 | return b.Close() 69 | } 70 | 71 | // Close close backup. 72 | func (b *SQLiteBackup) Close() error { 73 | ret := C.sqlite3_backup_finish(b.b) 74 | 75 | // sqlite3_backup_finish() never fails, it just returns the 76 | // error code from previous operations, so clean up before 77 | // checking and returning an error 78 | b.b = nil 79 | runtime.SetFinalizer(b, nil) 80 | 81 | if ret != 0 { 82 | return Error{Code: ErrNo(ret)} 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /backup_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build cgo 7 | // +build cgo 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "fmt" 14 | "os" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | // The number of rows of test data to create in the source database. 20 | // Can be used to control how many pages are available to be backed up. 21 | const testRowCount = 100 22 | 23 | // The maximum number of seconds after which the page-by-page backup is considered to have taken too long. 24 | const usePagePerStepsTimeoutSeconds = 30 25 | 26 | // Test the backup functionality. 27 | func testBackup(t *testing.T, testRowCount int, usePerPageSteps bool) { 28 | // This function will be called multiple times. 29 | // It uses sql.Register(), which requires the name parameter value to be unique. 30 | // There does not currently appear to be a way to unregister a registered driver, however. 31 | // So generate a database driver name that will likely be unique. 32 | var driverName = fmt.Sprintf("sqlite3_testBackup_%v_%v_%v", testRowCount, usePerPageSteps, time.Now().UnixNano()) 33 | 34 | // The driver's connection will be needed in order to perform the backup. 35 | driverConns := []*SQLiteConn{} 36 | sql.Register(driverName, &SQLiteDriver{ 37 | ConnectHook: func(conn *SQLiteConn) error { 38 | driverConns = append(driverConns, conn) 39 | return nil 40 | }, 41 | }) 42 | 43 | // Connect to the source database. 44 | srcTempFilename := TempFilename(t) 45 | defer os.Remove(srcTempFilename) 46 | srcDb, err := sql.Open(driverName, srcTempFilename) 47 | if err != nil { 48 | t.Fatal("Failed to open the source database:", err) 49 | } 50 | defer srcDb.Close() 51 | err = srcDb.Ping() 52 | if err != nil { 53 | t.Fatal("Failed to connect to the source database:", err) 54 | } 55 | 56 | // Connect to the destination database. 57 | destTempFilename := TempFilename(t) 58 | defer os.Remove(destTempFilename) 59 | destDb, err := sql.Open(driverName, destTempFilename) 60 | if err != nil { 61 | t.Fatal("Failed to open the destination database:", err) 62 | } 63 | defer destDb.Close() 64 | err = destDb.Ping() 65 | if err != nil { 66 | t.Fatal("Failed to connect to the destination database:", err) 67 | } 68 | 69 | // Check the driver connections. 70 | if len(driverConns) != 2 { 71 | t.Fatalf("Expected 2 driver connections, but found %v.", len(driverConns)) 72 | } 73 | srcDbDriverConn := driverConns[0] 74 | if srcDbDriverConn == nil { 75 | t.Fatal("The source database driver connection is nil.") 76 | } 77 | destDbDriverConn := driverConns[1] 78 | if destDbDriverConn == nil { 79 | t.Fatal("The destination database driver connection is nil.") 80 | } 81 | 82 | // Generate some test data for the given ID. 83 | var generateTestData = func(id int) string { 84 | return fmt.Sprintf("test-%v", id) 85 | } 86 | 87 | // Populate the source database with a test table containing some test data. 88 | tx, err := srcDb.Begin() 89 | if err != nil { 90 | t.Fatal("Failed to begin a transaction when populating the source database:", err) 91 | } 92 | _, err = srcDb.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)") 93 | if err != nil { 94 | tx.Rollback() 95 | t.Fatal("Failed to create the source database \"test\" table:", err) 96 | } 97 | for id := 0; id < testRowCount; id++ { 98 | _, err = srcDb.Exec("INSERT INTO test (id, value) VALUES (?, ?)", id, generateTestData(id)) 99 | if err != nil { 100 | tx.Rollback() 101 | t.Fatal("Failed to insert a row into the source database \"test\" table:", err) 102 | } 103 | } 104 | err = tx.Commit() 105 | if err != nil { 106 | t.Fatal("Failed to populate the source database:", err) 107 | } 108 | 109 | // Confirm that the destination database is initially empty. 110 | var destTableCount int 111 | err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount) 112 | if err != nil { 113 | t.Fatal("Failed to check the destination table count:", err) 114 | } 115 | if destTableCount != 0 { 116 | t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount) 117 | } 118 | 119 | // Prepare to perform the backup. 120 | backup, err := destDbDriverConn.Backup("main", srcDbDriverConn, "main") 121 | if err != nil { 122 | t.Fatal("Failed to initialize the backup:", err) 123 | } 124 | 125 | // Allow the initial page count and remaining values to be retrieved. 126 | // According to , the page count and remaining values are "... only updated by sqlite3_backup_step()." 127 | isDone, err := backup.Step(0) 128 | if err != nil { 129 | t.Fatal("Unable to perform an initial 0-page backup step:", err) 130 | } 131 | if isDone { 132 | t.Fatal("Backup is unexpectedly done.") 133 | } 134 | 135 | // Check that the page count and remaining values are reasonable. 136 | initialPageCount := backup.PageCount() 137 | if initialPageCount <= 0 { 138 | t.Fatalf("Unexpected initial page count value: %v", initialPageCount) 139 | } 140 | initialRemaining := backup.Remaining() 141 | if initialRemaining <= 0 { 142 | t.Fatalf("Unexpected initial remaining value: %v", initialRemaining) 143 | } 144 | if initialRemaining != initialPageCount { 145 | t.Fatalf("Initial remaining value differs from the initial page count value; remaining: %v; page count: %v", initialRemaining, initialPageCount) 146 | } 147 | 148 | // Perform the backup. 149 | if usePerPageSteps { 150 | var startTime = time.Now().Unix() 151 | 152 | // Test backing-up using a page-by-page approach. 153 | var latestRemaining = initialRemaining 154 | for { 155 | // Perform the backup step. 156 | isDone, err = backup.Step(1) 157 | if err != nil { 158 | t.Fatal("Failed to perform a backup step:", err) 159 | } 160 | 161 | // The page count should remain unchanged from its initial value. 162 | currentPageCount := backup.PageCount() 163 | if currentPageCount != initialPageCount { 164 | t.Fatalf("Current page count differs from the initial page count; initial page count: %v; current page count: %v", initialPageCount, currentPageCount) 165 | } 166 | 167 | // There should now be one less page remaining. 168 | currentRemaining := backup.Remaining() 169 | expectedRemaining := latestRemaining - 1 170 | if currentRemaining != expectedRemaining { 171 | t.Fatalf("Unexpected remaining value; expected remaining value: %v; actual remaining value: %v", expectedRemaining, currentRemaining) 172 | } 173 | latestRemaining = currentRemaining 174 | 175 | if isDone { 176 | break 177 | } 178 | 179 | // Limit the runtime of the backup attempt. 180 | if (time.Now().Unix() - startTime) > usePagePerStepsTimeoutSeconds { 181 | t.Fatal("Backup is taking longer than expected.") 182 | } 183 | } 184 | } else { 185 | // Test the copying of all remaining pages. 186 | isDone, err = backup.Step(-1) 187 | if err != nil { 188 | t.Fatal("Failed to perform a backup step:", err) 189 | } 190 | if !isDone { 191 | t.Fatal("Backup is unexpectedly not done.") 192 | } 193 | } 194 | 195 | // Check that the page count and remaining values are reasonable. 196 | finalPageCount := backup.PageCount() 197 | if finalPageCount != initialPageCount { 198 | t.Fatalf("Final page count differs from the initial page count; initial page count: %v; final page count: %v", initialPageCount, finalPageCount) 199 | } 200 | finalRemaining := backup.Remaining() 201 | if finalRemaining != 0 { 202 | t.Fatalf("Unexpected remaining value: %v", finalRemaining) 203 | } 204 | 205 | // Finish the backup. 206 | err = backup.Finish() 207 | if err != nil { 208 | t.Fatal("Failed to finish backup:", err) 209 | } 210 | 211 | // Confirm that the "test" table now exists in the destination database. 212 | var doesTestTableExist bool 213 | err = destDb.QueryRow("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'test' LIMIT 1) AS test_table_exists").Scan(&doesTestTableExist) 214 | if err != nil { 215 | t.Fatal("Failed to check if the \"test\" table exists in the destination database:", err) 216 | } 217 | if !doesTestTableExist { 218 | t.Fatal("The \"test\" table could not be found in the destination database.") 219 | } 220 | 221 | // Confirm that the number of rows in the destination database's "test" table matches that of the source table. 222 | var actualTestTableRowCount int 223 | err = destDb.QueryRow("SELECT COUNT(*) FROM test").Scan(&actualTestTableRowCount) 224 | if err != nil { 225 | t.Fatal("Failed to determine the rowcount of the \"test\" table in the destination database:", err) 226 | } 227 | if testRowCount != actualTestTableRowCount { 228 | t.Fatalf("Unexpected destination \"test\" table row count; expected: %v; found: %v", testRowCount, actualTestTableRowCount) 229 | } 230 | 231 | // Check each of the rows in the destination database. 232 | for id := 0; id < testRowCount; id++ { 233 | var checkedValue string 234 | err = destDb.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&checkedValue) 235 | if err != nil { 236 | t.Fatal("Failed to query the \"test\" table in the destination database:", err) 237 | } 238 | 239 | var expectedValue = generateTestData(id) 240 | if checkedValue != expectedValue { 241 | t.Fatalf("Unexpected value in the \"test\" table in the destination database; expected value: %v; actual value: %v", expectedValue, checkedValue) 242 | } 243 | } 244 | } 245 | 246 | func TestBackupStepByStep(t *testing.T) { 247 | testBackup(t, testRowCount, true) 248 | } 249 | 250 | func TestBackupAllRemainingPages(t *testing.T) { 251 | testBackup(t, testRowCount, false) 252 | } 253 | 254 | // Test the error reporting when preparing to perform a backup. 255 | func TestBackupError(t *testing.T) { 256 | const driverName = "sqlite3_TestBackupError" 257 | 258 | // The driver's connection will be needed in order to perform the backup. 259 | var dbDriverConn *SQLiteConn 260 | sql.Register(driverName, &SQLiteDriver{ 261 | ConnectHook: func(conn *SQLiteConn) error { 262 | dbDriverConn = conn 263 | return nil 264 | }, 265 | }) 266 | 267 | // Connect to the database. 268 | dbTempFilename := TempFilename(t) 269 | defer os.Remove(dbTempFilename) 270 | db, err := sql.Open(driverName, dbTempFilename) 271 | if err != nil { 272 | t.Fatal("Failed to open the database:", err) 273 | } 274 | defer db.Close() 275 | db.Ping() 276 | 277 | // Need the driver connection in order to perform the backup. 278 | if dbDriverConn == nil { 279 | t.Fatal("Failed to get the driver connection.") 280 | } 281 | 282 | // Prepare to perform the backup. 283 | // Intentionally using the same connection for both the source and destination databases, to trigger an error result. 284 | backup, err := dbDriverConn.Backup("main", dbDriverConn, "main") 285 | if err == nil { 286 | t.Fatal("Failed to get the expected error result.") 287 | } 288 | const expectedError = "source and destination must be distinct" 289 | if err.Error() != expectedError { 290 | t.Fatalf("Unexpected error message; expected value: \"%v\"; actual value: \"%v\"", expectedError, err.Error()) 291 | } 292 | if backup != nil { 293 | t.Fatal("Failed to get the expected nil backup result.") 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /callback.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | // You can't export a Go function to C and have definitions in the C 9 | // preamble in the same file, so we have to have callbackTrampoline in 10 | // its own file. Because we need a separate file anyway, the support 11 | // code for SQLite custom functions is in here. 12 | 13 | /* 14 | #ifndef USE_LIBSQLITE3 15 | #include "sqlite3-binding.h" 16 | #else 17 | #include 18 | #endif 19 | #include 20 | 21 | void _sqlite3_result_text(sqlite3_context* ctx, const char* s); 22 | void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l); 23 | */ 24 | import "C" 25 | 26 | import ( 27 | "errors" 28 | "fmt" 29 | "math" 30 | "reflect" 31 | "sync" 32 | "unsafe" 33 | ) 34 | 35 | //export callbackTrampoline 36 | func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { 37 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] 38 | fi := lookupHandle(C.sqlite3_user_data(ctx)).(*functionInfo) 39 | fi.Call(ctx, args) 40 | } 41 | 42 | //export stepTrampoline 43 | func stepTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) { 44 | args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)] 45 | ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo) 46 | ai.Step(ctx, args) 47 | } 48 | 49 | //export doneTrampoline 50 | func doneTrampoline(ctx *C.sqlite3_context) { 51 | ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo) 52 | ai.Done(ctx) 53 | } 54 | 55 | //export compareTrampoline 56 | func compareTrampoline(handlePtr unsafe.Pointer, la C.int, a *C.char, lb C.int, b *C.char) C.int { 57 | cmp := lookupHandle(handlePtr).(func(string, string) int) 58 | return C.int(cmp(C.GoStringN(a, la), C.GoStringN(b, lb))) 59 | } 60 | 61 | //export commitHookTrampoline 62 | func commitHookTrampoline(handle unsafe.Pointer) int { 63 | callback := lookupHandle(handle).(func() int) 64 | return callback() 65 | } 66 | 67 | //export rollbackHookTrampoline 68 | func rollbackHookTrampoline(handle unsafe.Pointer) { 69 | callback := lookupHandle(handle).(func()) 70 | callback() 71 | } 72 | 73 | //export updateHookTrampoline 74 | func updateHookTrampoline(handle unsafe.Pointer, op int, db *C.char, table *C.char, rowid int64) { 75 | callback := lookupHandle(handle).(func(int, string, string, int64)) 76 | callback(op, C.GoString(db), C.GoString(table), rowid) 77 | } 78 | 79 | //export authorizerTrampoline 80 | func authorizerTrampoline(handle unsafe.Pointer, op int, arg1 *C.char, arg2 *C.char, arg3 *C.char) int { 81 | callback := lookupHandle(handle).(func(int, string, string, string) int) 82 | return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3)) 83 | } 84 | 85 | //export preUpdateHookTrampoline 86 | func preUpdateHookTrampoline(handle unsafe.Pointer, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) { 87 | hval := lookupHandleVal(handle) 88 | data := SQLitePreUpdateData{ 89 | Conn: hval.db, 90 | Op: op, 91 | DatabaseName: C.GoString(db), 92 | TableName: C.GoString(table), 93 | OldRowID: oldrowid, 94 | NewRowID: newrowid, 95 | } 96 | callback := hval.val.(func(SQLitePreUpdateData)) 97 | callback(data) 98 | } 99 | 100 | // Use handles to avoid passing Go pointers to C. 101 | type handleVal struct { 102 | db *SQLiteConn 103 | val any 104 | } 105 | 106 | var handleLock sync.Mutex 107 | var handleVals = make(map[unsafe.Pointer]handleVal) 108 | 109 | func newHandle(db *SQLiteConn, v any) unsafe.Pointer { 110 | handleLock.Lock() 111 | defer handleLock.Unlock() 112 | val := handleVal{db: db, val: v} 113 | var p unsafe.Pointer = C.malloc(C.size_t(1)) 114 | if p == nil { 115 | panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil") 116 | } 117 | handleVals[p] = val 118 | return p 119 | } 120 | 121 | func lookupHandleVal(handle unsafe.Pointer) handleVal { 122 | handleLock.Lock() 123 | defer handleLock.Unlock() 124 | return handleVals[handle] 125 | } 126 | 127 | func lookupHandle(handle unsafe.Pointer) any { 128 | return lookupHandleVal(handle).val 129 | } 130 | 131 | func deleteHandles(db *SQLiteConn) { 132 | handleLock.Lock() 133 | defer handleLock.Unlock() 134 | for handle, val := range handleVals { 135 | if val.db == db { 136 | delete(handleVals, handle) 137 | C.free(handle) 138 | } 139 | } 140 | } 141 | 142 | // This is only here so that tests can refer to it. 143 | type callbackArgRaw C.sqlite3_value 144 | 145 | type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error) 146 | 147 | type callbackArgCast struct { 148 | f callbackArgConverter 149 | typ reflect.Type 150 | } 151 | 152 | func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) { 153 | val, err := c.f(v) 154 | if err != nil { 155 | return reflect.Value{}, err 156 | } 157 | if !val.Type().ConvertibleTo(c.typ) { 158 | return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ) 159 | } 160 | return val.Convert(c.typ), nil 161 | } 162 | 163 | func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) { 164 | if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { 165 | return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") 166 | } 167 | return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil 168 | } 169 | 170 | func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) { 171 | if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { 172 | return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") 173 | } 174 | i := int64(C.sqlite3_value_int64(v)) 175 | val := false 176 | if i != 0 { 177 | val = true 178 | } 179 | return reflect.ValueOf(val), nil 180 | } 181 | 182 | func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) { 183 | if C.sqlite3_value_type(v) != C.SQLITE_FLOAT { 184 | return reflect.Value{}, fmt.Errorf("argument must be a FLOAT") 185 | } 186 | return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil 187 | } 188 | 189 | func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) { 190 | switch C.sqlite3_value_type(v) { 191 | case C.SQLITE_BLOB: 192 | l := C.sqlite3_value_bytes(v) 193 | p := C.sqlite3_value_blob(v) 194 | return reflect.ValueOf(C.GoBytes(p, l)), nil 195 | case C.SQLITE_TEXT: 196 | l := C.sqlite3_value_bytes(v) 197 | c := unsafe.Pointer(C.sqlite3_value_text(v)) 198 | return reflect.ValueOf(C.GoBytes(c, l)), nil 199 | default: 200 | return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") 201 | } 202 | } 203 | 204 | func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) { 205 | switch C.sqlite3_value_type(v) { 206 | case C.SQLITE_BLOB: 207 | l := C.sqlite3_value_bytes(v) 208 | p := (*C.char)(C.sqlite3_value_blob(v)) 209 | return reflect.ValueOf(C.GoStringN(p, l)), nil 210 | case C.SQLITE_TEXT: 211 | c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v))) 212 | return reflect.ValueOf(C.GoString(c)), nil 213 | default: 214 | return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") 215 | } 216 | } 217 | 218 | func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) { 219 | switch C.sqlite3_value_type(v) { 220 | case C.SQLITE_INTEGER: 221 | return callbackArgInt64(v) 222 | case C.SQLITE_FLOAT: 223 | return callbackArgFloat64(v) 224 | case C.SQLITE_TEXT: 225 | return callbackArgString(v) 226 | case C.SQLITE_BLOB: 227 | return callbackArgBytes(v) 228 | case C.SQLITE_NULL: 229 | // Interpret NULL as a nil byte slice. 230 | var ret []byte 231 | return reflect.ValueOf(ret), nil 232 | default: 233 | panic("unreachable") 234 | } 235 | } 236 | 237 | func callbackArg(typ reflect.Type) (callbackArgConverter, error) { 238 | switch typ.Kind() { 239 | case reflect.Interface: 240 | if typ.NumMethod() != 0 { 241 | return nil, errors.New("the only supported interface type is any") 242 | } 243 | return callbackArgGeneric, nil 244 | case reflect.Slice: 245 | if typ.Elem().Kind() != reflect.Uint8 { 246 | return nil, errors.New("the only supported slice type is []byte") 247 | } 248 | return callbackArgBytes, nil 249 | case reflect.String: 250 | return callbackArgString, nil 251 | case reflect.Bool: 252 | return callbackArgBool, nil 253 | case reflect.Int64: 254 | return callbackArgInt64, nil 255 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 256 | c := callbackArgCast{callbackArgInt64, typ} 257 | return c.Run, nil 258 | case reflect.Float64: 259 | return callbackArgFloat64, nil 260 | case reflect.Float32: 261 | c := callbackArgCast{callbackArgFloat64, typ} 262 | return c.Run, nil 263 | default: 264 | return nil, fmt.Errorf("don't know how to convert to %s", typ) 265 | } 266 | } 267 | 268 | func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) { 269 | var args []reflect.Value 270 | 271 | if len(argv) < len(converters) { 272 | return nil, fmt.Errorf("function requires at least %d arguments", len(converters)) 273 | } 274 | 275 | for i, arg := range argv[:len(converters)] { 276 | v, err := converters[i](arg) 277 | if err != nil { 278 | return nil, err 279 | } 280 | args = append(args, v) 281 | } 282 | 283 | if variadic != nil { 284 | for _, arg := range argv[len(converters):] { 285 | v, err := variadic(arg) 286 | if err != nil { 287 | return nil, err 288 | } 289 | args = append(args, v) 290 | } 291 | } 292 | return args, nil 293 | } 294 | 295 | type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error 296 | 297 | func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error { 298 | switch v.Type().Kind() { 299 | case reflect.Int64: 300 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 301 | v = v.Convert(reflect.TypeOf(int64(0))) 302 | case reflect.Bool: 303 | b := v.Interface().(bool) 304 | if b { 305 | v = reflect.ValueOf(int64(1)) 306 | } else { 307 | v = reflect.ValueOf(int64(0)) 308 | } 309 | default: 310 | return fmt.Errorf("cannot convert %s to INTEGER", v.Type()) 311 | } 312 | 313 | C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64))) 314 | return nil 315 | } 316 | 317 | func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error { 318 | switch v.Type().Kind() { 319 | case reflect.Float64: 320 | case reflect.Float32: 321 | v = v.Convert(reflect.TypeOf(float64(0))) 322 | default: 323 | return fmt.Errorf("cannot convert %s to FLOAT", v.Type()) 324 | } 325 | 326 | C.sqlite3_result_double(ctx, C.double(v.Interface().(float64))) 327 | return nil 328 | } 329 | 330 | func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error { 331 | if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 { 332 | return fmt.Errorf("cannot convert %s to BLOB", v.Type()) 333 | } 334 | i := v.Interface() 335 | if i == nil || len(i.([]byte)) == 0 { 336 | C.sqlite3_result_null(ctx) 337 | } else { 338 | bs := i.([]byte) 339 | C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs))) 340 | } 341 | return nil 342 | } 343 | 344 | func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error { 345 | if v.Type().Kind() != reflect.String { 346 | return fmt.Errorf("cannot convert %s to TEXT", v.Type()) 347 | } 348 | cstr := C.CString(v.Interface().(string)) 349 | C._sqlite3_result_text(ctx, cstr) 350 | return nil 351 | } 352 | 353 | func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error { 354 | return nil 355 | } 356 | 357 | func callbackRetGeneric(ctx *C.sqlite3_context, v reflect.Value) error { 358 | if v.IsNil() { 359 | C.sqlite3_result_null(ctx) 360 | return nil 361 | } 362 | 363 | cb, err := callbackRet(v.Elem().Type()) 364 | if err != nil { 365 | return err 366 | } 367 | 368 | return cb(ctx, v.Elem()) 369 | } 370 | 371 | func callbackRet(typ reflect.Type) (callbackRetConverter, error) { 372 | switch typ.Kind() { 373 | case reflect.Interface: 374 | errorInterface := reflect.TypeOf((*error)(nil)).Elem() 375 | if typ.Implements(errorInterface) { 376 | return callbackRetNil, nil 377 | } 378 | 379 | if typ.NumMethod() == 0 { 380 | return callbackRetGeneric, nil 381 | } 382 | 383 | fallthrough 384 | case reflect.Slice: 385 | if typ.Elem().Kind() != reflect.Uint8 { 386 | return nil, errors.New("the only supported slice type is []byte") 387 | } 388 | return callbackRetBlob, nil 389 | case reflect.String: 390 | return callbackRetText, nil 391 | case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: 392 | return callbackRetInteger, nil 393 | case reflect.Float32, reflect.Float64: 394 | return callbackRetFloat, nil 395 | default: 396 | return nil, fmt.Errorf("don't know how to convert to %s", typ) 397 | } 398 | } 399 | 400 | func callbackError(ctx *C.sqlite3_context, err error) { 401 | cstr := C.CString(err.Error()) 402 | defer C.free(unsafe.Pointer(cstr)) 403 | C.sqlite3_result_error(ctx, cstr, C.int(-1)) 404 | } 405 | 406 | // Test support code. Tests are not allowed to import "C", so we can't 407 | // declare any functions that use C.sqlite3_value. 408 | func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter { 409 | return func(*C.sqlite3_value) (reflect.Value, error) { 410 | return v, err 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /callback_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build cgo 7 | // +build cgo 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "errors" 13 | "math" 14 | "reflect" 15 | "testing" 16 | ) 17 | 18 | func TestCallbackArgCast(t *testing.T) { 19 | intConv := callbackSyntheticForTests(reflect.ValueOf(int64(math.MaxInt64)), nil) 20 | floatConv := callbackSyntheticForTests(reflect.ValueOf(float64(math.MaxFloat64)), nil) 21 | errConv := callbackSyntheticForTests(reflect.Value{}, errors.New("test")) 22 | 23 | tests := []struct { 24 | f callbackArgConverter 25 | o reflect.Value 26 | }{ 27 | {intConv, reflect.ValueOf(int8(-1))}, 28 | {intConv, reflect.ValueOf(int16(-1))}, 29 | {intConv, reflect.ValueOf(int32(-1))}, 30 | {intConv, reflect.ValueOf(uint8(math.MaxUint8))}, 31 | {intConv, reflect.ValueOf(uint16(math.MaxUint16))}, 32 | {intConv, reflect.ValueOf(uint32(math.MaxUint32))}, 33 | // Special case, int64->uint64 is only 1<<63 - 1, not 1<<64 - 1 34 | {intConv, reflect.ValueOf(uint64(math.MaxInt64))}, 35 | {floatConv, reflect.ValueOf(float32(math.Inf(1)))}, 36 | } 37 | 38 | for _, test := range tests { 39 | conv := callbackArgCast{test.f, test.o.Type()} 40 | val, err := conv.Run(nil) 41 | if err != nil { 42 | t.Errorf("Couldn't convert to %s: %s", test.o.Type(), err) 43 | } else if !reflect.DeepEqual(val.Interface(), test.o.Interface()) { 44 | t.Errorf("Unexpected result from converting to %s: got %v, want %v", test.o.Type(), val.Interface(), test.o.Interface()) 45 | } 46 | } 47 | 48 | conv := callbackArgCast{errConv, reflect.TypeOf(int8(0))} 49 | _, err := conv.Run(nil) 50 | if err == nil { 51 | t.Errorf("Expected error during callbackArgCast, but got none") 52 | } 53 | } 54 | 55 | func TestCallbackConverters(t *testing.T) { 56 | tests := []struct { 57 | v any 58 | err bool 59 | }{ 60 | // Unfortunately, we can't tell which converter was returned, 61 | // but we can at least check which types can be converted. 62 | {[]byte{0}, false}, 63 | {"text", false}, 64 | {true, false}, 65 | {int8(0), false}, 66 | {int16(0), false}, 67 | {int32(0), false}, 68 | {int64(0), false}, 69 | {uint8(0), false}, 70 | {uint16(0), false}, 71 | {uint32(0), false}, 72 | {uint64(0), false}, 73 | {int(0), false}, 74 | {uint(0), false}, 75 | {float64(0), false}, 76 | {float32(0), false}, 77 | 78 | {func() {}, true}, 79 | {complex64(complex(0, 0)), true}, 80 | {complex128(complex(0, 0)), true}, 81 | {struct{}{}, true}, 82 | {map[string]string{}, true}, 83 | {[]string{}, true}, 84 | {(*int8)(nil), true}, 85 | {make(chan int), true}, 86 | } 87 | 88 | for _, test := range tests { 89 | _, err := callbackArg(reflect.TypeOf(test.v)) 90 | if test.err && err == nil { 91 | t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v)) 92 | } else if !test.err && err != nil { 93 | t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err) 94 | } 95 | } 96 | 97 | for _, test := range tests { 98 | _, err := callbackRet(reflect.TypeOf(test.v)) 99 | if test.err && err == nil { 100 | t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v)) 101 | } else if !test.err && err != nil { 102 | t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err) 103 | } 104 | } 105 | } 106 | 107 | func TestCallbackReturnAny(t *testing.T) { 108 | udf := func() any { 109 | return 1 110 | } 111 | 112 | typ := reflect.TypeOf(udf) 113 | _, err := callbackRet(typ.Out(0)) 114 | if err != nil { 115 | t.Errorf("Expected valid callback for any return type, got: %s", err) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /convert.go: -------------------------------------------------------------------------------- 1 | // Extracted from Go database/sql source code 2 | 3 | // Copyright 2011 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // Type conversions for Scan. 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "database/sql/driver" 14 | "errors" 15 | "fmt" 16 | "reflect" 17 | "strconv" 18 | "time" 19 | ) 20 | 21 | var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error 22 | 23 | // convertAssign copies to dest the value in src, converting it if possible. 24 | // An error is returned if the copy would result in loss of information. 25 | // dest should be a pointer type. 26 | func convertAssign(dest, src any) error { 27 | // Common cases, without reflect. 28 | switch s := src.(type) { 29 | case string: 30 | switch d := dest.(type) { 31 | case *string: 32 | if d == nil { 33 | return errNilPtr 34 | } 35 | *d = s 36 | return nil 37 | case *[]byte: 38 | if d == nil { 39 | return errNilPtr 40 | } 41 | *d = []byte(s) 42 | return nil 43 | case *sql.RawBytes: 44 | if d == nil { 45 | return errNilPtr 46 | } 47 | *d = append((*d)[:0], s...) 48 | return nil 49 | } 50 | case []byte: 51 | switch d := dest.(type) { 52 | case *string: 53 | if d == nil { 54 | return errNilPtr 55 | } 56 | *d = string(s) 57 | return nil 58 | case *any: 59 | if d == nil { 60 | return errNilPtr 61 | } 62 | *d = cloneBytes(s) 63 | return nil 64 | case *[]byte: 65 | if d == nil { 66 | return errNilPtr 67 | } 68 | *d = cloneBytes(s) 69 | return nil 70 | case *sql.RawBytes: 71 | if d == nil { 72 | return errNilPtr 73 | } 74 | *d = s 75 | return nil 76 | } 77 | case time.Time: 78 | switch d := dest.(type) { 79 | case *time.Time: 80 | *d = s 81 | return nil 82 | case *string: 83 | *d = s.Format(time.RFC3339Nano) 84 | return nil 85 | case *[]byte: 86 | if d == nil { 87 | return errNilPtr 88 | } 89 | *d = []byte(s.Format(time.RFC3339Nano)) 90 | return nil 91 | case *sql.RawBytes: 92 | if d == nil { 93 | return errNilPtr 94 | } 95 | *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) 96 | return nil 97 | } 98 | case nil: 99 | switch d := dest.(type) { 100 | case *any: 101 | if d == nil { 102 | return errNilPtr 103 | } 104 | *d = nil 105 | return nil 106 | case *[]byte: 107 | if d == nil { 108 | return errNilPtr 109 | } 110 | *d = nil 111 | return nil 112 | case *sql.RawBytes: 113 | if d == nil { 114 | return errNilPtr 115 | } 116 | *d = nil 117 | return nil 118 | } 119 | } 120 | 121 | var sv reflect.Value 122 | 123 | switch d := dest.(type) { 124 | case *string: 125 | sv = reflect.ValueOf(src) 126 | switch sv.Kind() { 127 | case reflect.Bool, 128 | reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 129 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 130 | reflect.Float32, reflect.Float64: 131 | *d = asString(src) 132 | return nil 133 | } 134 | case *[]byte: 135 | sv = reflect.ValueOf(src) 136 | if b, ok := asBytes(nil, sv); ok { 137 | *d = b 138 | return nil 139 | } 140 | case *sql.RawBytes: 141 | sv = reflect.ValueOf(src) 142 | if b, ok := asBytes([]byte(*d)[:0], sv); ok { 143 | *d = sql.RawBytes(b) 144 | return nil 145 | } 146 | case *bool: 147 | bv, err := driver.Bool.ConvertValue(src) 148 | if err == nil { 149 | *d = bv.(bool) 150 | } 151 | return err 152 | case *any: 153 | *d = src 154 | return nil 155 | } 156 | 157 | if scanner, ok := dest.(sql.Scanner); ok { 158 | return scanner.Scan(src) 159 | } 160 | 161 | dpv := reflect.ValueOf(dest) 162 | if dpv.Kind() != reflect.Ptr { 163 | return errors.New("destination not a pointer") 164 | } 165 | if dpv.IsNil() { 166 | return errNilPtr 167 | } 168 | 169 | if !sv.IsValid() { 170 | sv = reflect.ValueOf(src) 171 | } 172 | 173 | dv := reflect.Indirect(dpv) 174 | if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { 175 | switch b := src.(type) { 176 | case []byte: 177 | dv.Set(reflect.ValueOf(cloneBytes(b))) 178 | default: 179 | dv.Set(sv) 180 | } 181 | return nil 182 | } 183 | 184 | if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { 185 | dv.Set(sv.Convert(dv.Type())) 186 | return nil 187 | } 188 | 189 | // The following conversions use a string value as an intermediate representation 190 | // to convert between various numeric types. 191 | // 192 | // This also allows scanning into user defined types such as "type Int int64". 193 | // For symmetry, also check for string destination types. 194 | switch dv.Kind() { 195 | case reflect.Ptr: 196 | if src == nil { 197 | dv.Set(reflect.Zero(dv.Type())) 198 | return nil 199 | } 200 | dv.Set(reflect.New(dv.Type().Elem())) 201 | return convertAssign(dv.Interface(), src) 202 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 203 | s := asString(src) 204 | i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) 205 | if err != nil { 206 | err = strconvErr(err) 207 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 208 | } 209 | dv.SetInt(i64) 210 | return nil 211 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 212 | s := asString(src) 213 | u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) 214 | if err != nil { 215 | err = strconvErr(err) 216 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 217 | } 218 | dv.SetUint(u64) 219 | return nil 220 | case reflect.Float32, reflect.Float64: 221 | s := asString(src) 222 | f64, err := strconv.ParseFloat(s, dv.Type().Bits()) 223 | if err != nil { 224 | err = strconvErr(err) 225 | return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) 226 | } 227 | dv.SetFloat(f64) 228 | return nil 229 | case reflect.String: 230 | switch v := src.(type) { 231 | case string: 232 | dv.SetString(v) 233 | return nil 234 | case []byte: 235 | dv.SetString(string(v)) 236 | return nil 237 | } 238 | } 239 | 240 | return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) 241 | } 242 | 243 | func strconvErr(err error) error { 244 | if ne, ok := err.(*strconv.NumError); ok { 245 | return ne.Err 246 | } 247 | return err 248 | } 249 | 250 | func cloneBytes(b []byte) []byte { 251 | if b == nil { 252 | return nil 253 | } 254 | c := make([]byte, len(b)) 255 | copy(c, b) 256 | return c 257 | } 258 | 259 | func asString(src any) string { 260 | switch v := src.(type) { 261 | case string: 262 | return v 263 | case []byte: 264 | return string(v) 265 | } 266 | rv := reflect.ValueOf(src) 267 | switch rv.Kind() { 268 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 269 | return strconv.FormatInt(rv.Int(), 10) 270 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 271 | return strconv.FormatUint(rv.Uint(), 10) 272 | case reflect.Float64: 273 | return strconv.FormatFloat(rv.Float(), 'g', -1, 64) 274 | case reflect.Float32: 275 | return strconv.FormatFloat(rv.Float(), 'g', -1, 32) 276 | case reflect.Bool: 277 | return strconv.FormatBool(rv.Bool()) 278 | } 279 | return fmt.Sprintf("%v", src) 280 | } 281 | 282 | func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { 283 | switch rv.Kind() { 284 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 285 | return strconv.AppendInt(buf, rv.Int(), 10), true 286 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 287 | return strconv.AppendUint(buf, rv.Uint(), 10), true 288 | case reflect.Float32: 289 | return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true 290 | case reflect.Float64: 291 | return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true 292 | case reflect.Bool: 293 | return strconv.AppendBool(buf, rv.Bool()), true 294 | case reflect.String: 295 | s := rv.String() 296 | return append(buf, s...), true 297 | } 298 | return 299 | } 300 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package sqlite3 provides interface to SQLite3 databases. 3 | 4 | This works as a driver for database/sql. 5 | 6 | Installation 7 | 8 | go get github.com/mattn/go-sqlite3 9 | 10 | # Supported Types 11 | 12 | Currently, go-sqlite3 supports the following data types. 13 | 14 | +------------------------------+ 15 | |go | sqlite3 | 16 | |----------|-------------------| 17 | |nil | null | 18 | |int | integer | 19 | |int64 | integer | 20 | |float64 | float | 21 | |bool | integer | 22 | |[]byte | blob | 23 | |string | text | 24 | |time.Time | timestamp/datetime| 25 | +------------------------------+ 26 | 27 | # SQLite3 Extension 28 | 29 | You can write your own extension module for sqlite3. For example, below is an 30 | extension for a Regexp matcher operation. 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | SQLITE_EXTENSION_INIT1 38 | static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) { 39 | if (argc >= 2) { 40 | const char *target = (const char *)sqlite3_value_text(argv[1]); 41 | const char *pattern = (const char *)sqlite3_value_text(argv[0]); 42 | const char* errstr = NULL; 43 | int erroff = 0; 44 | int vec[500]; 45 | int n, rc; 46 | pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); 47 | rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); 48 | if (rc <= 0) { 49 | sqlite3_result_error(context, errstr, 0); 50 | return; 51 | } 52 | sqlite3_result_int(context, 1); 53 | } 54 | } 55 | 56 | #ifdef _WIN32 57 | __declspec(dllexport) 58 | #endif 59 | int sqlite3_extension_init(sqlite3 *db, char **errmsg, 60 | const sqlite3_api_routines *api) { 61 | SQLITE_EXTENSION_INIT2(api); 62 | return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 63 | (void*)db, regexp_func, NULL, NULL); 64 | } 65 | 66 | It needs to be built as a so/dll shared library. And you need to register 67 | the extension module like below. 68 | 69 | sql.Register("sqlite3_with_extensions", 70 | &sqlite3.SQLiteDriver{ 71 | Extensions: []string{ 72 | "sqlite3_mod_regexp", 73 | }, 74 | }) 75 | 76 | Then, you can use this extension. 77 | 78 | rows, err := db.Query("select text from mytable where name regexp '^golang'") 79 | 80 | # Connection Hook 81 | 82 | You can hook and inject your code when the connection is established by setting 83 | ConnectHook to get the SQLiteConn. 84 | 85 | sql.Register("sqlite3_with_hook_example", 86 | &sqlite3.SQLiteDriver{ 87 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 88 | sqlite3conn = append(sqlite3conn, conn) 89 | return nil 90 | }, 91 | }) 92 | 93 | You can also use database/sql.Conn.Raw (Go >= 1.13): 94 | 95 | conn, err := db.Conn(context.Background()) 96 | // if err != nil { ... } 97 | defer conn.Close() 98 | err = conn.Raw(func (driverConn any) error { 99 | sqliteConn := driverConn.(*sqlite3.SQLiteConn) 100 | // ... use sqliteConn 101 | }) 102 | // if err != nil { ... } 103 | 104 | # Go SQlite3 Extensions 105 | 106 | If you want to register Go functions as SQLite extension functions 107 | you can make a custom driver by calling RegisterFunction from 108 | ConnectHook. 109 | 110 | regex = func(re, s string) (bool, error) { 111 | return regexp.MatchString(re, s) 112 | } 113 | sql.Register("sqlite3_extended", 114 | &sqlite3.SQLiteDriver{ 115 | ConnectHook: func(conn *sqlite3.SQLiteConn) error { 116 | return conn.RegisterFunc("regexp", regex, true) 117 | }, 118 | }) 119 | 120 | You can then use the custom driver by passing its name to sql.Open. 121 | 122 | var i int 123 | conn, err := sql.Open("sqlite3_extended", "./foo.db") 124 | if err != nil { 125 | panic(err) 126 | } 127 | err = db.QueryRow(`SELECT regexp("foo.*", "seafood")`).Scan(&i) 128 | if err != nil { 129 | panic(err) 130 | } 131 | 132 | See the documentation of RegisterFunc for more details. 133 | */ 134 | package sqlite3 135 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | /* 9 | #ifndef USE_LIBSQLITE3 10 | #include "sqlite3-binding.h" 11 | #else 12 | #include 13 | #endif 14 | */ 15 | import "C" 16 | import "syscall" 17 | 18 | // ErrNo inherit errno. 19 | type ErrNo int 20 | 21 | // ErrNoMask is mask code. 22 | const ErrNoMask C.int = 0xff 23 | 24 | // ErrNoExtended is extended errno. 25 | type ErrNoExtended int 26 | 27 | // Error implement sqlite error code. 28 | type Error struct { 29 | Code ErrNo /* The error code returned by SQLite */ 30 | ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */ 31 | SystemErrno syscall.Errno /* The system errno returned by the OS through SQLite, if applicable */ 32 | err string /* The error string returned by sqlite3_errmsg(), 33 | this usually contains more specific details. */ 34 | } 35 | 36 | // result codes from http://www.sqlite.org/c3ref/c_abort.html 37 | var ( 38 | ErrError = ErrNo(1) /* SQL error or missing database */ 39 | ErrInternal = ErrNo(2) /* Internal logic error in SQLite */ 40 | ErrPerm = ErrNo(3) /* Access permission denied */ 41 | ErrAbort = ErrNo(4) /* Callback routine requested an abort */ 42 | ErrBusy = ErrNo(5) /* The database file is locked */ 43 | ErrLocked = ErrNo(6) /* A table in the database is locked */ 44 | ErrNomem = ErrNo(7) /* A malloc() failed */ 45 | ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */ 46 | ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ 47 | ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */ 48 | ErrCorrupt = ErrNo(11) /* The database disk image is malformed */ 49 | ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ 50 | ErrFull = ErrNo(13) /* Insertion failed because database is full */ 51 | ErrCantOpen = ErrNo(14) /* Unable to open the database file */ 52 | ErrProtocol = ErrNo(15) /* Database lock protocol error */ 53 | ErrEmpty = ErrNo(16) /* Database is empty */ 54 | ErrSchema = ErrNo(17) /* The database schema changed */ 55 | ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */ 56 | ErrConstraint = ErrNo(19) /* Abort due to constraint violation */ 57 | ErrMismatch = ErrNo(20) /* Data type mismatch */ 58 | ErrMisuse = ErrNo(21) /* Library used incorrectly */ 59 | ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */ 60 | ErrAuth = ErrNo(23) /* Authorization denied */ 61 | ErrFormat = ErrNo(24) /* Auxiliary database format error */ 62 | ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ 63 | ErrNotADB = ErrNo(26) /* File opened that is not a database file */ 64 | ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */ 65 | ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */ 66 | ) 67 | 68 | // Error return error message from errno. 69 | func (err ErrNo) Error() string { 70 | return Error{Code: err}.Error() 71 | } 72 | 73 | // Extend return extended errno. 74 | func (err ErrNo) Extend(by int) ErrNoExtended { 75 | return ErrNoExtended(int(err) | (by << 8)) 76 | } 77 | 78 | // Error return error message that is extended code. 79 | func (err ErrNoExtended) Error() string { 80 | return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() 81 | } 82 | 83 | func (err Error) Error() string { 84 | var str string 85 | if err.err != "" { 86 | str = err.err 87 | } else { 88 | str = C.GoString(C.sqlite3_errstr(C.int(err.Code))) 89 | } 90 | if err.SystemErrno != 0 { 91 | str += ": " + err.SystemErrno.Error() 92 | } 93 | return str 94 | } 95 | 96 | // result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html 97 | var ( 98 | ErrIoErrRead = ErrIoErr.Extend(1) 99 | ErrIoErrShortRead = ErrIoErr.Extend(2) 100 | ErrIoErrWrite = ErrIoErr.Extend(3) 101 | ErrIoErrFsync = ErrIoErr.Extend(4) 102 | ErrIoErrDirFsync = ErrIoErr.Extend(5) 103 | ErrIoErrTruncate = ErrIoErr.Extend(6) 104 | ErrIoErrFstat = ErrIoErr.Extend(7) 105 | ErrIoErrUnlock = ErrIoErr.Extend(8) 106 | ErrIoErrRDlock = ErrIoErr.Extend(9) 107 | ErrIoErrDelete = ErrIoErr.Extend(10) 108 | ErrIoErrBlocked = ErrIoErr.Extend(11) 109 | ErrIoErrNoMem = ErrIoErr.Extend(12) 110 | ErrIoErrAccess = ErrIoErr.Extend(13) 111 | ErrIoErrCheckReservedLock = ErrIoErr.Extend(14) 112 | ErrIoErrLock = ErrIoErr.Extend(15) 113 | ErrIoErrClose = ErrIoErr.Extend(16) 114 | ErrIoErrDirClose = ErrIoErr.Extend(17) 115 | ErrIoErrSHMOpen = ErrIoErr.Extend(18) 116 | ErrIoErrSHMSize = ErrIoErr.Extend(19) 117 | ErrIoErrSHMLock = ErrIoErr.Extend(20) 118 | ErrIoErrSHMMap = ErrIoErr.Extend(21) 119 | ErrIoErrSeek = ErrIoErr.Extend(22) 120 | ErrIoErrDeleteNoent = ErrIoErr.Extend(23) 121 | ErrIoErrMMap = ErrIoErr.Extend(24) 122 | ErrIoErrGetTempPath = ErrIoErr.Extend(25) 123 | ErrIoErrConvPath = ErrIoErr.Extend(26) 124 | ErrLockedSharedCache = ErrLocked.Extend(1) 125 | ErrBusyRecovery = ErrBusy.Extend(1) 126 | ErrBusySnapshot = ErrBusy.Extend(2) 127 | ErrCantOpenNoTempDir = ErrCantOpen.Extend(1) 128 | ErrCantOpenIsDir = ErrCantOpen.Extend(2) 129 | ErrCantOpenFullPath = ErrCantOpen.Extend(3) 130 | ErrCantOpenConvPath = ErrCantOpen.Extend(4) 131 | ErrCorruptVTab = ErrCorrupt.Extend(1) 132 | ErrReadonlyRecovery = ErrReadonly.Extend(1) 133 | ErrReadonlyCantLock = ErrReadonly.Extend(2) 134 | ErrReadonlyRollback = ErrReadonly.Extend(3) 135 | ErrReadonlyDbMoved = ErrReadonly.Extend(4) 136 | ErrAbortRollback = ErrAbort.Extend(2) 137 | ErrConstraintCheck = ErrConstraint.Extend(1) 138 | ErrConstraintCommitHook = ErrConstraint.Extend(2) 139 | ErrConstraintForeignKey = ErrConstraint.Extend(3) 140 | ErrConstraintFunction = ErrConstraint.Extend(4) 141 | ErrConstraintNotNull = ErrConstraint.Extend(5) 142 | ErrConstraintPrimaryKey = ErrConstraint.Extend(6) 143 | ErrConstraintTrigger = ErrConstraint.Extend(7) 144 | ErrConstraintUnique = ErrConstraint.Extend(8) 145 | ErrConstraintVTab = ErrConstraint.Extend(9) 146 | ErrConstraintRowID = ErrConstraint.Extend(10) 147 | ErrNoticeRecoverWAL = ErrNotice.Extend(1) 148 | ErrNoticeRecoverRollback = ErrNotice.Extend(2) 149 | ErrWarningAutoIndex = ErrWarning.Extend(1) 150 | ) 151 | -------------------------------------------------------------------------------- /error_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build cgo 7 | // +build cgo 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "io/ioutil" 14 | "os" 15 | "path" 16 | "testing" 17 | ) 18 | 19 | func TestSimpleError(t *testing.T) { 20 | e := ErrError.Error() 21 | if e != "SQL logic error or missing database" && e != "SQL logic error" { 22 | t.Error("wrong error code: " + e) 23 | } 24 | } 25 | 26 | func TestCorruptDbErrors(t *testing.T) { 27 | dirName, err := ioutil.TempDir("", "sqlite3") 28 | if err != nil { 29 | t.Fatal(err) 30 | } 31 | defer os.RemoveAll(dirName) 32 | 33 | dbFileName := path.Join(dirName, "test.db") 34 | f, err := os.Create(dbFileName) 35 | if err != nil { 36 | t.Error(err) 37 | } 38 | f.Write([]byte{1, 2, 3, 4, 5}) 39 | f.Close() 40 | 41 | db, err := sql.Open("sqlite3", dbFileName) 42 | if err == nil { 43 | _, err = db.Exec("drop table foo") 44 | } 45 | 46 | sqliteErr := err.(Error) 47 | if sqliteErr.Code != ErrNotADB { 48 | t.Error("wrong error code for corrupted DB") 49 | } 50 | if err.Error() == "" { 51 | t.Error("wrong error string for corrupted DB") 52 | } 53 | db.Close() 54 | } 55 | 56 | func TestSqlLogicErrors(t *testing.T) { 57 | dirName, err := ioutil.TempDir("", "sqlite3") 58 | if err != nil { 59 | t.Fatal(err) 60 | } 61 | defer os.RemoveAll(dirName) 62 | 63 | dbFileName := path.Join(dirName, "test.db") 64 | db, err := sql.Open("sqlite3", dbFileName) 65 | if err != nil { 66 | t.Error(err) 67 | } 68 | defer db.Close() 69 | 70 | _, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)") 71 | if err != nil { 72 | t.Error(err) 73 | } 74 | 75 | const expectedErr = "table Foo already exists" 76 | _, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)") 77 | if err.Error() != expectedErr { 78 | t.Errorf("Unexpected error: %s, expected %s", err.Error(), expectedErr) 79 | } 80 | 81 | } 82 | 83 | func TestExtendedErrorCodes_ForeignKey(t *testing.T) { 84 | dirName, err := ioutil.TempDir("", "sqlite3-err") 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | defer os.RemoveAll(dirName) 89 | 90 | dbFileName := path.Join(dirName, "test.db") 91 | db, err := sql.Open("sqlite3", dbFileName) 92 | if err != nil { 93 | t.Error(err) 94 | } 95 | defer db.Close() 96 | 97 | _, err = db.Exec("PRAGMA foreign_keys=ON;") 98 | if err != nil { 99 | t.Errorf("PRAGMA foreign_keys=ON: %v", err) 100 | } 101 | 102 | _, err = db.Exec(`CREATE TABLE Foo ( 103 | id INTEGER PRIMARY KEY AUTOINCREMENT, 104 | value INTEGER NOT NULL, 105 | ref INTEGER NULL REFERENCES Foo (id), 106 | UNIQUE(value) 107 | );`) 108 | if err != nil { 109 | t.Error(err) 110 | } 111 | 112 | _, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (100, 100);") 113 | if err == nil { 114 | t.Error("No error!") 115 | } else { 116 | sqliteErr := err.(Error) 117 | if sqliteErr.Code != ErrConstraint { 118 | t.Errorf("Wrong basic error code: %d != %d", 119 | sqliteErr.Code, ErrConstraint) 120 | } 121 | if sqliteErr.ExtendedCode != ErrConstraintForeignKey { 122 | t.Errorf("Wrong extended error code: %d != %d", 123 | sqliteErr.ExtendedCode, ErrConstraintForeignKey) 124 | } 125 | } 126 | 127 | } 128 | 129 | func TestExtendedErrorCodes_NotNull(t *testing.T) { 130 | dirName, err := ioutil.TempDir("", "sqlite3-err") 131 | if err != nil { 132 | t.Fatal(err) 133 | } 134 | defer os.RemoveAll(dirName) 135 | 136 | dbFileName := path.Join(dirName, "test.db") 137 | db, err := sql.Open("sqlite3", dbFileName) 138 | if err != nil { 139 | t.Error(err) 140 | } 141 | defer db.Close() 142 | 143 | _, err = db.Exec("PRAGMA foreign_keys=ON;") 144 | if err != nil { 145 | t.Errorf("PRAGMA foreign_keys=ON: %v", err) 146 | } 147 | 148 | _, err = db.Exec(`CREATE TABLE Foo ( 149 | id INTEGER PRIMARY KEY AUTOINCREMENT, 150 | value INTEGER NOT NULL, 151 | ref INTEGER NULL REFERENCES Foo (id), 152 | UNIQUE(value) 153 | );`) 154 | if err != nil { 155 | t.Error(err) 156 | } 157 | 158 | res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);") 159 | if err != nil { 160 | t.Fatalf("Creating first row: %v", err) 161 | } 162 | 163 | id, err := res.LastInsertId() 164 | if err != nil { 165 | t.Fatalf("Retrieving last insert id: %v", err) 166 | } 167 | 168 | _, err = db.Exec("INSERT INTO Foo (ref) VALUES (?);", id) 169 | if err == nil { 170 | t.Error("No error!") 171 | } else { 172 | sqliteErr := err.(Error) 173 | if sqliteErr.Code != ErrConstraint { 174 | t.Errorf("Wrong basic error code: %d != %d", 175 | sqliteErr.Code, ErrConstraint) 176 | } 177 | if sqliteErr.ExtendedCode != ErrConstraintNotNull { 178 | t.Errorf("Wrong extended error code: %d != %d", 179 | sqliteErr.ExtendedCode, ErrConstraintNotNull) 180 | } 181 | } 182 | 183 | } 184 | 185 | func TestExtendedErrorCodes_Unique(t *testing.T) { 186 | dirName, err := ioutil.TempDir("", "sqlite3-err") 187 | if err != nil { 188 | t.Fatal(err) 189 | } 190 | defer os.RemoveAll(dirName) 191 | 192 | dbFileName := path.Join(dirName, "test.db") 193 | db, err := sql.Open("sqlite3", dbFileName) 194 | if err != nil { 195 | t.Error(err) 196 | } 197 | defer db.Close() 198 | 199 | _, err = db.Exec("PRAGMA foreign_keys=ON;") 200 | if err != nil { 201 | t.Errorf("PRAGMA foreign_keys=ON: %v", err) 202 | } 203 | 204 | _, err = db.Exec(`CREATE TABLE Foo ( 205 | id INTEGER PRIMARY KEY AUTOINCREMENT, 206 | value INTEGER NOT NULL, 207 | ref INTEGER NULL REFERENCES Foo (id), 208 | UNIQUE(value) 209 | );`) 210 | if err != nil { 211 | t.Error(err) 212 | } 213 | 214 | res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);") 215 | if err != nil { 216 | t.Fatalf("Creating first row: %v", err) 217 | } 218 | 219 | id, err := res.LastInsertId() 220 | if err != nil { 221 | t.Fatalf("Retrieving last insert id: %v", err) 222 | } 223 | 224 | _, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (?, 100);", id) 225 | if err == nil { 226 | t.Error("No error!") 227 | } else { 228 | sqliteErr := err.(Error) 229 | if sqliteErr.Code != ErrConstraint { 230 | t.Errorf("Wrong basic error code: %d != %d", 231 | sqliteErr.Code, ErrConstraint) 232 | } 233 | if sqliteErr.ExtendedCode != ErrConstraintUnique { 234 | t.Errorf("Wrong extended error code: %d != %d", 235 | sqliteErr.ExtendedCode, ErrConstraintUnique) 236 | } 237 | extended := sqliteErr.Code.Extend(3).Error() 238 | expected := "constraint failed" 239 | if extended != expected { 240 | t.Errorf("Wrong basic error code: %q != %q", 241 | extended, expected) 242 | } 243 | } 244 | } 245 | 246 | func TestError_SystemErrno(t *testing.T) { 247 | _, n, _ := Version() 248 | if n < 3012000 { 249 | t.Skip("sqlite3_system_errno requires sqlite3 >= 3.12.0") 250 | } 251 | 252 | // open a non-existent database in read-only mode so we get an IO error. 253 | db, err := sql.Open("sqlite3", "file:nonexistent.db?mode=ro") 254 | if err != nil { 255 | t.Fatal(err) 256 | } 257 | defer db.Close() 258 | 259 | err = db.Ping() 260 | if err == nil { 261 | t.Fatal("expected error pinging read-only non-existent database, but got nil") 262 | } 263 | 264 | serr, ok := err.(Error) 265 | if !ok { 266 | t.Fatalf("expected error to be of type Error, but got %[1]T %[1]v", err) 267 | } 268 | 269 | if serr.SystemErrno == 0 { 270 | t.Fatal("expected SystemErrno to be set") 271 | } 272 | 273 | if !os.IsNotExist(serr.SystemErrno) { 274 | t.Errorf("expected SystemErrno to be a not exists error, but got %v", serr.SystemErrno) 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-sqlite3 2 | 3 | go 1.19 4 | 5 | retract ( 6 | [v2.0.0+incompatible, v2.0.6+incompatible] // Accidental; no major changes or features. 7 | ) 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattn/go-sqlite3/f76bae4b0044cbba8fb2c72b8e4559e8fbcffd86/go.sum -------------------------------------------------------------------------------- /sqlite3_context.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | /* 9 | 10 | #ifndef USE_LIBSQLITE3 11 | #include "sqlite3-binding.h" 12 | #else 13 | #include 14 | #endif 15 | #include 16 | // These wrappers are necessary because SQLITE_TRANSIENT 17 | // is a pointer constant, and cgo doesn't translate them correctly. 18 | 19 | static inline void my_result_text(sqlite3_context *ctx, char *p, int np) { 20 | sqlite3_result_text(ctx, p, np, SQLITE_TRANSIENT); 21 | } 22 | 23 | static inline void my_result_blob(sqlite3_context *ctx, void *p, int np) { 24 | sqlite3_result_blob(ctx, p, np, SQLITE_TRANSIENT); 25 | } 26 | */ 27 | import "C" 28 | 29 | import ( 30 | "math" 31 | "reflect" 32 | "unsafe" 33 | ) 34 | 35 | const i64 = unsafe.Sizeof(int(0)) > 4 36 | 37 | // SQLiteContext behave sqlite3_context 38 | type SQLiteContext C.sqlite3_context 39 | 40 | // ResultBool sets the result of an SQL function. 41 | func (c *SQLiteContext) ResultBool(b bool) { 42 | if b { 43 | c.ResultInt(1) 44 | } else { 45 | c.ResultInt(0) 46 | } 47 | } 48 | 49 | // ResultBlob sets the result of an SQL function. 50 | // See: sqlite3_result_blob, http://sqlite.org/c3ref/result_blob.html 51 | func (c *SQLiteContext) ResultBlob(b []byte) { 52 | if i64 && len(b) > math.MaxInt32 { 53 | C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) 54 | return 55 | } 56 | var p *byte 57 | if len(b) > 0 { 58 | p = &b[0] 59 | } 60 | C.my_result_blob((*C.sqlite3_context)(c), unsafe.Pointer(p), C.int(len(b))) 61 | } 62 | 63 | // ResultDouble sets the result of an SQL function. 64 | // See: sqlite3_result_double, http://sqlite.org/c3ref/result_blob.html 65 | func (c *SQLiteContext) ResultDouble(d float64) { 66 | C.sqlite3_result_double((*C.sqlite3_context)(c), C.double(d)) 67 | } 68 | 69 | // ResultInt sets the result of an SQL function. 70 | // See: sqlite3_result_int, http://sqlite.org/c3ref/result_blob.html 71 | func (c *SQLiteContext) ResultInt(i int) { 72 | if i64 && (i > math.MaxInt32 || i < math.MinInt32) { 73 | C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) 74 | } else { 75 | C.sqlite3_result_int((*C.sqlite3_context)(c), C.int(i)) 76 | } 77 | } 78 | 79 | // ResultInt64 sets the result of an SQL function. 80 | // See: sqlite3_result_int64, http://sqlite.org/c3ref/result_blob.html 81 | func (c *SQLiteContext) ResultInt64(i int64) { 82 | C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) 83 | } 84 | 85 | // ResultNull sets the result of an SQL function. 86 | // See: sqlite3_result_null, http://sqlite.org/c3ref/result_blob.html 87 | func (c *SQLiteContext) ResultNull() { 88 | C.sqlite3_result_null((*C.sqlite3_context)(c)) 89 | } 90 | 91 | // ResultText sets the result of an SQL function. 92 | // See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html 93 | func (c *SQLiteContext) ResultText(s string) { 94 | h := (*reflect.StringHeader)(unsafe.Pointer(&s)) 95 | cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) 96 | C.my_result_text((*C.sqlite3_context)(c), cs, l) 97 | } 98 | 99 | // ResultZeroblob sets the result of an SQL function. 100 | // See: sqlite3_result_zeroblob, http://sqlite.org/c3ref/result_blob.html 101 | func (c *SQLiteContext) ResultZeroblob(n int) { 102 | C.sqlite3_result_zeroblob((*C.sqlite3_context)(c), C.int(n)) 103 | } 104 | -------------------------------------------------------------------------------- /sqlite3_func_crypt.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | import ( 9 | "crypto/sha1" 10 | "crypto/sha256" 11 | "crypto/sha512" 12 | ) 13 | 14 | // This file provides several different implementations for the 15 | // default embedded sqlite_crypt function. 16 | // This function is uses a caesar-cypher by default 17 | // and is used within the UserAuthentication module to encode 18 | // the password. 19 | // 20 | // The provided functions can be used as an overload to the sqlite_crypt 21 | // function through the use of the RegisterFunc on the connection. 22 | // 23 | // Because the functions can serv a purpose to an end-user 24 | // without using the UserAuthentication module 25 | // the functions are default compiled in. 26 | // 27 | // From SQLITE3 - user-auth.txt 28 | // The sqlite_user.pw field is encoded by a built-in SQL function 29 | // "sqlite_crypt(X,Y)". The two arguments are both BLOBs. The first argument 30 | // is the plaintext password supplied to the sqlite3_user_authenticate() 31 | // interface. The second argument is the sqlite_user.pw value and is supplied 32 | // so that the function can extract the "salt" used by the password encoder. 33 | // The result of sqlite_crypt(X,Y) is another blob which is the value that 34 | // ends up being stored in sqlite_user.pw. To verify credentials X supplied 35 | // by the sqlite3_user_authenticate() routine, SQLite runs: 36 | // 37 | // sqlite_user.pw == sqlite_crypt(X, sqlite_user.pw) 38 | // 39 | // To compute an appropriate sqlite_user.pw value from a new or modified 40 | // password X, sqlite_crypt(X,NULL) is run. A new random salt is selected 41 | // when the second argument is NULL. 42 | // 43 | // The built-in version of of sqlite_crypt() uses a simple Caesar-cypher 44 | // which prevents passwords from being revealed by searching the raw database 45 | // for ASCII text, but is otherwise trivally broken. For better password 46 | // security, the database should be encrypted using the SQLite Encryption 47 | // Extension or similar technology. Or, the application can use the 48 | // sqlite3_create_function() interface to provide an alternative 49 | // implementation of sqlite_crypt() that computes a stronger password hash, 50 | // perhaps using a cryptographic hash function like SHA1. 51 | 52 | // CryptEncoderSHA1 encodes a password with SHA1 53 | func CryptEncoderSHA1(pass []byte, hash any) []byte { 54 | h := sha1.Sum(pass) 55 | return h[:] 56 | } 57 | 58 | // CryptEncoderSSHA1 encodes a password with SHA1 with the 59 | // configured salt. 60 | func CryptEncoderSSHA1(salt string) func(pass []byte, hash any) []byte { 61 | return func(pass []byte, hash any) []byte { 62 | s := []byte(salt) 63 | p := append(pass, s...) 64 | h := sha1.Sum(p) 65 | return h[:] 66 | } 67 | } 68 | 69 | // CryptEncoderSHA256 encodes a password with SHA256 70 | func CryptEncoderSHA256(pass []byte, hash any) []byte { 71 | h := sha256.Sum256(pass) 72 | return h[:] 73 | } 74 | 75 | // CryptEncoderSSHA256 encodes a password with SHA256 76 | // with the configured salt 77 | func CryptEncoderSSHA256(salt string) func(pass []byte, hash any) []byte { 78 | return func(pass []byte, hash any) []byte { 79 | s := []byte(salt) 80 | p := append(pass, s...) 81 | h := sha256.Sum256(p) 82 | return h[:] 83 | } 84 | } 85 | 86 | // CryptEncoderSHA384 encodes a password with SHA384 87 | func CryptEncoderSHA384(pass []byte, hash any) []byte { 88 | h := sha512.Sum384(pass) 89 | return h[:] 90 | } 91 | 92 | // CryptEncoderSSHA384 encodes a password with SHA384 93 | // with the configured salt 94 | func CryptEncoderSSHA384(salt string) func(pass []byte, hash any) []byte { 95 | return func(pass []byte, hash any) []byte { 96 | s := []byte(salt) 97 | p := append(pass, s...) 98 | h := sha512.Sum384(p) 99 | return h[:] 100 | } 101 | } 102 | 103 | // CryptEncoderSHA512 encodes a password with SHA512 104 | func CryptEncoderSHA512(pass []byte, hash any) []byte { 105 | h := sha512.Sum512(pass) 106 | return h[:] 107 | } 108 | 109 | // CryptEncoderSSHA512 encodes a password with SHA512 110 | // with the configured salt 111 | func CryptEncoderSSHA512(salt string) func(pass []byte, hash any) []byte { 112 | return func(pass []byte, hash any) []byte { 113 | s := []byte(salt) 114 | p := append(pass, s...) 115 | h := sha512.Sum512(p) 116 | return h[:] 117 | } 118 | } 119 | 120 | // EOF 121 | -------------------------------------------------------------------------------- /sqlite3_func_crypt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package sqlite3 7 | 8 | import ( 9 | "fmt" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | // TestCryptEncoders to increase coverage 15 | func TestCryptEncoders(t *testing.T) { 16 | tests := []struct { 17 | enc string 18 | salt string 19 | expected string 20 | }{ 21 | {"sha1", "", "d033e22ae348aeb5660fc2140aec35850c4da997"}, 22 | {"sha256", "", "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"}, 23 | {"sha384", "", "9ca694a90285c034432c9550421b7b9dbd5c0f4b6673f05f6dbce58052ba20e4248041956ee8c9a2ec9f10290cdc0782"}, 24 | {"sha512", "", "c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec"}, 25 | {"ssha1", "salt", "9bc7aa55f08fdad935c3f8362d3f48bcf70eb280"}, 26 | {"ssha256", "salt", "f9a81477552594c79f2abc3fc099daa896a6e3a3590a55ffa392b6000412e80b"}, 27 | {"ssha384", "salt", "9ed776b477fcfc1b5e584989e8d770f5e17d98a7643546a63c2b07d4ab00f1348f6b8e73103d3a23554f727136e8c215"}, 28 | {"ssha512", "salt", "3c4a79782143337be4492be072abcfe979dd703c00541a8c39a0f3df4bab2029c050cf46fddc47090b5b04ac537b3e78189e3de16e601e859f95c51ac9f6dafb"}, 29 | } 30 | 31 | for _, e := range tests { 32 | var fn func(pass []byte, hash any) []byte 33 | switch e.enc { 34 | case "sha1": 35 | fn = CryptEncoderSHA1 36 | case "ssha1": 37 | fn = CryptEncoderSSHA1(e.salt) 38 | case "sha256": 39 | fn = CryptEncoderSHA256 40 | case "ssha256": 41 | fn = CryptEncoderSSHA256(e.salt) 42 | case "sha384": 43 | fn = CryptEncoderSHA384 44 | case "ssha384": 45 | fn = CryptEncoderSSHA384(e.salt) 46 | case "sha512": 47 | fn = CryptEncoderSHA512 48 | case "ssha512": 49 | fn = CryptEncoderSSHA512(e.salt) 50 | } 51 | 52 | h := fn([]byte("admin"), nil) 53 | if strings.Compare(fmt.Sprintf("%x", h), e.expected) != 0 { 54 | t.Fatalf("Invalid %s hash: expected: %s; got: %x", strings.ToUpper(e.enc), e.expected, h) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sqlite3_go113_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build go1.13 && cgo 7 | // +build go1.13,cgo 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "context" 13 | "database/sql" 14 | "database/sql/driver" 15 | "errors" 16 | "os" 17 | "testing" 18 | ) 19 | 20 | func TestBeginTxCancel(t *testing.T) { 21 | srcTempFilename := TempFilename(t) 22 | defer os.Remove(srcTempFilename) 23 | 24 | db, err := sql.Open("sqlite3", srcTempFilename) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | db.SetMaxOpenConns(10) 30 | db.SetMaxIdleConns(5) 31 | 32 | defer db.Close() 33 | initDatabase(t, db, 100) 34 | 35 | // create several go-routines to expose racy issue 36 | for i := 0; i < 1000; i++ { 37 | func() { 38 | ctx, cancel := context.WithCancel(context.Background()) 39 | conn, err := db.Conn(ctx) 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | defer func() { 44 | if err := conn.Close(); err != nil { 45 | t.Error(err) 46 | } 47 | }() 48 | 49 | err = conn.Raw(func(driverConn any) error { 50 | d, ok := driverConn.(driver.ConnBeginTx) 51 | if !ok { 52 | t.Fatal("unexpected: wrong type") 53 | } 54 | // checks that conn.Raw can be used to get *SQLiteConn 55 | if _, ok = driverConn.(*SQLiteConn); !ok { 56 | t.Fatalf("conn.Raw() driverConn type=%T, expected *SQLiteConn", driverConn) 57 | } 58 | 59 | go cancel() // make it cancel concurrently with exec("BEGIN"); 60 | tx, err := d.BeginTx(ctx, driver.TxOptions{}) 61 | switch err { 62 | case nil: 63 | switch err := tx.Rollback(); err { 64 | case nil, sql.ErrTxDone: 65 | default: 66 | return err 67 | } 68 | case context.Canceled: 69 | default: 70 | // must not fail with "cannot start a transaction within a transaction" 71 | return err 72 | } 73 | return nil 74 | }) 75 | if err != nil { 76 | t.Fatal(err) 77 | } 78 | }() 79 | } 80 | } 81 | 82 | func TestStmtReadonly(t *testing.T) { 83 | db, err := sql.Open("sqlite3", ":memory:") 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | 88 | _, err = db.Exec("CREATE TABLE t (count INT)") 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | 93 | isRO := func(query string) bool { 94 | c, err := db.Conn(context.Background()) 95 | if err != nil { 96 | return false 97 | } 98 | 99 | var ro bool 100 | c.Raw(func(dc any) error { 101 | stmt, err := dc.(*SQLiteConn).Prepare(query) 102 | if err != nil { 103 | return err 104 | } 105 | if stmt == nil { 106 | return errors.New("stmt is nil") 107 | } 108 | ro = stmt.(*SQLiteStmt).Readonly() 109 | return nil 110 | }) 111 | return ro // On errors ro will remain false. 112 | } 113 | 114 | if !isRO(`select * from t`) { 115 | t.Error("select not seen as read-only") 116 | } 117 | if isRO(`insert into t values (1), (2)`) { 118 | t.Error("insert seen as read-only") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /sqlite3_go18.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build cgo && go1.8 7 | // +build cgo,go1.8 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql/driver" 13 | 14 | "context" 15 | ) 16 | 17 | // Ping implement Pinger. 18 | func (c *SQLiteConn) Ping(ctx context.Context) error { 19 | if c.db == nil { 20 | // must be ErrBadConn for sql to close the database 21 | return driver.ErrBadConn 22 | } 23 | return nil 24 | } 25 | 26 | // QueryContext implement QueryerContext. 27 | func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { 28 | return c.query(ctx, query, args) 29 | } 30 | 31 | // ExecContext implement ExecerContext. 32 | func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { 33 | return c.exec(ctx, query, args) 34 | } 35 | 36 | // PrepareContext implement ConnPrepareContext. 37 | func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { 38 | return c.prepare(ctx, query) 39 | } 40 | 41 | // BeginTx implement ConnBeginTx. 42 | func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { 43 | return c.begin(ctx) 44 | } 45 | 46 | // QueryContext implement QueryerContext. 47 | func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { 48 | return s.query(ctx, args) 49 | } 50 | 51 | // ExecContext implement ExecerContext. 52 | func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { 53 | return s.exec(ctx, args) 54 | } 55 | -------------------------------------------------------------------------------- /sqlite3_go18_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build go1.8 && cgo 7 | // +build go1.8,cgo 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "context" 13 | "database/sql" 14 | "fmt" 15 | "io/ioutil" 16 | "math/rand" 17 | "os" 18 | "sync" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | func TestNamedParams(t *testing.T) { 24 | tempFilename := TempFilename(t) 25 | defer os.Remove(tempFilename) 26 | db, err := sql.Open("sqlite3", tempFilename) 27 | if err != nil { 28 | t.Fatal("Failed to open database:", err) 29 | } 30 | defer db.Close() 31 | 32 | _, err = db.Exec(` 33 | create table foo (id integer, name text, extra text); 34 | `) 35 | if err != nil { 36 | t.Error("Failed to call db.Query:", err) 37 | } 38 | 39 | _, err = db.Exec(`insert into foo(id, name, extra) values(:id, :name, :name)`, sql.Named("name", "foo"), sql.Named("id", 1)) 40 | if err != nil { 41 | t.Error("Failed to call db.Exec:", err) 42 | } 43 | 44 | row := db.QueryRow(`select id, extra from foo where id = :id and extra = :extra`, sql.Named("id", 1), sql.Named("extra", "foo")) 45 | if row == nil { 46 | t.Error("Failed to call db.QueryRow") 47 | } 48 | var id int 49 | var extra string 50 | err = row.Scan(&id, &extra) 51 | if err != nil { 52 | t.Error("Failed to db.Scan:", err) 53 | } 54 | if id != 1 || extra != "foo" { 55 | t.Error("Failed to db.QueryRow: not matched results") 56 | } 57 | } 58 | 59 | var ( 60 | testTableStatements = []string{ 61 | `DROP TABLE IF EXISTS test_table`, 62 | ` 63 | CREATE TABLE IF NOT EXISTS test_table ( 64 | key1 VARCHAR(64) PRIMARY KEY, 65 | key_id VARCHAR(64) NOT NULL, 66 | key2 VARCHAR(64) NOT NULL, 67 | key3 VARCHAR(64) NOT NULL, 68 | key4 VARCHAR(64) NOT NULL, 69 | key5 VARCHAR(64) NOT NULL, 70 | key6 VARCHAR(64) NOT NULL, 71 | data BLOB NOT NULL 72 | );`, 73 | } 74 | letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 75 | ) 76 | 77 | func randStringBytes(n int) string { 78 | b := make([]byte, n) 79 | for i := range b { 80 | b[i] = letterBytes[rand.Intn(len(letterBytes))] 81 | } 82 | return string(b) 83 | } 84 | 85 | func initDatabase(t *testing.T, db *sql.DB, rowCount int64) { 86 | for _, query := range testTableStatements { 87 | _, err := db.Exec(query) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | } 92 | for i := int64(0); i < rowCount; i++ { 93 | query := `INSERT INTO test_table 94 | (key1, key_id, key2, key3, key4, key5, key6, data) 95 | VALUES 96 | (?, ?, ?, ?, ?, ?, ?, ?);` 97 | args := []interface{}{ 98 | randStringBytes(50), 99 | fmt.Sprint(i), 100 | randStringBytes(50), 101 | randStringBytes(50), 102 | randStringBytes(50), 103 | randStringBytes(50), 104 | randStringBytes(50), 105 | randStringBytes(50), 106 | randStringBytes(2048), 107 | } 108 | _, err := db.Exec(query, args...) 109 | if err != nil { 110 | t.Fatal(err) 111 | } 112 | } 113 | } 114 | 115 | func TestShortTimeout(t *testing.T) { 116 | srcTempFilename := TempFilename(t) 117 | defer os.Remove(srcTempFilename) 118 | 119 | db, err := sql.Open("sqlite3", srcTempFilename) 120 | if err != nil { 121 | t.Fatal(err) 122 | } 123 | defer db.Close() 124 | initDatabase(t, db, 100) 125 | 126 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond) 127 | defer cancel() 128 | query := `SELECT key1, key_id, key2, key3, key4, key5, key6, data 129 | FROM test_table 130 | ORDER BY key2 ASC` 131 | _, err = db.QueryContext(ctx, query) 132 | if err != nil && err != context.DeadlineExceeded { 133 | t.Fatal(err) 134 | } 135 | if ctx.Err() != nil && ctx.Err() != context.DeadlineExceeded { 136 | t.Fatal(ctx.Err()) 137 | } 138 | } 139 | 140 | func TestExecContextCancel(t *testing.T) { 141 | srcTempFilename := TempFilename(t) 142 | defer os.Remove(srcTempFilename) 143 | 144 | db, err := sql.Open("sqlite3", srcTempFilename) 145 | if err != nil { 146 | t.Fatal(err) 147 | } 148 | 149 | defer db.Close() 150 | 151 | ts := time.Now() 152 | initDatabase(t, db, 1000) 153 | spent := time.Since(ts) 154 | const minTestTime = 100 * time.Millisecond 155 | if spent < minTestTime { 156 | t.Skipf("test will be too racy (spent=%s < min=%s) as ExecContext below will be too fast.", 157 | spent.String(), minTestTime.String(), 158 | ) 159 | } 160 | 161 | // expected to be extremely slow query 162 | q := ` 163 | INSERT INTO test_table (key1, key_id, key2, key3, key4, key5, key6, data) 164 | SELECT t1.key1 || t2.key1, t1.key_id || t2.key_id, t1.key2 || t2.key2, t1.key3 || t2.key3, t1.key4 || t2.key4, t1.key5 || t2.key5, t1.key6 || t2.key6, t1.data || t2.data 165 | FROM test_table t1 LEFT OUTER JOIN test_table t2` 166 | // expect query above take ~ same time as setup above 167 | // This is racy: the context must be valid so sql/db.ExecContext calls the sqlite3 driver. 168 | // It starts the query, the context expires, then calls sqlite3_interrupt 169 | ctx, cancel := context.WithTimeout(context.Background(), minTestTime/2) 170 | defer cancel() 171 | ts = time.Now() 172 | r, err := db.ExecContext(ctx, q) 173 | // racy check 174 | if r != nil { 175 | n, err := r.RowsAffected() 176 | t.Logf("query should not have succeeded: rows=%d; err=%v; duration=%s", 177 | n, err, time.Since(ts).String()) 178 | } 179 | if err != context.DeadlineExceeded { 180 | t.Fatal(err, ctx.Err()) 181 | } 182 | } 183 | 184 | func TestQueryRowContextCancel(t *testing.T) { 185 | srcTempFilename := TempFilename(t) 186 | defer os.Remove(srcTempFilename) 187 | 188 | db, err := sql.Open("sqlite3", srcTempFilename) 189 | if err != nil { 190 | t.Fatal(err) 191 | } 192 | defer db.Close() 193 | initDatabase(t, db, 100) 194 | 195 | const query = `SELECT key_id FROM test_table ORDER BY key2 ASC` 196 | var keyID string 197 | unexpectedErrors := make(map[string]int) 198 | for i := 0; i < 10000; i++ { 199 | ctx, cancel := context.WithCancel(context.Background()) 200 | row := db.QueryRowContext(ctx, query) 201 | 202 | cancel() 203 | // it is fine to get "nil" as context cancellation can be handled with delay 204 | if err := row.Scan(&keyID); err != nil && err != context.Canceled { 205 | if err.Error() == "sql: Rows are closed" { 206 | // see https://github.com/golang/go/issues/24431 207 | // fixed in 1.11.1 to properly return context error 208 | continue 209 | } 210 | unexpectedErrors[err.Error()]++ 211 | } 212 | } 213 | for errText, count := range unexpectedErrors { 214 | t.Error(errText, count) 215 | } 216 | } 217 | 218 | func TestQueryRowContextCancelParallel(t *testing.T) { 219 | srcTempFilename := TempFilename(t) 220 | defer os.Remove(srcTempFilename) 221 | 222 | db, err := sql.Open("sqlite3", srcTempFilename) 223 | if err != nil { 224 | t.Fatal(err) 225 | } 226 | db.SetMaxOpenConns(10) 227 | db.SetMaxIdleConns(5) 228 | 229 | defer db.Close() 230 | initDatabase(t, db, 100) 231 | 232 | const query = `SELECT key_id FROM test_table ORDER BY key2 ASC` 233 | wg := sync.WaitGroup{} 234 | defer wg.Wait() 235 | 236 | testCtx, cancel := context.WithCancel(context.Background()) 237 | defer cancel() 238 | 239 | for i := 0; i < 10; i++ { 240 | wg.Add(1) 241 | go func() { 242 | defer wg.Done() 243 | 244 | var keyID string 245 | for { 246 | select { 247 | case <-testCtx.Done(): 248 | return 249 | default: 250 | } 251 | ctx, cancel := context.WithCancel(context.Background()) 252 | row := db.QueryRowContext(ctx, query) 253 | 254 | cancel() 255 | _ = row.Scan(&keyID) // see TestQueryRowContextCancel 256 | } 257 | }() 258 | } 259 | 260 | var keyID string 261 | for i := 0; i < 10000; i++ { 262 | // note that testCtx is not cancelled during query execution 263 | row := db.QueryRowContext(testCtx, query) 264 | 265 | if err := row.Scan(&keyID); err != nil { 266 | t.Fatal(i, err) 267 | } 268 | } 269 | } 270 | 271 | func TestExecCancel(t *testing.T) { 272 | db, err := sql.Open("sqlite3", ":memory:") 273 | if err != nil { 274 | t.Fatal(err) 275 | } 276 | defer db.Close() 277 | 278 | if _, err = db.Exec("create table foo (id integer primary key)"); err != nil { 279 | t.Fatal(err) 280 | } 281 | 282 | for n := 0; n < 100; n++ { 283 | ctx, cancel := context.WithCancel(context.Background()) 284 | _, err = db.ExecContext(ctx, "insert into foo (id) values (?)", n) 285 | cancel() 286 | if err != nil { 287 | t.Fatal(err) 288 | } 289 | } 290 | } 291 | 292 | func doTestOpenContext(t *testing.T, option string) (string, error) { 293 | tempFilename := TempFilename(t) 294 | url := tempFilename + option 295 | 296 | defer func() { 297 | err := os.Remove(tempFilename) 298 | if err != nil { 299 | t.Error("temp file remove error:", err) 300 | } 301 | }() 302 | 303 | db, err := sql.Open("sqlite3", url) 304 | if err != nil { 305 | return "Failed to open database:", err 306 | } 307 | 308 | defer func() { 309 | err = db.Close() 310 | if err != nil { 311 | t.Error("db close error:", err) 312 | } 313 | }() 314 | 315 | ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second) 316 | err = db.PingContext(ctx) 317 | cancel() 318 | if err != nil { 319 | return "ping error:", err 320 | } 321 | 322 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 323 | _, err = db.ExecContext(ctx, "drop table foo") 324 | cancel() 325 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 326 | _, err = db.ExecContext(ctx, "create table foo (id integer)") 327 | cancel() 328 | if err != nil { 329 | return "Failed to create table:", err 330 | } 331 | 332 | if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() { 333 | return "Failed to create ./foo.db", nil 334 | } 335 | 336 | return "", nil 337 | } 338 | 339 | func TestOpenContext(t *testing.T) { 340 | cases := map[string]bool{ 341 | "": true, 342 | "?_txlock=immediate": true, 343 | "?_txlock=deferred": true, 344 | "?_txlock=exclusive": true, 345 | "?_txlock=bogus": false, 346 | } 347 | for option, expectedPass := range cases { 348 | result, err := doTestOpenContext(t, option) 349 | if result == "" { 350 | if !expectedPass { 351 | errmsg := fmt.Sprintf("_txlock error not caught at dbOpen with option: %s", option) 352 | t.Fatal(errmsg) 353 | } 354 | } else if expectedPass { 355 | if err == nil { 356 | t.Fatal(result) 357 | } else { 358 | t.Fatal(result, err) 359 | } 360 | } 361 | } 362 | } 363 | 364 | func TestFileCopyTruncate(t *testing.T) { 365 | var err error 366 | tempFilename := TempFilename(t) 367 | 368 | defer func() { 369 | err = os.Remove(tempFilename) 370 | if err != nil { 371 | t.Error("temp file remove error:", err) 372 | } 373 | }() 374 | 375 | db, err := sql.Open("sqlite3", tempFilename) 376 | if err != nil { 377 | t.Fatal("open error:", err) 378 | } 379 | 380 | defer func() { 381 | err = db.Close() 382 | if err != nil { 383 | t.Error("db close error:", err) 384 | } 385 | }() 386 | 387 | ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second) 388 | err = db.PingContext(ctx) 389 | cancel() 390 | if err != nil { 391 | t.Fatal("ping error:", err) 392 | } 393 | 394 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 395 | _, err = db.ExecContext(ctx, "drop table foo") 396 | cancel() 397 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 398 | _, err = db.ExecContext(ctx, "create table foo (id integer)") 399 | cancel() 400 | if err != nil { 401 | t.Fatal("create table error:", err) 402 | } 403 | 404 | // copy db to new file 405 | var data []byte 406 | data, err = ioutil.ReadFile(tempFilename) 407 | if err != nil { 408 | t.Fatal("read file error:", err) 409 | } 410 | 411 | var f *os.File 412 | f, err = os.Create(tempFilename + "-db-copy") 413 | if err != nil { 414 | t.Fatal("create file error:", err) 415 | } 416 | 417 | defer func() { 418 | err = os.Remove(tempFilename + "-db-copy") 419 | if err != nil { 420 | t.Error("temp file moved remove error:", err) 421 | } 422 | }() 423 | 424 | _, err = f.Write(data) 425 | if err != nil { 426 | f.Close() 427 | t.Fatal("write file error:", err) 428 | } 429 | err = f.Close() 430 | if err != nil { 431 | t.Fatal("close file error:", err) 432 | } 433 | 434 | // truncate current db file 435 | f, err = os.OpenFile(tempFilename, os.O_WRONLY|os.O_TRUNC, 0666) 436 | if err != nil { 437 | t.Fatal("open file error:", err) 438 | } 439 | err = f.Close() 440 | if err != nil { 441 | t.Fatal("close file error:", err) 442 | } 443 | 444 | // test db after file truncate 445 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 446 | err = db.PingContext(ctx) 447 | cancel() 448 | if err != nil { 449 | t.Fatal("ping error:", err) 450 | } 451 | 452 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 453 | _, err = db.ExecContext(ctx, "drop table foo") 454 | cancel() 455 | if err == nil { 456 | t.Fatal("drop table no error") 457 | } 458 | 459 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 460 | _, err = db.ExecContext(ctx, "create table foo (id integer)") 461 | cancel() 462 | if err != nil { 463 | t.Fatal("create table error:", err) 464 | } 465 | 466 | err = db.Close() 467 | if err != nil { 468 | t.Error("db close error:", err) 469 | } 470 | 471 | // test copied file 472 | db, err = sql.Open("sqlite3", tempFilename+"-db-copy") 473 | if err != nil { 474 | t.Fatal("open error:", err) 475 | } 476 | 477 | defer func() { 478 | err = db.Close() 479 | if err != nil { 480 | t.Error("db close error:", err) 481 | } 482 | }() 483 | 484 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 485 | err = db.PingContext(ctx) 486 | cancel() 487 | if err != nil { 488 | t.Fatal("ping error:", err) 489 | } 490 | 491 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 492 | _, err = db.ExecContext(ctx, "drop table foo") 493 | cancel() 494 | if err != nil { 495 | t.Fatal("drop table error:", err) 496 | } 497 | 498 | ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second) 499 | _, err = db.ExecContext(ctx, "create table foo (id integer)") 500 | cancel() 501 | if err != nil { 502 | t.Fatal("create table error:", err) 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /sqlite3_libsqlite3.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build libsqlite3 7 | // +build libsqlite3 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DUSE_LIBSQLITE3 13 | #cgo linux LDFLAGS: -lsqlite3 14 | #cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3 15 | #cgo darwin,amd64 CFLAGS: -I/usr/local/opt/sqlite/include 16 | #cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/sqlite/lib -lsqlite3 17 | #cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/sqlite/include 18 | #cgo openbsd LDFLAGS: -lsqlite3 19 | #cgo solaris LDFLAGS: -lsqlite3 20 | #cgo windows LDFLAGS: -lsqlite3 21 | #cgo zos LDFLAGS: -lsqlite3 22 | */ 23 | import "C" 24 | -------------------------------------------------------------------------------- /sqlite3_load_extension.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build !sqlite_omit_load_extension 7 | // +build !sqlite_omit_load_extension 8 | 9 | package sqlite3 10 | 11 | /* 12 | #ifndef USE_LIBSQLITE3 13 | #include "sqlite3-binding.h" 14 | #else 15 | #include 16 | #endif 17 | #include 18 | */ 19 | import "C" 20 | import ( 21 | "errors" 22 | "unsafe" 23 | ) 24 | 25 | func (c *SQLiteConn) loadExtensions(extensions []string) error { 26 | rv := C.sqlite3_enable_load_extension(c.db, 1) 27 | if rv != C.SQLITE_OK { 28 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 29 | } 30 | 31 | for _, extension := range extensions { 32 | if err := c.loadExtension(extension, nil); err != nil { 33 | C.sqlite3_enable_load_extension(c.db, 0) 34 | return err 35 | } 36 | } 37 | 38 | rv = C.sqlite3_enable_load_extension(c.db, 0) 39 | if rv != C.SQLITE_OK { 40 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 41 | } 42 | 43 | return nil 44 | } 45 | 46 | // LoadExtension load the sqlite3 extension. 47 | func (c *SQLiteConn) LoadExtension(lib string, entry string) error { 48 | rv := C.sqlite3_enable_load_extension(c.db, 1) 49 | if rv != C.SQLITE_OK { 50 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 51 | } 52 | 53 | if err := c.loadExtension(lib, &entry); err != nil { 54 | C.sqlite3_enable_load_extension(c.db, 0) 55 | return err 56 | } 57 | 58 | rv = C.sqlite3_enable_load_extension(c.db, 0) 59 | if rv != C.SQLITE_OK { 60 | return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) 61 | } 62 | 63 | return nil 64 | } 65 | 66 | func (c *SQLiteConn) loadExtension(lib string, entry *string) error { 67 | clib := C.CString(lib) 68 | defer C.free(unsafe.Pointer(clib)) 69 | 70 | var centry *C.char 71 | if entry != nil { 72 | centry = C.CString(*entry) 73 | defer C.free(unsafe.Pointer(centry)) 74 | } 75 | 76 | var errMsg *C.char 77 | defer C.sqlite3_free(unsafe.Pointer(errMsg)) 78 | 79 | rv := C.sqlite3_load_extension(c.db, clib, centry, &errMsg) 80 | if rv != C.SQLITE_OK { 81 | return errors.New(C.GoString(errMsg)) 82 | } 83 | 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /sqlite3_load_extension_omit.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_omit_load_extension 7 | // +build sqlite_omit_load_extension 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION 13 | */ 14 | import "C" 15 | import ( 16 | "errors" 17 | ) 18 | 19 | func (c *SQLiteConn) loadExtensions(extensions []string) error { 20 | return errors.New("Extensions have been disabled for static builds") 21 | } 22 | 23 | func (c *SQLiteConn) LoadExtension(lib string, entry string) error { 24 | return errors.New("Extensions have been disabled for static builds") 25 | } 26 | -------------------------------------------------------------------------------- /sqlite3_load_extension_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build !sqlite_omit_load_extension 7 | // +build !sqlite_omit_load_extension 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "testing" 14 | ) 15 | 16 | func TestExtensionsError(t *testing.T) { 17 | sql.Register("sqlite3_TestExtensionsError", 18 | &SQLiteDriver{ 19 | Extensions: []string{ 20 | "foobar", 21 | }, 22 | }, 23 | ) 24 | 25 | db, err := sql.Open("sqlite3_TestExtensionsError", ":memory:") 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | defer db.Close() 30 | 31 | err = db.Ping() 32 | if err == nil { 33 | t.Fatal("expected error loading non-existent extension") 34 | } 35 | 36 | if err.Error() == "not an error" { 37 | t.Fatal("expected error from sqlite3_enable_load_extension to be returned") 38 | } 39 | } 40 | 41 | func TestLoadExtensionError(t *testing.T) { 42 | sql.Register("sqlite3_TestLoadExtensionError", 43 | &SQLiteDriver{ 44 | ConnectHook: func(c *SQLiteConn) error { 45 | return c.LoadExtension("foobar", "") 46 | }, 47 | }, 48 | ) 49 | 50 | db, err := sql.Open("sqlite3_TestLoadExtensionError", ":memory:") 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | defer db.Close() 55 | 56 | err = db.Ping() 57 | if err == nil { 58 | t.Fatal("expected error loading non-existent extension") 59 | } 60 | 61 | if err.Error() == "not an error" { 62 | t.Fatal("expected error from sqlite3_enable_load_extension to be returned") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sqlite3_opt_allow_uri_authority.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_allow_uri_authority 8 | // +build sqlite_allow_uri_authority 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_ALLOW_URI_AUTHORITY 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_app_armor.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build !windows && sqlite_app_armor 8 | // +build !windows,sqlite_app_armor 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_ENABLE_API_ARMOR 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_column_metadata.go: -------------------------------------------------------------------------------- 1 | //go:build sqlite_column_metadata 2 | // +build sqlite_column_metadata 3 | 4 | package sqlite3 5 | 6 | /* 7 | #ifndef USE_LIBSQLITE3 8 | #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA 9 | #include 10 | #else 11 | #include 12 | #endif 13 | */ 14 | import "C" 15 | 16 | // ColumnTableName returns the table that is the origin of a particular result 17 | // column in a SELECT statement. 18 | // 19 | // See https://www.sqlite.org/c3ref/column_database_name.html 20 | func (s *SQLiteStmt) ColumnTableName(n int) string { 21 | return C.GoString(C.sqlite3_column_table_name(s.s, C.int(n))) 22 | } 23 | -------------------------------------------------------------------------------- /sqlite3_opt_column_metadata_test.go: -------------------------------------------------------------------------------- 1 | //go:build sqlite_column_metadata 2 | // +build sqlite_column_metadata 3 | 4 | package sqlite3 5 | 6 | import "testing" 7 | 8 | func TestColumnTableName(t *testing.T) { 9 | d := SQLiteDriver{} 10 | conn, err := d.Open(":memory:") 11 | if err != nil { 12 | t.Fatal("failed to get database connection:", err) 13 | } 14 | defer conn.Close() 15 | sqlite3conn := conn.(*SQLiteConn) 16 | 17 | _, err = sqlite3conn.Exec(`CREATE TABLE foo (name string)`, nil) 18 | if err != nil { 19 | t.Fatal("Failed to create table:", err) 20 | } 21 | _, err = sqlite3conn.Exec(`CREATE TABLE bar (name string)`, nil) 22 | if err != nil { 23 | t.Fatal("Failed to create table:", err) 24 | } 25 | 26 | stmt, err := sqlite3conn.Prepare(`SELECT * FROM foo JOIN bar ON foo.name = bar.name`) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | if exp, got := "foo", stmt.(*SQLiteStmt).ColumnTableName(0); exp != got { 32 | t.Fatalf("Incorrect table name returned expected: %s, got: %s", exp, got) 33 | } 34 | if exp, got := "bar", stmt.(*SQLiteStmt).ColumnTableName(1); exp != got { 35 | t.Fatalf("Incorrect table name returned expected: %s, got: %s", exp, got) 36 | } 37 | if exp, got := "", stmt.(*SQLiteStmt).ColumnTableName(2); exp != got { 38 | t.Fatalf("Incorrect table name returned expected: %s, got: %s", exp, got) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sqlite3_opt_foreign_keys.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_foreign_keys 8 | // +build sqlite_foreign_keys 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_DEFAULT_FOREIGN_KEYS=1 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_fts3_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build cgo 7 | // +build cgo 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "os" 14 | "testing" 15 | ) 16 | 17 | func TestFTS3(t *testing.T) { 18 | tempFilename := TempFilename(t) 19 | defer os.Remove(tempFilename) 20 | db, err := sql.Open("sqlite3", tempFilename) 21 | if err != nil { 22 | t.Fatal("Failed to open database:", err) 23 | } 24 | defer db.Close() 25 | 26 | _, err = db.Exec("DROP TABLE foo") 27 | _, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts3(id INTEGER PRIMARY KEY, value TEXT)") 28 | if err != nil { 29 | t.Fatal("Failed to create table:", err) 30 | } 31 | 32 | _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `今日の 晩御飯は 天麩羅よ`) 33 | if err != nil { 34 | t.Fatal("Failed to insert value:", err) 35 | } 36 | 37 | _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 2, `今日は いい 天気だ`) 38 | if err != nil { 39 | t.Fatal("Failed to insert value:", err) 40 | } 41 | 42 | rows, err := db.Query("SELECT id, value FROM foo WHERE value MATCH '今日* 天*'") 43 | if err != nil { 44 | t.Fatal("Unable to query foo table:", err) 45 | } 46 | defer rows.Close() 47 | 48 | for rows.Next() { 49 | var id int 50 | var value string 51 | 52 | if err := rows.Scan(&id, &value); err != nil { 53 | t.Error("Unable to scan results:", err) 54 | continue 55 | } 56 | 57 | if id == 1 && value != `今日の 晩御飯は 天麩羅よ` { 58 | t.Error("Value for id 1 should be `今日の 晩御飯は 天麩羅よ`, but:", value) 59 | } else if id == 2 && value != `今日は いい 天気だ` { 60 | t.Error("Value for id 2 should be `今日は いい 天気だ`, but:", value) 61 | } 62 | } 63 | 64 | rows, err = db.Query("SELECT value FROM foo WHERE value MATCH '今日* 天麩羅*'") 65 | if err != nil { 66 | t.Fatal("Unable to query foo table:", err) 67 | } 68 | defer rows.Close() 69 | 70 | var value string 71 | if !rows.Next() { 72 | t.Fatal("Result should be only one") 73 | } 74 | 75 | if err := rows.Scan(&value); err != nil { 76 | t.Fatal("Unable to scan results:", err) 77 | } 78 | 79 | if value != `今日の 晩御飯は 天麩羅よ` { 80 | t.Fatal("Value should be `今日の 晩御飯は 天麩羅よ`, but:", value) 81 | } 82 | 83 | if rows.Next() { 84 | t.Fatal("Result should be only one") 85 | } 86 | } 87 | 88 | func TestFTS4(t *testing.T) { 89 | tempFilename := TempFilename(t) 90 | defer os.Remove(tempFilename) 91 | db, err := sql.Open("sqlite3", tempFilename) 92 | if err != nil { 93 | t.Fatal("Failed to open database:", err) 94 | } 95 | defer db.Close() 96 | 97 | _, err = db.Exec("DROP TABLE foo") 98 | _, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts4(tokenize=unicode61, id INTEGER PRIMARY KEY, value TEXT)") 99 | switch { 100 | case err != nil && err.Error() == "unknown tokenizer: unicode61": 101 | t.Skip("FTS4 not supported") 102 | case err != nil: 103 | t.Fatal("Failed to create table:", err) 104 | } 105 | 106 | _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `février`) 107 | if err != nil { 108 | t.Fatal("Failed to insert value:", err) 109 | } 110 | 111 | rows, err := db.Query("SELECT value FROM foo WHERE value MATCH 'fevrier'") 112 | if err != nil { 113 | t.Fatal("Unable to query foo table:", err) 114 | } 115 | defer rows.Close() 116 | 117 | var value string 118 | if !rows.Next() { 119 | t.Fatal("Result should be only one") 120 | } 121 | 122 | if err := rows.Scan(&value); err != nil { 123 | t.Fatal("Unable to scan results:", err) 124 | } 125 | 126 | if value != `février` { 127 | t.Fatal("Value should be `février`, but:", value) 128 | } 129 | 130 | if rows.Next() { 131 | t.Fatal("Result should be only one") 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /sqlite3_opt_fts5.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_fts5 || fts5 7 | // +build sqlite_fts5 fts5 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_ENABLE_FTS5 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /sqlite3_opt_icu.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_icu || icu 7 | // +build sqlite_icu icu 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo LDFLAGS: -licuuc -licui18n 13 | #cgo CFLAGS: -DSQLITE_ENABLE_ICU 14 | #cgo darwin,amd64 CFLAGS: -I/usr/local/opt/icu4c/include 15 | #cgo darwin,amd64 LDFLAGS: -L/usr/local/opt/icu4c/lib 16 | #cgo darwin,arm64 CFLAGS: -I/opt/homebrew/opt/icu4c/include 17 | #cgo darwin,arm64 LDFLAGS: -L/opt/homebrew/opt/icu4c/lib 18 | #cgo openbsd LDFLAGS: -lsqlite3 19 | */ 20 | import "C" 21 | -------------------------------------------------------------------------------- /sqlite3_opt_introspect.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_introspect 8 | // +build sqlite_introspect 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_INTROSPECTION_PRAGMAS 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_math_functions.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_math_functions 7 | // +build sqlite_math_functions 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_ENABLE_MATH_FUNCTIONS 13 | #cgo LDFLAGS: -lm 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /sqlite3_opt_math_functions_test.go: -------------------------------------------------------------------------------- 1 | //go:build sqlite_math_functions 2 | // +build sqlite_math_functions 3 | 4 | package sqlite3 5 | 6 | import ( 7 | "database/sql" 8 | "testing" 9 | ) 10 | 11 | func TestMathFunctions(t *testing.T) { 12 | db, err := sql.Open("sqlite3", ":memory:") 13 | if err != nil { 14 | t.Fatal("Failed to open database:", err) 15 | } 16 | defer db.Close() 17 | 18 | queries := []string{ 19 | `SELECT acos(1)`, 20 | `SELECT log(10, 100)`, 21 | `SELECT power(2, 2)`, 22 | } 23 | 24 | for _, query := range queries { 25 | var result float64 26 | if err := db.QueryRow(query).Scan(&result); err != nil { 27 | t.Errorf("invoking math function query %q: %v", query, err) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sqlite3_opt_os_trace.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2022 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_os_trace 7 | // +build sqlite_os_trace 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_FORCE_OS_TRACE=1 13 | #cgo CFLAGS: -DSQLITE_DEBUG_OS_TRACE=1 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /sqlite3_opt_preupdate.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 G.J.R. Timmer . 2 | // Copyright (C) 2018 segment.com 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build cgo 8 | // +build cgo 9 | 10 | package sqlite3 11 | 12 | // SQLitePreUpdateData represents all of the data available during a 13 | // pre-update hook call. 14 | type SQLitePreUpdateData struct { 15 | Conn *SQLiteConn 16 | Op int 17 | DatabaseName string 18 | TableName string 19 | OldRowID int64 20 | NewRowID int64 21 | } 22 | -------------------------------------------------------------------------------- /sqlite3_opt_preupdate_hook.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 G.J.R. Timmer . 2 | // Copyright (C) 2018 segment.com 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_preupdate_hook 8 | // +build sqlite_preupdate_hook 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_ENABLE_PREUPDATE_HOOK 14 | #cgo LDFLAGS: -lm 15 | 16 | #ifndef USE_LIBSQLITE3 17 | #include "sqlite3-binding.h" 18 | #else 19 | #include 20 | #endif 21 | #include 22 | #include 23 | 24 | void preUpdateHookTrampoline(void*, sqlite3 *, int, char *, char *, sqlite3_int64, sqlite3_int64); 25 | */ 26 | import "C" 27 | import ( 28 | "errors" 29 | "unsafe" 30 | ) 31 | 32 | // RegisterPreUpdateHook sets the pre-update hook for a connection. 33 | // 34 | // The callback is passed a SQLitePreUpdateData struct with the data for 35 | // the update, as well as methods for fetching copies of impacted data. 36 | // 37 | // If there is an existing preupdate hook for this connection, it will be 38 | // removed. If callback is nil the existing hook (if any) will be removed 39 | // without creating a new one. 40 | func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) { 41 | if callback == nil { 42 | C.sqlite3_preupdate_hook(c.db, nil, nil) 43 | } else { 44 | C.sqlite3_preupdate_hook(c.db, (*[0]byte)(unsafe.Pointer(C.preUpdateHookTrampoline)), unsafe.Pointer(newHandle(c, callback))) 45 | } 46 | } 47 | 48 | // Depth returns the source path of the write, see sqlite3_preupdate_depth() 49 | func (d *SQLitePreUpdateData) Depth() int { 50 | return int(C.sqlite3_preupdate_depth(d.Conn.db)) 51 | } 52 | 53 | // Count returns the number of columns in the row 54 | func (d *SQLitePreUpdateData) Count() int { 55 | return int(C.sqlite3_preupdate_count(d.Conn.db)) 56 | } 57 | 58 | func (d *SQLitePreUpdateData) row(dest []any, new bool) error { 59 | for i := 0; i < d.Count() && i < len(dest); i++ { 60 | var val *C.sqlite3_value 61 | var src any 62 | 63 | // Initially I tried making this just a function pointer argument, but 64 | // it's absurdly complicated to pass C function pointers. 65 | if new { 66 | C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val) 67 | } else { 68 | C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val) 69 | } 70 | 71 | switch C.sqlite3_value_type(val) { 72 | case C.SQLITE_INTEGER: 73 | src = int64(C.sqlite3_value_int64(val)) 74 | case C.SQLITE_FLOAT: 75 | src = float64(C.sqlite3_value_double(val)) 76 | case C.SQLITE_BLOB: 77 | len := C.sqlite3_value_bytes(val) 78 | blobptr := C.sqlite3_value_blob(val) 79 | src = C.GoBytes(blobptr, len) 80 | case C.SQLITE_TEXT: 81 | len := C.sqlite3_value_bytes(val) 82 | cstrptr := unsafe.Pointer(C.sqlite3_value_text(val)) 83 | src = C.GoBytes(cstrptr, len) 84 | case C.SQLITE_NULL: 85 | src = nil 86 | } 87 | 88 | err := convertAssign(&dest[i], src) 89 | if err != nil { 90 | return err 91 | } 92 | } 93 | 94 | return nil 95 | } 96 | 97 | // Old populates dest with the row data to be replaced. This works similar to 98 | // database/sql's Rows.Scan() 99 | func (d *SQLitePreUpdateData) Old(dest ...any) error { 100 | if d.Op == SQLITE_INSERT { 101 | return errors.New("There is no old row for INSERT operations") 102 | } 103 | return d.row(dest, false) 104 | } 105 | 106 | // New populates dest with the replacement row data. This works similar to 107 | // database/sql's Rows.Scan() 108 | func (d *SQLitePreUpdateData) New(dest ...any) error { 109 | if d.Op == SQLITE_DELETE { 110 | return errors.New("There is no new row for DELETE operations") 111 | } 112 | return d.row(dest, true) 113 | } 114 | -------------------------------------------------------------------------------- /sqlite3_opt_preupdate_hook_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 G.J.R. Timmer . 2 | // Copyright (C) 2018 segment.com 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_preupdate_hook 8 | // +build sqlite_preupdate_hook 9 | 10 | package sqlite3 11 | 12 | import ( 13 | "database/sql" 14 | "testing" 15 | ) 16 | 17 | type preUpdateHookDataForTest struct { 18 | databaseName string 19 | tableName string 20 | count int 21 | op int 22 | oldRow []any 23 | newRow []any 24 | } 25 | 26 | func TestPreUpdateHook(t *testing.T) { 27 | var events []preUpdateHookDataForTest 28 | 29 | sql.Register("sqlite3_PreUpdateHook", &SQLiteDriver{ 30 | ConnectHook: func(conn *SQLiteConn) error { 31 | conn.RegisterPreUpdateHook(func(data SQLitePreUpdateData) { 32 | eval := -1 33 | oldRow := []any{eval} 34 | if data.Op != SQLITE_INSERT { 35 | err := data.Old(oldRow...) 36 | if err != nil { 37 | t.Fatalf("Unexpected error calling SQLitePreUpdateData.Old: %v", err) 38 | } 39 | } 40 | 41 | eval2 := -1 42 | newRow := []any{eval2} 43 | if data.Op != SQLITE_DELETE { 44 | err := data.New(newRow...) 45 | if err != nil { 46 | t.Fatalf("Unexpected error calling SQLitePreUpdateData.New: %v", err) 47 | } 48 | } 49 | 50 | // tests dest bound checks in loop 51 | var tooSmallRow []any 52 | if data.Op != SQLITE_INSERT { 53 | err := data.Old(tooSmallRow...) 54 | if err != nil { 55 | t.Fatalf("Unexpected error calling SQLitePreUpdateData.Old: %v", err) 56 | } 57 | if len(tooSmallRow) != 0 { 58 | t.Errorf("Expected tooSmallRow to be empty, got: %v", tooSmallRow) 59 | } 60 | } 61 | 62 | events = append(events, preUpdateHookDataForTest{ 63 | databaseName: data.DatabaseName, 64 | tableName: data.TableName, 65 | count: data.Count(), 66 | op: data.Op, 67 | oldRow: oldRow, 68 | newRow: newRow, 69 | }) 70 | }) 71 | return nil 72 | }, 73 | }) 74 | 75 | db, err := sql.Open("sqlite3_PreUpdateHook", ":memory:") 76 | if err != nil { 77 | t.Fatal("Failed to open database:", err) 78 | } 79 | defer db.Close() 80 | 81 | statements := []string{ 82 | "create table foo (id integer primary key)", 83 | "insert into foo values (9)", 84 | "update foo set id = 99 where id = 9", 85 | "delete from foo where id = 99", 86 | } 87 | for _, statement := range statements { 88 | _, err = db.Exec(statement) 89 | if err != nil { 90 | t.Fatalf("Unable to prepare test data [%v]: %v", statement, err) 91 | } 92 | } 93 | 94 | if len(events) != 3 { 95 | t.Errorf("Events should be 3 entries, got: %d", len(events)) 96 | } 97 | 98 | if events[0].op != SQLITE_INSERT { 99 | t.Errorf("Op isn't as expected: %v", events[0].op) 100 | } 101 | 102 | if events[1].op != SQLITE_UPDATE { 103 | t.Errorf("Op isn't as expected: %v", events[1].op) 104 | } 105 | 106 | if events[1].count != 1 { 107 | t.Errorf("Expected event row 1 to have 1 column, had: %v", events[1].count) 108 | } 109 | 110 | newRow_0_0 := events[0].newRow[0].(int64) 111 | if newRow_0_0 != 9 { 112 | t.Errorf("Expected event row 0 new column 0 to be == 9, got: %v", newRow_0_0) 113 | } 114 | 115 | oldRow_1_0 := events[1].oldRow[0].(int64) 116 | if oldRow_1_0 != 9 { 117 | t.Errorf("Expected event row 1 old column 0 to be == 9, got: %v", oldRow_1_0) 118 | } 119 | 120 | newRow_1_0 := events[1].newRow[0].(int64) 121 | if newRow_1_0 != 99 { 122 | t.Errorf("Expected event row 1 new column 0 to be == 99, got: %v", newRow_1_0) 123 | } 124 | 125 | oldRow_2_0 := events[2].oldRow[0].(int64) 126 | if oldRow_2_0 != 99 { 127 | t.Errorf("Expected event row 1 new column 0 to be == 99, got: %v", oldRow_2_0) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /sqlite3_opt_preupdate_omit.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 G.J.R. Timmer . 2 | // Copyright (C) 2018 segment.com 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build !sqlite_preupdate_hook && cgo 8 | // +build !sqlite_preupdate_hook,cgo 9 | 10 | package sqlite3 11 | 12 | // RegisterPreUpdateHook sets the pre-update hook for a connection. 13 | // 14 | // The callback is passed a SQLitePreUpdateData struct with the data for 15 | // the update, as well as methods for fetching copies of impacted data. 16 | // 17 | // If there is an existing preupdate hook for this connection, it will be 18 | // removed. If callback is nil the existing hook (if any) will be removed 19 | // without creating a new one. 20 | func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) { 21 | // NOOP 22 | } 23 | -------------------------------------------------------------------------------- /sqlite3_opt_secure_delete.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_secure_delete 8 | // +build sqlite_secure_delete 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_SECURE_DELETE=1 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_secure_delete_fast.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_secure_delete_fast 8 | // +build sqlite_secure_delete_fast 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_SECURE_DELETE=FAST 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_serialize.go: -------------------------------------------------------------------------------- 1 | //go:build !libsqlite3 || sqlite_serialize 2 | // +build !libsqlite3 sqlite_serialize 3 | 4 | package sqlite3 5 | 6 | /* 7 | #ifndef USE_LIBSQLITE3 8 | #include 9 | #else 10 | #include 11 | #endif 12 | #include 13 | #include 14 | */ 15 | import "C" 16 | 17 | import ( 18 | "fmt" 19 | "math" 20 | "reflect" 21 | "unsafe" 22 | ) 23 | 24 | // Serialize returns a byte slice that is a serialization of the database. 25 | // 26 | // See https://www.sqlite.org/c3ref/serialize.html 27 | func (c *SQLiteConn) Serialize(schema string) ([]byte, error) { 28 | if schema == "" { 29 | schema = "main" 30 | } 31 | var zSchema *C.char 32 | zSchema = C.CString(schema) 33 | defer C.free(unsafe.Pointer(zSchema)) 34 | 35 | var sz C.sqlite3_int64 36 | ptr := C.sqlite3_serialize(c.db, zSchema, &sz, 0) 37 | if ptr == nil { 38 | return nil, fmt.Errorf("serialize failed") 39 | } 40 | defer C.sqlite3_free(unsafe.Pointer(ptr)) 41 | 42 | if sz > C.sqlite3_int64(math.MaxInt) { 43 | return nil, fmt.Errorf("serialized database is too large (%d bytes)", sz) 44 | } 45 | 46 | cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ 47 | Data: uintptr(unsafe.Pointer(ptr)), 48 | Len: int(sz), 49 | Cap: int(sz), 50 | })) 51 | 52 | res := make([]byte, int(sz)) 53 | copy(res, cBuf) 54 | return res, nil 55 | } 56 | 57 | // Deserialize causes the connection to disconnect from the current database and 58 | // then re-open as an in-memory database based on the contents of the byte slice. 59 | // 60 | // See https://www.sqlite.org/c3ref/deserialize.html 61 | func (c *SQLiteConn) Deserialize(b []byte, schema string) error { 62 | if schema == "" { 63 | schema = "main" 64 | } 65 | var zSchema *C.char 66 | zSchema = C.CString(schema) 67 | defer C.free(unsafe.Pointer(zSchema)) 68 | 69 | tmpBuf := (*C.uchar)(C.sqlite3_malloc64(C.sqlite3_uint64(len(b)))) 70 | cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ 71 | Data: uintptr(unsafe.Pointer(tmpBuf)), 72 | Len: len(b), 73 | Cap: len(b), 74 | })) 75 | copy(cBuf, b) 76 | 77 | rc := C.sqlite3_deserialize(c.db, zSchema, tmpBuf, C.sqlite3_int64(len(b)), 78 | C.sqlite3_int64(len(b)), C.SQLITE_DESERIALIZE_FREEONCLOSE) 79 | if rc != C.SQLITE_OK { 80 | return fmt.Errorf("deserialize failed with return %v", rc) 81 | } 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /sqlite3_opt_serialize_omit.go: -------------------------------------------------------------------------------- 1 | //go:build libsqlite3 && !sqlite_serialize 2 | // +build libsqlite3,!sqlite_serialize 3 | 4 | package sqlite3 5 | 6 | import ( 7 | "errors" 8 | ) 9 | 10 | /* 11 | #cgo CFLAGS: -DSQLITE_OMIT_DESERIALIZE 12 | */ 13 | import "C" 14 | 15 | func (c *SQLiteConn) Serialize(schema string) ([]byte, error) { 16 | return nil, errors.New("sqlite3: Serialize requires the sqlite_serialize build tag when using the libsqlite3 build tag") 17 | } 18 | 19 | func (c *SQLiteConn) Deserialize(b []byte, schema string) error { 20 | return errors.New("sqlite3: Deserialize requires the sqlite_serialize build tag when using the libsqlite3 build tag") 21 | } 22 | -------------------------------------------------------------------------------- /sqlite3_opt_serialize_test.go: -------------------------------------------------------------------------------- 1 | //go:build !libsqlite3 || sqlite_serialize 2 | // +build !libsqlite3 sqlite_serialize 3 | 4 | package sqlite3 5 | 6 | import ( 7 | "context" 8 | "database/sql" 9 | "os" 10 | "testing" 11 | ) 12 | 13 | func TestSerializeDeserialize(t *testing.T) { 14 | // Connect to the source database. 15 | srcTempFilename := TempFilename(t) 16 | defer os.Remove(srcTempFilename) 17 | srcDb, err := sql.Open(driverName, srcTempFilename) 18 | if err != nil { 19 | t.Fatal("Failed to open the source database:", err) 20 | } 21 | defer srcDb.Close() 22 | err = srcDb.Ping() 23 | if err != nil { 24 | t.Fatal("Failed to connect to the source database:", err) 25 | } 26 | 27 | // Connect to the destination database. 28 | destTempFilename := TempFilename(t) 29 | defer os.Remove(destTempFilename) 30 | destDb, err := sql.Open(driverName, destTempFilename) 31 | if err != nil { 32 | t.Fatal("Failed to open the destination database:", err) 33 | } 34 | defer destDb.Close() 35 | err = destDb.Ping() 36 | if err != nil { 37 | t.Fatal("Failed to connect to the destination database:", err) 38 | } 39 | 40 | // Write data to source database. 41 | _, err = srcDb.Exec(`CREATE TABLE foo (name string)`) 42 | if err != nil { 43 | t.Fatal("Failed to create table in source database:", err) 44 | } 45 | _, err = srcDb.Exec(`INSERT INTO foo(name) VALUES("alice")`) 46 | if err != nil { 47 | t.Fatal("Failed to insert data into source database", err) 48 | } 49 | 50 | // Serialize the source database 51 | srcConn, err := srcDb.Conn(context.Background()) 52 | if err != nil { 53 | t.Fatal("Failed to get connection to source database:", err) 54 | } 55 | defer srcConn.Close() 56 | 57 | var serialized []byte 58 | if err := srcConn.Raw(func(raw any) error { 59 | var err error 60 | serialized, err = raw.(*SQLiteConn).Serialize("") 61 | return err 62 | }); err != nil { 63 | t.Fatal("Failed to serialize source database:", err) 64 | } 65 | srcConn.Close() 66 | 67 | // Confirm that the destination database is initially empty. 68 | var destTableCount int 69 | err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount) 70 | if err != nil { 71 | t.Fatal("Failed to check the destination table count:", err) 72 | } 73 | if destTableCount != 0 { 74 | t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount) 75 | } 76 | 77 | // Deserialize to destination database 78 | destConn, err := destDb.Conn(context.Background()) 79 | if err != nil { 80 | t.Fatal("Failed to get connection to destination database:", err) 81 | } 82 | defer destConn.Close() 83 | 84 | if err := destConn.Raw(func(raw any) error { 85 | return raw.(*SQLiteConn).Deserialize(serialized, "") 86 | }); err != nil { 87 | t.Fatal("Failed to deserialize source database:", err) 88 | } 89 | destConn.Close() 90 | 91 | // Confirm that destination database has been loaded correctly. 92 | var destRowCount int 93 | err = destDb.QueryRow(`SELECT COUNT(*) FROM foo`).Scan(&destRowCount) 94 | if err != nil { 95 | t.Fatal("Failed to count rows in destination database table", err) 96 | } 97 | if destRowCount != 1 { 98 | t.Fatalf("Destination table does not have the expected records") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /sqlite3_opt_stat4.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_stat4 8 | // +build sqlite_stat4 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_ENABLE_STAT4 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_unlock_notify.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY 7 | #include 8 | #ifndef USE_LIBSQLITE3 9 | #include "sqlite3-binding.h" 10 | #else 11 | #include 12 | #endif 13 | 14 | extern int unlock_notify_wait(sqlite3 *db); 15 | 16 | int 17 | _sqlite3_step_blocking(sqlite3_stmt *stmt) 18 | { 19 | int rv; 20 | sqlite3* db; 21 | 22 | db = sqlite3_db_handle(stmt); 23 | for (;;) { 24 | rv = sqlite3_step(stmt); 25 | if (rv != SQLITE_LOCKED) { 26 | break; 27 | } 28 | if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) { 29 | break; 30 | } 31 | rv = unlock_notify_wait(db); 32 | if (rv != SQLITE_OK) { 33 | break; 34 | } 35 | sqlite3_reset(stmt); 36 | } 37 | 38 | return rv; 39 | } 40 | 41 | int 42 | _sqlite3_step_row_blocking(sqlite3_stmt* stmt, long long* rowid, long long* changes) 43 | { 44 | int rv; 45 | sqlite3* db; 46 | 47 | db = sqlite3_db_handle(stmt); 48 | for (;;) { 49 | rv = sqlite3_step(stmt); 50 | if (rv!=SQLITE_LOCKED) { 51 | break; 52 | } 53 | if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) { 54 | break; 55 | } 56 | rv = unlock_notify_wait(db); 57 | if (rv != SQLITE_OK) { 58 | break; 59 | } 60 | sqlite3_reset(stmt); 61 | } 62 | 63 | *rowid = (long long) sqlite3_last_insert_rowid(db); 64 | *changes = (long long) sqlite3_changes(db); 65 | return rv; 66 | } 67 | 68 | int 69 | _sqlite3_prepare_v2_blocking(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail) 70 | { 71 | int rv; 72 | 73 | for (;;) { 74 | rv = sqlite3_prepare_v2(db, zSql, nBytes, ppStmt, pzTail); 75 | if (rv!=SQLITE_LOCKED) { 76 | break; 77 | } 78 | if (sqlite3_extended_errcode(db) != SQLITE_LOCKED_SHAREDCACHE) { 79 | break; 80 | } 81 | rv = unlock_notify_wait(db); 82 | if (rv != SQLITE_OK) { 83 | break; 84 | } 85 | } 86 | 87 | return rv; 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /sqlite3_opt_unlock_notify.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build cgo && sqlite_unlock_notify 7 | // +build cgo,sqlite_unlock_notify 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY 13 | 14 | #include 15 | #ifndef USE_LIBSQLITE3 16 | #include "sqlite3-binding.h" 17 | #else 18 | #include 19 | #endif 20 | 21 | extern void unlock_notify_callback(void *arg, int argc); 22 | */ 23 | import "C" 24 | import ( 25 | "fmt" 26 | "math" 27 | "sync" 28 | "unsafe" 29 | ) 30 | 31 | type unlock_notify_table struct { 32 | sync.Mutex 33 | seqnum uint 34 | table map[uint]chan struct{} 35 | } 36 | 37 | var unt unlock_notify_table = unlock_notify_table{table: make(map[uint]chan struct{})} 38 | 39 | func (t *unlock_notify_table) add(c chan struct{}) uint { 40 | t.Lock() 41 | defer t.Unlock() 42 | h := t.seqnum 43 | t.table[h] = c 44 | t.seqnum++ 45 | return h 46 | } 47 | 48 | func (t *unlock_notify_table) remove(h uint) { 49 | t.Lock() 50 | defer t.Unlock() 51 | delete(t.table, h) 52 | } 53 | 54 | func (t *unlock_notify_table) get(h uint) chan struct{} { 55 | t.Lock() 56 | defer t.Unlock() 57 | c, ok := t.table[h] 58 | if !ok { 59 | panic(fmt.Sprintf("Non-existent key for unlcok-notify channel: %d", h)) 60 | } 61 | return c 62 | } 63 | 64 | //export unlock_notify_callback 65 | func unlock_notify_callback(argv unsafe.Pointer, argc C.int) { 66 | for i := 0; i < int(argc); i++ { 67 | parg := ((*(*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.uint)(nil))]*[1]uint)(argv))[i]) 68 | arg := *parg 69 | h := arg[0] 70 | c := unt.get(h) 71 | c <- struct{}{} 72 | } 73 | } 74 | 75 | //export unlock_notify_wait 76 | func unlock_notify_wait(db *C.sqlite3) C.int { 77 | // It has to be a bufferred channel to not block in sqlite_unlock_notify 78 | // as sqlite_unlock_notify could invoke the callback before it returns. 79 | c := make(chan struct{}, 1) 80 | defer close(c) 81 | 82 | h := unt.add(c) 83 | defer unt.remove(h) 84 | 85 | pargv := C.malloc(C.sizeof_uint) 86 | defer C.free(pargv) 87 | 88 | argv := (*[1]uint)(pargv) 89 | argv[0] = h 90 | if rv := C.sqlite3_unlock_notify(db, (*[0]byte)(C.unlock_notify_callback), unsafe.Pointer(pargv)); rv != C.SQLITE_OK { 91 | return rv 92 | } 93 | 94 | <-c 95 | 96 | return C.SQLITE_OK 97 | } 98 | -------------------------------------------------------------------------------- /sqlite3_opt_unlock_notify_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_unlock_notify 7 | // +build sqlite_unlock_notify 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "fmt" 14 | "os" 15 | "sync" 16 | "testing" 17 | "time" 18 | ) 19 | 20 | func TestUnlockNotify(t *testing.T) { 21 | tempFilename := TempFilename(t) 22 | defer os.Remove(tempFilename) 23 | dsn := fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d", tempFilename, 500) 24 | db, err := sql.Open("sqlite3", dsn) 25 | if err != nil { 26 | t.Fatal("Failed to open database:", err) 27 | } 28 | defer db.Close() 29 | 30 | _, err = db.Exec("CREATE TABLE foo(id INTEGER, status INTEGER)") 31 | if err != nil { 32 | t.Fatal("Failed to create table:", err) 33 | } 34 | 35 | tx, err := db.Begin() 36 | if err != nil { 37 | t.Fatal("Failed to begin transaction:", err) 38 | } 39 | 40 | _, err = tx.Exec("INSERT INTO foo(id, status) VALUES(1, 100)") 41 | if err != nil { 42 | t.Fatal("Failed to insert null:", err) 43 | } 44 | 45 | _, err = tx.Exec("UPDATE foo SET status = 200 WHERE id = 1") 46 | if err != nil { 47 | t.Fatal("Failed to update table:", err) 48 | } 49 | 50 | wg := sync.WaitGroup{} 51 | wg.Add(1) 52 | timer := time.NewTimer(500 * time.Millisecond) 53 | go func() { 54 | <-timer.C 55 | err := tx.Commit() 56 | if err != nil { 57 | t.Fatal("Failed to commit transaction:", err) 58 | } 59 | wg.Done() 60 | }() 61 | 62 | rows, err := db.Query("SELECT count(*) from foo") 63 | if err != nil { 64 | t.Fatal("Unable to query foo table:", err) 65 | } 66 | 67 | if rows.Next() { 68 | var count int 69 | if err := rows.Scan(&count); err != nil { 70 | t.Fatal("Failed to Scan rows", err) 71 | } 72 | } 73 | if err := rows.Err(); err != nil { 74 | t.Fatal("Failed at the call to Next:", err) 75 | } 76 | wg.Wait() 77 | 78 | } 79 | 80 | func TestUnlockNotifyMany(t *testing.T) { 81 | tempFilename := TempFilename(t) 82 | defer os.Remove(tempFilename) 83 | dsn := fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d", tempFilename, 500) 84 | db, err := sql.Open("sqlite3", dsn) 85 | if err != nil { 86 | t.Fatal("Failed to open database:", err) 87 | } 88 | defer db.Close() 89 | 90 | _, err = db.Exec("CREATE TABLE foo(id INTEGER, status INTEGER)") 91 | if err != nil { 92 | t.Fatal("Failed to create table:", err) 93 | } 94 | 95 | tx, err := db.Begin() 96 | if err != nil { 97 | t.Fatal("Failed to begin transaction:", err) 98 | } 99 | 100 | _, err = tx.Exec("INSERT INTO foo(id, status) VALUES(1, 100)") 101 | if err != nil { 102 | t.Fatal("Failed to insert null:", err) 103 | } 104 | 105 | _, err = tx.Exec("UPDATE foo SET status = 200 WHERE id = 1") 106 | if err != nil { 107 | t.Fatal("Failed to update table:", err) 108 | } 109 | 110 | wg := sync.WaitGroup{} 111 | wg.Add(1) 112 | timer := time.NewTimer(500 * time.Millisecond) 113 | go func() { 114 | <-timer.C 115 | err := tx.Commit() 116 | if err != nil { 117 | t.Fatal("Failed to commit transaction:", err) 118 | } 119 | wg.Done() 120 | }() 121 | 122 | const concurrentQueries = 1000 123 | wg.Add(concurrentQueries) 124 | for i := 0; i < concurrentQueries; i++ { 125 | go func() { 126 | rows, err := db.Query("SELECT count(*) from foo") 127 | if err != nil { 128 | t.Fatal("Unable to query foo table:", err) 129 | } 130 | 131 | if rows.Next() { 132 | var count int 133 | if err := rows.Scan(&count); err != nil { 134 | t.Fatal("Failed to Scan rows", err) 135 | } 136 | } 137 | if err := rows.Err(); err != nil { 138 | t.Fatal("Failed at the call to Next:", err) 139 | } 140 | wg.Done() 141 | }() 142 | } 143 | wg.Wait() 144 | } 145 | 146 | func TestUnlockNotifyDeadlock(t *testing.T) { 147 | tempFilename := TempFilename(t) 148 | defer os.Remove(tempFilename) 149 | dsn := fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d", tempFilename, 500) 150 | db, err := sql.Open("sqlite3", dsn) 151 | if err != nil { 152 | t.Fatal("Failed to open database:", err) 153 | } 154 | defer db.Close() 155 | 156 | _, err = db.Exec("CREATE TABLE foo(id INTEGER, status INTEGER)") 157 | if err != nil { 158 | t.Fatal("Failed to create table:", err) 159 | } 160 | 161 | tx, err := db.Begin() 162 | if err != nil { 163 | t.Fatal("Failed to begin transaction:", err) 164 | } 165 | 166 | _, err = tx.Exec("INSERT INTO foo(id, status) VALUES(1, 100)") 167 | if err != nil { 168 | t.Fatal("Failed to insert null:", err) 169 | } 170 | 171 | _, err = tx.Exec("UPDATE foo SET status = 200 WHERE id = 1") 172 | if err != nil { 173 | t.Fatal("Failed to update table:", err) 174 | } 175 | 176 | wg := sync.WaitGroup{} 177 | wg.Add(1) 178 | timer := time.NewTimer(500 * time.Millisecond) 179 | go func() { 180 | <-timer.C 181 | err := tx.Commit() 182 | if err != nil { 183 | t.Fatal("Failed to commit transaction:", err) 184 | } 185 | wg.Done() 186 | }() 187 | 188 | wg.Add(1) 189 | go func() { 190 | tx2, err := db.Begin() 191 | if err != nil { 192 | t.Fatal("Failed to begin transaction:", err) 193 | } 194 | defer tx2.Rollback() 195 | 196 | _, err = tx2.Exec("DELETE FROM foo") 197 | if err != nil { 198 | t.Fatal("Failed to delete table:", err) 199 | } 200 | err = tx2.Commit() 201 | if err != nil { 202 | t.Fatal("Failed to commit transaction:", err) 203 | } 204 | wg.Done() 205 | }() 206 | 207 | rows, err := tx.Query("SELECT count(*) from foo") 208 | if err != nil { 209 | t.Fatal("Unable to query foo table:", err) 210 | } 211 | 212 | if rows.Next() { 213 | var count int 214 | if err := rows.Scan(&count); err != nil { 215 | t.Fatal("Failed to Scan rows", err) 216 | } 217 | } 218 | if err := rows.Err(); err != nil { 219 | t.Fatal("Failed at the call to Next:", err) 220 | } 221 | 222 | wg.Wait() 223 | } 224 | -------------------------------------------------------------------------------- /sqlite3_opt_userauth.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_userauth 7 | // +build sqlite_userauth 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -DSQLITE_USER_AUTHENTICATION 13 | #cgo LDFLAGS: -lm 14 | #ifndef USE_LIBSQLITE3 15 | #include "sqlite3-binding.h" 16 | #else 17 | #include 18 | #endif 19 | #include 20 | 21 | static int 22 | _sqlite3_user_authenticate(sqlite3* db, const char* zUsername, const char* aPW, int nPW) 23 | { 24 | return sqlite3_user_authenticate(db, zUsername, aPW, nPW); 25 | } 26 | 27 | static int 28 | _sqlite3_user_add(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin) 29 | { 30 | return sqlite3_user_add(db, zUsername, aPW, nPW, isAdmin); 31 | } 32 | 33 | static int 34 | _sqlite3_user_change(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin) 35 | { 36 | return sqlite3_user_change(db, zUsername, aPW, nPW, isAdmin); 37 | } 38 | 39 | static int 40 | _sqlite3_user_delete(sqlite3* db, const char* zUsername) 41 | { 42 | return sqlite3_user_delete(db, zUsername); 43 | } 44 | 45 | static int 46 | _sqlite3_auth_enabled(sqlite3* db) 47 | { 48 | int exists = -1; 49 | 50 | sqlite3_stmt *stmt; 51 | sqlite3_prepare_v2(db, "select count(type) from sqlite_master WHERE type='table' and name='sqlite_user';", -1, &stmt, NULL); 52 | 53 | while ( sqlite3_step(stmt) == SQLITE_ROW) { 54 | exists = sqlite3_column_int(stmt, 0); 55 | } 56 | 57 | sqlite3_finalize(stmt); 58 | 59 | return exists; 60 | } 61 | */ 62 | import "C" 63 | import ( 64 | "errors" 65 | "unsafe" 66 | ) 67 | 68 | const ( 69 | SQLITE_AUTH = C.SQLITE_AUTH 70 | ) 71 | 72 | var ( 73 | ErrUnauthorized = errors.New("SQLITE_AUTH: Unauthorized") 74 | ErrAdminRequired = errors.New("SQLITE_AUTH: Unauthorized; Admin Privileges Required") 75 | ) 76 | 77 | // Authenticate will perform an authentication of the provided username 78 | // and password against the database. 79 | // 80 | // If a database contains the SQLITE_USER table, then the 81 | // call to Authenticate must be invoked with an 82 | // appropriate username and password prior to enable read and write 83 | // access to the database. 84 | // 85 | // Return SQLITE_OK on success or SQLITE_ERROR if the username/password 86 | // combination is incorrect or unknown. 87 | // 88 | // If the SQLITE_USER table is not present in the database file, then 89 | // this interface is a harmless no-op returning SQLITE_OK. 90 | func (c *SQLiteConn) Authenticate(username, password string) error { 91 | rv := c.authenticate(username, password) 92 | switch rv { 93 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 94 | return ErrUnauthorized 95 | case C.SQLITE_OK: 96 | return nil 97 | default: 98 | return c.lastError() 99 | } 100 | } 101 | 102 | // authenticate provides the actual authentication to SQLite. 103 | // This is not exported for usage in Go. 104 | // It is however exported for usage within SQL by the user. 105 | // 106 | // Returns: 107 | // 108 | // C.SQLITE_OK (0) 109 | // C.SQLITE_ERROR (1) 110 | // C.SQLITE_AUTH (23) 111 | func (c *SQLiteConn) authenticate(username, password string) int { 112 | // Allocate C Variables 113 | cuser := C.CString(username) 114 | cpass := C.CString(password) 115 | 116 | // Free C Variables 117 | defer func() { 118 | C.free(unsafe.Pointer(cuser)) 119 | C.free(unsafe.Pointer(cpass)) 120 | }() 121 | 122 | return int(C._sqlite3_user_authenticate(c.db, cuser, cpass, C.int(len(password)))) 123 | } 124 | 125 | // AuthUserAdd can be used (by an admin user only) 126 | // to create a new user. When called on a no-authentication-required 127 | // database, this routine converts the database into an authentication- 128 | // required database, automatically makes the added user an 129 | // administrator, and logs in the current connection as that user. 130 | // The AuthUserAdd only works for the "main" database, not 131 | // for any ATTACH-ed databases. Any call to AuthUserAdd by a 132 | // non-admin user results in an error. 133 | func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error { 134 | isAdmin := 0 135 | if admin { 136 | isAdmin = 1 137 | } 138 | 139 | rv := c.authUserAdd(username, password, isAdmin) 140 | switch rv { 141 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 142 | return ErrAdminRequired 143 | case C.SQLITE_OK: 144 | return nil 145 | default: 146 | return c.lastError() 147 | } 148 | } 149 | 150 | // authUserAdd enables the User Authentication if not enabled. 151 | // Otherwise it will add a user. 152 | // 153 | // When user authentication is already enabled then this function 154 | // can only be called by an admin. 155 | // 156 | // This is not exported for usage in Go. 157 | // It is however exported for usage within SQL by the user. 158 | // 159 | // Returns: 160 | // 161 | // C.SQLITE_OK (0) 162 | // C.SQLITE_ERROR (1) 163 | // C.SQLITE_AUTH (23) 164 | func (c *SQLiteConn) authUserAdd(username, password string, admin int) int { 165 | // Allocate C Variables 166 | cuser := C.CString(username) 167 | cpass := C.CString(password) 168 | 169 | // Free C Variables 170 | defer func() { 171 | C.free(unsafe.Pointer(cuser)) 172 | C.free(unsafe.Pointer(cpass)) 173 | }() 174 | 175 | return int(C._sqlite3_user_add(c.db, cuser, cpass, C.int(len(password)), C.int(admin))) 176 | } 177 | 178 | // AuthUserChange can be used to change a users 179 | // login credentials or admin privilege. Any user can change their own 180 | // login credentials. Only an admin user can change another users login 181 | // credentials or admin privilege setting. No user may change their own 182 | // admin privilege setting. 183 | func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error { 184 | isAdmin := 0 185 | if admin { 186 | isAdmin = 1 187 | } 188 | 189 | rv := c.authUserChange(username, password, isAdmin) 190 | switch rv { 191 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 192 | return ErrAdminRequired 193 | case C.SQLITE_OK: 194 | return nil 195 | default: 196 | return c.lastError() 197 | } 198 | } 199 | 200 | // authUserChange allows to modify a user. 201 | // Users can change their own password. 202 | // 203 | // Only admins can change passwords for other users 204 | // and modify the admin flag. 205 | // 206 | // The admin flag of the current logged in user cannot be changed. 207 | // THis ensures that their is always an admin. 208 | // 209 | // This is not exported for usage in Go. 210 | // It is however exported for usage within SQL by the user. 211 | // 212 | // Returns: 213 | // 214 | // C.SQLITE_OK (0) 215 | // C.SQLITE_ERROR (1) 216 | // C.SQLITE_AUTH (23) 217 | func (c *SQLiteConn) authUserChange(username, password string, admin int) int { 218 | // Allocate C Variables 219 | cuser := C.CString(username) 220 | cpass := C.CString(password) 221 | 222 | // Free C Variables 223 | defer func() { 224 | C.free(unsafe.Pointer(cuser)) 225 | C.free(unsafe.Pointer(cpass)) 226 | }() 227 | 228 | return int(C._sqlite3_user_change(c.db, cuser, cpass, C.int(len(password)), C.int(admin))) 229 | } 230 | 231 | // AuthUserDelete can be used (by an admin user only) 232 | // to delete a user. The currently logged-in user cannot be deleted, 233 | // which guarantees that there is always an admin user and hence that 234 | // the database cannot be converted into a no-authentication-required 235 | // database. 236 | func (c *SQLiteConn) AuthUserDelete(username string) error { 237 | rv := c.authUserDelete(username) 238 | switch rv { 239 | case C.SQLITE_ERROR, C.SQLITE_AUTH: 240 | return ErrAdminRequired 241 | case C.SQLITE_OK: 242 | return nil 243 | default: 244 | return c.lastError() 245 | } 246 | } 247 | 248 | // authUserDelete can be used to delete a user. 249 | // 250 | // This function can only be executed by an admin. 251 | // 252 | // This is not exported for usage in Go. 253 | // It is however exported for usage within SQL by the user. 254 | // 255 | // Returns: 256 | // 257 | // C.SQLITE_OK (0) 258 | // C.SQLITE_ERROR (1) 259 | // C.SQLITE_AUTH (23) 260 | func (c *SQLiteConn) authUserDelete(username string) int { 261 | // Allocate C Variables 262 | cuser := C.CString(username) 263 | 264 | // Free C Variables 265 | defer func() { 266 | C.free(unsafe.Pointer(cuser)) 267 | }() 268 | 269 | return int(C._sqlite3_user_delete(c.db, cuser)) 270 | } 271 | 272 | // AuthEnabled checks if the database is protected by user authentication 273 | func (c *SQLiteConn) AuthEnabled() (exists bool) { 274 | rv := c.authEnabled() 275 | if rv == 1 { 276 | exists = true 277 | } 278 | 279 | return 280 | } 281 | 282 | // authEnabled perform the actual check for user authentication. 283 | // 284 | // This is not exported for usage in Go. 285 | // It is however exported for usage within SQL by the user. 286 | // 287 | // Returns: 288 | // 289 | // 0 - Disabled 290 | // 1 - Enabled 291 | func (c *SQLiteConn) authEnabled() int { 292 | return int(C._sqlite3_auth_enabled(c.db)) 293 | } 294 | 295 | // EOF 296 | -------------------------------------------------------------------------------- /sqlite3_opt_userauth_omit.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build !sqlite_userauth 7 | // +build !sqlite_userauth 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "C" 13 | ) 14 | 15 | // Authenticate will perform an authentication of the provided username 16 | // and password against the database. 17 | // 18 | // If a database contains the SQLITE_USER table, then the 19 | // call to Authenticate must be invoked with an 20 | // appropriate username and password prior to enable read and write 21 | // access to the database. 22 | // 23 | // Return SQLITE_OK on success or SQLITE_ERROR if the username/password 24 | // combination is incorrect or unknown. 25 | // 26 | // If the SQLITE_USER table is not present in the database file, then 27 | // this interface is a harmless no-op returnning SQLITE_OK. 28 | func (c *SQLiteConn) Authenticate(username, password string) error { 29 | // NOOP 30 | return nil 31 | } 32 | 33 | // authenticate provides the actual authentication to SQLite. 34 | // This is not exported for usage in Go. 35 | // It is however exported for usage within SQL by the user. 36 | // 37 | // Returns: 38 | // 39 | // C.SQLITE_OK (0) 40 | // C.SQLITE_ERROR (1) 41 | // C.SQLITE_AUTH (23) 42 | func (c *SQLiteConn) authenticate(username, password string) int { 43 | // NOOP 44 | return 0 45 | } 46 | 47 | // AuthUserAdd can be used (by an admin user only) 48 | // to create a new user. When called on a no-authentication-required 49 | // database, this routine converts the database into an authentication- 50 | // required database, automatically makes the added user an 51 | // administrator, and logs in the current connection as that user. 52 | // The AuthUserAdd only works for the "main" database, not 53 | // for any ATTACH-ed databases. Any call to AuthUserAdd by a 54 | // non-admin user results in an error. 55 | func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error { 56 | // NOOP 57 | return nil 58 | } 59 | 60 | // authUserAdd enables the User Authentication if not enabled. 61 | // Otherwise it will add a user. 62 | // 63 | // When user authentication is already enabled then this function 64 | // can only be called by an admin. 65 | // 66 | // This is not exported for usage in Go. 67 | // It is however exported for usage within SQL by the user. 68 | // 69 | // Returns: 70 | // 71 | // C.SQLITE_OK (0) 72 | // C.SQLITE_ERROR (1) 73 | // C.SQLITE_AUTH (23) 74 | func (c *SQLiteConn) authUserAdd(username, password string, admin int) int { 75 | // NOOP 76 | return 0 77 | } 78 | 79 | // AuthUserChange can be used to change a users 80 | // login credentials or admin privilege. Any user can change their own 81 | // login credentials. Only an admin user can change another users login 82 | // credentials or admin privilege setting. No user may change their own 83 | // admin privilege setting. 84 | func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error { 85 | // NOOP 86 | return nil 87 | } 88 | 89 | // authUserChange allows to modify a user. 90 | // Users can change their own password. 91 | // 92 | // Only admins can change passwords for other users 93 | // and modify the admin flag. 94 | // 95 | // The admin flag of the current logged in user cannot be changed. 96 | // THis ensures that their is always an admin. 97 | // 98 | // This is not exported for usage in Go. 99 | // It is however exported for usage within SQL by the user. 100 | // 101 | // Returns: 102 | // 103 | // C.SQLITE_OK (0) 104 | // C.SQLITE_ERROR (1) 105 | // C.SQLITE_AUTH (23) 106 | func (c *SQLiteConn) authUserChange(username, password string, admin int) int { 107 | // NOOP 108 | return 0 109 | } 110 | 111 | // AuthUserDelete can be used (by an admin user only) 112 | // to delete a user. The currently logged-in user cannot be deleted, 113 | // which guarantees that there is always an admin user and hence that 114 | // the database cannot be converted into a no-authentication-required 115 | // database. 116 | func (c *SQLiteConn) AuthUserDelete(username string) error { 117 | // NOOP 118 | return nil 119 | } 120 | 121 | // authUserDelete can be used to delete a user. 122 | // 123 | // This function can only be executed by an admin. 124 | // 125 | // This is not exported for usage in Go. 126 | // It is however exported for usage within SQL by the user. 127 | // 128 | // Returns: 129 | // 130 | // C.SQLITE_OK (0) 131 | // C.SQLITE_ERROR (1) 132 | // C.SQLITE_AUTH (23) 133 | func (c *SQLiteConn) authUserDelete(username string) int { 134 | // NOOP 135 | return 0 136 | } 137 | 138 | // AuthEnabled checks if the database is protected by user authentication 139 | func (c *SQLiteConn) AuthEnabled() (exists bool) { 140 | // NOOP 141 | return false 142 | } 143 | 144 | // authEnabled perform the actual check for user authentication. 145 | // 146 | // This is not exported for usage in Go. 147 | // It is however exported for usage within SQL by the user. 148 | // 149 | // Returns: 150 | // 151 | // 0 - Disabled 152 | // 1 - Enabled 153 | func (c *SQLiteConn) authEnabled() int { 154 | // NOOP 155 | return 0 156 | } 157 | 158 | // EOF 159 | -------------------------------------------------------------------------------- /sqlite3_opt_vacuum_full.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_vacuum_full 8 | // +build sqlite_vacuum_full 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=1 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_opt_vacuum_incr.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Copyright (C) 2018 G.J.R. Timmer . 3 | // 4 | // Use of this source code is governed by an MIT-style 5 | // license that can be found in the LICENSE file. 6 | 7 | //go:build sqlite_vacuum_incr 8 | // +build sqlite_vacuum_incr 9 | 10 | package sqlite3 11 | 12 | /* 13 | #cgo CFLAGS: -DSQLITE_DEFAULT_AUTOVACUUM=2 14 | #cgo LDFLAGS: -lm 15 | */ 16 | import "C" 17 | -------------------------------------------------------------------------------- /sqlite3_other.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build !windows 7 | // +build !windows 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -I. 13 | #cgo linux LDFLAGS: -ldl 14 | #cgo linux,ppc LDFLAGS: -lpthread 15 | #cgo linux,ppc64 LDFLAGS: -lpthread 16 | #cgo linux,ppc64le LDFLAGS: -lpthread 17 | */ 18 | import "C" 19 | -------------------------------------------------------------------------------- /sqlite3_solaris.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build solaris 7 | // +build solaris 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -D__EXTENSIONS__=1 13 | #cgo LDFLAGS: -lc 14 | */ 15 | import "C" 16 | -------------------------------------------------------------------------------- /sqlite3_trace.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build sqlite_trace || trace 7 | // +build sqlite_trace trace 8 | 9 | package sqlite3 10 | 11 | /* 12 | #ifndef USE_LIBSQLITE3 13 | #include "sqlite3-binding.h" 14 | #else 15 | #include 16 | #endif 17 | #include 18 | 19 | int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); 20 | */ 21 | import "C" 22 | 23 | import ( 24 | "fmt" 25 | "strings" 26 | "sync" 27 | "unsafe" 28 | ) 29 | 30 | // Trace... constants identify the possible events causing callback invocation. 31 | // Values are same as the corresponding SQLite Trace Event Codes. 32 | const ( 33 | TraceStmt = uint32(C.SQLITE_TRACE_STMT) 34 | TraceProfile = uint32(C.SQLITE_TRACE_PROFILE) 35 | TraceRow = uint32(C.SQLITE_TRACE_ROW) 36 | TraceClose = uint32(C.SQLITE_TRACE_CLOSE) 37 | ) 38 | 39 | type TraceInfo struct { 40 | // Pack together the shorter fields, to keep the struct smaller. 41 | // On a 64-bit machine there would be padding 42 | // between EventCode and ConnHandle; having AutoCommit here is "free": 43 | EventCode uint32 44 | AutoCommit bool 45 | ConnHandle uintptr 46 | 47 | // Usually filled, unless EventCode = TraceClose = SQLITE_TRACE_CLOSE: 48 | // identifier for a prepared statement: 49 | StmtHandle uintptr 50 | 51 | // Two strings filled when EventCode = TraceStmt = SQLITE_TRACE_STMT: 52 | // (1) either the unexpanded SQL text of the prepared statement, or 53 | // an SQL comment that indicates the invocation of a trigger; 54 | // (2) expanded SQL, if requested and if (1) is not an SQL comment. 55 | StmtOrTrigger string 56 | ExpandedSQL string // only if requested (TraceConfig.WantExpandedSQL = true) 57 | 58 | // filled when EventCode = TraceProfile = SQLITE_TRACE_PROFILE: 59 | // estimated number of nanoseconds that the prepared statement took to run: 60 | RunTimeNanosec int64 61 | 62 | DBError Error 63 | } 64 | 65 | // TraceUserCallback gives the signature for a trace function 66 | // provided by the user (Go application programmer). 67 | // SQLite 3.14 documentation (as of September 2, 2016) 68 | // for SQL Trace Hook = sqlite3_trace_v2(): 69 | // The integer return value from the callback is currently ignored, 70 | // though this may change in future releases. Callback implementations 71 | // should return zero to ensure future compatibility. 72 | type TraceUserCallback func(TraceInfo) int 73 | 74 | type TraceConfig struct { 75 | Callback TraceUserCallback 76 | EventMask uint32 77 | WantExpandedSQL bool 78 | } 79 | 80 | func fillDBError(dbErr *Error, db *C.sqlite3) { 81 | // See SQLiteConn.lastError(), in file 'sqlite3.go' at the time of writing (Sept 5, 2016) 82 | dbErr.Code = ErrNo(C.sqlite3_errcode(db)) 83 | dbErr.ExtendedCode = ErrNoExtended(C.sqlite3_extended_errcode(db)) 84 | dbErr.err = C.GoString(C.sqlite3_errmsg(db)) 85 | } 86 | 87 | func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) { 88 | if pStmt == nil { 89 | panic("No SQLite statement pointer in P arg of trace_v2 callback") 90 | } 91 | 92 | expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt)) 93 | defer C.sqlite3_free(unsafe.Pointer(expSQLiteCStr)) 94 | if expSQLiteCStr == nil { 95 | fillDBError(&info.DBError, db) 96 | return 97 | } 98 | info.ExpandedSQL = C.GoString(expSQLiteCStr) 99 | } 100 | 101 | //export traceCallbackTrampoline 102 | func traceCallbackTrampoline( 103 | traceEventCode C.uint, 104 | // Parameter named 'C' in SQLite docs = Context given at registration: 105 | ctx unsafe.Pointer, 106 | // Parameter named 'P' in SQLite docs (Primary event data?): 107 | p unsafe.Pointer, 108 | // Parameter named 'X' in SQLite docs (eXtra event data?): 109 | xValue unsafe.Pointer) C.int { 110 | 111 | eventCode := uint32(traceEventCode) 112 | 113 | if ctx == nil { 114 | panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) 115 | } 116 | 117 | contextDB := (*C.sqlite3)(ctx) 118 | connHandle := uintptr(ctx) 119 | 120 | var traceConf TraceConfig 121 | var found bool 122 | if eventCode == TraceClose { 123 | // clean up traceMap: 'pop' means get and delete 124 | traceConf, found = popTraceMapping(connHandle) 125 | } else { 126 | traceConf, found = lookupTraceMapping(connHandle) 127 | } 128 | 129 | if !found { 130 | panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)", 131 | connHandle, eventCode)) 132 | } 133 | 134 | var info TraceInfo 135 | 136 | info.EventCode = eventCode 137 | info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0) 138 | info.ConnHandle = connHandle 139 | 140 | switch eventCode { 141 | case TraceStmt: 142 | info.StmtHandle = uintptr(p) 143 | 144 | var xStr string 145 | if xValue != nil { 146 | xStr = C.GoString((*C.char)(xValue)) 147 | } 148 | info.StmtOrTrigger = xStr 149 | if !strings.HasPrefix(xStr, "--") { 150 | // Not SQL comment, therefore the current event 151 | // is not related to a trigger. 152 | // The user might want to receive the expanded SQL; 153 | // let's check: 154 | if traceConf.WantExpandedSQL { 155 | fillExpandedSQL(&info, contextDB, p) 156 | } 157 | } 158 | 159 | case TraceProfile: 160 | info.StmtHandle = uintptr(p) 161 | 162 | if xValue == nil { 163 | panic("NULL pointer in X arg of trace_v2 callback for SQLITE_TRACE_PROFILE event") 164 | } 165 | 166 | info.RunTimeNanosec = *(*int64)(xValue) 167 | 168 | // sample the error //TODO: is it safe? is it useful? 169 | fillDBError(&info.DBError, contextDB) 170 | 171 | case TraceRow: 172 | info.StmtHandle = uintptr(p) 173 | 174 | case TraceClose: 175 | handle := uintptr(p) 176 | if handle != info.ConnHandle { 177 | panic(fmt.Sprintf("Different conn handle 0x%x (expected 0x%x) in SQLITE_TRACE_CLOSE event.", 178 | handle, info.ConnHandle)) 179 | } 180 | 181 | default: 182 | // Pass unsupported events to the user callback (if configured); 183 | // let the user callback decide whether to panic or ignore them. 184 | } 185 | 186 | // Do not execute user callback when the event was not requested by user! 187 | // Remember that the Close event is always selected when 188 | // registering this callback trampoline with SQLite --- for cleanup. 189 | // In the future there may be more events forced to "selected" in SQLite 190 | // for the driver's needs. 191 | if traceConf.EventMask&eventCode == 0 { 192 | return 0 193 | } 194 | 195 | r := 0 196 | if traceConf.Callback != nil { 197 | r = traceConf.Callback(info) 198 | } 199 | return C.int(r) 200 | } 201 | 202 | type traceMapEntry struct { 203 | config TraceConfig 204 | } 205 | 206 | var traceMapLock sync.Mutex 207 | var traceMap = make(map[uintptr]traceMapEntry) 208 | 209 | func addTraceMapping(connHandle uintptr, traceConf TraceConfig) { 210 | traceMapLock.Lock() 211 | defer traceMapLock.Unlock() 212 | 213 | oldEntryCopy, found := traceMap[connHandle] 214 | if found { 215 | panic(fmt.Sprintf("Adding trace config %v: handle 0x%x already registered (%v).", 216 | traceConf, connHandle, oldEntryCopy.config)) 217 | } 218 | traceMap[connHandle] = traceMapEntry{config: traceConf} 219 | } 220 | 221 | func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) { 222 | traceMapLock.Lock() 223 | defer traceMapLock.Unlock() 224 | 225 | entryCopy, found := traceMap[connHandle] 226 | return entryCopy.config, found 227 | } 228 | 229 | // 'pop' = get and delete from map before returning the value to the caller 230 | func popTraceMapping(connHandle uintptr) (TraceConfig, bool) { 231 | traceMapLock.Lock() 232 | defer traceMapLock.Unlock() 233 | 234 | entryCopy, found := traceMap[connHandle] 235 | if found { 236 | delete(traceMap, connHandle) 237 | } 238 | return entryCopy.config, found 239 | } 240 | 241 | // SetTrace installs or removes the trace callback for the given database connection. 242 | // It's not named 'RegisterTrace' because only one callback can be kept and called. 243 | // Calling SetTrace a second time on same database connection 244 | // overrides (cancels) any prior callback and all its settings: 245 | // event mask, etc. 246 | func (c *SQLiteConn) SetTrace(requested *TraceConfig) error { 247 | connHandle := uintptr(unsafe.Pointer(c.db)) 248 | 249 | _, _ = popTraceMapping(connHandle) 250 | 251 | if requested == nil { 252 | // The traceMap entry was deleted already by popTraceMapping(): 253 | // can disable all events now, no need to watch for TraceClose. 254 | err := c.setSQLiteTrace(0) 255 | return err 256 | } 257 | 258 | reqCopy := *requested 259 | 260 | // Disable potentially expensive operations 261 | // if their result will not be used. We are doing this 262 | // just in case the caller provided nonsensical input. 263 | if reqCopy.EventMask&TraceStmt == 0 { 264 | reqCopy.WantExpandedSQL = false 265 | } 266 | 267 | addTraceMapping(connHandle, reqCopy) 268 | 269 | // The callback trampoline function does cleanup on Close event, 270 | // regardless of the presence or absence of the user callback. 271 | // Therefore it needs the Close event to be selected: 272 | actualEventMask := uint(reqCopy.EventMask | TraceClose) 273 | err := c.setSQLiteTrace(actualEventMask) 274 | return err 275 | } 276 | 277 | func (c *SQLiteConn) setSQLiteTrace(sqliteEventMask uint) error { 278 | rv := C.sqlite3_trace_v2(c.db, 279 | C.uint(sqliteEventMask), 280 | (*[0]byte)(unsafe.Pointer(C.traceCallbackTrampoline)), 281 | unsafe.Pointer(c.db)) // Fourth arg is same as first: we are 282 | // passing the database connection handle as callback context. 283 | 284 | if rv != C.SQLITE_OK { 285 | return c.lastError() 286 | } 287 | return nil 288 | } 289 | -------------------------------------------------------------------------------- /sqlite3_type.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package sqlite3 6 | 7 | /* 8 | #ifndef USE_LIBSQLITE3 9 | #include "sqlite3-binding.h" 10 | #else 11 | #include 12 | #endif 13 | */ 14 | import "C" 15 | import ( 16 | "database/sql" 17 | "reflect" 18 | "strings" 19 | ) 20 | 21 | // ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. 22 | func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { 23 | return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) 24 | } 25 | 26 | /* 27 | func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) { 28 | return 0, false 29 | } 30 | 31 | func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { 32 | return 0, 0, false 33 | } 34 | */ 35 | 36 | // ColumnTypeNullable implement RowsColumnTypeNullable. 37 | func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { 38 | return true, true 39 | } 40 | 41 | // ColumnTypeScanType implement RowsColumnTypeScanType. 42 | func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { 43 | //ct := C.sqlite3_column_type(rc.s.s, C.int(i)) // Always returns 5 44 | return scanType(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) 45 | } 46 | 47 | const ( 48 | SQLITE_INTEGER = iota 49 | SQLITE_TEXT 50 | SQLITE_BLOB 51 | SQLITE_REAL 52 | SQLITE_NUMERIC 53 | SQLITE_TIME 54 | SQLITE_BOOL 55 | SQLITE_NULL 56 | ) 57 | 58 | func scanType(cdt string) reflect.Type { 59 | t := strings.ToUpper(cdt) 60 | i := databaseTypeConvSqlite(t) 61 | switch i { 62 | case SQLITE_INTEGER: 63 | return reflect.TypeOf(sql.NullInt64{}) 64 | case SQLITE_TEXT: 65 | return reflect.TypeOf(sql.NullString{}) 66 | case SQLITE_BLOB: 67 | return reflect.TypeOf(sql.RawBytes{}) 68 | case SQLITE_REAL: 69 | return reflect.TypeOf(sql.NullFloat64{}) 70 | case SQLITE_NUMERIC: 71 | return reflect.TypeOf(sql.NullFloat64{}) 72 | case SQLITE_BOOL: 73 | return reflect.TypeOf(sql.NullBool{}) 74 | case SQLITE_TIME: 75 | return reflect.TypeOf(sql.NullTime{}) 76 | } 77 | return reflect.TypeOf(new(any)) 78 | } 79 | 80 | func databaseTypeConvSqlite(t string) int { 81 | if strings.Contains(t, "INT") { 82 | return SQLITE_INTEGER 83 | } 84 | if t == "CLOB" || t == "TEXT" || 85 | strings.Contains(t, "CHAR") { 86 | return SQLITE_TEXT 87 | } 88 | if t == "BLOB" { 89 | return SQLITE_BLOB 90 | } 91 | if t == "REAL" || t == "FLOAT" || 92 | strings.Contains(t, "DOUBLE") { 93 | return SQLITE_REAL 94 | } 95 | if t == "DATE" || t == "DATETIME" || 96 | t == "TIMESTAMP" { 97 | return SQLITE_TIME 98 | } 99 | if t == "NUMERIC" || 100 | strings.Contains(t, "DECIMAL") { 101 | return SQLITE_NUMERIC 102 | } 103 | if t == "BOOLEAN" { 104 | return SQLITE_BOOL 105 | } 106 | 107 | return SQLITE_NULL 108 | } 109 | -------------------------------------------------------------------------------- /sqlite3_usleep_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2018 G.J.R. Timmer . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build cgo 7 | // +build cgo 8 | 9 | package sqlite3 10 | 11 | // usleep is a function available on *nix based systems. 12 | // This function is not present in Windows. 13 | // Windows has a sleep function but this works with seconds 14 | // and not with microseconds as usleep. 15 | // 16 | // This code should improve performance on windows because 17 | // without the presence of usleep SQLite waits 1 second. 18 | // 19 | // Source: https://github.com/php/php-src/blob/PHP-5.0/win32/time.c 20 | // License: https://github.com/php/php-src/blob/PHP-5.0/LICENSE 21 | // Details: https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa 22 | 23 | /* 24 | #include 25 | 26 | void usleep(__int64 usec) 27 | { 28 | HANDLE timer; 29 | LARGE_INTEGER ft; 30 | 31 | // Convert to 100 nanosecond interval, negative value indicates relative time 32 | ft.QuadPart = -(10*usec); 33 | 34 | timer = CreateWaitableTimer(NULL, TRUE, NULL); 35 | SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); 36 | WaitForSingleObject(timer, INFINITE); 37 | CloseHandle(timer); 38 | } 39 | */ 40 | import "C" 41 | 42 | // EOF 43 | -------------------------------------------------------------------------------- /sqlite3_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build windows 7 | // +build windows 8 | 9 | package sqlite3 10 | 11 | /* 12 | #cgo CFLAGS: -I. 13 | #cgo CFLAGS: -fno-stack-check 14 | #cgo CFLAGS: -fno-stack-protector 15 | #cgo CFLAGS: -mno-stack-arg-probe 16 | #cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T 17 | */ 18 | import "C" 19 | -------------------------------------------------------------------------------- /static_mock.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 Yasuhiro Matsumoto . 2 | // 3 | // Use of this source code is governed by an MIT-style 4 | // license that can be found in the LICENSE file. 5 | 6 | //go:build !cgo 7 | // +build !cgo 8 | 9 | package sqlite3 10 | 11 | import ( 12 | "database/sql" 13 | "database/sql/driver" 14 | "errors" 15 | ) 16 | 17 | var errorMsg = errors.New("Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub") 18 | 19 | func init() { 20 | sql.Register("sqlite3", &SQLiteDriver{}) 21 | } 22 | 23 | type ( 24 | SQLiteDriver struct { 25 | Extensions []string 26 | ConnectHook func(*SQLiteConn) error 27 | } 28 | SQLiteConn struct{} 29 | ) 30 | 31 | func (SQLiteDriver) Open(s string) (driver.Conn, error) { return nil, errorMsg } 32 | func (c *SQLiteConn) RegisterAggregator(string, any, bool) error { return errorMsg } 33 | func (c *SQLiteConn) RegisterAuthorizer(func(int, string, string, string) int) {} 34 | func (c *SQLiteConn) RegisterCollation(string, func(string, string) int) error { return errorMsg } 35 | func (c *SQLiteConn) RegisterCommitHook(func() int) {} 36 | func (c *SQLiteConn) RegisterFunc(string, any, bool) error { return errorMsg } 37 | func (c *SQLiteConn) RegisterRollbackHook(func()) {} 38 | func (c *SQLiteConn) RegisterUpdateHook(func(int, string, string, int64)) {} 39 | -------------------------------------------------------------------------------- /upgrade/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-sqlite3/upgrade 2 | 3 | go 1.19 4 | 5 | require github.com/PuerkitoBio/goquery v1.7.1 // indirect 6 | -------------------------------------------------------------------------------- /upgrade/go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4= 2 | github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY= 3 | github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= 4 | github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= 5 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 6 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= 7 | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 8 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 9 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 10 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 11 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 12 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 13 | -------------------------------------------------------------------------------- /upgrade/package.go: -------------------------------------------------------------------------------- 1 | // Package upgrade is a dummy package to ensure package can be loaded 2 | // 3 | // This file is to avoid the following error: 4 | // can't load package: package go-sqlite3/upgrade: build constraints exclude all Go files in go-sqlite3\upgrade 5 | package upgrade 6 | -------------------------------------------------------------------------------- /upgrade/upgrade.go: -------------------------------------------------------------------------------- 1 | //go:build !cgo && upgrade && ignore 2 | // +build !cgo,upgrade,ignore 3 | 4 | package main 5 | 6 | import ( 7 | "archive/zip" 8 | "bufio" 9 | "bytes" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "log" 14 | "net/http" 15 | "os" 16 | "path" 17 | "path/filepath" 18 | "strings" 19 | "time" 20 | 21 | "github.com/PuerkitoBio/goquery" 22 | ) 23 | 24 | func download(prefix string) (url string, content []byte, err error) { 25 | year := time.Now().Year() 26 | 27 | site := "https://www.sqlite.org/download.html" 28 | //fmt.Printf("scraping %v\n", site) 29 | doc, err := goquery.NewDocument(site) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | doc.Find("a").Each(func(_ int, s *goquery.Selection) { 35 | if strings.HasPrefix(s.Text(), prefix) { 36 | url = fmt.Sprintf("https://www.sqlite.org/%d/", year) + s.Text() 37 | } 38 | }) 39 | 40 | if url == "" { 41 | return "", nil, fmt.Errorf("Unable to find prefix '%s' on sqlite.org", prefix) 42 | } 43 | 44 | fmt.Printf("Downloading %v\n", url) 45 | resp, err := http.Get(url) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | // Ready Body Content 51 | content, err = ioutil.ReadAll(resp.Body) 52 | defer resp.Body.Close() 53 | if err != nil { 54 | return "", nil, err 55 | } 56 | 57 | return url, content, nil 58 | } 59 | 60 | func mergeFile(src string, dst string) error { 61 | defer func() error { 62 | fmt.Printf("Removing: %s\n", src) 63 | err := os.Remove(src) 64 | 65 | if err != nil { 66 | return err 67 | } 68 | 69 | return nil 70 | }() 71 | 72 | // Open destination 73 | fdst, err := os.OpenFile(dst, os.O_APPEND|os.O_WRONLY, 0666) 74 | if err != nil { 75 | return err 76 | } 77 | defer fdst.Close() 78 | 79 | // Read source content 80 | content, err := ioutil.ReadFile(src) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | // Add Additional newline 86 | if _, err := fdst.WriteString("\n"); err != nil { 87 | return err 88 | } 89 | 90 | fmt.Printf("Merging: %s into %s\n", src, dst) 91 | if _, err = fdst.Write(content); err != nil { 92 | return err 93 | } 94 | 95 | return nil 96 | } 97 | 98 | func main() { 99 | fmt.Println("Go-SQLite3 Upgrade Tool") 100 | 101 | wd, err := os.Getwd() 102 | if err != nil { 103 | log.Fatal(err) 104 | } 105 | if filepath.Base(wd) != "upgrade" { 106 | log.Printf("Current directory is %q but should run in upgrade directory", wd) 107 | os.Exit(1) 108 | } 109 | 110 | // Download Amalgamation 111 | _, amalgamation, err := download("sqlite-amalgamation-") 112 | if err != nil { 113 | log.Fatalf("Failed to download: sqlite-amalgamation; %s", err) 114 | } 115 | 116 | // Download Source 117 | _, source, err := download("sqlite-src-") 118 | if err != nil { 119 | log.Fatalf("Failed to download: sqlite-src; %s", err) 120 | } 121 | 122 | // Create Amalgamation Zip Reader 123 | rAmalgamation, err := zip.NewReader(bytes.NewReader(amalgamation), int64(len(amalgamation))) 124 | if err != nil { 125 | log.Fatal(err) 126 | } 127 | 128 | // Create Source Zip Reader 129 | rSource, err := zip.NewReader(bytes.NewReader(source), int64(len(source))) 130 | if err != nil { 131 | log.Fatal(err) 132 | } 133 | 134 | // Extract Amalgamation 135 | for _, zf := range rAmalgamation.File { 136 | var f *os.File 137 | switch path.Base(zf.Name) { 138 | case "sqlite3.c": 139 | f, err = os.Create("../sqlite3-binding.c") 140 | case "sqlite3.h": 141 | f, err = os.Create("../sqlite3-binding.h") 142 | case "sqlite3ext.h": 143 | f, err = os.Create("../sqlite3ext.h") 144 | default: 145 | continue 146 | } 147 | if err != nil { 148 | log.Fatal(err) 149 | } 150 | zr, err := zf.Open() 151 | if err != nil { 152 | log.Fatal(err) 153 | } 154 | 155 | _, err = io.WriteString(f, "#ifndef USE_LIBSQLITE3\n") 156 | if err != nil { 157 | zr.Close() 158 | f.Close() 159 | log.Fatal(err) 160 | } 161 | scanner := bufio.NewScanner(zr) 162 | for scanner.Scan() { 163 | text := scanner.Text() 164 | if text == `#include "sqlite3.h"` { 165 | text = `#include "sqlite3-binding.h" 166 | #ifdef __clang__ 167 | #define assert(condition) ((void)0) 168 | #endif 169 | ` 170 | } 171 | _, err = fmt.Fprintln(f, text) 172 | if err != nil { 173 | break 174 | } 175 | } 176 | err = scanner.Err() 177 | if err != nil { 178 | zr.Close() 179 | f.Close() 180 | log.Fatal(err) 181 | } 182 | _, err = io.WriteString(f, "#else // USE_LIBSQLITE3\n // If users really want to link against the system sqlite3 we\n// need to make this file a noop.\n #endif") 183 | if err != nil { 184 | zr.Close() 185 | f.Close() 186 | log.Fatal(err) 187 | } 188 | zr.Close() 189 | f.Close() 190 | fmt.Printf("Extracted: %v\n", filepath.Base(f.Name())) 191 | } 192 | 193 | //Extract Source 194 | for _, zf := range rSource.File { 195 | var f *os.File 196 | switch path.Base(zf.Name) { 197 | case "userauth.c": 198 | f, err = os.Create("../userauth.c") 199 | case "sqlite3userauth.h": 200 | f, err = os.Create("../userauth.h") 201 | default: 202 | continue 203 | } 204 | if err != nil { 205 | log.Fatal(err) 206 | } 207 | zr, err := zf.Open() 208 | if err != nil { 209 | log.Fatal(err) 210 | } 211 | 212 | _, err = io.Copy(f, zr) 213 | if err != nil { 214 | log.Fatal(err) 215 | } 216 | 217 | zr.Close() 218 | f.Close() 219 | fmt.Printf("extracted %v\n", filepath.Base(f.Name())) 220 | } 221 | 222 | // Merge SQLite User Authentication into amalgamation 223 | if err := mergeFile("../userauth.c", "../sqlite3-binding.c"); err != nil { 224 | log.Fatal(err) 225 | } 226 | if err := mergeFile("../userauth.h", "../sqlite3-binding.h"); err != nil { 227 | log.Fatal(err) 228 | } 229 | 230 | os.Exit(0) 231 | } 232 | --------------------------------------------------------------------------------