Skip to content

Commit 823c1f6

Browse files
committed
SQLiteConn: Add 'Raw' method to give access to underlying sqlite3 context structure
This is necessary, for instance, to allow users of the package to make their own CGo callbacks, and have the underlying sqlite3 library call those C functions directly. Note that the intention from the sqlite3 library seems to be that there should be a measure of binary compatibility between versions: even extensions compiled against an older different version of sqlite3 should be able to call sqlite3_create_function with no problems. So this should work even if users of the package have a slightly different version of the sqlite3.h header. Note that the code itself was written by @rittneje. I tested it and wrote the comments. Signed-off-by: George Dunlap <[email protected]> v2: Just copy @rittneje's code exactly: - Grab the lock while the function is running - Replace the error code with our own if it's of type ErrNo
1 parent 8bf7a8a commit 823c1f6

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

doc.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,12 @@ You can then use the custom driver by passing its name to sql.Open.
130130
}
131131
132132
See the documentation of RegisterFunc for more details.
133+
134+
# Cgo SQLite3 Extensions
135+
136+
Go callbacks are convenient, but the runtime overhead of reflecting
137+
incoming types can be significant. For performance-critical
138+
functions, Cgo functions can also be defined. See SQLiteConn's Raw
139+
method for an example.
133140
*/
134141
package sqlite3

sqlite3.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,53 @@ func (c *SQLiteConn) RegisterAggregator(name string, impl any, pure bool) error
809809
return nil
810810
}
811811

812+
// Raw provides access to the underlying raw C sqlite context pointer
813+
// by casting the `raw` argument from `unsafe.Pointer` to
814+
// `*C.sqlite3`. This is can be used, for example, to add your own C
815+
// functions directly (which, due to fewer runtime reflection checks,
816+
// typically run an order of magnitude faster). For example:
817+
//
818+
// /*
819+
// #include <sqlite3.h>
820+
// ...
821+
// void myFunc(sqlite3_context *context, int argc, sqlite3_value **argv) {
822+
// // Function definition
823+
// }
824+
//
825+
// int myfunc_setup(sqlite3 *db) {
826+
// return sqlite3_create_function(db, "myFunc", ...);
827+
// }
828+
// */
829+
// import "C"
830+
//
831+
// d := &sqlite3.SQLiteDriver{
832+
// ConnectHook: func(c *SQLiteConn) error {
833+
// return c.Raw(func(raw unsafe.Pointer) error {
834+
// db := (*C.sqlite3)(raw)
835+
// if rv := C.myfunc_setup(db); rv != C.SQLITE_OK {
836+
// return sqlite3.ErrNo(rv)
837+
// }
838+
// return nil
839+
// }
840+
// },
841+
// }
842+
//
843+
// Note that as of 1.13, go doesn't correctly handle passing C
844+
// function pointers back to C functions, so C.sqlite3_create_function
845+
// can't be called from Go directly. See
846+
// https://github.com/golang/go/issues/19835 for more details.
847+
func (c *SQLiteConn) Raw(f func(unsafe.Pointer) error) error {
848+
c.mu.Lock()
849+
defer c.mu.Unlock()
850+
if err := f(unsafe.Pointer(c.db)); err != nil {
851+
if _, ok := err.(ErrNo); ok {
852+
return c.lastError()
853+
}
854+
return err
855+
}
856+
return nil
857+
}
858+
812859
// AutoCommit return which currently auto commit or not.
813860
func (c *SQLiteConn) AutoCommit() bool {
814861
c.mu.Lock()

0 commit comments

Comments
 (0)