Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

access to C struct to support statically linking spatialite #1155

Open
vcabbage opened this issue Apr 20, 2023 · 2 comments
Open

access to C struct to support statically linking spatialite #1155

vcabbage opened this issue Apr 20, 2023 · 2 comments

Comments

@vcabbage
Copy link

Background

Spatialite can be initialized in two ways. Most commonly mod_spatialite is loaded via sqlite APIs, which eventually use dlopen to dynamically load the shared library. This doesn't allow for statically linking spatialite. Additionally if sqlite is statically linked it can't load extensions because dlopen doesn't work.

The other option is to link libspatialite into the binary and initialize spatialite on the sqlite3 handle via spatialite's C API.

Roughly:

var db *C.sqlite3
C.sqlite3_open_v2(..., &db, ...)

cache := C.spatialite_alloc_connection()
C.spatialite_init_ex(db, cache, 0)

This method does allow for static linking.

Request

I have a need to build fully static binaries including sqlite/spatialite. I don't think it would be reasonable to ask that this be directly supported in this library since spatialite isn't designed to be self contained and easy to compile like the sqlite amalgamation. It would be helpful to be able to access the *C.sqlite3 struct without forking the library though.

Would you consider providing a way to access *C.sqlite3 from the connection? Even if it's a method like UnsafeDontAskForSupport() *C.sqlite3 😀.

Thanks for the library and considering this request!


Aside: If you're wondering about the potential leak of the allocation by C.spatialite_alloc_connection(), my solution is to wrap the go-sqlite3 driver.Driver and driver.Conn so that it can be freed when the connection is closed.

@riyaz-ali
Copy link

+1

Having access to underlying connection object (*C.struct_sqlite3) allows to register extensions built externally, a use-case I ran into when building sqlean.go.

There I've managed to use reflect package access conn.db field from struct SQLiteConn, like:

func init() {
  sql.Register("sqlean", &sqlite3.SQLiteDriver{
    ConnectHook: func(conn *sqlite3.SQLiteConn) error {
      // db points to the underlying *C.struct_sqlite3
      var db = reflect.Indirect(reflect.ValueOf(conn)).FieldByName("db").UnsafePointer()
      return nil
    },
  })
}

But this rely on reflect and, I feel, is a bit fragile (what if the layout or name of the field changes?)

@vcabbage
Copy link
Author

@riyaz-ali I've been doing something similar and agree that it's fragile. FWIW, I also put in this guard as a best effort way of detecting changes instead of silently corrupting memory or panicking.

// Check the field is a *C.sqlite3. C types are unusual in that the
// reflect.Types can't be compared directly due to having the namespace of
// the importing package. This appears to be the most reliable way to
// validate the type in this case.
field := reflect.ValueOf(c).Elem().FieldByName("db")
if field.Type().String() != "*sqlite3._Ctype_struct_sqlite3" {
	return nil, fmt.Errorf(`field "db" %s not *C.sqlite3`, field.Type())
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants