Skip to content

Commit 5a4c7a5

Browse files
authored
Refactor CREATE parser. (#111)
1 parent 90f7e50 commit 5a4c7a5

File tree

12 files changed

+289
-98
lines changed

12 files changed

+289
-98
lines changed

embed/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ ROOT=../
77
BINARYEN="$ROOT/tools/binaryen-version_117/bin"
88
WASI_SDK="$ROOT/tools/wasi-sdk-22.0/bin"
99

10-
"$WASI_SDK/clang" --target=wasm32-wasi -std=c17 -flto -g0 -O2 \
10+
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -flto -g0 -O2 \
1111
-Wall -Wextra -Wno-unused-parameter -Wno-unused-function \
1212
-o sqlite3.wasm "$ROOT/sqlite3/main.c" \
1313
-I"$ROOT/sqlite3" \

ext/csv/types.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package csv
22

33
import (
4-
_ "embed"
54
"strings"
65

76
"github.com/ncruces/go-sqlite3/util/vtabutil"
@@ -22,12 +21,10 @@ func getColumnAffinities(schema string) ([]affinity, error) {
2221
if err != nil {
2322
return nil, err
2423
}
25-
defer tab.Close()
2624

27-
types := make([]affinity, tab.NumColumns())
28-
for i := range types {
29-
col := tab.Column(i)
30-
types[i] = getAffinity(col.Type())
25+
types := make([]affinity, len(tab.Columns))
26+
for i, col := range tab.Columns {
27+
types[i] = getAffinity(col.Type)
3128
}
3229
return types, nil
3330
}

ext/csv/types_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package csv
22

3-
import (
4-
_ "embed"
5-
"testing"
6-
)
3+
import "testing"
74

85
func Test_getAffinity(t *testing.T) {
96
tests := []struct {

util/vtabutil/const.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package vtabutil
2+
3+
const (
4+
_NONE = iota
5+
_MEMORY
6+
_SYNTAX
7+
_UNSUPPORTEDSQL
8+
)
9+
10+
type ConflictClause uint32
11+
12+
const (
13+
CONFLICT_NONE ConflictClause = iota
14+
CONFLICT_ROLLBACK
15+
CONFLICT_ABORT
16+
CONFLICT_FAIL
17+
CONFLICT_IGNORE
18+
CONFLICT_REPLACE
19+
)
20+
21+
type OrderClause uint32
22+
23+
const (
24+
ORDER_NONE OrderClause = iota
25+
ORDER_ASC
26+
ORDER_DESC
27+
)
28+
29+
type FKAction uint32
30+
31+
const (
32+
FKACTION_NONE FKAction = iota
33+
FKACTION_SETNULL
34+
FKACTION_SETDEFAULT
35+
FKACTION_CASCADE
36+
FKACTION_RESTRICT
37+
FKACTION_NOACTION
38+
)
39+
40+
type FKDefType uint32
41+
42+
const (
43+
DEFTYPE_NONE FKDefType = iota
44+
DEFTYPE_DEFERRABLE
45+
DEFTYPE_DEFERRABLE_INITIALLY_DEFERRED
46+
DEFTYPE_DEFERRABLE_INITIALLY_IMMEDIATE
47+
DEFTYPE_NOTDEFERRABLE
48+
DEFTYPE_NOTDEFERRABLE_INITIALLY_DEFERRED
49+
DEFTYPE_NOTDEFERRABLE_INITIALLY_IMMEDIATE
50+
)
51+
52+
type StatementType uint32
53+
54+
const (
55+
CREATE_UNKNOWN StatementType = iota
56+
CREATE_TABLE
57+
ALTER_RENAME_TABLE
58+
ALTER_RENAME_COLUMN
59+
ALTER_ADD_COLUMN
60+
ALTER_DROP_COLUMN
61+
)

util/vtabutil/parse.go

Lines changed: 143 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,60 +12,50 @@ import (
1212
)
1313

1414
const (
15-
_NONE = iota
16-
_MEMORY
17-
_SYNTAX
18-
_UNSUPPORTEDSQL
19-
20-
codeptr = 4
21-
baseptr = 8
15+
errp = 4
16+
sqlp = 8
2217
)
2318

2419
var (
2520
//go:embed parse/sql3parse_table.wasm
26-
binary []byte
27-
ctx context.Context
28-
once sync.Once
29-
runtime wazero.Runtime
30-
module wazero.CompiledModule
21+
binary []byte
22+
once sync.Once
23+
runtime wazero.Runtime
24+
compiled wazero.CompiledModule
3125
)
3226

33-
// Table holds metadata about a table.
34-
type Table struct {
35-
mod api.Module
36-
ptr uint32
37-
sql string
38-
}
39-
4027
// Parse parses a [CREATE] or [ALTER TABLE] command.
4128
//
4229
// [CREATE]: https://sqlite.org/lang_createtable.html
4330
// [ALTER TABLE]: https://sqlite.org/lang_altertable.html
4431
func Parse(sql string) (_ *Table, err error) {
4532
once.Do(func() {
46-
ctx = context.Background()
47-
cfg := wazero.NewRuntimeConfigInterpreter().WithDebugInfoEnabled(false)
33+
ctx := context.Background()
34+
cfg := wazero.NewRuntimeConfigInterpreter()
4835
runtime = wazero.NewRuntimeWithConfig(ctx, cfg)
49-
module, err = runtime.CompileModule(ctx, binary)
36+
compiled, err = runtime.CompileModule(ctx, binary)
5037
})
5138
if err != nil {
5239
return nil, err
5340
}
5441

55-
mod, err := runtime.InstantiateModule(ctx, module, wazero.NewModuleConfig().WithName(""))
42+
ctx := context.Background()
43+
mod, err := runtime.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName(""))
5644
if err != nil {
5745
return nil, err
5846
}
47+
defer mod.Close(ctx)
5948

60-
if buf, ok := mod.Memory().Read(baseptr, uint32(len(sql))); ok {
49+
if buf, ok := mod.Memory().Read(sqlp, uint32(len(sql))); ok {
6150
copy(buf, sql)
6251
}
63-
r, err := mod.ExportedFunction("sql3parse_table").Call(ctx, baseptr, uint64(len(sql)), codeptr)
52+
53+
r, err := mod.ExportedFunction("sql3parse_table").Call(ctx, sqlp, uint64(len(sql)), errp)
6454
if err != nil {
6555
return nil, err
6656
}
6757

68-
c, _ := mod.Memory().ReadUint32Le(codeptr)
58+
c, _ := mod.Memory().ReadUint32Le(errp)
6959
switch c {
7060
case _MEMORY:
7161
panic(util.OOMErr)
@@ -74,68 +64,146 @@ func Parse(sql string) (_ *Table, err error) {
7464
case _UNSUPPORTEDSQL:
7565
return nil, util.ErrorString("sql3parse: unsupported SQL")
7666
}
77-
if r[0] == 0 {
78-
return nil, nil
79-
}
80-
return &Table{
81-
sql: sql,
82-
mod: mod,
83-
ptr: uint32(r[0]),
84-
}, nil
67+
68+
var tab Table
69+
tab.load(mod, uint32(r[0]), sql)
70+
return &tab, nil
8571
}
8672

87-
// Close closes a table handle.
88-
func (t *Table) Close() error {
89-
mod := t.mod
90-
t.mod = nil
91-
return mod.Close(ctx)
73+
// Table holds metadata about a table.
74+
type Table struct {
75+
Name string
76+
Schema string
77+
Comment string
78+
IsTemporary bool
79+
IsIfNotExists bool
80+
IsWithoutRowID bool
81+
IsStrict bool
82+
Columns []Column
83+
Type StatementType
84+
CurrentName string
85+
NewName string
9286
}
9387

94-
// NumColumns returns the number of columns of the table.
95-
func (t *Table) NumColumns() int {
96-
r, err := t.mod.ExportedFunction("sql3table_num_columns").Call(ctx, uint64(t.ptr))
97-
if err != nil {
98-
panic(err)
99-
}
100-
return int(int32(r[0]))
88+
func (t *Table) load(mod api.Module, ptr uint32, sql string) {
89+
t.Name = loadString(mod, ptr+0, sql)
90+
t.Schema = loadString(mod, ptr+8, sql)
91+
t.Comment = loadString(mod, ptr+16, sql)
92+
93+
t.IsTemporary = loadBool(mod, ptr+24)
94+
t.IsIfNotExists = loadBool(mod, ptr+25)
95+
t.IsWithoutRowID = loadBool(mod, ptr+26)
96+
t.IsStrict = loadBool(mod, ptr+27)
97+
98+
t.Columns = loadSlice(mod, ptr+28, func(ptr uint32, res *Column) {
99+
p, _ := mod.Memory().ReadUint32Le(ptr)
100+
res.load(mod, p, sql)
101+
})
102+
103+
t.Type = loadEnum[StatementType](mod, ptr+44)
104+
t.CurrentName = loadString(mod, ptr+48, sql)
105+
t.NewName = loadString(mod, ptr+56, sql)
101106
}
102107

103-
// Column returns data for the ith column of the table.
104-
//
105-
// https://sqlite.org/lang_createtable.html#column_definitions
106-
func (t *Table) Column(i int) Column {
107-
r, err := t.mod.ExportedFunction("sql3table_get_column").Call(ctx, uint64(t.ptr), uint64(i))
108-
if err != nil {
109-
panic(err)
110-
}
111-
return Column{
112-
tab: t,
113-
ptr: uint32(r[0]),
108+
// Column holds metadata about a column.
109+
type Column struct {
110+
Name string
111+
Type string
112+
Length string
113+
ConstraintName string
114+
Comment string
115+
IsPrimaryKey bool
116+
IsAutoIncrement bool
117+
IsNotNull bool
118+
IsUnique bool
119+
PKOrder OrderClause
120+
PKConflictClause ConflictClause
121+
NotNullConflictClause ConflictClause
122+
UniqueConflictClause ConflictClause
123+
CheckExpr string
124+
DefaultExpr string
125+
CollateName string
126+
ForeignKeyClause *ForeignKey
127+
}
128+
129+
func (c *Column) load(mod api.Module, ptr uint32, sql string) {
130+
c.Name = loadString(mod, ptr+0, sql)
131+
c.Type = loadString(mod, ptr+8, sql)
132+
c.Length = loadString(mod, ptr+16, sql)
133+
c.ConstraintName = loadString(mod, ptr+24, sql)
134+
c.Comment = loadString(mod, ptr+32, sql)
135+
136+
c.IsPrimaryKey = loadBool(mod, ptr+40)
137+
c.IsAutoIncrement = loadBool(mod, ptr+41)
138+
c.IsNotNull = loadBool(mod, ptr+42)
139+
c.IsUnique = loadBool(mod, ptr+43)
140+
141+
c.PKOrder = loadEnum[OrderClause](mod, ptr+44)
142+
c.PKConflictClause = loadEnum[ConflictClause](mod, ptr+48)
143+
c.NotNullConflictClause = loadEnum[ConflictClause](mod, ptr+52)
144+
c.UniqueConflictClause = loadEnum[ConflictClause](mod, ptr+56)
145+
146+
c.CheckExpr = loadString(mod, ptr+60, sql)
147+
c.DefaultExpr = loadString(mod, ptr+68, sql)
148+
c.CollateName = loadString(mod, ptr+76, sql)
149+
150+
if ptr, _ := mod.Memory().ReadUint32Le(ptr + 84); ptr != 0 {
151+
c.ForeignKeyClause = &ForeignKey{}
152+
c.ForeignKeyClause.load(mod, ptr, sql)
114153
}
115154
}
116155

117-
func (t *Table) string(ptr uint32) string {
118-
if ptr == 0 {
156+
type ForeignKey struct {
157+
Table string
158+
Columns []string
159+
OnDelete FKAction
160+
OnUpdate FKAction
161+
Match string
162+
Deferrable FKDefType
163+
}
164+
165+
func (f *ForeignKey) load(mod api.Module, ptr uint32, sql string) {
166+
f.Table = loadString(mod, ptr+0, sql)
167+
168+
f.Columns = loadSlice(mod, ptr+8, func(ptr uint32, res *string) {
169+
*res = loadString(mod, ptr, sql)
170+
})
171+
172+
f.OnDelete = loadEnum[FKAction](mod, ptr+16)
173+
f.OnUpdate = loadEnum[FKAction](mod, ptr+20)
174+
f.Match = loadString(mod, ptr+24, sql)
175+
f.Deferrable = loadEnum[FKDefType](mod, ptr+32)
176+
}
177+
178+
func loadString(mod api.Module, ptr uint32, sql string) string {
179+
off, _ := mod.Memory().ReadUint32Le(ptr + 0)
180+
if off == 0 {
119181
return ""
120182
}
121-
off, _ := t.mod.Memory().ReadUint32Le(ptr + 0)
122-
len, _ := t.mod.Memory().ReadUint32Le(ptr + 4)
123-
return t.sql[off-baseptr : off+len-baseptr]
183+
len, _ := mod.Memory().ReadUint32Le(ptr + 4)
184+
return sql[off-sqlp : off+len-sqlp]
124185
}
125186

126-
// Column holds metadata about a column.
127-
type Column struct {
128-
tab *Table
129-
ptr uint32
187+
func loadSlice[T any](mod api.Module, ptr uint32, fn func(uint32, *T)) []T {
188+
ref, _ := mod.Memory().ReadUint32Le(ptr + 4)
189+
if ref == 0 {
190+
return nil
191+
}
192+
len, _ := mod.Memory().ReadUint32Le(ptr + 0)
193+
res := make([]T, len)
194+
for i := range res {
195+
fn(ref, &res[i])
196+
ref += 4
197+
}
198+
return res
130199
}
131200

132-
// Type returns the declared type of a column.
133-
//
134-
// https://sqlite.org/lang_createtable.html#column_data_types
135-
func (c Column) Type() string {
136-
r, err := c.tab.mod.ExportedFunction("sql3column_type").Call(ctx, uint64(c.ptr))
137-
if err != nil {
138-
panic(err)
139-
}
140-
return c.tab.string(uint32(r[0]))
201+
func loadEnum[T ~uint32](mod api.Module, ptr uint32) T {
202+
val, _ := mod.Memory().ReadUint32Le(ptr)
203+
return T(val)
204+
}
205+
206+
func loadBool(mod api.Module, ptr uint32) bool {
207+
val, _ := mod.Memory().ReadByte(ptr)
208+
return val != 0
141209
}

0 commit comments

Comments
 (0)