Compare commits

..

12 Commits

Author SHA1 Message Date
Nuno Cruces
58a32d7c9d Update GORM. 2023-08-20 00:56:08 +01:00
Nuno Cruces
6765e883c1 Register collation. 2023-08-10 13:39:52 +01:00
Nuno Cruces
18fc608433 Embed database as string. 2023-08-10 13:23:54 +01:00
Nuno Cruces
77f37893b9 Driver connector. 2023-08-10 13:18:13 +01:00
Nuno Cruces
f1e36e2581 Updated dependencies. 2023-08-09 16:30:32 +01:00
Nuno Cruces
772b9153c7 Use clear builtin. 2023-08-09 16:16:45 +01:00
Nuno Cruces
4b280a3a7e Updated dependencies. 2023-08-09 15:22:48 +01:00
Nuno Cruces
19b6098bf6 Update go.yml (#28) 2023-08-05 01:12:16 +01:00
dependabot[bot]
2aa685320f Bump golang.org/x/text from 0.11.0 to 0.12.0 (#26)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.11.0 to 0.12.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 00:36:56 +01:00
dependabot[bot]
9941be05c2 Bump golang.org/x/sys from 0.10.0 to 0.11.0 (#27)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.10.0 to 0.11.0.
- [Commits](https://github.com/golang/sys/compare/v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-05 00:35:11 +01:00
Nuno Cruces
a0a9ab7737 Avoid unnecessary alloc. 2023-08-04 14:12:36 +01:00
Nuno Cruces
a77727a1ce Port script. 2023-07-31 15:27:10 +01:00
18 changed files with 177 additions and 115 deletions

View File

@@ -57,6 +57,7 @@ jobs:
with:
chart: 'true'
amend: 'true'
reuse-go: 'true'
if: |
matrix.os == 'ubuntu-latest' &&
github.event_name == 'push'

View File

@@ -50,13 +50,15 @@ OFD locks are fully compatible with process-associated POSIX advisory locks.
On BSD Unixes, this module uses
[BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2).
BSD locks may _not_ be compatible with process-associated POSIX advisory locks.
BSD locks may _not_ be compatible with process-associated POSIX advisory locks
(they are on FreeBSD).
#### Testing
The pure Go VFS is tested by running an unmodified build of SQLite's
The pure Go VFS is tested by running SQLite's
[mptest](https://github.com/sqlite/sqlite/blob/master/mptest/mptest.c)
on Linux, macOS and Windows.
on Linux, macOS and Windows;
BSD code paths are tested on macOS using the `sqlite3_bsd` build tag.
Performance is tested by running
[speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c).
@@ -73,6 +75,7 @@ Performance is tested by running
- [x] in-memory VFS
- [x] read-only VFS, wrapping an [`io.ReaderAt`](https://pkg.go.dev/io#ReaderAt)
- [ ] cloud-based VFS, based on [Cloud Backed SQLite](https://sqlite.org/cloudsqlite/doc/trunk/www/index.wiki)
- [ ] [MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) VFS, using [BadgerDB](https://github.com/dgraph-io/badger)
### Alternatives

View File

@@ -46,9 +46,42 @@ func init() {
type sqlite struct{}
func (sqlite) Open(name string) (_ driver.Conn, err error) {
func (sqlite) Open(name string) (driver.Conn, error) {
c, err := sqlite{}.OpenConnector(name)
if err != nil {
return nil, err
}
return c.Connect(context.Background())
}
func (sqlite) OpenConnector(name string) (driver.Connector, error) {
c := connector{name: name}
if strings.HasPrefix(name, "file:") {
if _, after, ok := strings.Cut(name, "?"); ok {
query, err := url.ParseQuery(after)
if err != nil {
return nil, err
}
c.txlock = query.Get("_txlock")
c.pragmas = len(query["_pragma"]) > 0
}
}
return &c, nil
}
type connector struct {
name string
txlock string
pragmas bool
}
func (n *connector) Driver() driver.Driver {
return sqlite{}
}
func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
var c conn
c.Conn, err = sqlite3.Open(name)
c.Conn, err = sqlite3.Open(n.name)
if err != nil {
return nil, err
}
@@ -58,25 +91,18 @@ func (sqlite) Open(name string) (_ driver.Conn, err error) {
}
}()
var pragmas bool
c.txBegin = "BEGIN"
if strings.HasPrefix(name, "file:") {
if _, after, ok := strings.Cut(name, "?"); ok {
query, _ := url.ParseQuery(after)
old := c.Conn.SetInterrupt(ctx)
defer c.Conn.SetInterrupt(old)
switch s := query.Get("_txlock"); s {
case "":
c.txBegin = "BEGIN"
case "deferred", "immediate", "exclusive":
c.txBegin = "BEGIN " + s
default:
return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", s)
}
pragmas = len(query["_pragma"]) > 0
}
switch n.txlock {
case "":
c.txBegin = "BEGIN"
case "deferred", "immediate", "exclusive":
c.txBegin = "BEGIN " + n.txlock
default:
return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", n.txlock)
}
if !pragmas {
if !n.pragmas {
err = c.Conn.Exec(`PRAGMA busy_timeout=60000`)
if err != nil {
return nil, err
@@ -256,13 +282,14 @@ func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
}
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
// Use QueryContext to setup bindings.
// No need to close rows: that simply resets the statement, exec does the same.
_, err := s.QueryContext(ctx, args)
err := s.setupBindings(args)
if err != nil {
return nil, err
}
old := s.Conn.SetInterrupt(ctx)
defer s.Conn.SetInterrupt(old)
err = s.Stmt.Exec()
if err != nil {
return nil, err
@@ -272,10 +299,18 @@ func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (drive
}
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
err := s.Stmt.ClearBindings()
err := s.setupBindings(args)
if err != nil {
return nil, err
}
return &rows{ctx, s.Stmt, s.Conn}, nil
}
func (s *stmt) setupBindings(args []driver.NamedValue) error {
err := s.Stmt.ClearBindings()
if err != nil {
return err
}
var ids [3]int
for _, arg := range args {
@@ -315,11 +350,10 @@ func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driv
}
}
if err != nil {
return nil, err
return err
}
}
return &rows{ctx, s.Stmt, s.Conn}, nil
return nil
}
func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error {

View File

@@ -45,13 +45,7 @@ func Register(db *sqlite3.Conn) {
return
}
tag, err := language.Parse(arg[0].Text())
if err != nil {
ctx.ResultError(err)
return
}
err = db.CreateCollation(name, collate.New(tag).Compare)
err := RegisterCollation(db, name, arg[0].Text())
if err != nil {
ctx.ResultError(err)
return
@@ -59,6 +53,14 @@ func Register(db *sqlite3.Conn) {
})
}
func RegisterCollation(db *sqlite3.Conn, name, lang string) error {
tag, err := language.Parse(lang)
if err != nil {
return err
}
return db.CreateCollation(name, collate.New(tag).Compare)
}
func upper(ctx sqlite3.Context, arg ...sqlite3.Value) {
if len(arg) == 1 {
ctx.ResultBlob(bytes.ToUpper(arg[0].RawBlob()))

8
go.mod
View File

@@ -1,14 +1,14 @@
module github.com/ncruces/go-sqlite3
go 1.19
go 1.21
require (
github.com/ncruces/julianday v0.1.5
github.com/psanford/httpreadat v0.1.0
github.com/tetratelabs/wazero v1.3.1
github.com/tetratelabs/wazero v1.4.0
golang.org/x/sync v0.3.0
golang.org/x/sys v0.10.0
golang.org/x/text v0.11.0
golang.org/x/sys v0.11.0
golang.org/x/text v0.12.0
)
retract v0.4.0 // tagged from the wrong branch

12
go.sum
View File

@@ -2,11 +2,11 @@ github.com/ncruces/julianday v0.1.5 h1:hDJ9ejiMp3DHsoZ5KW4c1lwfMjbARS7u/gbYcd0FB
github.com/ncruces/julianday v0.1.5/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
github.com/tetratelabs/wazero v1.3.1 h1:rnb9FgOEQRLLR8tgoD1mfjNjMhFeWRUk+a4b4j/GpUM=
github.com/tetratelabs/wazero v1.3.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
github.com/tetratelabs/wazero v1.4.0 h1:9/MirYvmkJ/zSUOygKY/ia3t+e+RqIZXKbylIby1WYk=
github.com/tetratelabs/wazero v1.4.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=

View File

@@ -1,4 +1,4 @@
go 1.19
go 1.21
use (
.

View File

@@ -1,16 +1,16 @@
module github.com/ncruces/go-sqlite3/gormlite
go 1.19
go 1.21
require (
github.com/ncruces/go-sqlite3 v0.8.3
gorm.io/gorm v1.25.2
github.com/ncruces/go-sqlite3 v0.8.5
gorm.io/gorm v1.25.4
)
require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/ncruces/julianday v0.1.5 // indirect
github.com/tetratelabs/wazero v1.3.1 // indirect
golang.org/x/sys v0.10.0 // indirect
github.com/tetratelabs/wazero v1.4.0 // indirect
golang.org/x/sys v0.11.0 // indirect
)

View File

@@ -2,14 +2,14 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/ncruces/go-sqlite3 v0.8.3 h1:kYUAqDpZ0OT+snTH1yWyxq9QSJ22HoM3WKfFEL4N694=
github.com/ncruces/go-sqlite3 v0.8.3/go.mod h1:DUdzKfMlIFmSLAtNHdIgxbdax/5NsQx2RlIlVO7EWfU=
github.com/ncruces/go-sqlite3 v0.8.5 h1:JeNcbJ4rsZ07ZVyqPdnFlfmVSWDW0ONoiuZSUBC369Y=
github.com/ncruces/go-sqlite3 v0.8.5/go.mod h1:XvDtjKk5MgwHX7L4I7BPzzKl36bTZ7+Hr6Kr2QeVkVw=
github.com/ncruces/julianday v0.1.5 h1:hDJ9ejiMp3DHsoZ5KW4c1lwfMjbARS7u/gbYcd0FBZk=
github.com/ncruces/julianday v0.1.5/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/tetratelabs/wazero v1.3.1 h1:rnb9FgOEQRLLR8tgoD1mfjNjMhFeWRUk+a4b4j/GpUM=
github.com/tetratelabs/wazero v1.3.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
github.com/tetratelabs/wazero v1.4.0 h1:9/MirYvmkJ/zSUOygKY/ia3t+e+RqIZXKbylIby1WYk=
github.com/tetratelabs/wazero v1.4.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=

View File

@@ -5,7 +5,6 @@ import (
"context"
"database/sql"
"strconv"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/callbacks"
@@ -136,19 +135,51 @@ func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement,
}
func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
writer.WriteByte('`')
if strings.Contains(str, ".") {
for idx, str := range strings.Split(str, ".") {
if idx > 0 {
writer.WriteString(".`")
var (
underQuoted, selfQuoted bool
continuousBacktick int8
shiftDelimiter int8
)
for _, v := range []byte(str) {
switch v {
case '`':
continuousBacktick++
if continuousBacktick == 2 {
writer.WriteString("``")
continuousBacktick = 0
}
writer.WriteString(str)
writer.WriteByte('`')
case '.':
if continuousBacktick > 0 || !selfQuoted {
shiftDelimiter = 0
underQuoted = false
continuousBacktick = 0
writer.WriteString("`")
}
writer.WriteByte(v)
continue
default:
if shiftDelimiter-continuousBacktick <= 0 && !underQuoted {
writer.WriteString("`")
underQuoted = true
if selfQuoted = continuousBacktick > 0; selfQuoted {
continuousBacktick -= 1
}
}
for ; continuousBacktick > 0; continuousBacktick -= 1 {
writer.WriteString("``")
}
writer.WriteByte(v)
}
} else {
writer.WriteString(str)
writer.WriteByte('`')
shiftDelimiter++
}
if continuousBacktick > 0 && !selfQuoted {
writer.WriteString("``")
}
writer.WriteString("`")
}
func (dialector Dialector) Explain(sql string, vars ...interface{}) string {

View File

@@ -3,7 +3,7 @@ set -euo pipefail
cd -P -- "$(dirname -- "$0")"
rm -rf gorm/ tests/ $TMPDIR/gorm.db
rm -rf gorm/ tests/
git clone --filter=blob:none https://github.com/go-gorm/gorm.git
mv gorm/tests tests
rm -rf gorm/
@@ -11,8 +11,14 @@ rm -rf gorm/
patch -p1 -N < tests.patch
cd tests
go mod edit \
-require github.com/ncruces/go-sqlite3/gormlite@v0.0.0 \
-replace github.com/ncruces/go-sqlite3/gormlite=../ \
-replace github.com/ncruces/go-sqlite3=../../ \
-droprequire gorm.io/driver/sqlite \
-dropreplace gorm.io/gorm
go mod tidy && go work use . && go test
cd ..
rm -rf tests/ $TMPDIR/gorm.db
rm -rf tests/
go work use -r .

View File

@@ -4,27 +4,6 @@ diff --git a/tests/.gitignore b/tests/.gitignore
@@ -1 +1 @@
-go.sum
+*
diff --git a/tests/go.mod b/tests/go.mod
--- a/tests/go.mod
+++ b/tests/go.mod
@@ -6,9 +6,10 @@ require (
github.com/google/uuid v1.3.0
github.com/jinzhu/now v1.1.5
github.com/lib/pq v1.10.9
+ github.com/ncruces/go-sqlite3 v0.8.3
+ github.com/ncruces/go-sqlite3/gormlite v0.0.0
gorm.io/driver/mysql v1.5.2-0.20230612053416-48b6526a21f0
gorm.io/driver/postgres v1.5.3-0.20230607070428-18bc84b75196
- gorm.io/driver/sqlite v1.5.2
gorm.io/driver/sqlserver v1.5.2-0.20230613072041-6e2cde390b0a
gorm.io/gorm v1.25.2
)
@@ -27,4 +28,4 @@ require (
golang.org/x/text v0.11.0 // indirect
)
-replace gorm.io/gorm => ../
+replace github.com/ncruces/go-sqlite3/gormlite => ../
diff --git a/tests/tests_test.go b/tests/tests_test.go
--- a/tests/tests_test.go
+++ b/tests/tests_test.go

View File

@@ -61,12 +61,12 @@ func (s *Stmt) ClearBindings() error {
func (s *Stmt) Step() bool {
s.c.checkInterrupt()
r := s.c.call(s.c.api.step, uint64(s.handle))
if r == _ROW {
switch r {
case _ROW:
return true
}
if r == _DONE {
case _DONE:
s.err = nil
} else {
default:
s.err = s.c.error(r)
}
return false

9
vfs/clear.go Normal file
View File

@@ -0,0 +1,9 @@
//go:build !go1.21
package vfs
func clear(b []byte) {
for i := range b {
b[i] = 0
}
}

10
vfs/memdb/clear.go Normal file
View File

@@ -0,0 +1,10 @@
//go:build !go1.21
package memdb
func clear[T any](b []T) {
var zero T
for i := range b {
b[i] = zero
}
}

View File

@@ -285,10 +285,3 @@ func divRoundUp(a, b int64) int64 {
func modRoundUp(a, b int64) int64 {
return b - (b-a%b)%b
}
func clear[T any](b []T) {
var zero T
for i := range b {
b[i] = zero
}
}

View File

@@ -1,10 +1,10 @@
package readervfs_test
import (
"bytes"
"database/sql"
"fmt"
"log"
"strings"
_ "embed"
@@ -15,7 +15,7 @@ import (
)
//go:embed testdata/test.db
var testDB []byte
var testDB string
func Example_http() {
readervfs.Create("demo.db", httpreadat.New("https://www.sanford.io/demo.db"))
@@ -65,7 +65,7 @@ func Example_http() {
}
func Example_embed() {
readervfs.Create("test.db", readervfs.NewSizeReaderAt(bytes.NewReader(testDB)))
readervfs.Create("test.db", readervfs.NewSizeReaderAt(strings.NewReader(testDB)))
defer readervfs.Delete("test.db")
db, err := sql.Open("sqlite3", "file:test.db?vfs=reader")

View File

@@ -432,9 +432,3 @@ func vfsErrorCode(err error, def _ErrorCode) _ErrorCode {
}
return def
}
func clear(b []byte) {
for i := range b {
b[i] = 0
}
}