diff --git a/func.go b/func.go index e795323..e7045a0 100644 --- a/func.go +++ b/func.go @@ -8,6 +8,21 @@ import ( "github.com/tetratelabs/wazero/api" ) +// CreateCollation defines a new collating sequence. +// +// https://www.sqlite.org/c3ref/create_collation.html +func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error { + namePtr := c.arena.string(name) + funcPtr := util.AddHandle(c.ctx, fn) + r := c.call(c.api.createCollation, + uint64(c.handle), uint64(namePtr), uint64(funcPtr)) + if err := c.error(r); err != nil { + util.DelHandle(c.ctx, funcPtr) + return err + } + return nil +} + func exportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder { util.ExportFuncVI(env, "go_destroy", cbDestroy) util.ExportFuncIIIIII(env, "go_compare", cbCompare) @@ -19,10 +34,13 @@ func exportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder return env } -func cbDestroy(ctx context.Context, mod api.Module, pArg uint32) {} +func cbDestroy(ctx context.Context, mod api.Module, pArg uint32) { + util.DelHandle(ctx, pArg) +} func cbCompare(ctx context.Context, mod api.Module, pArg, nKey1, pKey1, nKey2, pKey2 uint32) uint32 { - return 0 + fn := util.GetHandle(ctx, pArg).(func(a, b []byte) int) + return uint32(fn(util.View(mod, pKey1, uint64(nKey1)), util.View(mod, pKey2, uint64(nKey2)))) } func cbFunc(ctx context.Context, mod api.Module, pCtx, nArg, pArg uint32) {} diff --git a/func_test.go b/func_test.go new file mode 100644 index 0000000..a39fd63 --- /dev/null +++ b/func_test.go @@ -0,0 +1,64 @@ +package sqlite3_test + +import ( + "fmt" + "log" + + "golang.org/x/text/collate" + "golang.org/x/text/language" + + "github.com/ncruces/go-sqlite3" + _ "github.com/ncruces/go-sqlite3/embed" +) + +func ExampleConn_CreateCollation() { + db, err := sqlite3.Open(memory) + if err != nil { + log.Fatal(err) + } + + err = db.Exec(`CREATE TABLE IF NOT EXISTS words (word VARCHAR(10))`) + if err != nil { + log.Fatal(err) + } + + err = db.Exec(`INSERT INTO words (word) VALUES ('côte'), ('cote'), ('coter'), ('coté'), ('cotée'), ('côté')`) + if err != nil { + log.Fatal(err) + } + + err = db.CreateCollation("french", collate.New(language.French).Compare) + if err != nil { + log.Fatal(err) + } + + stmt, _, err := db.Prepare(`SELECT word FROM words ORDER BY word COLLATE french`) + if err != nil { + log.Fatal(err) + } + defer stmt.Close() + + for stmt.Step() { + fmt.Println(stmt.ColumnText(0)) + } + if err := stmt.Err(); err != nil { + log.Fatal(err) + } + + err = stmt.Close() + if err != nil { + log.Fatal(err) + } + + err = db.Close() + if err != nil { + log.Fatal(err) + } + // Output: + // cote + // coté + // côte + // côté + // cotée + // coter +} diff --git a/go.mod b/go.mod index 3d66bb2..befd310 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/tetratelabs/wazero v1.2.1 golang.org/x/sync v0.3.0 golang.org/x/sys v0.9.0 + golang.org/x/text v0.10.0 ) retract v0.4.0 // tagged from the wrong branch diff --git a/go.sum b/go.sum index 9ca008c..479c643 100644 --- a/go.sum +++ b/go.sum @@ -8,3 +8,5 @@ golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= diff --git a/module.go b/module.go index 33bb8a5..cf6358e 100644 --- a/module.go +++ b/module.go @@ -155,6 +155,7 @@ func newModule(mod api.Module) (m *module, err error) { changes: getFun("sqlite3_changes64"), lastRowid: getFun("sqlite3_last_insert_rowid"), autocommit: getFun("sqlite3_get_autocommit"), + createCollation: getFun("sqlite3_create_go_collation"), } if err != nil { return nil, err @@ -350,5 +351,6 @@ type sqliteAPI struct { changes api.Function lastRowid api.Function autocommit api.Function + createCollation api.Function destructor uint32 }