forked from mirror/go-sqlite3
Compare commits
22 Commits
json-examp
...
master
Author | SHA1 | Date |
---|---|---|
Charlie Vieth | 7ce62b2ade | |
Philip O'Toole | 1603038a4d | |
Philip O'Toole | bce3773726 | |
Yasuhiro Matsumoto | 31c761827c | |
David Vassallo | 4b8633cceb | |
David Vassallo | 0b3708425e | |
Luca Guidi | 90900be5db | |
kkqy | be28dec3df | |
Levi Gruspe | 17f6553f94 | |
KiYugadgeter | 7476442ed6 | |
RewardedIvan | da62659c58 | |
Joshua Hull | 4ef63c9c0d | |
Joshua Hull | f92b6bb2a1 | |
Joshua Hull | d5355d86f9 | |
Yoshiki Shibukawa | c8a114388a | |
Oliver Giles | f1eef49b3f | |
Ben Johnson | a2e94c9d58 | |
Yasuhiro Matsumoto | d8e192b752 | |
Bryan C. Mills | 595e13285d | |
Aoang | fd616a2f47 | |
Phil Eaton | 3ccccfb4c9 | |
Kristóf Havasi | b819467576 |
|
@ -19,4 +19,4 @@ jobs:
|
|||
run: |
|
||||
cd ./_example/simple
|
||||
docker build -t simple .
|
||||
docker run simple | grep 99\ こんにちわ世界099
|
||||
docker run simple | grep 99\ こんにちは世界099
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
go: ['1.16', '1.17', '1.18']
|
||||
go: ['1.17', '1.18', '1.19']
|
||||
fail-fast: false
|
||||
env:
|
||||
OS: ${{ matrix.os }}
|
||||
|
@ -44,7 +44,7 @@ jobs:
|
|||
run: go-acc . -- -race -v -tags "libsqlite3"
|
||||
|
||||
- name: 'Tags: full'
|
||||
run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable sqlite_unlock_notify sqlite_column_metadata"
|
||||
run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_os_trace sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable"
|
||||
|
||||
- name: 'Tags: vacuum'
|
||||
run: go-acc . -- -race -v -tags "sqlite_vacuum_full"
|
||||
|
@ -64,7 +64,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
go: ['1.16', '1.17', '1.18']
|
||||
go: ['1.17', '1.18', '1.19']
|
||||
fail-fast: false
|
||||
env:
|
||||
OS: windows-latest
|
||||
|
@ -99,7 +99,7 @@ jobs:
|
|||
- name: 'Tags: full'
|
||||
run: |
|
||||
echo 'skip this test'
|
||||
echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable sqlite_unlock_notify"
|
||||
echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable"
|
||||
shell: msys2 {0}
|
||||
|
||||
- name: 'Tags: vacuum'
|
||||
|
|
17
README.md
17
README.md
|
@ -1,7 +1,7 @@
|
|||
go-sqlite3
|
||||
==========
|
||||
|
||||
[![GoDoc Reference](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3)
|
||||
[![Go Reference](https://pkg.go.dev/badge/github.com/mattn/go-sqlite3.svg)](https://pkg.go.dev/github.com/mattn/go-sqlite3)
|
||||
[![GitHub Actions](https://github.com/mattn/go-sqlite3/workflows/Go/badge.svg)](https://github.com/mattn/go-sqlite3/actions?query=workflow%3AGo)
|
||||
[![Financial Contributors on Open Collective](https://opencollective.com/mattn-go-sqlite3/all/badge.svg?label=financial+contributors)](https://opencollective.com/mattn-go-sqlite3)
|
||||
[![codecov](https://codecov.io/gh/mattn/go-sqlite3/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-sqlite3)
|
||||
|
@ -165,6 +165,7 @@ go build --tags "icu json1 fts5 secure_delete"
|
|||
| Allow URI Authority | sqlite_allow_uri_authority | URI filenames normally throws an error if the authority section is not either empty or "localhost".<br><br>However, if SQLite is compiled with the SQLITE_ALLOW_URI_AUTHORITY compile-time option, then the URI is converted into a Uniform Naming Convention (UNC) filename and passed down to the underlying operating system that way |
|
||||
| App Armor | sqlite_app_armor | When defined, this C-preprocessor macro activates extra code that attempts to detect misuse of the SQLite API, such as passing in NULL pointers to required parameters or using objects after they have been destroyed. <br><br>App Armor is not available under `Windows`. |
|
||||
| Disable Load Extensions | sqlite_omit_load_extension | Loading of external extensions is enabled by default.<br><br>To disable extension loading add the build tag `sqlite_omit_load_extension`. |
|
||||
| Enable Serialization with `libsqlite3` | sqlite_serialize | Serialization and deserialization of a SQLite database is available by default, unless the build tag `libsqlite3` is set.<br><br>To enable this functionality even if `libsqlite3` is set, add the build tag `sqlite_serialize`. |
|
||||
| Foreign Keys | sqlite_foreign_keys | This macro determines whether enforcement of foreign key constraints is enabled or disabled by default for new database connections.<br><br>Each database connection can always turn enforcement of foreign key constraints on and off and run-time using the foreign_keys pragma.<br><br>Enforcement of foreign key constraints is normally off by default, but if this compile-time parameter is set to 1, enforcement of foreign key constraints will be on by default |
|
||||
| Full Auto Vacuum | sqlite_vacuum_full | Set the default auto vacuum to full |
|
||||
| Incremental Auto Vacuum | sqlite_vacuum_incr | Set the default auto vacuum to incremental |
|
||||
|
@ -172,15 +173,18 @@ go build --tags "icu json1 fts5 secure_delete"
|
|||
| International Components for Unicode | sqlite_icu | This option causes the International Components for Unicode or "ICU" extension to SQLite to be added to the build |
|
||||
| Introspect PRAGMAS | sqlite_introspect | This option adds some extra PRAGMA statements. <ul><li>PRAGMA function_list</li><li>PRAGMA module_list</li><li>PRAGMA pragma_list</li></ul> |
|
||||
| JSON SQL Functions | sqlite_json | When this option is defined in the amalgamation, the JSON SQL functions are added to the build automatically |
|
||||
| Math Functions | sqlite_math_functions | This compile-time option enables built-in scalar math functions. For more information see [Built-In Mathematical SQL Functions](https://www.sqlite.org/lang_mathfunc.html) |
|
||||
| OS Trace | sqlite_os_trace | This option enables OSTRACE() debug logging. This can be verbose and should not be used in production. |
|
||||
| Pre Update Hook | sqlite_preupdate_hook | Registers a callback function that is invoked prior to each INSERT, UPDATE, and DELETE operation on a database table. |
|
||||
| Secure Delete | sqlite_secure_delete | This compile-time option changes the default setting of the secure_delete pragma.<br><br>When this option is not used, secure_delete defaults to off. When this option is present, secure_delete defaults to on.<br><br>The secure_delete setting causes deleted content to be overwritten with zeros. There is a small performance penalty since additional I/O must occur.<br><br>On the other hand, secure_delete can prevent fragments of sensitive information from lingering in unused parts of the database file after it has been deleted. See the documentation on the secure_delete pragma for additional information |
|
||||
| Secure Delete (FAST) | sqlite_secure_delete_fast | For more information see [PRAGMA secure_delete](https://www.sqlite.org/pragma.html#pragma_secure_delete) |
|
||||
| Tracing / Debug | sqlite_trace | Activate trace functions |
|
||||
| User Authentication | sqlite_userauth | SQLite User Authentication see [User Authentication](#user-authentication) for more information. |
|
||||
| Virtual Tables | sqlite_vtable | SQLite Virtual Tables see [SQLite Official VTABLE Documentation](https://www.sqlite.org/vtab.html) for more information, and a [full example here](https://github.com/mattn/go-sqlite3/tree/master/_example/vtable) |
|
||||
|
||||
# Compilation
|
||||
|
||||
This package requires the `CGO_ENABLED=1` ennvironment variable if not set by default, and the presence of the `gcc` compiler.
|
||||
This package requires the `CGO_ENABLED=1` environment variable if not set by default, and the presence of the `gcc` compiler.
|
||||
|
||||
If you need to add additional CFLAGS or LDFLAGS to the build command, and do not want to modify this package, then this can be achieved by using the `CGO_CFLAGS` and `CGO_LDFLAGS` environment variables.
|
||||
|
||||
|
@ -216,14 +220,13 @@ This library can be cross-compiled.
|
|||
In some cases you are required to the `CC` environment variable with the cross compiler.
|
||||
|
||||
## Cross Compiling from MAC OSX
|
||||
The simplest way to cross compile from OSX is to use [xgo](https://github.com/karalabe/xgo).
|
||||
The simplest way to cross compile from OSX is to use [musl-cross](https://github.com/FiloSottile/homebrew-musl-cross).
|
||||
|
||||
Steps:
|
||||
- Install [xgo](https://github.com/karalabe/xgo) (`go get github.com/karalabe/xgo`).
|
||||
- Ensure that your project is within your `GOPATH`.
|
||||
- Run `xgo local/path/to/project`.
|
||||
- Install [musl-cross](https://github.com/FiloSottile/homebrew-musl-cross) (`brew install FiloSottile/musl-cross/musl-cross`).
|
||||
- Run `CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ GOARCH=amd64 GOOS=linux CGO_ENABLED=1 go build -ldflags "-linkmode external -extldflags -static"`.
|
||||
|
||||
Please refer to the project's [README](https://github.com/karalabe/xgo/blob/master/README.md) for further information.
|
||||
Please refer to the project's [README](https://github.com/FiloSottile/homebrew-musl-cross#readme) for further information.
|
||||
|
||||
# Google Cloud Platform
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ func createBulkInsertQuery(n int, start int) (query string, args []interface{})
|
|||
for i := 0; i < n; i++ {
|
||||
values[i] = "(?, ?)"
|
||||
args[pos] = start + i
|
||||
args[pos+1] = fmt.Sprintf("こんにちわ世界%03d", i)
|
||||
args[pos+1] = fmt.Sprintf("こんにちは世界%03d", i)
|
||||
pos += 2
|
||||
}
|
||||
query = fmt.Sprintf(
|
||||
|
|
|
@ -33,7 +33,7 @@ RUN \
|
|||
RUN \
|
||||
# Smoke test
|
||||
set -o pipefail; \
|
||||
/go/bin/simple | grep 99\ こんにちわ世界099
|
||||
/go/bin/simple | grep 99\ こんにちは世界099
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Main Stage
|
||||
|
|
|
@ -37,12 +37,15 @@ func main() {
|
|||
}
|
||||
defer stmt.Close()
|
||||
for i := 0; i < 100; i++ {
|
||||
_, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i))
|
||||
_, err = stmt.Exec(i, fmt.Sprintf("こんにちは世界%03d", i))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rows, err := db.Query("select id, name from foo")
|
||||
if err != nil {
|
||||
|
|
|
@ -63,7 +63,12 @@ func (v *ghRepoTable) Open() (sqlite3.VTabCursor, error) {
|
|||
}
|
||||
|
||||
func (v *ghRepoTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
|
||||
return &sqlite3.IndexResult{}, nil
|
||||
used := make([]bool, len(csts))
|
||||
return &sqlite3.IndexResult{
|
||||
IdxNum: 0,
|
||||
IdxStr: "default",
|
||||
Used: used,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *ghRepoTable) Disconnect() error { return nil }
|
||||
|
|
19
callback.go
19
callback.go
|
@ -353,6 +353,20 @@ func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func callbackRetGeneric(ctx *C.sqlite3_context, v reflect.Value) error {
|
||||
if v.IsNil() {
|
||||
C.sqlite3_result_null(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
cb, err := callbackRet(v.Elem().Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cb(ctx, v.Elem())
|
||||
}
|
||||
|
||||
func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
|
||||
switch typ.Kind() {
|
||||
case reflect.Interface:
|
||||
|
@ -360,6 +374,11 @@ func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
|
|||
if typ.Implements(errorInterface) {
|
||||
return callbackRetNil, nil
|
||||
}
|
||||
|
||||
if typ.NumMethod() == 0 {
|
||||
return callbackRetGeneric, nil
|
||||
}
|
||||
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if typ.Elem().Kind() != reflect.Uint8 {
|
||||
|
|
|
@ -102,3 +102,15 @@ func TestCallbackConverters(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallbackReturnAny(t *testing.T) {
|
||||
udf := func() interface{} {
|
||||
return 1
|
||||
}
|
||||
|
||||
typ := reflect.TypeOf(udf)
|
||||
_, err := callbackRet(typ.Out(0))
|
||||
if err != nil {
|
||||
t.Errorf("Expected valid callback for any return type, got: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
6
go.mod
6
go.mod
|
@ -1,3 +1,7 @@
|
|||
module github.com/mattn/go-sqlite3
|
||||
|
||||
go 1.12
|
||||
go 1.16
|
||||
|
||||
retract (
|
||||
[v2.0.0+incompatible, v2.0.6+incompatible] // Accidental; no major changes or features.
|
||||
)
|
||||
|
|
9505
sqlite3-binding.c
9505
sqlite3-binding.c
File diff suppressed because it is too large
Load Diff
|
@ -147,9 +147,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.38.5"
|
||||
#define SQLITE_VERSION_NUMBER 3038005
|
||||
#define SQLITE_SOURCE_ID "2022-05-06 15:25:27 78d9c993d404cdfaa7fdd2973fa1052e3da9f66215cff9c5540ebe55c407d9fe"
|
||||
#define SQLITE_VERSION "3.39.4"
|
||||
#define SQLITE_VERSION_NUMBER 3039004
|
||||
#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26b309"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -5594,7 +5594,8 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
|
|||
** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
|
||||
** is a [protected sqlite3_value] object even if the input is not.
|
||||
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
|
||||
** memory allocation fails.
|
||||
** memory allocation fails. ^If V is a [pointer value], then the result
|
||||
** of sqlite3_value_dup(V) is a NULL value.
|
||||
**
|
||||
** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
|
||||
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
|
||||
|
@ -6276,6 +6277,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
|
|||
*/
|
||||
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return The Schema Name For A Database Connection
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
|
||||
** for the N-th database on database connection D, or a NULL pointer of N is
|
||||
** out of range. An N value of 0 means the main database file. An N of 1 is
|
||||
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
|
||||
** databases.
|
||||
**
|
||||
** Space to hold the string that is returned by sqlite3_db_name() is managed
|
||||
** by SQLite itself. The string might be deallocated by any operation that
|
||||
** changes the schema, including [ATTACH] or [DETACH] or calls to
|
||||
** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that
|
||||
** occur on a different thread. Applications that need to
|
||||
** remember the string long-term should make their own copy. Applications that
|
||||
** are accessing the same database connection simultaneously on multiple
|
||||
** threads should mutex-protect calls to this API and should make their own
|
||||
** private copy of the result prior to releasing the mutex.
|
||||
*/
|
||||
SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return The Filename For A Database Connection
|
||||
** METHOD: sqlite3
|
||||
|
@ -9555,8 +9578,8 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
|
|||
** of a [virtual table] implementation. The result of calling this
|
||||
** interface from outside of xBestIndex() is undefined and probably harmful.
|
||||
**
|
||||
** ^The sqlite3_vtab_distinct() interface returns an integer that is
|
||||
** either 0, 1, or 2. The integer returned by sqlite3_vtab_distinct()
|
||||
** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and
|
||||
** 3. The integer returned by sqlite3_vtab_distinct()
|
||||
** gives the virtual table additional information about how the query
|
||||
** planner wants the output to be ordered. As long as the virtual table
|
||||
** can meet the ordering requirements of the query planner, it may set
|
||||
|
@ -9588,6 +9611,13 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
|
|||
** that have the same value for all columns identified by "aOrderBy".
|
||||
** ^However omitting the extra rows is optional.
|
||||
** This mode is used for a DISTINCT query.
|
||||
** <li value="3"><p>
|
||||
** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
|
||||
** that the query planner needs only distinct rows but it does need the
|
||||
** rows to be sorted.)^ ^The virtual table implementation is free to omit
|
||||
** rows that are identical in all aOrderBy columns, if it wants to, but
|
||||
** it is not required to omit any rows. This mode is used for queries
|
||||
** that have both DISTINCT and ORDER BY clauses.
|
||||
** </ol>
|
||||
**
|
||||
** ^For the purposes of comparing virtual table output values to see if the
|
||||
|
|
44
sqlite3.go
44
sqlite3.go
|
@ -494,10 +494,12 @@ func (ai *aggInfo) Done(ctx *C.sqlite3_context) {
|
|||
// Commit transaction.
|
||||
func (tx *SQLiteTx) Commit() error {
|
||||
_, err := tx.c.exec(context.Background(), "COMMIT", nil)
|
||||
if err != nil && err.(Error).Code == C.SQLITE_BUSY {
|
||||
// sqlite3 will leave the transaction open in this scenario.
|
||||
if err != nil {
|
||||
// sqlite3 may leave the transaction open in this scenario.
|
||||
// However, database/sql considers the transaction complete once we
|
||||
// return from Commit() - we must clean up to honour its semantics.
|
||||
// We don't know if the ROLLBACK is strictly necessary, but according
|
||||
// to sqlite's docs, there is no harm in calling ROLLBACK unnecessarily.
|
||||
tx.c.exec(context.Background(), "ROLLBACK", nil)
|
||||
}
|
||||
return err
|
||||
|
@ -835,9 +837,9 @@ func lastError(db *C.sqlite3) error {
|
|||
|
||||
// Exec implements Execer.
|
||||
func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = namedValue{
|
||||
list[i] = driver.NamedValue{
|
||||
Ordinal: i + 1,
|
||||
Value: v,
|
||||
}
|
||||
|
@ -845,7 +847,7 @@ func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, err
|
|||
return c.exec(context.Background(), query, list)
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue) (driver.Result, error) {
|
||||
func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||
start := 0
|
||||
for {
|
||||
s, err := c.prepare(ctx, query)
|
||||
|
@ -854,7 +856,7 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue)
|
|||
}
|
||||
var res driver.Result
|
||||
if s.(*SQLiteStmt).s != nil {
|
||||
stmtArgs := make([]namedValue, 0, len(args))
|
||||
stmtArgs := make([]driver.NamedValue, 0, len(args))
|
||||
na := s.NumInput()
|
||||
if len(args)-start < na {
|
||||
s.Close()
|
||||
|
@ -892,17 +894,11 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue)
|
|||
}
|
||||
}
|
||||
|
||||
type namedValue struct {
|
||||
Name string
|
||||
Ordinal int
|
||||
Value driver.Value
|
||||
}
|
||||
|
||||
// Query implements Queryer.
|
||||
func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = namedValue{
|
||||
list[i] = driver.NamedValue{
|
||||
Ordinal: i + 1,
|
||||
Value: v,
|
||||
}
|
||||
|
@ -910,10 +906,10 @@ func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
|||
return c.query(context.Background(), query, list)
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) query(ctx context.Context, query string, args []namedValue) (driver.Rows, error) {
|
||||
func (c *SQLiteConn) query(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||
start := 0
|
||||
for {
|
||||
stmtArgs := make([]namedValue, 0, len(args))
|
||||
stmtArgs := make([]driver.NamedValue, 0, len(args))
|
||||
s, err := c.prepare(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1910,7 +1906,7 @@ func (s *SQLiteStmt) NumInput() int {
|
|||
|
||||
var placeHolder = []byte{0}
|
||||
|
||||
func (s *SQLiteStmt) bind(args []namedValue) error {
|
||||
func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
|
||||
rv := C.sqlite3_reset(s.s)
|
||||
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
|
||||
return s.c.lastError()
|
||||
|
@ -1980,9 +1976,9 @@ func (s *SQLiteStmt) bind(args []namedValue) error {
|
|||
|
||||
// Query the statement with arguments. Return records.
|
||||
func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = namedValue{
|
||||
list[i] = driver.NamedValue{
|
||||
Ordinal: i + 1,
|
||||
Value: v,
|
||||
}
|
||||
|
@ -1990,7 +1986,7 @@ func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) {
|
|||
return s.query(context.Background(), list)
|
||||
}
|
||||
|
||||
func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) {
|
||||
func (s *SQLiteStmt) query(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||
if err := s.bind(args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2020,9 +2016,9 @@ func (r *SQLiteResult) RowsAffected() (int64, error) {
|
|||
|
||||
// Exec execute the statement with arguments. Return result object.
|
||||
func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = namedValue{
|
||||
list[i] = driver.NamedValue{
|
||||
Ordinal: i + 1,
|
||||
Value: v,
|
||||
}
|
||||
|
@ -2039,7 +2035,7 @@ func isInterruptErr(err error) bool {
|
|||
}
|
||||
|
||||
// exec executes a query that doesn't return rows. Attempts to honor context timeout.
|
||||
func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) {
|
||||
func (s *SQLiteStmt) exec(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||
if ctx.Done() == nil {
|
||||
return s.execSync(args)
|
||||
}
|
||||
|
@ -2071,7 +2067,7 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result
|
|||
return rv.r, rv.err
|
||||
}
|
||||
|
||||
func (s *SQLiteStmt) execSync(args []namedValue) (driver.Result, error) {
|
||||
func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) {
|
||||
if err := s.bind(args); err != nil {
|
||||
C.sqlite3_reset(s.s)
|
||||
C.sqlite3_clear_bindings(s.s)
|
||||
|
|
|
@ -25,20 +25,12 @@ func (c *SQLiteConn) Ping(ctx context.Context) error {
|
|||
|
||||
// QueryContext implement QueryerContext.
|
||||
func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
for i, nv := range args {
|
||||
list[i] = namedValue(nv)
|
||||
}
|
||||
return c.query(ctx, query, list)
|
||||
return c.query(ctx, query, args)
|
||||
}
|
||||
|
||||
// ExecContext implement ExecerContext.
|
||||
func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
for i, nv := range args {
|
||||
list[i] = namedValue(nv)
|
||||
}
|
||||
return c.exec(ctx, query, list)
|
||||
return c.exec(ctx, query, args)
|
||||
}
|
||||
|
||||
// PrepareContext implement ConnPrepareContext.
|
||||
|
@ -53,18 +45,10 @@ func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver
|
|||
|
||||
// QueryContext implement QueryerContext.
|
||||
func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
for i, nv := range args {
|
||||
list[i] = namedValue(nv)
|
||||
}
|
||||
return s.query(ctx, list)
|
||||
return s.query(ctx, args)
|
||||
}
|
||||
|
||||
// ExecContext implement ExecerContext.
|
||||
func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||
list := make([]namedValue, len(args))
|
||||
for i, nv := range args {
|
||||
list[i] = namedValue(nv)
|
||||
}
|
||||
return s.exec(ctx, list)
|
||||
return s.exec(ctx, args)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (C) 2022 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build sqlite_math_functions
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -DSQLITE_ENABLE_MATH_FUNCTIONS
|
||||
#cgo LDFLAGS: -lm
|
||||
*/
|
||||
import "C"
|
|
@ -0,0 +1,29 @@
|
|||
// +build sqlite_math_functions
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMathFunctions(t *testing.T) {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to open database:", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
queries := []string{
|
||||
`SELECT acos(1)`,
|
||||
`SELECT log(10, 100)`,
|
||||
`SELECT power(2, 2)`,
|
||||
}
|
||||
|
||||
for _, query := range queries {
|
||||
var result float64
|
||||
if err := db.QueryRow(query).Scan(&result); err != nil {
|
||||
t.Errorf("invoking math function query %q: %v", query, err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (C) 2022 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build sqlite_os_trace
|
||||
// +build sqlite_os_trace
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -DSQLITE_FORCE_OS_TRACE=1
|
||||
#cgo CFLAGS: -DSQLITE_DEBUG_OS_TRACE=1
|
||||
*/
|
||||
import "C"
|
|
@ -0,0 +1,82 @@
|
|||
// +build !libsqlite3 sqlite_serialize
|
||||
|
||||
package sqlite3
|
||||
|
||||
/*
|
||||
#ifndef USE_LIBSQLITE3
|
||||
#include <sqlite3-binding.h>
|
||||
#else
|
||||
#include <sqlite3.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Serialize returns a byte slice that is a serialization of the database.
|
||||
//
|
||||
// See https://www.sqlite.org/c3ref/serialize.html
|
||||
func (c *SQLiteConn) Serialize(schema string) ([]byte, error) {
|
||||
if schema == "" {
|
||||
schema = "main"
|
||||
}
|
||||
var zSchema *C.char
|
||||
zSchema = C.CString(schema)
|
||||
defer C.free(unsafe.Pointer(zSchema))
|
||||
|
||||
var sz C.sqlite3_int64
|
||||
ptr := C.sqlite3_serialize(c.db, zSchema, &sz, 0)
|
||||
if ptr == nil {
|
||||
return nil, fmt.Errorf("serialize failed")
|
||||
}
|
||||
defer C.sqlite3_free(unsafe.Pointer(ptr))
|
||||
|
||||
if sz > C.sqlite3_int64(math.MaxInt) {
|
||||
return nil, fmt.Errorf("serialized database is too large (%d bytes)", sz)
|
||||
}
|
||||
|
||||
cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(ptr)),
|
||||
Len: int(sz),
|
||||
Cap: int(sz),
|
||||
}))
|
||||
|
||||
res := make([]byte, int(sz))
|
||||
copy(res, cBuf)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Deserialize causes the connection to disconnect from the current database and
|
||||
// then re-open as an in-memory database based on the contents of the byte slice.
|
||||
//
|
||||
// See https://www.sqlite.org/c3ref/deserialize.html
|
||||
func (c *SQLiteConn) Deserialize(b []byte, schema string) error {
|
||||
if schema == "" {
|
||||
schema = "main"
|
||||
}
|
||||
var zSchema *C.char
|
||||
zSchema = C.CString(schema)
|
||||
defer C.free(unsafe.Pointer(zSchema))
|
||||
|
||||
tmpBuf := (*C.uchar)(C.sqlite3_malloc64(C.sqlite3_uint64(len(b))))
|
||||
cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(tmpBuf)),
|
||||
Len: len(b),
|
||||
Cap: len(b),
|
||||
}))
|
||||
copy(cBuf, b)
|
||||
|
||||
rc := C.sqlite3_deserialize(c.db, zSchema, tmpBuf, C.sqlite3_int64(len(b)),
|
||||
C.sqlite3_int64(len(b)), C.SQLITE_DESERIALIZE_FREEONCLOSE)
|
||||
if rc != C.SQLITE_OK {
|
||||
return fmt.Errorf("deserialize failed with return %v", rc)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// +build libsqlite3,!sqlite_serialize
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -DSQLITE_OMIT_DESERIALIZE
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (c *SQLiteConn) Serialize(schema string) ([]byte, error) {
|
||||
return nil, errors.New("sqlite3: Serialize requires the sqlite_serialize build tag when using the libsqlite3 build tag")
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) Deserialize(b []byte, schema string) error {
|
||||
return errors.New("sqlite3: Deserialize requires the sqlite_serialize build tag when using the libsqlite3 build tag")
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// +build !libsqlite3 sqlite_serialize
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSerializeDeserialize(t *testing.T) {
|
||||
// Connect to the source database.
|
||||
srcTempFilename := TempFilename(t)
|
||||
defer os.Remove(srcTempFilename)
|
||||
srcDb, err := sql.Open(driverName, srcTempFilename)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to open the source database:", err)
|
||||
}
|
||||
defer srcDb.Close()
|
||||
err = srcDb.Ping()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to connect to the source database:", err)
|
||||
}
|
||||
|
||||
// Connect to the destination database.
|
||||
destTempFilename := TempFilename(t)
|
||||
defer os.Remove(destTempFilename)
|
||||
destDb, err := sql.Open(driverName, destTempFilename)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to open the destination database:", err)
|
||||
}
|
||||
defer destDb.Close()
|
||||
err = destDb.Ping()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to connect to the destination database:", err)
|
||||
}
|
||||
|
||||
// Write data to source database.
|
||||
_, err = srcDb.Exec(`CREATE TABLE foo (name string)`)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create table in source database:", err)
|
||||
}
|
||||
_, err = srcDb.Exec(`INSERT INTO foo(name) VALUES("alice")`)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to insert data into source database", err)
|
||||
}
|
||||
|
||||
// Serialize the source database
|
||||
srcConn, err := srcDb.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get connection to source database:", err)
|
||||
}
|
||||
defer srcConn.Close()
|
||||
|
||||
var serialized []byte
|
||||
if err := srcConn.Raw(func(raw interface{}) error {
|
||||
var err error
|
||||
serialized, err = raw.(*SQLiteConn).Serialize("")
|
||||
return err
|
||||
}); err != nil {
|
||||
t.Fatal("Failed to serialize source database:", err)
|
||||
}
|
||||
srcConn.Close()
|
||||
|
||||
// Confirm that the destination database is initially empty.
|
||||
var destTableCount int
|
||||
err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to check the destination table count:", err)
|
||||
}
|
||||
if destTableCount != 0 {
|
||||
t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount)
|
||||
}
|
||||
|
||||
// Deserialize to destination database
|
||||
destConn, err := destDb.Conn(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get connection to destination database:", err)
|
||||
}
|
||||
defer destConn.Close()
|
||||
|
||||
if err := destConn.Raw(func(raw interface{}) error {
|
||||
return raw.(*SQLiteConn).Deserialize(serialized, "")
|
||||
}); err != nil {
|
||||
t.Fatal("Failed to deserialize source database:", err)
|
||||
}
|
||||
destConn.Close()
|
||||
|
||||
// Confirm that destination database has been loaded correctly.
|
||||
var destRowCount int
|
||||
err = destDb.QueryRow(`SELECT COUNT(*) FROM foo`).Scan(&destRowCount)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to count rows in destination database table", err)
|
||||
}
|
||||
if destRowCount != 1 {
|
||||
t.Fatalf("Destination table does not have the expected records")
|
||||
}
|
||||
}
|
138
sqlite3_test.go
138
sqlite3_test.go
|
@ -248,6 +248,43 @@ func TestForeignKeys(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDeferredForeignKey(t *testing.T) {
|
||||
fname := TempFilename(t)
|
||||
uri := "file:" + fname + "?_foreign_keys=1"
|
||||
db, err := sql.Open("sqlite3", uri)
|
||||
if err != nil {
|
||||
os.Remove(fname)
|
||||
t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err)
|
||||
}
|
||||
_, err = db.Exec("CREATE TABLE bar (id INTEGER PRIMARY KEY)")
|
||||
if err != nil {
|
||||
t.Errorf("failed creating tables: %v", err)
|
||||
}
|
||||
_, err = db.Exec("CREATE TABLE foo (bar_id INTEGER, FOREIGN KEY(bar_id) REFERENCES bar(id) DEFERRABLE INITIALLY DEFERRED)")
|
||||
if err != nil {
|
||||
t.Errorf("failed creating tables: %v", err)
|
||||
}
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to begin transaction: %v", err)
|
||||
}
|
||||
_, err = tx.Exec("INSERT INTO foo (bar_id) VALUES (123)")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to insert row: %v", err)
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error: %v", err)
|
||||
}
|
||||
_, err = db.Begin()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to begin transaction: %v", err)
|
||||
}
|
||||
|
||||
db.Close()
|
||||
os.Remove(fname)
|
||||
}
|
||||
|
||||
func TestRecursiveTriggers(t *testing.T) {
|
||||
cases := map[string]bool{
|
||||
"?_recursive_triggers=1": true,
|
||||
|
@ -1063,34 +1100,44 @@ func TestQueryer(t *testing.T) {
|
|||
defer db.Close()
|
||||
|
||||
_, err = db.Exec(`
|
||||
create table foo (id integer);
|
||||
create table foo (id integer);
|
||||
`)
|
||||
if err != nil {
|
||||
t.Error("Failed to call db.Query:", err)
|
||||
}
|
||||
|
||||
rows, err := db.Query(`
|
||||
insert into foo(id) values(?);
|
||||
insert into foo(id) values(?);
|
||||
insert into foo(id) values(?);
|
||||
select id from foo order by id;
|
||||
_, err = db.Exec(`
|
||||
insert into foo(id) values(?);
|
||||
insert into foo(id) values(?);
|
||||
insert into foo(id) values(?);
|
||||
`, 3, 2, 1)
|
||||
if err != nil {
|
||||
t.Error("Failed to call db.Exec:", err)
|
||||
}
|
||||
rows, err := db.Query(`
|
||||
select id from foo order by id;
|
||||
`)
|
||||
if err != nil {
|
||||
t.Error("Failed to call db.Query:", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
n := 1
|
||||
if rows != nil {
|
||||
for rows.Next() {
|
||||
var id int
|
||||
err = rows.Scan(&id)
|
||||
if err != nil {
|
||||
t.Error("Failed to db.Query:", err)
|
||||
}
|
||||
if id != n {
|
||||
t.Error("Failed to db.Query: not matched results")
|
||||
}
|
||||
n := 0
|
||||
for rows.Next() {
|
||||
var id int
|
||||
err = rows.Scan(&id)
|
||||
if err != nil {
|
||||
t.Error("Failed to db.Query:", err)
|
||||
}
|
||||
if id != n + 1 {
|
||||
t.Error("Failed to db.Query: not matched results")
|
||||
}
|
||||
n = n + 1
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
t.Errorf("Post-scan failed: %v\n", err)
|
||||
}
|
||||
if n != 3 {
|
||||
t.Errorf("Expected 3 rows but retrieved %v", n)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1449,6 +1496,63 @@ func TestAggregatorRegistration(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type mode struct {
|
||||
counts map[interface{}]int
|
||||
top interface{}
|
||||
topCount int
|
||||
}
|
||||
|
||||
func newMode() *mode {
|
||||
return &mode{
|
||||
counts: map[interface{}]int{},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mode) Step(x interface{}) {
|
||||
m.counts[x]++
|
||||
c := m.counts[x]
|
||||
if c > m.topCount {
|
||||
m.top = x
|
||||
m.topCount = c
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mode) Done() interface{} {
|
||||
return m.top
|
||||
}
|
||||
|
||||
func TestAggregatorRegistration_GenericReturn(t *testing.T) {
|
||||
sql.Register("sqlite3_AggregatorRegistration_GenericReturn", &SQLiteDriver{
|
||||
ConnectHook: func(conn *SQLiteConn) error {
|
||||
return conn.RegisterAggregator("mode", newMode, true)
|
||||
},
|
||||
})
|
||||
db, err := sql.Open("sqlite3_AggregatorRegistration_GenericReturn", ":memory:")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to open database:", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
_, err = db.Exec("create table foo (department integer, profits integer)")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to create table:", err)
|
||||
}
|
||||
_, err = db.Exec("insert into foo values (1, 10), (1, 20), (1, 45), (2, 42), (2, 115), (2, 20)")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to insert records:", err)
|
||||
}
|
||||
|
||||
var mode int
|
||||
err = db.QueryRow("select mode(profits) from foo").Scan(&mode)
|
||||
if err != nil {
|
||||
t.Fatal("MODE query error:", err)
|
||||
}
|
||||
|
||||
if mode != 20 {
|
||||
t.Fatal("Got incorrect mode. Wanted 20, got: ", mode)
|
||||
}
|
||||
}
|
||||
|
||||
func rot13(r rune) rune {
|
||||
switch {
|
||||
case r >= 'A' && r <= 'Z':
|
||||
|
|
|
@ -12,7 +12,6 @@ package sqlite3
|
|||
#cgo CFLAGS: -fno-stack-check
|
||||
#cgo CFLAGS: -fno-stack-protector
|
||||
#cgo CFLAGS: -mno-stack-arg-probe
|
||||
#cgo LDFLAGS: -lmingwex -lmingw32
|
||||
#cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T
|
||||
*/
|
||||
import "C"
|
||||
|
|
12
sqlite3ext.h
12
sqlite3ext.h
|
@ -356,6 +356,12 @@ struct sqlite3_api_routines {
|
|||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
||||
/* Version 3.39.0 and later */
|
||||
int (*deserialize)(sqlite3*,const char*,unsigned char*,
|
||||
sqlite3_int64,sqlite3_int64,unsigned);
|
||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||
unsigned int);
|
||||
const char *(*db_name)(sqlite3*,int);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -674,6 +680,12 @@ typedef int (*sqlite3_loadext_entry)(
|
|||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
||||
#define sqlite3_serialize sqlite3_api->serialize
|
||||
#endif
|
||||
#define sqlite3_db_name sqlite3_api->db_name
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
|
Loading…
Reference in New Issue