1818//
1919// A dialect may also hold an extensions/ directory with one directory per
2020// extension, each a smaller bundle of the same files, applied when a schema
21- // says CREATE EXTENSION.
21+ // says CREATE EXTENSION — or, for a dialect whose settings map virtual table
22+ // modules to extensions, CREATE VIRTUAL TABLE ... USING.
2223package seed
2324
2425import (
@@ -28,6 +29,7 @@ import (
2829 "fmt"
2930 "io"
3031 "io/fs"
32+ "path"
3133 "slices"
3234 "strings"
3335
@@ -78,6 +80,15 @@ type Settings struct {
7880 // same kind of value resolves. "*" makes every seeded type implicitly
7981 // castable to every other, for dialects that compare across categories.
8082 CastCategories string `json:"cast_categories,omitempty"`
83+
84+ // Modules names the extension a virtual table module belongs to, for a
85+ // dialect whose schemas say CREATE VIRTUAL TABLE ... USING rather than
86+ // CREATE EXTENSION: SQLite's fts5 module comes with the functions its
87+ // enable_fts5 compile option adds.
88+ Modules map [string ]string `json:"modules,omitempty"`
89+
90+ // fsys is the dialect directory the settings were read from.
91+ fsys fs.FS
8192}
8293
8394// Type is a type the dialect defines. Aliases are spellings of the same type
@@ -155,21 +166,60 @@ func Dialect(fsys fs.FS, dir string) core.Option {
155166 if err != nil {
156167 return fmt .Errorf ("seed: %s: %w" , dir , err )
157168 }
158- if err := apply (cat , sub ); err != nil {
169+ settings , err := loadSettings (sub )
170+ if err != nil {
171+ return err
172+ }
173+ if err := apply (cat , sub , settings ); err != nil {
159174 return err
160175 }
161176 cat .SetExtensionLoader (func (name string ) error {
162- return applyExtension (cat , sub , name )
177+ dir , ok := settings .extensionDir (name )
178+ if ! ok {
179+ // An extension sqlc has no data for adds nothing, the way
180+ // the legacy catalog has always treated one.
181+ return nil
182+ }
183+ return applyExtension (cat , sub , dir )
163184 })
164185 return nil
165186 })
166187}
167188
168- func apply (cat * core.Catalog , fsys fs.FS ) error {
169- settings , err := loadSettings (fsys )
189+ // ExtensionDir resolves what a schema named — an extension, or a virtual
190+ // table module the dialect's settings map to one — to the extension's
191+ // directory under dir, reporting whether the dialect has data for it.
192+ func ExtensionDir (fsys fs.FS , dir , name string ) (string , bool ) {
193+ sub , err := fs .Sub (fsys , dir )
170194 if err != nil {
171- return err
195+ return "" , false
172196 }
197+ settings , err := loadSettings (sub )
198+ if err != nil {
199+ return "" , false
200+ }
201+ rel , ok := settings .extensionDir (name )
202+ if ! ok {
203+ return "" , false
204+ }
205+ return path .Join (dir , rel ), true
206+ }
207+
208+ // extensionDir is the directory of the extension a name refers to,
209+ // relative to the dialect, if the dialect ships one.
210+ func (s Settings ) extensionDir (name string ) (string , bool ) {
211+ if ext , ok := s .Modules [strings .ToLower (name )]; ok {
212+ name = ext
213+ }
214+ dir := path .Join (ExtensionsDir , name )
215+ if _ , err := fs .Stat (s .fsys , dir ); err != nil {
216+ return "" , false
217+ }
218+ return dir , true
219+ }
220+
221+ func apply (cat * core.Catalog , fsys fs.FS , settings Settings ) error {
222+ var err error
173223 b := & builder {
174224 cat : cat ,
175225 settings : settings ,
@@ -222,6 +272,7 @@ func loadSettings(fsys fs.FS) (Settings, error) {
222272 if settings .Dialect == "" {
223273 return Settings {}, fmt .Errorf ("seed: %s: dialect has no name" , SettingsFile )
224274 }
275+ settings .fsys = fsys
225276 return settings , nil
226277}
227278
0 commit comments