Add connection hook
Add a connection hook, so the user can add their own functionality to the process of creating a connection.
My use-case is to add extra ATTACH
databases. IDK if this kind of functionality should be integrated into the project directly, I think since it's not standard sqlite syntax, I think leaving it up to users to create hooks is useful. I have this kind of thing copy-pasted in a few projects, so I think I would create a library for building the connection string and adding the attach hook.
Here's example usage:
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"net/url"
"modernc.org/sqlite"
)
func OpenSQLite() *sql.DB {
attachQuery := url.Values{
"_name": []string{
"test2",
},
"_pragma": []string{
"journal_mode=wal",
},
}
attachDSN := url.URL{
Path: "test2.db",
RawQuery: attachQuery.Encode(),
}
query := url.Values{
"_pragma": []string{
"auto_vacuum=incremental",
"journal_mode=wal",
},
"_attach": []string{
attachDSN.String(),
},
}
dsn := url.URL{
Path: "test.db",
RawQuery: query.Encode(),
}
// test.db?_attach=test2.db%3F_name%3Dtest2%26_pragma%3Djournal_mode%253Dwal&_pragma=auto_vacuum%3Dincremental&_pragma=journal_mode%3Dwal
db, err := sql.Open("sqlite", dsn.String())
if err != nil {
panic(err)
}
return db
}
func init() {
sqlite.RegisterConnectionHook(attachHook)
}
func attachHook(
conn interface {
driver.ExecerContext
driver.QueryerContext
}, dsn string,
) error {
uri, err := url.Parse(dsn)
if err != nil {
return fmt.Errorf("parse dsn: %w", err)
}
query := uri.Query()
if !query.Has("_attach") {
return nil
}
for _, attachStr := range query["_attach"] {
unescapedAttachStr, err := url.QueryUnescape(attachStr)
if err != nil {
return fmt.Errorf(
"unescape attach filename: %s: %w", attachStr, err)
}
attachURI, err := url.Parse(unescapedAttachStr)
if err != nil {
return fmt.Errorf(
"parse attach uri: %s: %w", unescapedAttachStr, err)
}
err = attachDatabase(conn, attachURI)
if err != nil {
return fmt.Errorf("attach database: %w", err)
}
}
return nil
}
func attachDatabase(conn driver.ExecerContext, uri *url.URL) error {
query := uri.Query()
databaseName := query.Get("_name")
_, err := conn.ExecContext(
context.Background(),
fmt.Sprintf(`ATTACH DATABASE '%s' AS %s`, uri.Path, databaseName),
nil,
)
if err != nil {
return fmt.Errorf("attach exec failed: %w", err)
}
for _, value := range query["_pragma"] {
cmd := "pragma " + databaseName + "." + value
_, err = conn.ExecContext(context.Background(), cmd, nil)
if err != nil {
return fmt.Errorf("pragma exec failed: %w", err)
}
}
return nil
}
Edited by Angus Dippenaar