on 9c79c19 help sign in
210 lines 5.4 kB history raw
1package sql3util
3import (
4 "context"
5 _ "embed"
6 "sync"
8 "github.com/tetratelabs/wazero"
9 "github.com/tetratelabs/wazero/api"
11 "github.com/ncruces/go-sqlite3/internal/util"
14const (
15 errp = 4
16 sqlp = 8
19var (
20 //go:embed wasm/sql3parse_table.wasm
21 binary []byte
22 once sync.Once
23 runtime wazero.Runtime
24 compiled wazero.CompiledModule
27// ParseTable parses a [CREATE] or [ALTER TABLE] command.
28//
29// [CREATE]: https://sqlite.org/lang_createtable.html
30// [ALTER TABLE]: https://sqlite.org/lang_altertable.html
31func ParseTable(sql string) (_ *Table, err error) {
32 once.Do(func() {
33 ctx := context.Background()
34 cfg := wazero.NewRuntimeConfigInterpreter()
35 runtime = wazero.NewRuntimeWithConfig(ctx, cfg)
36 compiled, err = runtime.CompileModule(ctx, binary)
37 })
38 if err != nil {
39 return nil, err
40 }
42 ctx := context.Background()
43 mod, err := runtime.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName(""))
44 if err != nil {
45 return nil, err
46 }
47 defer mod.Close(ctx)
49 if buf, ok := mod.Memory().Read(sqlp, uint32(len(sql))); ok {
50 copy(buf, sql)
51 }
53 stack := [...]util.Stk_t{sqlp, util.Stk_t(len(sql)), errp}
54 err = mod.ExportedFunction("sql3parse_table").CallWithStack(ctx, stack[:])
55 if err != nil {
56 return nil, err
57 }
59 c, _ := mod.Memory().ReadUint32Le(errp)
60 switch c {
61 case _MEMORY:
62 panic(util.OOMErr)
63 case _SYNTAX:
64 return nil, util.ErrorString("sql3parse: invalid syntax")
65 case _UNSUPPORTEDSQL:
66 return nil, util.ErrorString("sql3parse: unsupported SQL")
67 }
69 var tab Table
70 tab.load(mod, uint32(stack[0]), sql)
71 return &tab, nil
74// Table holds metadata about a table.
75type Table struct {
76 Name string
77 Schema string
78 Comment string
79 IsTemporary bool
80 IsIfNotExists bool
81 IsWithoutRowID bool
82 IsStrict bool
83 Columns []Column
84 Type StatementType
85 CurrentName string
86 NewName string
89func (t *Table) load(mod api.Module, ptr uint32, sql string) {
90 t.Name = loadString(mod, ptr+0, sql)
91 t.Schema = loadString(mod, ptr+8, sql)
92 t.Comment = loadString(mod, ptr+16, sql)
94 t.IsTemporary = loadBool(mod, ptr+24)
95 t.IsIfNotExists = loadBool(mod, ptr+25)
96 t.IsWithoutRowID = loadBool(mod, ptr+26)
97 t.IsStrict = loadBool(mod, ptr+27)
99 t.Columns = loadSlice(mod, ptr+28, func(ptr uint32, ret *Column) {
100 p, _ := mod.Memory().ReadUint32Le(ptr)
101 ret.load(mod, p, sql)
102 })
104 t.Type = loadEnum[StatementType](mod, ptr+44)
105 t.CurrentName = loadString(mod, ptr+48, sql)
106 t.NewName = loadString(mod, ptr+56, sql)
109// Column holds metadata about a column.
110type Column struct {
111 Name string
112 Type string
113 Length string
114 ConstraintName string
115 Comment string
116 IsPrimaryKey bool
117 IsAutoIncrement bool
118 IsNotNull bool
119 IsUnique bool
120 PKOrder OrderClause
121 PKConflictClause ConflictClause
122 NotNullConflictClause ConflictClause
123 UniqueConflictClause ConflictClause
124 CheckExpr string
125 DefaultExpr string
126 CollateName string
127 ForeignKeyClause *ForeignKey
130func (c *Column) load(mod api.Module, ptr uint32, sql string) {
131 c.Name = loadString(mod, ptr+0, sql)
132 c.Type = loadString(mod, ptr+8, sql)
133 c.Length = loadString(mod, ptr+16, sql)
134 c.ConstraintName = loadString(mod, ptr+24, sql)
135 c.Comment = loadString(mod, ptr+32, sql)
137 c.IsPrimaryKey = loadBool(mod, ptr+40)
138 c.IsAutoIncrement = loadBool(mod, ptr+41)
139 c.IsNotNull = loadBool(mod, ptr+42)
140 c.IsUnique = loadBool(mod, ptr+43)
142 c.PKOrder = loadEnum[OrderClause](mod, ptr+44)
143 c.PKConflictClause = loadEnum[ConflictClause](mod, ptr+48)
144 c.NotNullConflictClause = loadEnum[ConflictClause](mod, ptr+52)
145 c.UniqueConflictClause = loadEnum[ConflictClause](mod, ptr+56)
147 c.CheckExpr = loadString(mod, ptr+60, sql)
148 c.DefaultExpr = loadString(mod, ptr+68, sql)
149 c.CollateName = loadString(mod, ptr+76, sql)
151 if ptr, _ := mod.Memory().ReadUint32Le(ptr + 84); ptr != 0 {
152 c.ForeignKeyClause = &ForeignKey{}
153 c.ForeignKeyClause.load(mod, ptr, sql)
154 }
157type ForeignKey struct {
158 Table string
159 Columns []string
160 OnDelete FKAction
161 OnUpdate FKAction
162 Match string
163 Deferrable FKDefType
166func (f *ForeignKey) load(mod api.Module, ptr uint32, sql string) {
167 f.Table = loadString(mod, ptr+0, sql)
169 f.Columns = loadSlice(mod, ptr+8, func(ptr uint32, ret *string) {
170 *ret = loadString(mod, ptr, sql)
171 })
173 f.OnDelete = loadEnum[FKAction](mod, ptr+16)
174 f.OnUpdate = loadEnum[FKAction](mod, ptr+20)
175 f.Match = loadString(mod, ptr+24, sql)
176 f.Deferrable = loadEnum[FKDefType](mod, ptr+32)
179func loadString(mod api.Module, ptr uint32, sql string) string {
180 off, _ := mod.Memory().ReadUint32Le(ptr + 0)
181 if off == 0 {
182 return ""
183 }
184 len, _ := mod.Memory().ReadUint32Le(ptr + 4)
185 return sql[off-sqlp : off+len-sqlp]
188func loadSlice[T any](mod api.Module, ptr uint32, fn func(uint32, *T)) []T {
189 ref, _ := mod.Memory().ReadUint32Le(ptr + 4)
190 if ref == 0 {
191 return nil
192 }
193 len, _ := mod.Memory().ReadUint32Le(ptr + 0)
194 ret := make([]T, len)
195 for i := range ret {
196 fn(ref, &ret[i])
197 ref += 4
198 }
199 return ret
202func loadEnum[T ~uint32](mod api.Module, ptr uint32) T {
203 val, _ := mod.Memory().ReadUint32Le(ptr)
204 return T(val)
207func loadBool(mod api.Module, ptr uint32) bool {
208 val, _ := mod.Memory().ReadByte(ptr)
209 return val != 0