Skip to content
Commits on Source (4)
......@@ -37,8 +37,8 @@ build_all_targets:
GOOS=linux GOARCH=riscv64 go build -v ./...
GOOS=linux GOARCH=s390x go test -c -o /dev/null
GOOS=linux GOARCH=s390x go build -v ./...
GOOS=netbsd GOARCH=amd64 go test -c -o /dev/null
GOOS=netbsd GOARCH=amd64 go build -v ./...
# GOOS=netbsd GOARCH=amd64 go test -c -o /dev/null
# GOOS=netbsd GOARCH=amd64 go build -v ./...
GOOS=openbsd GOARCH=amd64 go test -c -o /dev/null
GOOS=openbsd GOARCH=amd64 go build -v ./...
GOOS=openbsd GOARCH=arm64 go test -c -o /dev/null
......@@ -57,7 +57,7 @@ clean:
edit:
@touch log
@if [ -f "Session.vim" ]; then novim -S & else novim -p Makefile go.mod builder.json all_test.go vendor_libsqlite3.go & fi
@if [ -f "Session.vim" ]; then gvim -S & else gvim -p Makefile go.mod builder.json all_test.go vendor_libsqlite3.go & fi
editor:
gofmt -l -s -w . 2>&1 | tee log-editor
......
module modernc.org/sqlite
go 1.20
go 1.21
require (
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd
......@@ -31,4 +31,6 @@ retract v1.20.1 // https://gitlab.com/cznic/sqlite/-/issues/123
retract v1.29.4 // tagged accidentally w/o builders checking the commit
retract v1.33.0 // intended to resolve #177 but break clients
retract v1.33.0 // intended to resolve #177 but breaks clients
retract v1.34.3 // intended to resolve #199 but breaks clients, see #200, fix in 1fcc86e9
......@@ -11,18 +11,24 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
......@@ -32,7 +38,9 @@ modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWP
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
......
module example.com/issue198
go 1.23.3
require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.22.0 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.34.1 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/sqlite v1.34.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk=
modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
// https://gitlab.com/cznic/sqlite/-/issues/198#note_2232364348
package main
import (
"context"
"database/sql"
"errors"
"fmt"
"io"
"net/http"
"os"
"os/signal"
"path/filepath"
"strings"
"time"
"golang.org/x/sync/errgroup"
_ "github.com/mattn/go-sqlite3"
_ "modernc.org/sqlite"
)
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
absDb, err := filepath.Abs("simple-web.db")
if err != nil {
panic(err)
}
dbSlash := "/" + strings.TrimPrefix(filepath.ToSlash(absDb), "/")
connStr := "file://" + dbSlash + "?_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(10000)"
// connStr = "file:///" + dbSlash + "?_foreign_keys=1&_journal_mode=WAL&_synchronous=NORMAL&_busy_timeout=10000&_mutex=no"
fmt.Printf("connecting to %s\n", connStr)
db, err := sql.Open("sqlite", connStr)
if err != nil {
panic(err)
}
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(2)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(1 * time.Minute)
defer db.Close()
if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL DEFAULT ''
)`); err != nil {
panic(err)
}
db.Exec(`DELETE FROM data`)
db.Exec(`INSERT INTO data (Id, Name) VALUES (1, 'A'),(2, 'B'),(3, 'C'),(4, 'D');`)
http.HandleFunc("/items/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
type entry struct {
Id int64 `sql:"id"`
Name string `sql:"name"`
}
scanEntry := func(ctx context.Context, id string) (entry, error) {
ctx = context.Background()
// modernc sqlite breaks connections when context gets cancelled
dbConn, err := db.Conn(ctx)
// var sqliteErr *sqlite.Error
// for err != nil && errors.As(err, &sqliteErr) && sqliteErr.Code() == sqlite3.SQLITE_BUSY {
// // fmt.Fprintf(os.Stderr, "failed to obtain connection. retrying %#v\n", err)
// time.Sleep(time.Microsecond)
// dbConn, err = db.Conn(ctx)
// }
if err != nil {
return entry{}, fmt.Errorf("obtaining db conn: %w", err)
}
defer dbConn.Close()
// modernc sqlite breaks connections when context gets cancelled
// ctx = context.Background()
row := dbConn.QueryRowContext(ctx, "SELECT Id,Name FROM data WHERE id = ? LIMIT 1", id)
if err := row.Err(); err != nil {
return entry{}, fmt.Errorf("retrieving row: %w", err)
}
e := entry{}
if err := row.Scan(&e.Id, &e.Name); err != nil {
return entry{}, fmt.Errorf("scanning entry: %w", err)
}
return e, nil
}
const HttpClientClosedRequest = 499
e, err := scanEntry(r.Context(), id)
if err != nil {
if errors.Is(err, context.Canceled) {
w.WriteHeader(HttpClientClosedRequest)
return
}
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(os.Stderr, "%v - failed to retrieve row %v\n", time.Now().Format(time.RFC3339), err)
return
}
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "%#v", e)
})
server := http.Server{
Addr: "127.0.0.1:8082",
Handler: http.DefaultServeMux,
}
go func() {
if err := server.ListenAndServe(); err != nil {
fmt.Printf("server: %v\n", err)
}
}()
go func() {
RunClient(ctx)
}()
<-ctx.Done()
server.Shutdown(context.Background())
db.Close()
}
func RunClient(ctx context.Context) {
eg, _ := errgroup.WithContext(ctx)
limit := 20
eg.SetLimit(limit)
c := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 6,
MaxConnsPerHost: 6,
DisableKeepAlives: false,
IdleConnTimeout: 10 * time.Second,
}, Timeout: 5 * time.Second}
for i := 0; i < limit; i++ {
eg.Go(func() error {
for ctx.Err() == nil {
res, err := c.Get("http://127.0.0.1:8082/items/2")
if err != nil {
fmt.Printf("err http.Do %v\n", err)
time.Sleep(time.Second)
continue
}
io.Copy(io.Discard, res.Body)
res.Body.Close()
}
return nil
})
}
if err := eg.Wait(); err != nil {
panic(err)
}
}
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !(linux && arm64)
package sqlite3
import (
......@@ -12,3 +14,7 @@ import (
func X__ccgo_sqlite3_log(t *libc.TLS, iErrCode int32, zFormat uintptr, va uintptr) { /* sqlite3.c:29405:17: */
libc.X__ccgo_sqlite3_log(t, iErrCode, zFormat, va)
}
func PatchIssue199() {
// nop
}
// Copyright 2019 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sqlite3
import (
"syscall"
"unsafe"
"modernc.org/libc"
)
// Format and write a message to the log if logging is enabled.
func X__ccgo_sqlite3_log(t *libc.TLS, iErrCode int32, zFormat uintptr, va uintptr) { /* sqlite3.c:29405:17: */
libc.X__ccgo_sqlite3_log(t, iErrCode, zFormat, va)
}
// https://gitlab.com/cznic/sqlite/-/issues/199
//
// We are currently stuck on libc@v1.55.3. Until that is resolved - fix the
// problem at runtime.
func PatchIssue199() {
p := unsafe.Pointer(&_aSyscall)
*(*uintptr)(unsafe.Add(p, 608)) = __ccgo_fp(_unixGetpagesizeIssue199)
}
func _unixGetpagesizeIssue199(tls *libc.TLS) (r int32) {
return int32(syscall.Getpagesize())
}
......@@ -18,6 +18,7 @@ import (
"net/url"
"reflect"
"runtime"
"sort"
"strconv"
"strings"
"sync"
......@@ -55,6 +56,11 @@ const (
sqliteLockedSharedcache = sqlite3.SQLITE_LOCKED | (1 << 8)
)
// https://gitlab.com/cznic/sqlite/-/issues/199
func init() {
sqlite3.PatchIssue199()
}
// Error represents sqlite library error code.
type Error struct {
msg string
......@@ -884,7 +890,25 @@ func applyQueryParams(c *conn, query string) error {
return err
}
var a []string
for _, v := range q["_pragma"] {
a = append(a, v)
}
// Push 'busy_timeout' first, the rest in lexicographic order, case insenstive.
// See https://gitlab.com/cznic/sqlite/-/issues/198#note_2233423463 for
// discussion.
sort.Slice(a, func(i, j int) bool {
x, y := strings.TrimSpace(strings.ToLower(a[i])), strings.TrimSpace(strings.ToLower(a[j]))
if strings.HasPrefix(x, "busy_timeout") {
return true
}
if strings.HasPrefix(y, "busy_timeout") {
return false
}
return x < y
})
for _, v := range a {
cmd := "pragma " + v
_, err := c.exec(context.Background(), cmd, nil)
if err != nil {
......@@ -1464,15 +1488,25 @@ func (c *conn) closeV2(db uintptr) error {
return nil
}
// ResetSession is called prior to executing a query on the connection if the
// connection has been used before. If the driver returns ErrBadConn the
// connection is discarded.
func (c *conn) ResetSession(ctx context.Context) error {
if c.db == 0 {
if !c.usable() {
return driver.ErrBadConn
}
return nil
}
// IsValid is called prior to placing the connection into the connection pool.
// The connection will be discarded if false is returned.
func (c *conn) IsValid() bool {
return c.db != 0
return c.usable()
}
func (c *conn) usable() bool {
return c.db != 0 && sqlite3.Xsqlite3_is_interrupted(c.tls, c.db) == 0
}
// FunctionImpl describes an [application-defined SQL function]. If Scalar is
......