Rename sql3util.

This commit is contained in:
Nuno Cruces
2024-10-22 23:32:57 +01:00
parent b9b489aae9
commit c69ee0fe8d
29 changed files with 128 additions and 102 deletions

View File

@@ -12,7 +12,7 @@ import (
"github.com/ncruces/go-sqlite3" "github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util" "github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/vtabutil" "github.com/ncruces/go-sqlite3/util/sql3util"
) )
const ( const (
@@ -39,17 +39,17 @@ func Register(db *sqlite3.Conn) error {
) )
for _, arg := range arg { for _, arg := range arg {
key, val := vtabutil.NamedArg(arg) key, val := sql3util.NamedArg(arg)
if done.Contains(key) { if done.Contains(key) {
return nil, fmt.Errorf("transitive_closure: more than one %q parameter", key) return nil, fmt.Errorf("transitive_closure: more than one %q parameter", key)
} }
switch key { switch key {
case "tablename": case "tablename":
table = vtabutil.Unquote(val) table = sql3util.Unquote(val)
case "idcolumn": case "idcolumn":
column = vtabutil.Unquote(val) column = sql3util.Unquote(val)
case "parentcolumn": case "parentcolumn":
parent = vtabutil.Unquote(val) parent = sql3util.Unquote(val)
default: default:
return nil, fmt.Errorf("transitive_closure: unknown %q parameter", key) return nil, fmt.Errorf("transitive_closure: unknown %q parameter", key)
} }

View File

@@ -4,8 +4,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/ncruces/go-sqlite3/internal/util" "github.com/ncruces/go-sqlite3/util/sql3util"
"github.com/ncruces/go-sqlite3/util/vtabutil"
) )
func uintArg(key, val string) (int, error) { func uintArg(key, val string) (int, error) {
@@ -20,7 +19,7 @@ func boolArg(key, val string) (bool, error) {
if val == "" { if val == "" {
return true, nil return true, nil
} }
b, ok := util.ParseBool(val) b, ok := sql3util.ParseBool(val)
if ok { if ok {
return b, nil return b, nil
} }
@@ -28,7 +27,7 @@ func boolArg(key, val string) (bool, error) {
} }
func runeArg(key, val string) (rune, error) { func runeArg(key, val string) (rune, error) {
r, _, tail, err := strconv.UnquoteChar(vtabutil.Unquote(val), 0) r, _, tail, err := strconv.UnquoteChar(sql3util.Unquote(val), 0)
if tail != "" || err != nil { if tail != "" || err != nil {
return 0, fmt.Errorf("csv: invalid %q parameter: %s", key, val) return 0, fmt.Errorf("csv: invalid %q parameter: %s", key, val)
} }

View File

@@ -3,7 +3,7 @@ package csv
import ( import (
"testing" "testing"
"github.com/ncruces/go-sqlite3/util/vtabutil" "github.com/ncruces/go-sqlite3/util/sql3util"
) )
func Test_uintArg(t *testing.T) { func Test_uintArg(t *testing.T) {
@@ -24,7 +24,7 @@ func Test_uintArg(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) { t.Run(tt.arg, func(t *testing.T) {
key, val := vtabutil.NamedArg(tt.arg) key, val := sql3util.NamedArg(tt.arg)
if key != tt.key { if key != tt.key {
t.Errorf("NamedArg() %v, want err %v", key, tt.key) t.Errorf("NamedArg() %v, want err %v", key, tt.key)
} }
@@ -62,7 +62,7 @@ func Test_boolArg(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) { t.Run(tt.arg, func(t *testing.T) {
key, val := vtabutil.NamedArg(tt.arg) key, val := sql3util.NamedArg(tt.arg)
if key != tt.key { if key != tt.key {
t.Errorf("NamedArg() %v, want err %v", key, tt.key) t.Errorf("NamedArg() %v, want err %v", key, tt.key)
} }
@@ -96,7 +96,7 @@ func Test_runeArg(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) { t.Run(tt.arg, func(t *testing.T) {
key, val := vtabutil.NamedArg(tt.arg) key, val := sql3util.NamedArg(tt.arg)
if key != tt.key { if key != tt.key {
t.Errorf("NamedArg() %v, want err %v", key, tt.key) t.Errorf("NamedArg() %v, want err %v", key, tt.key)
} }

View File

@@ -18,7 +18,7 @@ import (
"github.com/ncruces/go-sqlite3" "github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util" "github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/osutil" "github.com/ncruces/go-sqlite3/util/osutil"
"github.com/ncruces/go-sqlite3/util/vtabutil" "github.com/ncruces/go-sqlite3/util/sql3util"
) )
// Register registers the CSV virtual table. // Register registers the CSV virtual table.
@@ -44,17 +44,17 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
) )
for _, arg := range arg { for _, arg := range arg {
key, val := vtabutil.NamedArg(arg) key, val := sql3util.NamedArg(arg)
if done.Contains(key) { if done.Contains(key) {
return nil, fmt.Errorf("csv: more than one %q parameter", key) return nil, fmt.Errorf("csv: more than one %q parameter", key)
} }
switch key { switch key {
case "filename": case "filename":
filename = vtabutil.Unquote(val) filename = sql3util.Unquote(val)
case "data": case "data":
data = vtabutil.Unquote(val) data = sql3util.Unquote(val)
case "schema": case "schema":
schema = vtabutil.Unquote(val) schema = sql3util.Unquote(val)
case "header": case "header":
header, err = boolArg(key, val) header, err = boolArg(key, val)
case "columns": case "columns":

View File

@@ -3,7 +3,7 @@ package csv
import ( import (
"strings" "strings"
"github.com/ncruces/go-sqlite3/util/vtabutil" "github.com/ncruces/go-sqlite3/util/sql3util"
) )
type affinity byte type affinity byte
@@ -17,7 +17,7 @@ const (
) )
func getColumnAffinities(schema string) ([]affinity, error) { func getColumnAffinities(schema string) ([]affinity, error) {
tab, err := vtabutil.Parse(schema) tab, err := sql3util.ParseTable(schema)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -1,22 +0,0 @@
package util
import "strings"
func ParseBool(s string) (b, ok bool) {
if len(s) == 0 {
return false, false
}
if s[0] == '0' {
return false, true
}
if '1' <= s[0] && s[0] <= '9' {
return true, true
}
switch strings.ToLower(s) {
case "true", "yes", "on":
return true, true
case "false", "no", "off":
return false, true
}
return false, false
}

View File

@@ -1,28 +0,0 @@
package util
import "testing"
func TestParseBool(t *testing.T) {
tests := []struct {
str string
val bool
ok bool
}{
{"", false, false},
{"0", false, true},
{"1", true, true},
{"9", true, true},
{"T", false, false},
{"true", true, true},
{"FALSE", false, true},
{"false?", false, false},
}
for _, tt := range tests {
t.Run(tt.str, func(t *testing.T) {
gotVal, gotOK := ParseBool(tt.str)
if gotVal != tt.val || gotOK != tt.ok {
t.Errorf("ParseBool(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
}
})
}
}

View File

@@ -1,19 +1,17 @@
package tests package tests
import ( import (
_ "embed"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
_ "embed"
"github.com/ncruces/go-sqlite3" "github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed" _ "github.com/ncruces/go-sqlite3/embed"
_ "github.com/ncruces/go-sqlite3/internal/testcfg" _ "github.com/ncruces/go-sqlite3/internal/testcfg"
"github.com/ncruces/go-sqlite3/vfs" "github.com/ncruces/go-sqlite3/vfs"
_ "github.com/ncruces/go-sqlite3/vfs/adiantum" _ "github.com/ncruces/go-sqlite3/vfs/adiantum"
"github.com/ncruces/go-sqlite3/vfs/memdb" "github.com/ncruces/go-sqlite3/vfs/memdb"
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
_ "github.com/ncruces/go-sqlite3/vfs/xts" _ "github.com/ncruces/go-sqlite3/vfs/xts"
) )

View File

@@ -1,6 +1,7 @@
# Virtual Table utility functions # SQLite utility functions
This package implements utilities mostly useful to virtual table implementations. This package implements assorted SQLite utilities
useful to extension writers.
It also wraps a [parser](https://github.com/marcobambini/sqlite-createtable-parser) It also wraps a [parser](https://github.com/marcobambini/sqlite-createtable-parser)
for the [`CREATE`](https://sqlite.org/lang_createtable.html) and for the [`CREATE`](https://sqlite.org/lang_createtable.html) and

View File

@@ -1,4 +1,4 @@
package vtabutil package sql3util
import "strings" import "strings"
@@ -17,19 +17,44 @@ func Unquote(val string) string {
if len(val) < 2 { if len(val) < 2 {
return val return val
} }
if val[0] != val[len(val)-1] { fst := val[0]
lst := val[len(val)-1]
rst := val[1 : len(val)-1]
if fst == '[' && lst == ']' {
return rst
}
if fst != lst {
return val return val
} }
var old, new string var old, new string
switch val[0] { switch fst {
default: default:
return val return val
case '"':
old, new = `""`, `"`
case '`': case '`':
old, new = "``", "`" old, new = "``", "`"
case '"':
old, new = `""`, `"`
case '\'': case '\'':
old, new = `''`, `'` old, new = `''`, `'`
} }
return strings.ReplaceAll(val[1:len(val)-1], old, new) return strings.ReplaceAll(rst, old, new)
}
func ParseBool(s string) (b, ok bool) {
if len(s) == 0 {
return false, false
}
if s[0] == '0' {
return false, true
}
if '1' <= s[0] && s[0] <= '9' {
return true, true
}
switch strings.ToLower(s) {
case "true", "yes", "on":
return true, true
case "false", "no", "off":
return false, true
}
return false, false
} }

55
util/sql3util/arg_test.go Normal file
View File

@@ -0,0 +1,55 @@
package sql3util_test
import (
"testing"
"github.com/ncruces/go-sqlite3/util/sql3util"
)
func TestUnquote(t *testing.T) {
tests := []struct {
val string
want string
}{
{"a", "a"},
{"abc", "abc"},
{"abba", "abba"},
{"`ab``c`", "ab`c"},
{"'ab''c'", "ab'c"},
{"'ab``c'", "ab``c"},
{"[ab``c]", "ab``c"},
{`"ab""c"`, `ab"c`},
}
for _, tt := range tests {
t.Run(tt.val, func(t *testing.T) {
if got := sql3util.Unquote(tt.val); got != tt.want {
t.Errorf("Unquote(%s) = %s, want %s", tt.val, got, tt.want)
}
})
}
}
func TestParseBool(t *testing.T) {
tests := []struct {
str string
val bool
ok bool
}{
{"", false, false},
{"0", false, true},
{"1", true, true},
{"9", true, true},
{"T", false, false},
{"true", true, true},
{"FALSE", false, true},
{"false?", false, false},
}
for _, tt := range tests {
t.Run(tt.str, func(t *testing.T) {
gotVal, gotOK := sql3util.ParseBool(tt.str)
if gotVal != tt.val || gotOK != tt.ok {
t.Errorf("ParseBool(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
}
})
}
}

View File

@@ -1,4 +1,4 @@
package vtabutil package sql3util
const ( const (
_NONE = iota _NONE = iota

View File

@@ -1,10 +1,9 @@
package vtabutil package sql3util
import ( import (
"context" "context"
"sync"
_ "embed" _ "embed"
"sync"
"github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/api"
@@ -25,11 +24,11 @@ var (
compiled wazero.CompiledModule compiled wazero.CompiledModule
) )
// Parse parses a [CREATE] or [ALTER TABLE] command. // ParseTable parses a [CREATE] or [ALTER TABLE] command.
// //
// [CREATE]: https://sqlite.org/lang_createtable.html // [CREATE]: https://sqlite.org/lang_createtable.html
// [ALTER TABLE]: https://sqlite.org/lang_altertable.html // [ALTER TABLE]: https://sqlite.org/lang_altertable.html
func Parse(sql string) (_ *Table, err error) { func ParseTable(sql string) (_ *Table, err error) {
once.Do(func() { once.Do(func() {
ctx := context.Background() ctx := context.Background()
cfg := wazero.NewRuntimeConfigInterpreter() cfg := wazero.NewRuntimeConfigInterpreter()

View File

@@ -1,13 +1,13 @@
package vtabutil_test package sql3util_test
import ( import (
"testing" "testing"
"github.com/ncruces/go-sqlite3/util/vtabutil" "github.com/ncruces/go-sqlite3/util/sql3util"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
tab, err := vtabutil.Parse(`CREATE TABLE child(x REFERENCES parent)`) tab, err := sql3util.ParseTable(`CREATE TABLE child(x REFERENCES parent)`)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -0,0 +1,2 @@
// Package sql3util implements SQLite utilities.
package sql3util

View File

@@ -1,4 +1,4 @@
// Package vtabutil implements virtual filesystem utilities. // Package vfsutil implements virtual filesystem utilities.
package vfsutil package vfsutil
import ( import (

View File

@@ -1,2 +0,0 @@
// Package vtabutil implements virtual table utilities.
package vtabutil

View File

@@ -2,11 +2,10 @@ package memdb_test
import ( import (
"database/sql" "database/sql"
_ "embed"
"fmt" "fmt"
"log" "log"
_ "embed"
_ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed" _ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/vfs/memdb" "github.com/ncruces/go-sqlite3/vfs/memdb"

View File

@@ -1,9 +1,8 @@
package memdb package memdb
import ( import (
"testing"
_ "embed" _ "embed"
"testing"
"github.com/ncruces/go-sqlite3" "github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed" _ "github.com/ncruces/go-sqlite3/embed"

View File

@@ -2,12 +2,11 @@ package readervfs_test
import ( import (
"database/sql" "database/sql"
_ "embed"
"fmt" "fmt"
"log" "log"
"strings" "strings"
_ "embed"
"github.com/psanford/httpreadat" "github.com/psanford/httpreadat"
_ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/driver"

View File

@@ -203,6 +203,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
func (s *vfsShm) shmBarrier() { func (s *vfsShm) shmBarrier() {
s.Lock() s.Lock()
//lint:ignore SA2001 memory barrier.
s.Unlock() s.Unlock()
} }

View File

@@ -273,5 +273,6 @@ func (s *vfsShm) shmUnmap(delete bool) {
func (s *vfsShm) shmBarrier() { func (s *vfsShm) shmBarrier() {
s.lockMtx.Lock() s.lockMtx.Lock()
//lint:ignore SA2001 memory barrier.
s.lockMtx.Unlock() s.lockMtx.Unlock()
} }

View File

@@ -5,6 +5,7 @@ import (
"compress/bzip2" "compress/bzip2"
"context" "context"
"crypto/rand" "crypto/rand"
_ "embed"
"flag" "flag"
"io" "io"
"os" "os"
@@ -14,8 +15,6 @@ import (
"strings" "strings"
"testing" "testing"
_ "embed"
"github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental" "github.com/tetratelabs/wazero/experimental"

View File

@@ -11,6 +11,7 @@ import (
"github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util" "github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/sql3util"
"github.com/ncruces/julianday" "github.com/ncruces/julianday"
) )
@@ -146,7 +147,7 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla
} }
if file, ok := file.(FilePowersafeOverwrite); ok { if file, ok := file.(FilePowersafeOverwrite); ok {
if b, ok := util.ParseBool(name.URIParameter("psow")); ok { if b, ok := sql3util.ParseBool(name.URIParameter("psow")); ok {
file.SetPowersafeOverwrite(b) file.SetPowersafeOverwrite(b)
} }
} }