Skip to content

Commit a68d40d

Browse files
committed
Updated
1 parent 6553ff6 commit a68d40d

File tree

12 files changed

+438
-30
lines changed

12 files changed

+438
-30
lines changed

etc/server.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,15 @@ sqlite3:
2222
# Databases to load and/or create. Only the 'main' database is required.
2323
databases:
2424
main: ":memory:"
25+
test: /tmp/test.sqlite
26+
2527
# Set create to true to allow databases which don't exist to be created.
2628
create: true
29+
2730
# Set trace to true to enable the ability to profile queries. Profiling information
2831
# can be displayed through the API.
2932
trace: true
33+
3034
# Set max number of connections that can be simultaneously opened
3135
max: 100
3236

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ go 1.17
44

55
require (
66
github.com/djthorpe/go-errors v1.0.2
7-
github.com/djthorpe/go-marshaler v0.0.13
8-
github.com/djthorpe/go-server v1.0.7
7+
github.com/djthorpe/go-marshaler v0.0.14
8+
github.com/djthorpe/go-server v1.0.8
99
github.com/hashicorp/go-multierror v1.1.1
1010
github.com/mattn/go-sqlite3 v1.14.8
1111
github.com/rjeczalik/notify v0.9.2

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
66
github.com/djthorpe/go-errors v1.0.2 h1:kZuNLhb6Yo1iNHaenGa9s5CpRbOG6KxbUtrME4LrAkk=
77
github.com/djthorpe/go-errors v1.0.2/go.mod h1:HtfrZnMd6HsX75Mtbv9Qcnn0BqOrrFArvCaj3RMnZhY=
88
github.com/djthorpe/go-marshaler v0.0.4/go.mod h1:xCXhTzj52UL3YStRsqUSfrKses7ofmfTXYQfVedn8Lw=
9-
github.com/djthorpe/go-marshaler v0.0.13 h1:d0qXD6c/L9MVoCeEzxF85GoyFO/S8ey1bxvt3ndFelY=
10-
github.com/djthorpe/go-marshaler v0.0.13/go.mod h1:xCXhTzj52UL3YStRsqUSfrKses7ofmfTXYQfVedn8Lw=
11-
github.com/djthorpe/go-server v1.0.7 h1:zNGbUhUPtAZjrA3T3RTfbjmTpbpVZL6WwfN+qxbflaI=
12-
github.com/djthorpe/go-server v1.0.7/go.mod h1:yio797qn9P5z2P9ZpASvCnHIFamAaRb+l3ejQJjGicE=
9+
github.com/djthorpe/go-marshaler v0.0.14 h1:L3qHWvEgqPGZI8ZQr4qAxOpKfSKAaF6EUPcGyWulIcQ=
10+
github.com/djthorpe/go-marshaler v0.0.14/go.mod h1:xCXhTzj52UL3YStRsqUSfrKses7ofmfTXYQfVedn8Lw=
11+
github.com/djthorpe/go-server v1.0.8 h1:Ml1SkYPiDmMVpSDMmVNjSMLFW7MLttZwkOnJYC5NgA4=
12+
github.com/djthorpe/go-server v1.0.8/go.mod h1:FShgU8xZdpIiNDhBD/SGytAEW1t3O4+eJdcBRU1vQwQ=
1313
github.com/djthorpe/go-sqlite v1.0.28/go.mod h1:TiGX+dIFea54xxIBVmFemTI/8KUjVUlEoDN2A5HKaMs=
1414
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
1515
github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=

pkg/sqlite3/auth_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,24 @@ func Test_Auth_001(t *testing.T) {
3636
defer pool.Put(conn)
3737

3838
// Make various requests
39-
if err := conn.Exec(N("table_a").CreateTable(N("a").WithType("TEXT").WithPrimary()), nil); err != nil {
39+
if err := conn.(*Conn).Exec(N("table_a").CreateTable(N("a").WithType("TEXT").WithPrimary()), nil); err != nil {
4040
t.Error(err)
4141
}
4242
for _, schema := range conn.Schemas() {
4343
conn.Tables(schema)
4444
}
4545

4646
// Insert a row
47-
conn.Exec(N("table_a").Insert().DefaultValues(), nil)
47+
conn.(*Conn).Exec(N("table_a").Insert().DefaultValues(), nil)
4848

4949
// Delete a row
50-
conn.Exec(N("table_a").Delete(true), nil)
50+
conn.(*Conn).Exec(N("table_a").Delete(true), nil)
5151

5252
// Do a transaction
53-
conn.Begin(sqlite3.SQLITE_TXN_DEFAULT)
54-
conn.Rollback()
55-
conn.Begin(sqlite3.SQLITE_TXN_IMMEDIATE)
56-
conn.Commit()
53+
conn.(*Conn).Begin(sqlite3.SQLITE_TXN_DEFAULT)
54+
conn.(*Conn).Rollback()
55+
conn.(*Conn).Begin(sqlite3.SQLITE_TXN_IMMEDIATE)
56+
conn.(*Conn).Commit()
5757
}
5858

5959
type Auth struct {

pkg/sqlite3/foreignkeys_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ func Test_ForeignKeys_001(t *testing.T) {
3131
}
3232
defer pool.Put(conn)
3333

34-
if err := conn.SetForeignKeyConstraints(true); err != nil {
34+
if err := conn.(*Conn).SetForeignKeyConstraints(true); err != nil {
3535
t.Error(err)
36-
} else if v, err := conn.ForeignKeyConstraints(); err != nil {
36+
} else if v, err := conn.(*Conn).ForeignKeyConstraints(); err != nil {
3737
t.Error(err)
3838
} else if v != true {
3939
t.Error("Unexpected response from ForeignKeyConstraints")
4040
}
4141

42-
if err := conn.SetForeignKeyConstraints(false); err != nil {
42+
if err := conn.(*Conn).SetForeignKeyConstraints(false); err != nil {
4343
t.Error(err)
44-
} else if v, err := conn.ForeignKeyConstraints(); err != nil {
44+
} else if v, err := conn.(*Conn).ForeignKeyConstraints(); err != nil {
4545
t.Error(err)
4646
} else if v != false {
4747
t.Error("Unexpected response from ForeignKeyConstraints")

pkg/sqlite3/pool.go

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sqlite3
33
import (
44
"context"
55
"fmt"
6+
"os"
67
"regexp"
78
"strings"
89
"sync"
@@ -229,7 +230,15 @@ func (p *Pool) new() (*Conn, error) {
229230
if defaultPath == "" {
230231
return nil, ErrNotFound.Withf("No default schema %q found", defaultSchema)
231232
}
232-
conn, err := OpenPath(defaultPath, p.Flags)
233+
234+
// Always allow memory databases to be created
235+
flags := p.Flags
236+
if defaultPath == defaultMemory {
237+
flags |= sqlite3.SQLITE_OPEN_MEMORY | sqlite3.SQLITE_OPEN_CREATE
238+
}
239+
240+
// Perform the open
241+
conn, err := OpenPath(defaultPath, flags)
233242
if err != nil {
234243
return nil, err
235244
}
@@ -336,7 +345,18 @@ func (p *Pool) attach(conn *Conn, schema, path string) error {
336345
if path == "" {
337346
return p.attach(conn, schema, defaultMemory)
338347
}
339-
return conn.Exec(Q("ATTACH DATABASE ", DoubleQuote(path), " AS ", QuoteIdentifier(schema)), nil)
348+
// Create a new database or return an error if it doesn't exist
349+
if path != defaultMemory {
350+
if _, err := os.Stat(path); os.IsNotExist(err) {
351+
if err := p.attachcreate(path); err != nil {
352+
return err
353+
}
354+
} else if err != nil {
355+
return err
356+
}
357+
}
358+
fmt.Println(Q("ATTACH DATABASE ", Quote(path), " AS ", QuoteIdentifier(schema)))
359+
return conn.Exec(Q("ATTACH DATABASE ", Quote(path), " AS ", QuoteIdentifier(schema)), nil)
340360
}
341361

342362
// Detach named database as schema
@@ -348,3 +368,18 @@ func (p *Pool) detach(conn *Conn, schema string) error {
348368
func (p *Pool) trace(c *Conn, s *sqlite3.Statement, ns int64) {
349369
fmt.Printf("TRACE %q => %v\n", s, time.Duration(ns)*time.Nanosecond)
350370
}
371+
372+
// Create a database before attaching
373+
func (p *Pool) attachcreate(path string) error {
374+
if p.PoolConfig.Flags&sqlite3.SQLITE_OPEN_CREATE == 0 {
375+
return ErrBadParameter.Withf("Database does not exist: %q", path)
376+
}
377+
// Open then close database before attaching
378+
if conn, err := sqlite3.OpenPath(path, p.PoolConfig.Flags, ""); err != nil {
379+
return err
380+
} else if err := conn.Close(); err != nil {
381+
return err
382+
} else {
383+
return nil
384+
}
385+
}

pkg/sqlite3/schema_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func Test_Schema_003(t *testing.T) {
9090
defer pool.Close()
9191

9292
// Create table_a and table_b in main schema
93-
conn := pool.Get(context.Background())
93+
conn := pool.Get(context.Background()).(*Conn)
9494
if conn == nil {
9595
t.Fatal("Unexpected nil connection")
9696
}
@@ -202,7 +202,7 @@ func Test_Schema_006(t *testing.T) {
202202
defer pool.Close()
203203

204204
// Create table_a and table_b in main schema
205-
conn := pool.Get(context.Background())
205+
conn := pool.Get(context.Background()).(*Conn)
206206
if conn == nil {
207207
t.Fatal("Unexpected nil connection")
208208
}
@@ -250,7 +250,7 @@ func Test_Schema_007(t *testing.T) {
250250
defer pool.Close()
251251

252252
// Create table_a and table_b in main schema
253-
conn := pool.Get(context.Background())
253+
conn := pool.Get(context.Background()).(*Conn)
254254
if conn == nil {
255255
t.Fatal("Unexpected nil connection")
256256
}

pkg/sqlite3/sqlite3.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ func Version() string {
2121
str, _, _ := sqlite3.Version()
2222
return str
2323
}
24+
25+
func IsComplete(v string) bool {
26+
return sqlite3.IsComplete(v)
27+
}

pkg/sqlite3/tokenizer.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package sqlite3
2+
3+
import (
4+
"bufio"
5+
"io"
6+
"regexp"
7+
"strings"
8+
"unicode"
9+
"unicode/utf8"
10+
11+
// Namespace imports
12+
. "github.com/djthorpe/go-errors"
13+
. "github.com/djthorpe/go-sqlite/pkg/quote"
14+
)
15+
16+
////////////////////////////////////////////////////////////////////////////////
17+
// TYPES
18+
19+
type Tokenizer struct {
20+
*bufio.Scanner
21+
}
22+
23+
type (
24+
KeywordToken string
25+
TypeToken string
26+
NameToken string
27+
ValueToken string
28+
PuncuationToken string
29+
WhitespaceToken string
30+
)
31+
32+
////////////////////////////////////////////////////////////////////////////////
33+
// GLOBALS
34+
35+
var (
36+
reWhitespace = regexp.MustCompile(`^\s*$`)
37+
reName = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
38+
reNumber = regexp.MustCompile(`^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$`)
39+
)
40+
41+
////////////////////////////////////////////////////////////////////////////////
42+
// LIFECYCLE
43+
44+
func NewTokenizer(v string) *Tokenizer {
45+
t := &Tokenizer{bufio.NewScanner(strings.NewReader(v))}
46+
t.Scanner.Split(sqlSplit)
47+
return t
48+
}
49+
50+
////////////////////////////////////////////////////////////////////////////////
51+
// METHODS
52+
53+
func (t *Tokenizer) Next() (interface{}, error) {
54+
if t.Scanner.Scan() {
55+
txt := t.Scanner.Text()
56+
return toToken(txt), nil
57+
}
58+
if t.Scanner.Err() != nil {
59+
return nil, t.Scanner.Err()
60+
} else {
61+
return nil, io.EOF
62+
}
63+
}
64+
65+
func toToken(v string) interface{} {
66+
if reWhitespace.MatchString(v) {
67+
return WhitespaceToken(v)
68+
} else if IsReservedWord(v) {
69+
return KeywordToken(v)
70+
} else if IsType(v) {
71+
return TypeToken(v)
72+
} else if reName.MatchString(v) {
73+
return NameToken(v)
74+
} else if reNumber.MatchString(v) {
75+
return ValueToken(v)
76+
} else {
77+
return PuncuationToken(v)
78+
}
79+
}
80+
81+
func sqlSplit(data []byte, atEOF bool) (int, []byte, error) {
82+
advance, token, err := bufio.ScanWords(data, atEOF)
83+
if err != nil {
84+
return advance, token, err
85+
}
86+
87+
// Check first letter for non-letter or non-digit
88+
r, width := utf8.DecodeRune(data)
89+
if width == 0 {
90+
return 0, token, ErrBadParameter.With("Invalid string")
91+
}
92+
if !(unicode.IsDigit(r) || unicode.IsLetter(r) || r == '_') {
93+
return width, []byte(string(r)), nil
94+
}
95+
96+
// Count until non-letter or non-digit
97+
for i := width; i < len(data); i += width {
98+
r, width = utf8.DecodeRune(data[i:])
99+
if width == 0 {
100+
return 0, token, ErrBadParameter.With("Invalid string")
101+
}
102+
if !(unicode.IsDigit(r) || unicode.IsLetter(r) || r == '_') {
103+
return i, data[:i], nil
104+
}
105+
}
106+
107+
// Return a word
108+
return advance, token, nil
109+
}

pkg/sqlite3/tokenizer_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package sqlite3_test
2+
3+
import (
4+
"testing"
5+
6+
// Namespace Imports
7+
. "github.com/djthorpe/go-sqlite/pkg/sqlite3"
8+
)
9+
10+
func Test_Tokenizer_001(t *testing.T) {
11+
var tests = []string{
12+
"CREATE TABLE foo (id INTEGER PRIMARY KEY, name TEXT);",
13+
`INSERT INTO foo VALUES ('string value',99,"name value",CURRENT_TIMESTAMP)`,
14+
}
15+
for _, test := range tests {
16+
tokenizer := NewTokenizer(test)
17+
tokens := []interface{}{}
18+
for {
19+
token, err := tokenizer.Next()
20+
if token == nil {
21+
break
22+
}
23+
if err != nil {
24+
t.Error(err)
25+
t.FailNow()
26+
}
27+
tokens = append(tokens, token)
28+
}
29+
t.Logf("%q =>", test)
30+
for _, token := range tokens {
31+
t.Logf(" <%T %q>", token, token)
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)