feat: Waku v2 bridge

Issue #12610
This commit is contained in:
Michal Iskierko
2023-11-12 13:29:38 +01:00
parent 56e7bd01ca
commit 6d31343205
6716 changed files with 1982502 additions and 5891 deletions

12
vendor/zombiezen.com/go/sqlite/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,12 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

200
vendor/zombiezen.com/go/sqlite/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,200 @@
# `zombiezen.com/go/sqlite` Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
[Unreleased]: https://github.com/zombiezen/go-sqlite/compare/v0.8.0...main
## [0.8.0][] - 2021-11-07
Version 0.8 adds new transaction functions to `sqlitex`.
[0.8.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.8.0
### Added
- Added `sqlitex.Transaction`, `sqlitex.ImmediateTransaction`, and
`sqlitex.ExclusiveTransaction`.
## [0.7.2][] - 2021-09-11
[0.7.2]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.7.2
### Fixed
- Updated `modernc.org/sqlite` dependency to a released version instead of a
prerelease
## [0.7.1][] - 2021-09-09
[0.7.1]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.7.1
### Added
- Added an example to `sqlitemigration.Schema`
## [0.7.0][] - 2021-08-27
[0.7.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.7.0
### Added
- `sqlitemigration.Schema` has a new option for disabling foreign keys for
individual migrations. This makes it easier to perform migrations that require
[reconstructing a table][]. ([#20](https://github.com/zombiezen/go-sqlite/issues/20))
[reconstructing a table]: https://sqlite.org/lang_altertable.html#making_other_kinds_of_table_schema_changes
### Changed
- `sqlitemigration.Migrate` and `*sqlitemigration.Pool` no longer use a
transaction to apply the entire set of migrations: they now only use
transactions during each individual migration. This was never documented, so
in theory no one should be depending on this behavior. However, this does mean
that two processes trying to open and migrate a database concurrently may race
to apply migrations, whereas before only one process would acquire the write
lock and migrate.
### Fixed
- Fixed compile breakage on 32-bit architectures. Thanks to Jan Mercl for the
report.
## [0.6.2][] - 2021-08-17
[0.6.2]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.6.2
### Changed
- `*sqlitex.Pool.Put` now accepts `nil` instead of panicing.
([#17](https://github.com/zombiezen/go-sqlite/issues/17))
## [0.6.1][] - 2021-08-16
[0.6.1]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.6.1
### Fixed
- Fixed a potential memory corruption issue introduced in 0.6.0. Thanks to
Jan Mercl for the report.
## [0.6.0][] - 2021-08-15
[0.6.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.6.0
### Added
- Added back the session API: `Session`, `ChangesetIterator`, `Changegroup`, and
various functions. There are some slight naming changes from the
`crawshaw.io/sqlite` API, but they can all be migrated automatically with the
migration tool. ([#16](https://github.com/zombiezen/go-sqlite/issues/16))
### Changed
- Method calls to a `nil` `*sqlite.Conn` will return an error rather than panic.
([#17](https://github.com/zombiezen/go-sqlite/issues/17))
### Removed
- Removed `OpenFlags` that are only used for VFS.
### Fixed
- Properly clean up WAL when using `sqlitex.Pool`
([#14](https://github.com/zombiezen/go-sqlite/issues/14))
- Disabled double-quoted string literals.
## [0.5.0][] - 2021-05-22
[0.5.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.5.0
### Added
- Added `shell` package with basic [REPL][]
- Added `SetAuthorizer`, `Limit`, and `SetDefensive` methods to `*Conn` for use
in ([#12](https://github.com/zombiezen/go-sqlite/issues/12))
- Added `Version` and `VersionNumber` constants
[REPL]: https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop
### Fixed
- Documented compiled-in extensions ([#11](https://github.com/zombiezen/go-sqlite/issues/11))
- Internal objects are no longer susceptible to ID wraparound issues
([#13](https://github.com/zombiezen/go-sqlite/issues/13))
## [0.4.0][] - 2021-05-13
[0.4.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.4.0
### Added
- Add Context.Conn method ([#10](https://github.com/zombiezen/go-sqlite/issues/10))
- Add methods to get and set auxiliary function data
([#3](https://github.com/zombiezen/go-sqlite/issues/3))
## [0.3.1][] - 2021-05-03
[0.3.1]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.3.1
### Fixed
- Fix conversion of BLOB to TEXT when returning BLOB from a user-defined function
## [0.3.0][] - 2021-04-27
[0.3.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.3.0
### Added
- Implement `io.StringWriter`, `io.ReaderFrom`, and `io.WriterTo` on `Blob`
([#2](https://github.com/zombiezen/go-sqlite/issues/2))
- Add godoc examples for `Blob`, `sqlitemigration`, and `SetInterrupt`
- Add more README documentation
## [0.2.2][] - 2021-04-24
[0.2.2]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.2.2
### Changed
- Simplified license to [ISC](https://github.com/zombiezen/go-sqlite/blob/v0.2.2/LICENSE)
### Fixed
- Updated version of `modernc.org/sqlite` to 1.10.4 to use [mutex initialization](https://gitlab.com/cznic/sqlite/-/issues/52)
- Fixed doc comment for `BindZeroBlob`
## [0.2.1][] - 2021-04-17
[0.2.1]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.2.1
### Fixed
- Removed bogus import comment
## [0.2.0][] - 2021-04-03
[0.2.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.2.0
### Added
- New migration tool. See [the README](https://github.com/zombiezen/go-sqlite/blob/v0.2.0/cmd/zombiezen-sqlite-migrate/README.md)
to get started. ([#1](https://github.com/zombiezen/go-sqlite/issues/1))
### Changed
- `*Conn.CreateFunction` has changed entirely. See the
[reference](https://pkg.go.dev/zombiezen.com/go/sqlite#Conn.CreateFunction)
for details.
- `sqlitex.File` and `sqlitex.Buffer` have been moved to the `sqlitefile` package
- The `sqlitefile.Exec*` functions have been moved to the `sqlitex` package
as `Exec*FS`.
## [0.1.0][] - 2021-03-31
Initial release
[0.1.0]: https://github.com/zombiezen/go-sqlite/releases/tag/v0.1.0

128
vendor/zombiezen.com/go/sqlite/CODE_OF_CONDUCT.md generated vendored Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at ross@zombiezen.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

13
vendor/zombiezen.com/go/sqlite/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,13 @@
# `zombiezen.com/go/sqlite` Contributor's Guide
`zombiezen.com/go/sqlite` is mostly a project to fill my own needs, but is
open source in the interest of availability and reuse. I am open to accepting
bug fixes, but generally not larger changes or features. If in doubt, ask me.
File a bug on the [issue tracker][] or [open a pull request][]. Please follow
the [Code of Conduct][] for all interactions. If applicable, add unit tests for
your change before sending out for review.
[Code of Conduct]: CODE_OF_CONDUCT.md
[issue tracker]: https://github.com/zombiezen/go-sqlite/issues/new
[open a pull request]: https://github.com/zombiezen/go-sqlite/compare

14
vendor/zombiezen.com/go/sqlite/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,14 @@
Copyright (c) 2018 David Crawshaw <david@zentus.com>
Copyright (c) 2021 Ross Light <ross@zombiezen.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

92
vendor/zombiezen.com/go/sqlite/README.md generated vendored Normal file
View File

@@ -0,0 +1,92 @@
# `zombiezen.com/go/sqlite`
[![Go Reference](https://pkg.go.dev/badge/zombiezen.com/go/sqlite.svg)][reference docs]
This package provides a low-level Go interface to [SQLite 3][]. It is a fork of
[`crawshaw.io/sqlite`][] that uses [`modernc.org/sqlite`][], a CGo-free SQLite
package. It aims to be a mostly drop-in replacement for
`crawshaw.io/sqlite`.
This package deliberately does not provide a `database/sql` driver. See
[David Crawshaw's rationale][] for an in-depth explanation. If you want to use
`database/sql` with SQLite without CGo, use `modernc.org/sqlite` directly.
[`crawshaw.io/sqlite`]: https://github.com/crawshaw/sqlite
[David Crawshaw's rationale]: https://crawshaw.io/blog/go-and-sqlite
[`modernc.org/sqlite`]: https://pkg.go.dev/modernc.org/sqlite
[reference docs]: https://pkg.go.dev/zombiezen.com/go/sqlite
[SQLite 3]: https://sqlite.org/
## Features
- Full SQLite functionality via `modernc.org/sqlite`, an automatically generated
translation of the original C source code of SQLite into Go
- Builds with `CGO_ENABLED=0`, allowing cross-compiling and data race detection
- Allows access to SQLite-specific features like [blob I/O][] and
[user-defined functions][]
- Includes a simple [schema migration package][]
- Utilities for [running embedded SQL scripts][ExecScriptFS] using the
[Go 1.16 embedding feature][]
- A [`go fix`-like tool][migration docs] for migrating existing code using
`crawshaw.io/sqlite`
- A [simple REPL][] for debugging
[blob I/O]: https://pkg.go.dev/zombiezen.com/go/sqlite#Blob
[ExecScriptFS]: https://pkg.go.dev/zombiezen.com/go/sqlite/sqlitex#ExecScriptFS
[Go 1.16 embedding feature]: https://pkg.go.dev/embed
[migration docs]: cmd/zombiezen-sqlite-migrate/README.md
[schema migration package]: https://pkg.go.dev/zombiezen.com/go/sqlite/sqlitemigration
[simple REPL]: https://pkg.go.dev/zombiezen.com/go/sqlite/shell
[user-defined functions]: https://pkg.go.dev/zombiezen.com/go/sqlite#Conn.CreateFunction
## Install
```shell
go get zombiezen.com/go/sqlite
```
While this library does not use CGo, make sure that you are building for one of
the [supported architectures][].
[supported architectures]: https://pkg.go.dev/modernc.org/sqlite#hdr-Supported_platforms_and_architectures
## Getting Started
```go
import (
"fmt"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/sqlitex"
)
// ...
// Open an in-memory database.
conn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite)
if err != nil {
return err
}
defer conn.Close()
// Execute a query.
err = sqlitex.ExecTransient(conn, "SELECT 'hello, world';", func(stmt *sqlite.Stmt) error {
fmt.Println(stmt.ColumnText(0))
return nil
})
if err != nil {
return err
}
```
If you're creating a new application, see the [package examples][] or the
[reference docs][].
If you're looking to switch existing code that uses `crawshaw.io/sqlite`, take
a look at the [migration docs][].
[package examples]: https://pkg.go.dev/zombiezen.com/go/sqlite#pkg-examples
## License
[ISC](LICENSE)

318
vendor/zombiezen.com/go/sqlite/auth.go generated vendored Normal file
View File

@@ -0,0 +1,318 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
package sqlite
import (
"fmt"
"strings"
"sync"
"unsafe"
"modernc.org/libc"
lib "modernc.org/sqlite/lib"
)
// An Authorizer is called during statement preparation to see whether an action
// is allowed by the application. An Authorizer must not modify the database
// connection, including by preparing statements.
//
// See https://sqlite.org/c3ref/set_authorizer.html for a longer explanation.
type Authorizer interface {
Authorize(Action) AuthResult
}
// SetAuthorizer registers an authorizer for the database connection.
// SetAuthorizer(nil) clears any authorizer previously set.
func (c *Conn) SetAuthorizer(auth Authorizer) error {
if c == nil {
return fmt.Errorf("sqlite: set authorizer: nil connection")
}
if auth == nil {
c.releaseAuthorizer()
res := ResultCode(lib.Xsqlite3_set_authorizer(c.tls, c.conn, 0, 0))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: set authorizer: %w", err)
}
return nil
}
authorizers.mu.Lock()
if authorizers.m == nil {
authorizers.m = make(map[uintptr]Authorizer)
}
authorizers.m[c.conn] = auth
authorizers.mu.Unlock()
// The following is a conversion from function value to uintptr. It assumes
// the memory representation described in https://golang.org/s/go11func.
//
// It does this by doing the following in order:
// 1) Create a Go struct containing a pointer to a pointer to
// authTrampoline. It is assumed that the pointer to authTrampoline will be
// stored in the read-only data section and thus will not move.
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
// 3) Dereference the pointer to uintptr to obtain the function value as a
// uintptr. This is safe as long as function values are passed as pointers.
xAuth := *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, int32, uintptr, uintptr, uintptr, uintptr) int32
}{authTrampoline}))
res := ResultCode(lib.Xsqlite3_set_authorizer(c.tls, c.conn, xAuth, c.conn))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: set authorizer: %w", err)
}
return nil
}
func (c *Conn) releaseAuthorizer() {
authorizers.mu.Lock()
delete(authorizers.m, c.conn)
authorizers.mu.Unlock()
}
var authorizers struct {
mu sync.RWMutex
m map[uintptr]Authorizer // sqlite3* -> Authorizer
}
func authTrampoline(tls *libc.TLS, conn uintptr, op int32, cArg1, cArg2, cDB, cTrigger uintptr) int32 {
authorizers.mu.RLock()
auth := authorizers.m[conn]
authorizers.mu.RUnlock()
return int32(auth.Authorize(Action{
op: OpType(op),
arg1: libc.GoString(cArg1),
arg2: libc.GoString(cArg2),
database: libc.GoString(cDB),
trigger: libc.GoString(cTrigger),
}))
}
// AuthorizeFunc is a function that implements Authorizer.
type AuthorizeFunc func(Action) AuthResult
// Authorize calls f.
func (f AuthorizeFunc) Authorize(action Action) AuthResult {
return f(action)
}
// AuthResult is the result of a call to an Authorizer. The zero value is
// AuthResultOK.
type AuthResult int32
// Possible return values from Authorize.
const (
// AuthResultOK allows the SQL statement to be compiled.
AuthResultOK AuthResult = lib.SQLITE_OK
// AuthResultDeny causes the entire SQL statement to be rejected with an error.
AuthResultDeny AuthResult = lib.SQLITE_DENY
// AuthResultIgnore disallows the specific action but allow the SQL statement
// to continue to be compiled. For OpRead, this substitutes a NULL for the
// column value. For OpDelete, the DELETE operation proceeds but the truncate
// optimization is disabled and all rows are deleted individually.
AuthResultIgnore AuthResult = lib.SQLITE_IGNORE
)
// String returns the C constant name of the result.
func (result AuthResult) String() string {
switch result {
case AuthResultOK:
return "SQLITE_OK"
case AuthResultDeny:
return "SQLITE_DENY"
case AuthResultIgnore:
return "SQLITE_IGNORE"
default:
return fmt.Sprintf("AuthResult(%d)", int32(result))
}
}
// Action represents an action to be authorized.
type Action struct {
op OpType
arg1 string
arg2 string
database string
trigger string
}
// Mapping of argument position to concept at:
// https://sqlite.org/c3ref/c_alter_table.html
// Type returns the type of action being authorized.
func (action Action) Type() OpType {
return action.op
}
// Accessor returns the name of the inner-most trigger or view that is
// responsible for the access attempt or the empty string if this access attempt
// is directly from top-level SQL code.
func (action Action) Accessor() string {
return action.trigger
}
// Database returns the name of the database (e.g. "main", "temp", etc.) this
// action affects or the empty string if not applicable.
func (action Action) Database() string {
switch action.op {
case OpDetach, OpAlterTable:
return action.arg1
default:
return action.database
}
}
// Index returns the name of the index this action affects or the empty string
// if not applicable.
func (action Action) Index() string {
switch action.op {
case OpCreateIndex, OpCreateTempIndex, OpDropIndex, OpDropTempIndex, OpReindex:
return action.arg1
default:
return ""
}
}
// Table returns the name of the table this action affects or the empty string
// if not applicable.
func (action Action) Table() string {
switch action.op {
case OpCreateTable, OpCreateTempTable, OpDelete, OpDropTable, OpDropTempTable, OpInsert, OpRead, OpUpdate, OpAnalyze, OpCreateVTable, OpDropVTable:
return action.arg1
case OpCreateIndex, OpCreateTempIndex, OpCreateTempTrigger, OpCreateTrigger, OpDropIndex, OpDropTempIndex, OpDropTempTrigger, OpDropTrigger, OpAlterTable:
return action.arg2
default:
return ""
}
}
// Trigger returns the name of the trigger this action affects or the empty
// string if not applicable.
func (action Action) Trigger() string {
switch action.op {
case OpCreateTempTrigger, OpCreateTrigger, OpDropTempTrigger, OpDropTrigger:
return action.arg1
default:
return ""
}
}
// View returns the name of the view this action affects or the empty string
// if not applicable.
func (action Action) View() string {
switch action.op {
case OpCreateTempView, OpCreateView, OpDropTempView, OpDropView:
return action.arg1
default:
return ""
}
}
// Pragma returns the name of the action's PRAGMA command or the empty string
// if the action does not represent a PRAGMA command.
// See https://sqlite.org/pragma.html#toc for a list of possible values.
func (action Action) Pragma() string {
if action.op != OpPragma {
return ""
}
return action.arg1
}
// PragmaArg returns the argument to the PRAGMA command or the empty string if
// the action does not represent a PRAGMA command or the PRAGMA command does not
// take an argument.
func (action Action) PragmaArg() string {
if action.op != OpPragma {
return ""
}
return action.arg2
}
// Column returns the name of the column this action affects or the empty string
// if not applicable. For OpRead actions, this will return the empty string if a
// table is referenced but no column values are extracted from that table
// (e.g. a query like "SELECT COUNT(*) FROM tab").
func (action Action) Column() string {
switch action.op {
case OpRead, OpUpdate:
return action.arg2
default:
return ""
}
}
// Operation returns one of "BEGIN", "COMMIT", "RELEASE", or "ROLLBACK" for a
// transaction or savepoint statement or the empty string otherwise.
func (action Action) Operation() string {
switch action.op {
case OpTransaction, OpSavepoint:
return action.arg1
default:
return ""
}
}
// File returns the name of the file being ATTACHed or the empty string if the
// action does not represent an ATTACH DATABASE statement.
func (action Action) File() string {
if action.op != OpAttach {
return ""
}
return action.arg1
}
// Module returns the module name given to the virtual table statement or the
// empty string if the action does not represent a CREATE VIRTUAL TABLE or
// DROP VIRTUAL TABLE statement.
func (action Action) Module() string {
switch action.op {
case OpCreateVTable, OpDropVTable:
return action.arg2
default:
return ""
}
}
// Savepoint returns the name given to the SAVEPOINT statement or the empty
// string if the action does not represent a SAVEPOINT statement.
func (action Action) Savepoint() string {
if action.op != OpSavepoint {
return ""
}
return action.arg2
}
// String returns a debugging representation of the action.
func (action Action) String() string {
sb := new(strings.Builder)
sb.WriteString(action.op.String())
params := []struct {
name, value string
}{
{"database", action.Database()},
{"file", action.File()},
{"trigger", action.Trigger()},
{"index", action.Index()},
{"table", action.Table()},
{"view", action.View()},
{"module", action.Module()},
{"column", action.Column()},
{"operation", action.Operation()},
{"savepoint", action.Savepoint()},
{"pragma", action.Pragma()},
{"arg", action.PragmaArg()},
}
for _, p := range params {
if p.value != "" {
sb.WriteString(" ")
sb.WriteString(p.name)
sb.WriteString(":")
sb.WriteString(p.value)
}
}
return sb.String()
}

319
vendor/zombiezen.com/go/sqlite/blob.go generated vendored Normal file
View File

@@ -0,0 +1,319 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <ross@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
package sqlite
import (
"errors"
"fmt"
"io"
"unsafe"
"modernc.org/libc"
lib "modernc.org/sqlite/lib"
)
const blobBufSize = 4096
var (
mainCString = mustCString("main")
tempCString = mustCString("temp")
)
// OpenBlob opens a blob in a particular {database,table,column,row}.
//
// https://www.sqlite.org/c3ref/blob_open.html
func (c *Conn) OpenBlob(dbn, table, column string, row int64, write bool) (*Blob, error) {
if c == nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: nil connection", table, column)
}
return c.openBlob(dbn, table, column, row, write)
}
func (c *Conn) openBlob(dbn, table, column string, row int64, write bool) (_ *Blob, err error) {
var cdb uintptr
switch dbn {
case "", "main":
cdb = mainCString
case "temp":
cdb = tempCString
default:
var err error
cdb, err = libc.CString(dbn)
if err != nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, err)
}
defer libc.Xfree(c.tls, cdb)
}
var writeFlag int32
if write {
writeFlag = 1
}
buf, err := malloc(c.tls, blobBufSize)
if err != nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, err)
}
defer func() {
if err != nil {
libc.Xfree(c.tls, buf)
}
}()
ctable, err := libc.CString(table)
if err != nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, err)
}
defer libc.Xfree(c.tls, ctable)
ccolumn, err := libc.CString(column)
if err != nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, err)
}
defer libc.Xfree(c.tls, ccolumn)
blobPtrPtr, err := malloc(c.tls, ptrSize)
if err != nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, err)
}
defer libc.Xfree(c.tls, blobPtrPtr)
for {
if err := c.interrupted(); err != nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, err)
}
res := ResultCode(lib.Xsqlite3_blob_open(
c.tls,
c.conn,
cdb,
ctable,
ccolumn,
row,
writeFlag,
blobPtrPtr,
))
switch res {
case ResultLockedSharedCache:
if err := reserr(waitForUnlockNotify(c.tls, c.conn, c.unlockNote)); err != nil {
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, err)
}
// loop
case ResultOK:
blobPtr := *(*uintptr)(unsafe.Pointer(blobPtrPtr))
return &Blob{
conn: c,
blob: blobPtr,
buf: buf,
size: lib.Xsqlite3_blob_bytes(c.tls, blobPtr),
}, nil
default:
return nil, fmt.Errorf("sqlite: open blob %q.%q: %w", table, column, c.extreserr(res))
}
}
}
// Blob provides streaming access to SQLite blobs.
type Blob struct {
conn *Conn
blob uintptr
buf uintptr
off int32
size int32
}
func (blob *Blob) bufSlice() []byte {
return libc.GoBytes(blob.buf, blobBufSize)
}
// Read reads up to len(p) bytes from the blob into p.
// https://www.sqlite.org/c3ref/blob_read.html
func (blob *Blob) Read(p []byte) (int, error) {
if blob.blob == 0 {
return 0, fmt.Errorf("sqlite: read blob: %w", errInvalidBlob)
}
if blob.off >= blob.size {
return 0, io.EOF
}
if err := blob.conn.interrupted(); err != nil {
return 0, fmt.Errorf("sqlite: read blob: %w", err)
}
if rem := blob.size - blob.off; len(p) > int(rem) {
p = p[:rem]
}
fullLen := len(p)
for len(p) > 0 {
nn := int32(blobBufSize)
if int(nn) > len(p) {
nn = int32(len(p))
}
res := ResultCode(lib.Xsqlite3_blob_read(blob.conn.tls, blob.blob, blob.buf, nn, blob.off))
if err := reserr(res); err != nil {
return fullLen - len(p), fmt.Errorf("sqlite: read blob: %w", err)
}
copy(p, blob.bufSlice()[:int(nn)])
p = p[nn:]
blob.off += nn
}
return fullLen, nil
}
// WriteTo copies the blob to w until there's no more data to write or
// an error occurs.
func (blob *Blob) WriteTo(w io.Writer) (n int64, err error) {
if blob.blob == 0 {
return 0, fmt.Errorf("sqlite: read blob: %w", errInvalidBlob)
}
if blob.off >= blob.size {
return 0, nil
}
if err := blob.conn.interrupted(); err != nil {
return 0, fmt.Errorf("sqlite: read blob: %w", err)
}
for blob.off < blob.size {
buf := blob.bufSlice()
if remaining := int(blob.size - blob.off); len(buf) > remaining {
buf = buf[:remaining]
}
res := ResultCode(lib.Xsqlite3_blob_read(blob.conn.tls, blob.blob, blob.buf, int32(len(buf)), blob.off))
if err := reserr(res); err != nil {
return n, fmt.Errorf("sqlite: read blob: %w", err)
}
nn, err := w.Write(buf)
blob.off += int32(nn)
n += int64(nn)
if err != nil {
return n, err
}
}
return n, nil
}
// Write writes len(p) from p to the blob.
// https://www.sqlite.org/c3ref/blob_write.html
func (blob *Blob) Write(p []byte) (int, error) {
if blob.blob == 0 {
return 0, fmt.Errorf("sqlite: write blob: %w", errInvalidBlob)
}
if err := blob.conn.interrupted(); err != nil {
return 0, fmt.Errorf("sqlite: write blob: %w", err)
}
fullLen := len(p)
for len(p) > 0 {
nn := copy(blob.bufSlice(), p)
res := ResultCode(lib.Xsqlite3_blob_write(blob.conn.tls, blob.blob, blob.buf, int32(nn), blob.off))
if err := reserr(res); err != nil {
return fullLen - len(p), fmt.Errorf("sqlite: write blob: %w", err)
}
p = p[nn:]
blob.off += int32(nn)
}
return fullLen, nil
}
// WriteString writes s to the blob.
// https://www.sqlite.org/c3ref/blob_write.html
func (blob *Blob) WriteString(s string) (int, error) {
if blob.blob == 0 {
return 0, fmt.Errorf("sqlite: write blob: %w", errInvalidBlob)
}
if err := blob.conn.interrupted(); err != nil {
return 0, fmt.Errorf("sqlite: write blob: %w", err)
}
fullLen := len(s)
for len(s) > 0 {
nn := copy(blob.bufSlice(), s)
res := ResultCode(lib.Xsqlite3_blob_write(blob.conn.tls, blob.blob, blob.buf, int32(nn), blob.off))
if err := reserr(res); err != nil {
return fullLen - len(s), fmt.Errorf("sqlite: write blob: %w", err)
}
s = s[nn:]
blob.off += int32(nn)
}
return fullLen, nil
}
// ReadFrom copies data from r to the blob until EOF or error.
func (blob *Blob) ReadFrom(r io.Reader) (n int64, err error) {
if blob.blob == 0 {
return 0, fmt.Errorf("sqlite: write blob: %w", errInvalidBlob)
}
if err := blob.conn.interrupted(); err != nil {
return 0, fmt.Errorf("sqlite: write blob: %w", err)
}
for {
nn, err := r.Read(blob.bufSlice())
if nn > 0 {
res := ResultCode(lib.Xsqlite3_blob_write(blob.conn.tls, blob.blob, blob.buf, int32(nn), blob.off))
if err := reserr(res); err != nil {
return n, fmt.Errorf("sqlite: write blob: %w", err)
}
n += int64(nn)
blob.off += int32(nn)
}
if err != nil {
if err == io.EOF {
err = nil
}
return n, err
}
}
}
// Seek sets the offset for the next Read or Write and returns the offset.
// Seeking past the end of the blob returns an error.
func (blob *Blob) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekStart:
// use offset directly
case io.SeekCurrent:
offset += int64(blob.off)
case io.SeekEnd:
offset += int64(blob.size)
default:
return int64(blob.off), fmt.Errorf("sqlite: seek blob: invalid whence %d", whence)
}
if offset < 0 {
return int64(blob.off), fmt.Errorf("sqlite: seek blob: negative offset %d", offset)
}
if offset > int64(blob.size) {
return int64(blob.off), fmt.Errorf("sqlite: seek blob: offset %d is past size %d", offset, blob.size)
}
blob.off = int32(offset)
return offset, nil
}
// Size returns the number of bytes in the blob.
func (blob *Blob) Size() int64 {
return int64(blob.size)
}
// Close releases any resources associated with the blob handle.
// https://www.sqlite.org/c3ref/blob_close.html
func (blob *Blob) Close() error {
if blob.blob == 0 {
return errInvalidBlob
}
libc.Xfree(blob.conn.tls, blob.buf)
blob.buf = 0
res := ResultCode(lib.Xsqlite3_blob_close(blob.conn.tls, blob.blob))
blob.blob = 0
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: close blob: %w", err)
}
return nil
}
var errInvalidBlob = errors.New("invalid blob")
// TODO: Blob Reopen

102
vendor/zombiezen.com/go/sqlite/blocking_step.go generated vendored Normal file
View File

@@ -0,0 +1,102 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <ross@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
package sqlite
import (
"fmt"
"sync"
"unsafe"
"modernc.org/libc"
"modernc.org/libc/sys/types"
lib "modernc.org/sqlite/lib"
)
// See https://sqlite.org/unlock_notify.html for detailed explanation.
// unlockNote is a C-allocated struct used as a condition variable.
type unlockNote struct {
mu sync.Mutex
wait sync.Mutex // held while fired == false
fired bool
}
func allocUnlockNote(tls *libc.TLS) (uintptr, error) {
ptr := libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(unlockNote{})))
if ptr == 0 {
return 0, fmt.Errorf("out of memory for unlockNote")
}
un := (*unlockNote)(unsafe.Pointer(ptr))
un.wait.Lock()
return ptr, nil
}
func fireUnlockNote(tls *libc.TLS, ptr uintptr) {
un := (*unlockNote)(unsafe.Pointer(ptr))
un.mu.Lock()
if !un.fired {
un.fired = true
un.wait.Unlock()
}
un.mu.Unlock()
}
func unlockNotifyCallback(tls *libc.TLS, apArg uintptr, nArg int32) {
for ; nArg > 0; nArg-- {
fireUnlockNote(tls, *(*uintptr)(unsafe.Pointer(apArg)))
// apArg is a C array of pointers.
apArg += unsafe.Sizeof(uintptr(0))
}
}
func waitForUnlockNotify(tls *libc.TLS, db uintptr, unPtr uintptr) ResultCode {
un := (*unlockNote)(unsafe.Pointer(unPtr))
if un.fired {
un.wait.Lock()
}
un.fired = false
// The following is a conversion from function value to uintptr. It assumes
// the memory representation described in https://golang.org/s/go11func.
//
// It does this by doing the following in order:
// 1) Create a Go struct containing a pointer to a pointer to
// unlockNotifyCallback. It is assumed that the pointer to
// unlockNotifyCallback will be stored in the read-only data section and
// thus will not move.
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
// 3) Dereference the pointer to uintptr to obtain the function value as a
// uintptr. This is safe as long as function values are passed as pointers.
cbPtr := *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, int32)
}{unlockNotifyCallback}))
res := ResultCode(lib.Xsqlite3_unlock_notify(tls, db, cbPtr, unPtr))
if res == ResultOK {
un.mu.Lock()
fired := un.fired
un.mu.Unlock()
if !fired {
un.wait.Lock()
un.wait.Unlock()
}
}
return res
}

86
vendor/zombiezen.com/go/sqlite/doc.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <ross@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
/*
Package sqlite provides a Go interface to SQLite 3.
The semantics of this package are deliberately close to the
SQLite3 C API, so it is helpful to be familiar with
http://www.sqlite.org/c3ref/intro.html.
An SQLite connection is represented by a *sqlite.Conn.
Connections cannot be used concurrently.
A typical Go program will create a pool of connections
(using Open to create a *sqlitex.Pool) so goroutines can
borrow a connection while they need to talk to the database.
This package assumes SQLite will be used concurrently by the
process through several connections, so the build options for
SQLite enable multi-threading and the shared cache:
https://www.sqlite.org/sharedcache.html
The implementation automatically handles shared cache locking,
see the documentation on Stmt.Step for details.
The optional SQLite 3 extensions compiled in are: session, FTS5, RTree, JSON1,
and GeoPoly.
This is not a database/sql driver. For helper functions that make some kinds of
statements easier to write, see the sqlitex package.
Statement Caching
Statements are prepared with the Prepare and PrepareTransient methods.
When using Prepare, statements are keyed inside a connection by the
original query string used to create them. This means long-running
high-performance code paths can write:
stmt, err := conn.Prepare("SELECT ...")
After all the connections in a pool have been warmed up by passing
through one of these Prepare calls, subsequent calls are simply a
map lookup that returns an existing statement.
Streaming Blobs
The sqlite package supports the SQLite incremental I/O interface for
streaming blob data into and out of the the database without loading
the entire blob into a single []byte.
(This is important when working either with very large blobs, or
more commonly, a large number of moderate-sized blobs concurrently.)
Deadlines and Cancellation
Every connection can have a done channel associated with it using
the SetInterrupt method. This is typically the channel returned by
a context.Context Done method.
As database connections are long-lived, the SetInterrupt method can
be called multiple times to reset the associated lifetime.
Transactions
SQLite transactions have to be managed manually with this package
by directly calling BEGIN / COMMIT / ROLLBACK or
SAVEPOINT / RELEASE/ ROLLBACK. The sqlitex has a Savepoint
function that helps automate this.
*/
package sqlite

5
vendor/zombiezen.com/go/sqlite/fs/doc.go generated vendored Normal file
View File

@@ -0,0 +1,5 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
// Package fs provides a fallback for the io/fs package in Go 1.16.
package fs

22
vendor/zombiezen.com/go/sqlite/fs/fs.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
// +build !go1.16
package fs
import (
"os"
)
// FS is a copy of Go 1.16's io/fs.FS interface.
type FS interface {
Open(name string) (File, error)
}
// File is a copy of Go 1.16's io/fs.File interface.
type File interface {
Stat() (os.FileInfo, error)
Read([]byte) (int, error)
Close() error
}

14
vendor/zombiezen.com/go/sqlite/fs/fs_go116.go generated vendored Normal file
View File

@@ -0,0 +1,14 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
// +build go1.16
package fs
import "io/fs"
// FS is an alias for the io/fs.FS interface.
type FS = fs.FS
// File is an alias for the io/fs.File interface.
type File = fs.File

603
vendor/zombiezen.com/go/sqlite/func.go generated vendored Normal file
View File

@@ -0,0 +1,603 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <ross@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
package sqlite
import (
"errors"
"fmt"
"math"
"math/bits"
"strconv"
"strings"
"sync"
"unsafe"
"modernc.org/libc"
"modernc.org/libc/sys/types"
lib "modernc.org/sqlite/lib"
)
var auxdata struct {
mu sync.RWMutex
m map[uintptr]interface{}
ids idGen
}
// Context is a SQL function execution context.
// It is in no way related to a Go context.Context.
// https://sqlite.org/c3ref/context.html
type Context struct {
tls *libc.TLS
ptr uintptr
}
// Conn returns the database connection that is calling the SQL function.
func (ctx Context) Conn() *Conn {
connPtr := lib.Xsqlite3_context_db_handle(ctx.tls, ctx.ptr)
allConns.mu.RLock()
defer allConns.mu.RUnlock()
return allConns.table[connPtr]
}
// AuxData returns the auxiliary data associated with the given argument, with
// zero being the leftmost argument, or nil if no such data is present.
//
// Auxiliary data may be used by (non-aggregate) SQL functions to associate
// metadata with argument values. If the same value is passed to multiple
// invocations of the same SQL function during query execution, under some
// circumstances the associated metadata may be preserved. An example of where
// this might be useful is in a regular-expression matching function. The
// compiled version of the regular expression can be stored as metadata
// associated with the pattern string. Then as long as the pattern string
// remains the same, the compiled regular expression can be reused on multiple
// invocations of the same function.
//
// For more details, see https://www.sqlite.org/c3ref/get_auxdata.html
func (ctx Context) AuxData(arg int) interface{} {
id := lib.Xsqlite3_get_auxdata(ctx.tls, ctx.ptr, int32(arg))
if id == 0 {
return nil
}
auxdata.mu.RLock()
defer auxdata.mu.RUnlock()
return auxdata.m[id]
}
// SetAuxData sets the auxiliary data associated with the given argument, with
// zero being the leftmost argument. SQLite is free to discard the metadata at
// any time, including during the call to SetAuxData.
//
// Auxiliary data may be used by (non-aggregate) SQL functions to associate
// metadata with argument values. If the same value is passed to multiple
// invocations of the same SQL function during query execution, under some
// circumstances the associated metadata may be preserved. An example of where
// this might be useful is in a regular-expression matching function. The
// compiled version of the regular expression can be stored as metadata
// associated with the pattern string. Then as long as the pattern string
// remains the same, the compiled regular expression can be reused on multiple
// invocations of the same function.
//
// For more details, see https://www.sqlite.org/c3ref/get_auxdata.html
func (ctx Context) SetAuxData(arg int, data interface{}) {
auxdata.mu.Lock()
id := auxdata.ids.next()
if auxdata.m == nil {
auxdata.m = make(map[uintptr]interface{})
}
auxdata.m[id] = data
auxdata.mu.Unlock()
// The following is a conversion from function value to uintptr. It assumes
// the memory representation described in https://golang.org/s/go11func.
//
// It does this by doing the following in order:
// 1) Create a Go struct containing a pointer to a pointer to
// freeAuxData. It is assumed that the pointer to freeAuxData will be
// stored in the read-only data section and thus will not move.
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
// 3) Dereference the pointer to uintptr to obtain the function value as a
// uintptr. This is safe as long as function values are passed as pointers.
deleteFn := *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr)
}{freeAuxData}))
lib.Xsqlite3_set_auxdata(ctx.tls, ctx.ptr, int32(arg), id, deleteFn)
}
func freeAuxData(tls *libc.TLS, id uintptr) {
auxdata.mu.Lock()
defer auxdata.mu.Unlock()
delete(auxdata.m, id)
auxdata.ids.reclaim(id)
}
func (ctx Context) result(v Value, err error) {
if err != nil {
ctx.resultError(err)
return
}
if v.tls != nil {
if ctx.tls != v.tls {
ctx.resultError(fmt.Errorf("function result Value from different connection"))
return
}
lib.Xsqlite3_result_value(ctx.tls, ctx.ptr, v.ptrOrType)
return
}
switch ColumnType(v.ptrOrType) {
case 0, TypeNull:
lib.Xsqlite3_result_null(ctx.tls, ctx.ptr)
case TypeInteger:
lib.Xsqlite3_result_int64(ctx.tls, ctx.ptr, v.n)
case TypeFloat:
lib.Xsqlite3_result_double(ctx.tls, ctx.ptr, v.float())
case TypeText:
if len(v.s) == 0 {
lib.Xsqlite3_result_text(ctx.tls, ctx.ptr, emptyCString, 0, sqliteStatic)
} else {
cv, err := libc.CString(v.s)
if err != nil {
ctx.resultError(fmt.Errorf("alloc function result: %w", err))
return
}
lib.Xsqlite3_result_text(ctx.tls, ctx.ptr, cv, int32(len(v.s)), freeFuncPtr)
}
case TypeBlob:
if len(v.s) == 0 {
lib.Xsqlite3_result_blob(ctx.tls, ctx.ptr, emptyCString, 0, sqliteStatic)
} else {
cv, err := malloc(ctx.tls, types.Size_t(len(v.s)))
if err != nil {
ctx.resultError(fmt.Errorf("alloc function result: %w", err))
return
}
copy(libc.GoBytes(cv, len(v.s)), v.s)
lib.Xsqlite3_result_blob(ctx.tls, ctx.ptr, cv, int32(len(v.s)), freeFuncPtr)
}
default:
panic("unknown result Value type")
}
}
func (ctx Context) resultError(err error) {
errstr := err.Error()
cerrstr, err := libc.CString(errstr)
if err != nil {
panic(err)
}
defer libc.Xfree(ctx.tls, cerrstr)
lib.Xsqlite3_result_error(ctx.tls, ctx.ptr, cerrstr, int32(len(errstr)))
lib.Xsqlite3_result_error_code(ctx.tls, ctx.ptr, int32(ErrCode(err)))
}
// Value represents a value that can be stored in a database table. The zero
// value is NULL. The accessor methods on Value may perform automatic
// conversions and thus methods on Value must not be called concurrently.
type Value struct {
tls *libc.TLS
ptrOrType uintptr // pointer to sqlite_value if tls != nil, ColumnType otherwise
s string
n int64
}
// IntegerValue returns a new Value representing the given integer.
func IntegerValue(i int64) Value {
return Value{ptrOrType: uintptr(TypeInteger), n: i}
}
// FloatValue returns a new Value representing the given floating-point number.
func FloatValue(f float64) Value {
return Value{ptrOrType: uintptr(TypeFloat), n: int64(math.Float64bits(f))}
}
// TextValue returns a new Value representing the given string.
func TextValue(s string) Value {
return Value{ptrOrType: uintptr(TypeText), s: s}
}
// BlobValue returns a new blob Value, copying the bytes from the given
// byte slice.
func BlobValue(b []byte) Value {
return Value{ptrOrType: uintptr(TypeBlob), s: string(b)}
}
// Type returns the data type of the value. The result of Type is undefined if
// an automatic type conversion has occurred due to calling one of the other
// accessor methods.
func (v Value) Type() ColumnType {
if v.ptrOrType == 0 {
return TypeNull
}
if v.tls == nil {
return ColumnType(v.ptrOrType)
}
return ColumnType(lib.Xsqlite3_value_type(v.tls, v.ptrOrType))
}
// Conversions follow the table in https://sqlite.org/c3ref/column_blob.html
// Int returns the value as an integer.
func (v Value) Int() int {
return int(v.Int64())
}
// Int64 returns the value as a 64-bit integer.
func (v Value) Int64() int64 {
if v.ptrOrType == 0 {
return 0
}
if v.tls == nil {
switch ColumnType(v.ptrOrType) {
case TypeNull:
return 0
case TypeInteger:
return v.n
case TypeFloat:
return int64(v.float())
case TypeBlob, TypeText:
return castTextToInteger(v.s)
default:
panic("unknown value type")
}
}
return int64(lib.Xsqlite3_value_int64(v.tls, v.ptrOrType))
}
// castTextToInteger emulates the SQLite CAST operator for a TEXT value to
// INTEGER, as documented in https://sqlite.org/lang_expr.html#castexpr
func castTextToInteger(s string) int64 {
const digits = "0123456789"
s = strings.TrimSpace(s)
if len(s) > 0 && (s[0] == '+' || s[0] == '-') {
s = s[:1+len(longestPrefix(s[1:], digits))]
} else {
s = longestPrefix(s, digits)
}
n, _ := strconv.ParseInt(s, 10, 64)
return n
}
func longestPrefix(s string, allowSet string) string {
sloop:
for i := 0; i < len(s); i++ {
for j := 0; j < len(allowSet); j++ {
if s[i] == allowSet[j] {
continue sloop
}
}
return s[:i]
}
return s
}
// Float returns the value as floating-point number
func (v Value) Float() float64 {
if v.ptrOrType == 0 {
return 0
}
if v.tls == nil {
switch ColumnType(v.ptrOrType) {
case TypeNull:
return 0
case TypeInteger:
return float64(v.n)
case TypeFloat:
return v.float()
case TypeBlob, TypeText:
return castTextToReal(v.s)
default:
panic("unknown value type")
}
}
return float64(lib.Xsqlite3_value_double(v.tls, v.ptrOrType))
}
func (v Value) float() float64 { return math.Float64frombits(uint64(v.n)) }
// castTextToReal emulates the SQLite CAST operator for a TEXT value to
// REAL, as documented in https://sqlite.org/lang_expr.html#castexpr
func castTextToReal(s string) float64 {
s = strings.TrimSpace(s)
for ; len(s) > 0; s = s[:len(s)-1] {
n, err := strconv.ParseFloat(s, 64)
if !errors.Is(err, strconv.ErrSyntax) {
return n
}
}
return 0
}
// Text returns the value as a string.
func (v Value) Text() string {
if v.ptrOrType == 0 {
return ""
}
if v.tls == nil {
switch ColumnType(v.ptrOrType) {
case TypeNull:
return ""
case TypeInteger:
return strconv.FormatInt(v.n, 10)
case TypeFloat:
return strconv.FormatFloat(v.float(), 'g', -1, 64)
case TypeText, TypeBlob:
return v.s
default:
panic("unknown value type")
}
}
ptr := lib.Xsqlite3_value_text(v.tls, v.ptrOrType)
return goStringN(ptr, int(lib.Xsqlite3_value_bytes(v.tls, v.ptrOrType)))
}
// Blob returns a copy of the value as a blob.
func (v Value) Blob() []byte {
if v.ptrOrType == 0 {
return nil
}
if v.tls == nil {
switch ColumnType(v.ptrOrType) {
case TypeNull:
return nil
case TypeInteger:
return strconv.AppendInt(nil, v.n, 10)
case TypeFloat:
return strconv.AppendFloat(nil, v.float(), 'g', -1, 64)
case TypeBlob, TypeText:
return []byte(v.s)
default:
panic("unknown value type")
}
}
ptr := lib.Xsqlite3_value_blob(v.tls, v.ptrOrType)
return libc.GoBytes(ptr, int(lib.Xsqlite3_value_bytes(v.tls, v.ptrOrType)))
}
type xfunc struct {
xFunc func(Context, []Value) (Value, error)
xStep func(Context, []Value)
xFinal func(Context) (Value, error)
}
var xfuncs = struct {
mu sync.RWMutex
m map[uintptr]*xfunc
ids idGen
}{
m: make(map[uintptr]*xfunc),
}
// FunctionImpl describes an application-defined SQL function. Either Scalar or
// both AggregateStep and AggregateFinal must be set.
type FunctionImpl struct {
// NArgs is the required number of arguments that the function accepts.
// If NArgs is negative, then the function is variadic.
//
// Multiple function implementations may be registered with the same name
// with different numbers of required arguments.
NArgs int
// Scalar is called when a scalar function is invoked in SQL.
Scalar func(ctx Context, args []Value) (Value, error)
// AggregateStep is called for each row of an aggregate function's
// SQL invocation.
AggregateStep func(ctx Context, rowArgs []Value)
// AggregateFinal is called after all of the aggregate function's input rows
// have been stepped through to construct the result.
//
// Use closure variables to pass information between AggregateStep and
// AggregateFinal. The AggregateFinal function should also reset any shared
// variables to their initial states before returning.
AggregateFinal func(ctx Context) (Value, error)
// If Deterministic is true, the function must always give the same output
// when the input parameters are the same. This enables functions to be used
// in additional contexts like the WHERE clause of partial indexes and enables
// additional optimizations.
//
// See https://sqlite.org/c3ref/c_deterministic.html#sqlitedeterministic for
// more details.
Deterministic bool
// If AllowIndirect is false, then the function may only be invoked from
// top-level SQL. If AllowIndirect is true, then the function can be used in
// VIEWs, TRIGGERs, and schema structures (e.g. CHECK constraints and DEFAULT
// clauses).
//
// This is the inverse of SQLITE_DIRECTONLY. See
// https://sqlite.org/c3ref/c_deterministic.html#sqlitedirectonly for more
// details. This defaults to false for better security by default.
AllowIndirect bool
}
// CreateFunction registers a Go function with SQLite
// for use in SQL queries.
//
// https://sqlite.org/appfunc.html
func (c *Conn) CreateFunction(name string, impl *FunctionImpl) error {
if c == nil {
return fmt.Errorf("sqlite: create function: nil connection")
}
if name == "" {
return fmt.Errorf("sqlite: create function: no name provided")
}
if impl.NArgs > 127 {
return fmt.Errorf("sqlite: create function %s: too many permitted arguments (%d)", name, impl.NArgs)
}
if impl.AggregateStep == nil {
if impl.Scalar == nil {
return fmt.Errorf("sqlite: create function %s: must specify one of Scalar or AggregateStep", name)
}
if impl.AggregateFinal != nil {
return fmt.Errorf("sqlite: create function %s: must not specify AggregateFinal with Scalar", name)
}
} else {
if impl.Scalar != nil {
return fmt.Errorf("sqlite: create function %s: both Scalar and AggregateStep specified", name)
}
if impl.AggregateFinal == nil {
return fmt.Errorf("sqlite: create function %s: AggregateStep specified without AggregateFinal", name)
}
}
cname, err := libc.CString(name)
if err != nil {
return fmt.Errorf("sqlite: create function %s: %w", name, err)
}
defer libc.Xfree(c.tls, cname)
eTextRep := int32(lib.SQLITE_UTF8)
if impl.Deterministic {
eTextRep |= lib.SQLITE_DETERMINISTIC
}
if !impl.AllowIndirect {
eTextRep |= lib.SQLITE_DIRECTONLY
}
x := &xfunc{
xFunc: impl.Scalar,
xStep: impl.AggregateStep,
xFinal: impl.AggregateFinal,
}
xfuncs.mu.Lock()
id := xfuncs.ids.next()
xfuncs.m[id] = x
xfuncs.mu.Unlock()
// The following are conversions from function values to uintptr. It assumes
// the memory representation described in https://golang.org/s/go11func.
//
// It does this by doing the following in order:
// 1) Create a Go struct containing a pointer to a pointer to
// the function. It is assumed that the pointer to the function will be
// stored in the read-only data section and thus will not move.
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
// 3) Dereference the pointer to uintptr to obtain the function value as a
// uintptr. This is safe as long as function values are passed as pointers.
var funcfn, stepfn, finalfn uintptr
if impl.Scalar == nil {
stepfn = *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, int32, uintptr)
}{stepTrampoline}))
finalfn = *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr)
}{finalTrampoline}))
} else {
funcfn = *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, int32, uintptr)
}{funcTrampoline}))
}
destroyfn := *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr)
}{destroyFuncTrampoline}))
numArgs := impl.NArgs
if numArgs < 0 {
numArgs = -1
}
res := ResultCode(lib.Xsqlite3_create_function_v2(
c.tls,
c.conn,
cname,
int32(numArgs),
eTextRep,
id,
funcfn,
stepfn,
finalfn,
destroyfn,
))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: create function %s: %w", name, err)
}
return nil
}
func getxfuncs(tls *libc.TLS, ctx uintptr) *xfunc {
id := lib.Xsqlite3_user_data(tls, ctx)
xfuncs.mu.RLock()
x := xfuncs.m[id]
xfuncs.mu.RUnlock()
return x
}
func funcTrampoline(tls *libc.TLS, ctx uintptr, n int32, valarray uintptr) {
vals := make([]Value, 0, int(n))
for ; len(vals) < cap(vals); valarray += uintptr(ptrSize) {
vals = append(vals, Value{
tls: tls,
ptrOrType: *(*uintptr)(unsafe.Pointer(valarray)),
})
}
goCtx := Context{tls: tls, ptr: ctx}
goCtx.result(getxfuncs(tls, ctx).xFunc(goCtx, vals))
}
func stepTrampoline(tls *libc.TLS, ctx uintptr, n int32, valarray uintptr) {
vals := make([]Value, 0, int(n))
for ; len(vals) < cap(vals); valarray += uintptr(ptrSize) {
vals = append(vals, Value{
tls: tls,
ptrOrType: *(*uintptr)(unsafe.Pointer(valarray)),
})
}
goCtx := Context{tls: tls, ptr: ctx}
getxfuncs(tls, ctx).xStep(goCtx, vals)
}
func finalTrampoline(tls *libc.TLS, ctx uintptr) {
x := getxfuncs(tls, ctx)
goCtx := Context{tls: tls, ptr: ctx}
goCtx.result(x.xFinal(goCtx))
}
func destroyFuncTrampoline(tls *libc.TLS, id uintptr) {
xfuncs.mu.Lock()
defer xfuncs.mu.Unlock()
delete(xfuncs.m, id)
xfuncs.ids.reclaim(id)
}
// idGen is an ID generator. The zero value is ready to use.
type idGen struct {
bitset []uint64
}
func (gen *idGen) next() uintptr {
base := uintptr(1)
for i := 0; i < len(gen.bitset); i, base = i+1, base+64 {
b := gen.bitset[i]
if b != 1<<64-1 {
n := uintptr(bits.TrailingZeros64(^b))
gen.bitset[i] |= 1 << n
return base + n
}
}
gen.bitset = append(gen.bitset, 1)
return base
}
func (gen *idGen) reclaim(id uintptr) {
bit := id - 1
gen.bitset[bit/64] &^= 1 << (bit % 64)
}

127
vendor/zombiezen.com/go/sqlite/op_type.go generated vendored Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
package sqlite
import (
"fmt"
lib "modernc.org/sqlite/lib"
)
// OpType is an enumeration of SQLite statements and authorizable actions.
type OpType int32
// Operation types
const (
OpCreateIndex OpType = lib.SQLITE_CREATE_INDEX
OpCreateTable OpType = lib.SQLITE_CREATE_TABLE
OpCreateTempIndex OpType = lib.SQLITE_CREATE_TEMP_INDEX
OpCreateTempTable OpType = lib.SQLITE_CREATE_TEMP_TABLE
OpCreateTempTrigger OpType = lib.SQLITE_CREATE_TEMP_TRIGGER
OpCreateTempView OpType = lib.SQLITE_CREATE_TEMP_VIEW
OpCreateTrigger OpType = lib.SQLITE_CREATE_TRIGGER
OpCreateView OpType = lib.SQLITE_CREATE_VIEW
OpDelete OpType = lib.SQLITE_DELETE
OpDropIndex OpType = lib.SQLITE_DROP_INDEX
OpDropTable OpType = lib.SQLITE_DROP_TABLE
OpDropTempIndex OpType = lib.SQLITE_DROP_TEMP_INDEX
OpDropTempTable OpType = lib.SQLITE_DROP_TEMP_TABLE
OpDropTempTrigger OpType = lib.SQLITE_DROP_TEMP_TRIGGER
OpDropTempView OpType = lib.SQLITE_DROP_TEMP_VIEW
OpDropTrigger OpType = lib.SQLITE_DROP_TRIGGER
OpDropView OpType = lib.SQLITE_DROP_VIEW
OpInsert OpType = lib.SQLITE_INSERT
OpPragma OpType = lib.SQLITE_PRAGMA
OpRead OpType = lib.SQLITE_READ
OpSelect OpType = lib.SQLITE_SELECT
OpTransaction OpType = lib.SQLITE_TRANSACTION
OpUpdate OpType = lib.SQLITE_UPDATE
OpAttach OpType = lib.SQLITE_ATTACH
OpDetach OpType = lib.SQLITE_DETACH
OpAlterTable OpType = lib.SQLITE_ALTER_TABLE
OpReindex OpType = lib.SQLITE_REINDEX
OpAnalyze OpType = lib.SQLITE_ANALYZE
OpCreateVTable OpType = lib.SQLITE_CREATE_VTABLE
OpDropVTable OpType = lib.SQLITE_DROP_VTABLE
OpFunction OpType = lib.SQLITE_FUNCTION
OpSavepoint OpType = lib.SQLITE_SAVEPOINT
OpCopy OpType = lib.SQLITE_COPY
OpRecursive OpType = lib.SQLITE_RECURSIVE
)
// String returns the C constant name of the operation type.
func (op OpType) String() string {
switch op {
case OpCreateIndex:
return "SQLITE_CREATE_INDEX"
case OpCreateTable:
return "SQLITE_CREATE_TABLE"
case OpCreateTempIndex:
return "SQLITE_CREATE_TEMP_INDEX"
case OpCreateTempTable:
return "SQLITE_CREATE_TEMP_TABLE"
case OpCreateTempTrigger:
return "SQLITE_CREATE_TEMP_TRIGGER"
case OpCreateTempView:
return "SQLITE_CREATE_TEMP_VIEW"
case OpCreateTrigger:
return "SQLITE_CREATE_TRIGGER"
case OpCreateView:
return "SQLITE_CREATE_VIEW"
case OpDelete:
return "SQLITE_DELETE"
case OpDropIndex:
return "SQLITE_DROP_INDEX"
case OpDropTable:
return "SQLITE_DROP_TABLE"
case OpDropTempIndex:
return "SQLITE_DROP_TEMP_INDEX"
case OpDropTempTable:
return "SQLITE_DROP_TEMP_TABLE"
case OpDropTempTrigger:
return "SQLITE_DROP_TEMP_TRIGGER"
case OpDropTempView:
return "SQLITE_DROP_TEMP_VIEW"
case OpDropTrigger:
return "SQLITE_DROP_TRIGGER"
case OpDropView:
return "SQLITE_DROP_VIEW"
case OpInsert:
return "SQLITE_INSERT"
case OpPragma:
return "SQLITE_PRAGMA"
case OpRead:
return "SQLITE_READ"
case OpSelect:
return "SQLITE_SELECT"
case OpTransaction:
return "SQLITE_TRANSACTION"
case OpUpdate:
return "SQLITE_UPDATE"
case OpAttach:
return "SQLITE_ATTACH"
case OpDetach:
return "SQLITE_DETACH"
case OpAlterTable:
return "SQLITE_ALTER_TABLE"
case OpReindex:
return "SQLITE_REINDEX"
case OpAnalyze:
return "SQLITE_ANALYZE"
case OpCreateVTable:
return "SQLITE_CREATE_VTABLE"
case OpDropVTable:
return "SQLITE_DROP_VTABLE"
case OpFunction:
return "SQLITE_FUNCTION"
case OpSavepoint:
return "SQLITE_SAVEPOINT"
case OpCopy:
return "SQLITE_COPY"
case OpRecursive:
return "SQLITE_RECURSIVE"
default:
return fmt.Sprintf("OpType(%d)", int32(op))
}
}

78
vendor/zombiezen.com/go/sqlite/openflags.go generated vendored Normal file
View File

@@ -0,0 +1,78 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
package sqlite
import (
"fmt"
"strings"
lib "modernc.org/sqlite/lib"
)
// OpenFlags are flags used when opening a Conn.
//
// https://www.sqlite.org/c3ref/c_open_autoproxy.html
type OpenFlags uint
const (
OpenReadOnly OpenFlags = lib.SQLITE_OPEN_READONLY
OpenReadWrite OpenFlags = lib.SQLITE_OPEN_READWRITE
OpenCreate OpenFlags = lib.SQLITE_OPEN_CREATE
OpenURI OpenFlags = lib.SQLITE_OPEN_URI
OpenMemory OpenFlags = lib.SQLITE_OPEN_MEMORY
OpenNoMutex OpenFlags = lib.SQLITE_OPEN_NOMUTEX
OpenFullMutex OpenFlags = lib.SQLITE_OPEN_FULLMUTEX
OpenSharedCache OpenFlags = lib.SQLITE_OPEN_SHAREDCACHE
OpenPrivateCache OpenFlags = lib.SQLITE_OPEN_PRIVATECACHE
OpenWAL OpenFlags = lib.SQLITE_OPEN_WAL
)
// String returns a pipe-separated list of the C constant names set in flags.
func (flags OpenFlags) String() string {
var parts []string
if flags&OpenReadOnly != 0 {
parts = append(parts, "SQLITE_OPEN_READONLY")
flags &^= OpenReadOnly
}
if flags&OpenReadWrite != 0 {
parts = append(parts, "SQLITE_OPEN_READWRITE")
flags &^= OpenReadWrite
}
if flags&OpenCreate != 0 {
parts = append(parts, "SQLITE_OPEN_CREATE")
flags &^= OpenCreate
}
if flags&OpenURI != 0 {
parts = append(parts, "SQLITE_OPEN_URI")
flags &^= OpenURI
}
if flags&OpenMemory != 0 {
parts = append(parts, "SQLITE_OPEN_MEMORY")
flags &^= OpenMemory
}
if flags&OpenNoMutex != 0 {
parts = append(parts, "SQLITE_OPEN_NOMUTEX")
flags &^= OpenNoMutex
}
if flags&OpenFullMutex != 0 {
parts = append(parts, "SQLITE_OPEN_FULLMUTEX")
flags &^= OpenFullMutex
}
if flags&OpenSharedCache != 0 {
parts = append(parts, "SQLITE_OPEN_SHAREDCACHE")
flags &^= OpenSharedCache
}
if flags&OpenPrivateCache != 0 {
parts = append(parts, "SQLITE_OPEN_PRIVATECACHE")
flags &^= OpenPrivateCache
}
if flags&OpenWAL != 0 {
parts = append(parts, "SQLITE_OPEN_WAL")
flags &^= OpenWAL
}
if flags != 0 || len(parts) == 0 {
parts = append(parts, fmt.Sprintf("%#x", uint(flags)))
}
return strings.Join(parts, "|")
}

359
vendor/zombiezen.com/go/sqlite/result.go generated vendored Normal file
View File

@@ -0,0 +1,359 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
package sqlite
import (
"errors"
"fmt"
"modernc.org/libc"
lib "modernc.org/sqlite/lib"
)
// ResultCode is an SQLite extended result code.
type ResultCode int32
// Primary result codes.
const (
ResultOK ResultCode = lib.SQLITE_OK
ResultError ResultCode = lib.SQLITE_ERROR
ResultInternal ResultCode = lib.SQLITE_INTERNAL
ResultPerm ResultCode = lib.SQLITE_PERM
ResultAbort ResultCode = lib.SQLITE_ABORT
ResultBusy ResultCode = lib.SQLITE_BUSY
ResultLocked ResultCode = lib.SQLITE_LOCKED
ResultNoMem ResultCode = lib.SQLITE_NOMEM
ResultReadOnly ResultCode = lib.SQLITE_READONLY
ResultInterrupt ResultCode = lib.SQLITE_INTERRUPT
ResultIOErr ResultCode = lib.SQLITE_IOERR
ResultCorrupt ResultCode = lib.SQLITE_CORRUPT
ResultNotFound ResultCode = lib.SQLITE_NOTFOUND
ResultFull ResultCode = lib.SQLITE_FULL
ResultCantOpen ResultCode = lib.SQLITE_CANTOPEN
ResultProtocol ResultCode = lib.SQLITE_PROTOCOL
ResultEmpty ResultCode = lib.SQLITE_EMPTY
ResultSchema ResultCode = lib.SQLITE_SCHEMA
ResultTooBig ResultCode = lib.SQLITE_TOOBIG
ResultConstraint ResultCode = lib.SQLITE_CONSTRAINT
ResultMismatch ResultCode = lib.SQLITE_MISMATCH
ResultMisuse ResultCode = lib.SQLITE_MISUSE
ResultNoLFS ResultCode = lib.SQLITE_NOLFS
ResultAuth ResultCode = lib.SQLITE_AUTH
ResultFormat ResultCode = lib.SQLITE_FORMAT
ResultRange ResultCode = lib.SQLITE_RANGE
ResultNotADB ResultCode = lib.SQLITE_NOTADB
ResultNotice ResultCode = lib.SQLITE_NOTICE
ResultWarning ResultCode = lib.SQLITE_WARNING
ResultRow ResultCode = lib.SQLITE_ROW
ResultDone ResultCode = lib.SQLITE_DONE
)
// Extended result codes.
const (
ResultErrorMissingCollSeq ResultCode = lib.SQLITE_ERROR_MISSING_COLLSEQ
ResultErrorRetry ResultCode = lib.SQLITE_ERROR_RETRY
ResultErrorSnapshot ResultCode = lib.SQLITE_ERROR_SNAPSHOT
ResultIOErrRead ResultCode = lib.SQLITE_IOERR_READ
ResultIOErrShortRead ResultCode = lib.SQLITE_IOERR_SHORT_READ
ResultIOErrWrite ResultCode = lib.SQLITE_IOERR_WRITE
ResultIOErrFsync ResultCode = lib.SQLITE_IOERR_FSYNC
ResultIOErrDirFsync ResultCode = lib.SQLITE_IOERR_DIR_FSYNC
ResultIOErrTruncate ResultCode = lib.SQLITE_IOERR_TRUNCATE
ResultIOErrFstat ResultCode = lib.SQLITE_IOERR_FSTAT
ResultIOErrUnlock ResultCode = lib.SQLITE_IOERR_UNLOCK
ResultIOErrReadLock ResultCode = lib.SQLITE_IOERR_RDLOCK
ResultIOErrDelete ResultCode = lib.SQLITE_IOERR_DELETE
ResultIOErrBlocked ResultCode = lib.SQLITE_IOERR_BLOCKED
ResultIOErrNoMem ResultCode = lib.SQLITE_IOERR_NOMEM
ResultIOErrAccess ResultCode = lib.SQLITE_IOERR_ACCESS
ResultIOErrCheckReservedLock ResultCode = lib.SQLITE_IOERR_CHECKRESERVEDLOCK
ResultIOErrLock ResultCode = lib.SQLITE_IOERR_LOCK
ResultIOErrClose ResultCode = lib.SQLITE_IOERR_CLOSE
ResultIOErrDirClose ResultCode = lib.SQLITE_IOERR_DIR_CLOSE
ResultIOErrSHMOpen ResultCode = lib.SQLITE_IOERR_SHMOPEN
ResultIOErrSHMSize ResultCode = lib.SQLITE_IOERR_SHMSIZE
ResultIOErrSHMLock ResultCode = lib.SQLITE_IOERR_SHMLOCK
ResultIOErrSHMMap ResultCode = lib.SQLITE_IOERR_SHMMAP
ResultIOErrSeek ResultCode = lib.SQLITE_IOERR_SEEK
ResultIOErrDeleteNoEnt ResultCode = lib.SQLITE_IOERR_DELETE_NOENT
ResultIOErrMMap ResultCode = lib.SQLITE_IOERR_MMAP
ResultIOErrGetTempPath ResultCode = lib.SQLITE_IOERR_GETTEMPPATH
ResultIOErrConvPath ResultCode = lib.SQLITE_IOERR_CONVPATH
ResultIOErrVNode ResultCode = lib.SQLITE_IOERR_VNODE
ResultIOErrAuth ResultCode = lib.SQLITE_IOERR_AUTH
ResultIOErrBeginAtomic ResultCode = lib.SQLITE_IOERR_BEGIN_ATOMIC
ResultIOErrCommitAtomic ResultCode = lib.SQLITE_IOERR_COMMIT_ATOMIC
ResultIOErrRollbackAtomic ResultCode = lib.SQLITE_IOERR_ROLLBACK_ATOMIC
ResultLockedSharedCache ResultCode = lib.SQLITE_LOCKED_SHAREDCACHE
ResultBusyRecovery ResultCode = lib.SQLITE_BUSY_RECOVERY
ResultBusySnapshot ResultCode = lib.SQLITE_BUSY_SNAPSHOT
ResultCantOpenNoTempDir ResultCode = lib.SQLITE_CANTOPEN_NOTEMPDIR
ResultCantOpenIsDir ResultCode = lib.SQLITE_CANTOPEN_ISDIR
ResultCantOpenFullPath ResultCode = lib.SQLITE_CANTOPEN_FULLPATH
ResultCantOpenConvPath ResultCode = lib.SQLITE_CANTOPEN_CONVPATH
ResultCorruptVTab ResultCode = lib.SQLITE_CORRUPT_VTAB
ResultReadOnlyRecovery ResultCode = lib.SQLITE_READONLY_RECOVERY
ResultReadOnlyCantLock ResultCode = lib.SQLITE_READONLY_CANTLOCK
ResultReadOnlyRollback ResultCode = lib.SQLITE_READONLY_ROLLBACK
ResultReadOnlyDBMoved ResultCode = lib.SQLITE_READONLY_DBMOVED
ResultReadOnlyCantInit ResultCode = lib.SQLITE_READONLY_CANTINIT
ResultReadOnlyDirectory ResultCode = lib.SQLITE_READONLY_DIRECTORY
ResultAbortRollback ResultCode = lib.SQLITE_ABORT_ROLLBACK
ResultConstraintCheck ResultCode = lib.SQLITE_CONSTRAINT_CHECK
ResultConstraintCommitHook ResultCode = lib.SQLITE_CONSTRAINT_COMMITHOOK
ResultConstraintForeignKey ResultCode = lib.SQLITE_CONSTRAINT_FOREIGNKEY
ResultConstraintFunction ResultCode = lib.SQLITE_CONSTRAINT_FUNCTION
ResultConstraintNotNull ResultCode = lib.SQLITE_CONSTRAINT_NOTNULL
ResultConstraintPrimaryKey ResultCode = lib.SQLITE_CONSTRAINT_PRIMARYKEY
ResultConstraintTrigger ResultCode = lib.SQLITE_CONSTRAINT_TRIGGER
ResultConstraintUnique ResultCode = lib.SQLITE_CONSTRAINT_UNIQUE
ResultConstraintVTab ResultCode = lib.SQLITE_CONSTRAINT_VTAB
ResultConstraintRowID ResultCode = lib.SQLITE_CONSTRAINT_ROWID
ResultNoticeRecoverWAL ResultCode = lib.SQLITE_NOTICE_RECOVER_WAL
ResultNoticeRecoverRollback ResultCode = lib.SQLITE_NOTICE_RECOVER_ROLLBACK
ResultWarningAutoIndex ResultCode = lib.SQLITE_WARNING_AUTOINDEX
ResultAuthUser ResultCode = lib.SQLITE_AUTH_USER
)
// ToPrimary returns the primary result code of the given code.
// https://sqlite.org/rescode.html#primary_result_codes_versus_extended_result_codes
func (code ResultCode) ToPrimary() ResultCode {
return code & 0xff
}
// IsSuccess reports whether code indicates success.
func (code ResultCode) IsSuccess() bool {
return code == ResultOK || code == ResultRow || code == ResultDone
}
// String returns the C constant name of the result code.
func (code ResultCode) String() string {
switch code {
case ResultOK:
return "SQLITE_OK"
case ResultError:
return "SQLITE_ERROR"
case ResultInternal:
return "SQLITE_INTERNAL"
case ResultPerm:
return "SQLITE_PERM"
case ResultAbort:
return "SQLITE_ABORT"
case ResultBusy:
return "SQLITE_BUSY"
case ResultLocked:
return "SQLITE_LOCKED"
case ResultNoMem:
return "SQLITE_NOMEM"
case ResultReadOnly:
return "SQLITE_READONLY"
case ResultInterrupt:
return "SQLITE_INTERRUPT"
case ResultIOErr:
return "SQLITE_IOERR"
case ResultCorrupt:
return "SQLITE_CORRUPT"
case ResultNotFound:
return "SQLITE_NOTFOUND"
case ResultFull:
return "SQLITE_FULL"
case ResultCantOpen:
return "SQLITE_CANTOPEN"
case ResultProtocol:
return "SQLITE_PROTOCOL"
case ResultEmpty:
return "SQLITE_EMPTY"
case ResultSchema:
return "SQLITE_SCHEMA"
case ResultTooBig:
return "SQLITE_TOOBIG"
case ResultConstraint:
return "SQLITE_CONSTRAINT"
case ResultMismatch:
return "SQLITE_MISMATCH"
case ResultMisuse:
return "SQLITE_MISUSE"
case ResultNoLFS:
return "SQLITE_NOLFS"
case ResultAuth:
return "SQLITE_AUTH"
case ResultFormat:
return "SQLITE_FORMAT"
case ResultRange:
return "SQLITE_RANGE"
case ResultNotADB:
return "SQLITE_NOTADB"
case ResultNotice:
return "SQLITE_NOTICE"
case ResultWarning:
return "SQLITE_WARNING"
case ResultRow:
return "SQLITE_ROW"
case ResultDone:
return "SQLITE_DONE"
case ResultErrorMissingCollSeq:
return "SQLITE_ERROR_MISSING_COLLSEQ"
case ResultErrorRetry:
return "SQLITE_ERROR_RETRY"
case ResultErrorSnapshot:
return "SQLITE_ERROR_SNAPSHOT"
case ResultIOErrRead:
return "SQLITE_IOERR_READ"
case ResultIOErrShortRead:
return "SQLITE_IOERR_SHORT_READ"
case ResultIOErrWrite:
return "SQLITE_IOERR_WRITE"
case ResultIOErrFsync:
return "SQLITE_IOERR_FSYNC"
case ResultIOErrDirFsync:
return "SQLITE_IOERR_DIR_FSYNC"
case ResultIOErrTruncate:
return "SQLITE_IOERR_TRUNCATE"
case ResultIOErrFstat:
return "SQLITE_IOERR_FSTAT"
case ResultIOErrUnlock:
return "SQLITE_IOERR_UNLOCK"
case ResultIOErrReadLock:
return "SQLITE_IOERR_RDLOCK"
case ResultIOErrDelete:
return "SQLITE_IOERR_DELETE"
case ResultIOErrBlocked:
return "SQLITE_IOERR_BLOCKED"
case ResultIOErrNoMem:
return "SQLITE_IOERR_NOMEM"
case ResultIOErrAccess:
return "SQLITE_IOERR_ACCESS"
case ResultIOErrCheckReservedLock:
return "SQLITE_IOERR_CHECKRESERVEDLOCK"
case ResultIOErrLock:
return "SQLITE_IOERR_LOCK"
case ResultIOErrClose:
return "SQLITE_IOERR_CLOSE"
case ResultIOErrDirClose:
return "SQLITE_IOERR_DIR_CLOSE"
case ResultIOErrSHMOpen:
return "SQLITE_IOERR_SHMOPEN"
case ResultIOErrSHMSize:
return "SQLITE_IOERR_SHMSIZE"
case ResultIOErrSHMLock:
return "SQLITE_IOERR_SHMLOCK"
case ResultIOErrSHMMap:
return "SQLITE_IOERR_SHMMAP"
case ResultIOErrSeek:
return "SQLITE_IOERR_SEEK"
case ResultIOErrDeleteNoEnt:
return "SQLITE_IOERR_DELETE_NOENT"
case ResultIOErrMMap:
return "SQLITE_IOERR_MMAP"
case ResultIOErrGetTempPath:
return "SQLITE_IOERR_GETTEMPPATH"
case ResultIOErrConvPath:
return "SQLITE_IOERR_CONVPATH"
case ResultIOErrVNode:
return "SQLITE_IOERR_VNODE"
case ResultIOErrAuth:
return "SQLITE_IOERR_AUTH"
case ResultIOErrBeginAtomic:
return "SQLITE_IOERR_BEGIN_ATOMIC"
case ResultIOErrCommitAtomic:
return "SQLITE_IOERR_COMMIT_ATOMIC"
case ResultIOErrRollbackAtomic:
return "SQLITE_IOERR_ROLLBACK_ATOMIC"
case ResultLockedSharedCache:
return "SQLITE_LOCKED_SHAREDCACHE"
case ResultBusyRecovery:
return "SQLITE_BUSY_RECOVERY"
case ResultBusySnapshot:
return "SQLITE_BUSY_SNAPSHOT"
case ResultCantOpenNoTempDir:
return "SQLITE_CANTOPEN_NOTEMPDIR"
case ResultCantOpenIsDir:
return "SQLITE_CANTOPEN_ISDIR"
case ResultCantOpenFullPath:
return "SQLITE_CANTOPEN_FULLPATH"
case ResultCantOpenConvPath:
return "SQLITE_CANTOPEN_CONVPATH"
case ResultCorruptVTab:
return "SQLITE_CORRUPT_VTAB"
case ResultReadOnlyRecovery:
return "SQLITE_READONLY_RECOVERY"
case ResultReadOnlyCantLock:
return "SQLITE_READONLY_CANTLOCK"
case ResultReadOnlyRollback:
return "SQLITE_READONLY_ROLLBACK"
case ResultReadOnlyDBMoved:
return "SQLITE_READONLY_DBMOVED"
case ResultReadOnlyCantInit:
return "SQLITE_READONLY_CANTINIT"
case ResultReadOnlyDirectory:
return "SQLITE_READONLY_DIRECTORY"
case ResultAbortRollback:
return "SQLITE_ABORT_ROLLBACK"
case ResultConstraintCheck:
return "SQLITE_CONSTRAINT_CHECK"
case ResultConstraintCommitHook:
return "SQLITE_CONSTRAINT_COMMITHOOK"
case ResultConstraintForeignKey:
return "SQLITE_CONSTRAINT_FOREIGNKEY"
case ResultConstraintFunction:
return "SQLITE_CONSTRAINT_FUNCTION"
case ResultConstraintNotNull:
return "SQLITE_CONSTRAINT_NOTNULL"
case ResultConstraintPrimaryKey:
return "SQLITE_CONSTRAINT_PRIMARYKEY"
case ResultConstraintTrigger:
return "SQLITE_CONSTRAINT_TRIGGER"
case ResultConstraintUnique:
return "SQLITE_CONSTRAINT_UNIQUE"
case ResultConstraintVTab:
return "SQLITE_CONSTRAINT_VTAB"
case ResultConstraintRowID:
return "SQLITE_CONSTRAINT_ROWID"
case ResultNoticeRecoverWAL:
return "SQLITE_NOTICE_RECOVER_WAL"
case ResultNoticeRecoverRollback:
return "SQLITE_NOTICE_RECOVER_ROLLBACK"
case ResultWarningAutoIndex:
return "SQLITE_WARNING_AUTOINDEX"
case ResultAuthUser:
return "SQLITE_AUTH_USER"
default:
return fmt.Sprintf("ResultCode(%d)", int32(code))
}
}
// Message returns the English-language text that describes the result code.
func (code ResultCode) Message() string {
cstr := lib.Xsqlite3_errstr(nil, int32(code))
return libc.GoString(cstr)
}
type sqliteError struct {
code ResultCode
}
func reserr(res ResultCode) error {
if res.IsSuccess() {
return nil
}
return sqliteError{res}
}
func (e sqliteError) Error() string {
return e.code.Message()
}
// ErrCode returns the error's SQLite error code or ResultError if the error
// does not represent a SQLite error. ErrorCode returns ResultOK if and only if
// the error is nil.
func ErrCode(err error) ResultCode {
if err == nil {
return ResultOK
}
if e := new(sqliteError); errors.As(err, e) {
return e.code
}
return ResultError
}

915
vendor/zombiezen.com/go/sqlite/session.go generated vendored Normal file
View File

@@ -0,0 +1,915 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <ross@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
package sqlite
import (
"fmt"
"io"
"io/ioutil"
"runtime"
"sync"
"unsafe"
"modernc.org/libc"
"modernc.org/libc/sys/types"
lib "modernc.org/sqlite/lib"
)
// A Session tracks database changes made by a Conn.
// It is used to build changesets.
//
// For more details: https://www.sqlite.org/sessionintro.html
type Session struct {
tls *libc.TLS
ptr uintptr
}
// CreateSession creates a new session object.
// If db is "", then a default of "main" is used.
// It is the caller's responsibility to call Delete when the session is
// no longer needed.
//
// https://www.sqlite.org/session/sqlite3session_create.html
func (c *Conn) CreateSession(db string) (*Session, error) {
if c == nil {
return nil, fmt.Errorf("sqlite: create session: nil connection")
}
var cdb uintptr
if db == "" || db == "main" {
cdb = mainCString
} else {
var err error
cdb, err = libc.CString(db)
if err != nil {
return nil, fmt.Errorf("sqlite: create session: %w", err)
}
defer libc.Xfree(c.tls, cdb)
}
doublePtr, err := malloc(c.tls, ptrSize)
if err != nil {
return nil, fmt.Errorf("sqlite: create session: %w", err)
}
defer libc.Xfree(c.tls, doublePtr)
res := ResultCode(lib.Xsqlite3session_create(c.tls, c.conn, cdb, doublePtr))
if err := reserr(res); err != nil {
return nil, fmt.Errorf("sqlite: create session: %w", err)
}
s := &Session{
tls: c.tls,
ptr: *(*uintptr)(unsafe.Pointer(doublePtr)),
}
runtime.SetFinalizer(s, func(s *Session) {
if s.ptr != 0 {
panic("open *sqlite.Session garbage collected, call Delete method")
}
})
return s, nil
}
// Delete releases any resources associated with the session.
// It must be called before closing the Conn the session is attached to.
func (s *Session) Delete() {
if s.ptr == 0 {
panic("Session.Delete called twice on same session")
}
lib.Xsqlite3session_delete(s.tls, s.ptr)
s.ptr = 0
s.tls = nil
}
// Enable enables recording of changes after a previous call to Disable.
// New sessions start enabled.
//
// https://www.sqlite.org/session/sqlite3session_enable.html
func (s *Session) Enable() {
if s.ptr == 0 {
panic("Session.Enable called on deleted session")
}
lib.Xsqlite3session_enable(s.tls, s.ptr, 1)
}
// Disable disables recording of changes.
//
// https://www.sqlite.org/session/sqlite3session_enable.html
func (s *Session) Disable() {
if s.ptr == 0 {
panic("Session.Disable called on deleted session")
}
lib.Xsqlite3session_enable(s.tls, s.ptr, 0)
}
// Attach attaches a table to the session object.
// Changes made to the table will be tracked by the session.
// An empty tableName attaches all the tables in the database.
func (s *Session) Attach(tableName string) error {
if s.ptr == 0 {
return fmt.Errorf("sqlite: attach table %q to session: session deleted", tableName)
}
var ctable uintptr
if tableName != "" {
var err error
ctable, err = libc.CString(tableName)
if err != nil {
return fmt.Errorf("sqlite: attach table %q to session: %v", tableName, err)
}
defer libc.Xfree(s.tls, ctable)
}
res := ResultCode(lib.Xsqlite3session_attach(s.tls, s.ptr, ctable))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: attach table %q to session: %w", tableName, err)
}
return nil
}
// Diff appends the difference between two tables (srcDB and the session DB) to
// the session. The two tables must have the same name and schema.
//
// https://www.sqlite.org/session/sqlite3session_diff.html
func (s *Session) Diff(srcDB, tableName string) error {
if s.ptr == 0 {
return fmt.Errorf("sqlite: diff table %q: session deleted", tableName)
}
errMsgPtr, err := malloc(s.tls, ptrSize)
if err != nil {
return fmt.Errorf("sqlite: diff table %q: %v", tableName, err)
}
defer libc.Xfree(s.tls, errMsgPtr)
csrcDB, err := libc.CString(srcDB)
if err != nil {
return fmt.Errorf("sqlite: diff table %q: %v", tableName, err)
}
defer libc.Xfree(s.tls, csrcDB)
ctable, err := libc.CString(tableName)
if err != nil {
return fmt.Errorf("sqlite: diff table %q: %v", tableName, err)
}
defer libc.Xfree(s.tls, ctable)
res := ResultCode(lib.Xsqlite3session_diff(s.tls, s.ptr, csrcDB, ctable, errMsgPtr))
if err := reserr(res); err != nil {
cerrMsg := *(*uintptr)(unsafe.Pointer(errMsgPtr))
if cerrMsg == 0 {
return fmt.Errorf("sqlite: diff table %q: %w", tableName, err)
}
errMsg := libc.GoString(cerrMsg)
lib.Xsqlite3_free(s.tls, cerrMsg)
return fmt.Errorf("sqlite: diff table %q: %w (%s)", tableName, err, errMsg)
}
return nil
}
// WriteChangeset generates a changeset from a session.
//
// https://www.sqlite.org/session/sqlite3session_changeset.html
func (s *Session) WriteChangeset(w io.Writer) error {
if s.ptr == 0 {
return fmt.Errorf("sqlite: write session changeset: session deleted")
}
xOutput, pOut := registerStreamWriter(w)
defer unregisterStreamWriter(pOut)
res := ResultCode(lib.Xsqlite3session_changeset_strm(s.tls, s.ptr, xOutput, pOut))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: write session changeset: %w", err)
}
return nil
}
// WritePatchset generates a patchset from a session.
//
// https://www.sqlite.org/session/sqlite3session_patchset.html
func (s *Session) WritePatchset(w io.Writer) error {
if s.ptr == 0 {
return fmt.Errorf("sqlite: write session patchset: session deleted")
}
xOutput, pOut := registerStreamWriter(w)
defer unregisterStreamWriter(pOut)
res := ResultCode(lib.Xsqlite3session_patchset_strm(s.tls, s.ptr, xOutput, pOut))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: write session patchset: %w", err)
}
return nil
}
// ApplyChangeset applies a changeset to the database.
//
// If filterFn is not nil and the changeset includes changes for a table for
// which the function reports false, then the changes are ignored. If filterFn
// is nil, then all changes are applied.
//
// If a changeset will not apply cleanly, then conflictFn will be called to
// resolve the conflict. See https://www.sqlite.org/session/sqlite3changeset_apply.html
// for more details.
func (c *Conn) ApplyChangeset(r io.Reader, filterFn func(tableName string) bool, conflictFn ConflictHandler) error {
if c == nil {
return fmt.Errorf("sqlite: apply changeset: nil connection")
}
if conflictFn == nil {
return fmt.Errorf("sqlite: apply changeset: no conflict handler provided")
}
xInput, pIn := registerStreamReader(r)
defer unregisterStreamReader(pIn)
appliesIDMu.Lock()
pCtx := appliesIDs.next()
appliesIDMu.Unlock()
applies.Store(pCtx, applyFuncs{
tls: c.tls,
filterFn: filterFn,
conflictFn: conflictFn,
})
defer func() {
applies.Delete(pCtx)
appliesIDMu.Lock()
appliesIDs.reclaim(pCtx)
appliesIDMu.Unlock()
}()
// The following are conversions from function values to uintptrs. It assumes
// the memory representation described in https://golang.org/s/go11func.
//
// It does this by doing the following in order:
// 1) Create a Go struct containing a pointer to a pointer to the functions.
// It is assumed that the pointer to the function will be
// stored in the read-only data section and thus will not move.
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
// 3) Dereference the pointer to uintptr to obtain the function value as a
// uintptr. This is safe as long as function values are passed as pointers.
xFilter := uintptr(0)
if filterFn != nil {
xFilter = *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, uintptr) int32
}{changesetApplyFilter}))
}
xConflict := *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, int32, uintptr) int32
}{changesetApplyConflict}))
res := ResultCode(lib.Xsqlite3changeset_apply_strm(c.tls, c.conn, xInput, pIn, xFilter, xConflict, pCtx))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: apply changeset: %w", err)
}
return nil
}
type applyFuncs struct {
tls *libc.TLS
filterFn func(tableName string) bool
conflictFn ConflictHandler
}
var (
applies sync.Map // map[uintptr]applyFuncs
appliesIDMu sync.Mutex
appliesIDs idGen
)
func changesetApplyFilter(tls *libc.TLS, pCtx uintptr, zTab uintptr) int32 {
appliesValue, _ := applies.Load(pCtx)
funcs := appliesValue.(applyFuncs)
tableName := libc.GoString(zTab)
if funcs.filterFn(tableName) {
return 1
} else {
return 0
}
}
func changesetApplyConflict(tls *libc.TLS, pCtx uintptr, eConflict int32, p uintptr) int32 {
appliesValue, _ := applies.Load(pCtx)
funcs := appliesValue.(applyFuncs)
return int32(funcs.conflictFn(ConflictType(eConflict), &ChangesetIterator{
tls: tls,
ptr: p,
}))
}
// ApplyInverseChangeset applies the inverse of a changeset to the database.
// See ApplyChangeset and InvertChangeset for more details.
func (c *Conn) ApplyInverseChangeset(r io.Reader, filterFn func(tableName string) bool, conflictFn ConflictHandler) error {
if c == nil {
return fmt.Errorf("sqlite: apply changeset: nil connection")
}
pr, pw := io.Pipe()
go func() {
err := InvertChangeset(pw, pr)
pw.CloseWithError(err)
}()
err := c.ApplyChangeset(pr, filterFn, conflictFn)
io.Copy(ioutil.Discard, pr) // wait for invert goroutine to finish
return err
}
// InvertChangeset generates an inverted changeset. Applying an inverted
// changeset to a database reverses the effects of applying the uninverted
// changeset.
//
// This function currently assumes that the input is a valid changeset.
// If it is not, the results are undefined.
//
// https://www.sqlite.org/session/sqlite3changeset_invert.html
func InvertChangeset(w io.Writer, r io.Reader) error {
tls := libc.NewTLS()
defer tls.Close()
xInput, pIn := registerStreamReader(r)
defer unregisterStreamReader(pIn)
xOutput, pOut := registerStreamWriter(w)
defer unregisterStreamWriter(pOut)
res := ResultCode(lib.Xsqlite3changeset_invert_strm(tls, xInput, pIn, xOutput, pOut))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: invert changeset: %w", err)
}
return nil
}
// ChangesetIterator is an iterator over a changeset.
type ChangesetIterator struct {
tls *libc.TLS
ptr uintptr
ownTLS bool
pIn uintptr // if non-zero, then must be unregistered at Finalize
}
// NewChangesetIterator returns a new iterator over the contents of the
// changeset. The caller is responsible for calling Finalize on the returned
// iterator.
//
// https://www.sqlite.org/session/sqlite3changeset_start.html
func NewChangesetIterator(r io.Reader) (*ChangesetIterator, error) {
tls := libc.NewTLS()
xInput, pIn := registerStreamReader(r)
pp, err := malloc(tls, ptrSize)
if err != nil {
unregisterStreamReader(pIn)
tls.Close()
return nil, fmt.Errorf("sqlite: start changeset iterator: %v", err)
}
defer libc.Xfree(tls, pp)
res := ResultCode(lib.Xsqlite3changeset_start_strm(tls, pp, xInput, pIn))
if err := reserr(res); err != nil {
unregisterStreamReader(pIn)
tls.Close()
return nil, fmt.Errorf("sqlite: start changeset iterator: %w", err)
}
iter := &ChangesetIterator{
tls: tls,
ownTLS: true,
ptr: *(*uintptr)(unsafe.Pointer(pp)),
pIn: pIn,
}
runtime.SetFinalizer(iter, func(iter *ChangesetIterator) {
if iter.ptr != 0 {
panic("open *sqlite.ChangesetIterator garbage collected, call Finalize method")
}
})
return iter, nil
}
// Close releases any resources associated with the iterator created with
// NewChangesetIterator.
func (iter *ChangesetIterator) Close() error {
if iter.ptr == 0 {
return fmt.Errorf("sqlite: finalize changeset iterator: called twice on same iterator")
}
res := ResultCode(lib.Xsqlite3changeset_finalize(iter.tls, iter.ptr))
iter.ptr = 0
if iter.ownTLS {
iter.tls.Close()
}
iter.tls = nil
if iter.pIn != 0 {
unregisterStreamReader(iter.pIn)
}
iter.pIn = 0
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: finalize changeset iterator: %w", err)
}
return nil
}
// Next advances the iterator to the next change in the changeset.
// It is an error to call Next on an iterator passed to an ApplyChangeset
// conflict handler.
//
// https://www.sqlite.org/session/sqlite3changeset_next.html
func (iter *ChangesetIterator) Next() (rowReturned bool, err error) {
res := ResultCode(lib.Xsqlite3changeset_next(iter.tls, iter.ptr))
switch res {
case ResultRow:
return true, nil
case ResultDone:
return false, nil
default:
return false, fmt.Errorf("sqlite: iterate changeset: %w", reserr(res))
}
}
// ChangesetOperation holds information about a change in a changeset.
type ChangesetOperation struct {
// Type is one of OpInsert, OpDelete, or OpUpdate.
Type OpType
// TableName is the name of the table affected by the change.
TableName string
// NumColumns is the number of columns in the table affected by the change.
NumColumns int
// Indirect is true if the session object "indirect" flag was set when the
// change was made or the change was made by an SQL trigger or foreign key
// action instead of directly as a result of a users SQL statement.
Indirect bool
}
// Operation obtains the current operation from the iterator.
//
// https://www.sqlite.org/session/sqlite3changeset_op.html
func (iter *ChangesetIterator) Operation() (*ChangesetOperation, error) {
if iter.ptr == 0 {
return nil, fmt.Errorf("sqlite: changeset iterator operation: iterator finalized")
}
pzTab, err := malloc(iter.tls, ptrSize)
if err != nil {
return nil, fmt.Errorf("sqlite: changeset iterator operation: %v", err)
}
defer libc.Xfree(iter.tls, pzTab)
pnCol, err := malloc(iter.tls, types.Size_t(unsafe.Sizeof(int32(0))))
if err != nil {
return nil, fmt.Errorf("sqlite: changeset iterator operation: %v", err)
}
defer libc.Xfree(iter.tls, pnCol)
pOp, err := malloc(iter.tls, types.Size_t(unsafe.Sizeof(int32(0))))
if err != nil {
return nil, fmt.Errorf("sqlite: changeset iterator operation: %v", err)
}
defer libc.Xfree(iter.tls, pOp)
pbIndirect, err := malloc(iter.tls, types.Size_t(unsafe.Sizeof(int32(0))))
if err != nil {
return nil, fmt.Errorf("sqlite: changeset iterator operation: %v", err)
}
defer libc.Xfree(iter.tls, pbIndirect)
res := ResultCode(lib.Xsqlite3changeset_op(iter.tls, iter.ptr, pzTab, pnCol, pOp, pbIndirect))
if err := reserr(res); err != nil {
return nil, fmt.Errorf("sqlite: changeset iterator operation: %w", err)
}
return &ChangesetOperation{
Type: OpType(*(*int32)(unsafe.Pointer(pOp))),
TableName: libc.GoString(*(*uintptr)(unsafe.Pointer(pzTab))),
NumColumns: int(*(*int32)(unsafe.Pointer(pnCol))),
Indirect: *(*int32)(unsafe.Pointer(pbIndirect)) != 0,
}, nil
}
// Old obtains the old row value from an iterator. Column indices start at 0.
// The returned value is valid until the iterator is finalized.
//
// https://www.sqlite.org/session/sqlite3changeset_old.html
func (iter *ChangesetIterator) Old(col int) (Value, error) {
if iter.ptr == 0 {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: iterator finalized")
}
ppValue, err := malloc(iter.tls, ptrSize)
if err != nil {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: %v", err)
}
defer libc.Xfree(iter.tls, ppValue)
res := ResultCode(lib.Xsqlite3changeset_old(iter.tls, iter.ptr, int32(col), ppValue))
if err := reserr(res); err != nil {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: %w", err)
}
return Value{
tls: iter.tls,
ptrOrType: *(*uintptr)(unsafe.Pointer(ppValue)),
}, nil
}
// New obtains the new row value from an iterator. Column indices start at 0.
// The returned value is valid until the iterator is finalized.
//
// https://www.sqlite.org/session/sqlite3changeset_new.html
func (iter *ChangesetIterator) New(col int) (Value, error) {
if iter.ptr == 0 {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: iterator finalized")
}
ppValue, err := malloc(iter.tls, ptrSize)
if err != nil {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: %v", err)
}
defer libc.Xfree(iter.tls, ppValue)
res := ResultCode(lib.Xsqlite3changeset_new(iter.tls, iter.ptr, int32(col), ppValue))
if err := reserr(res); err != nil {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: %w", err)
}
return Value{
tls: iter.tls,
ptrOrType: *(*uintptr)(unsafe.Pointer(ppValue)),
}, nil
}
// ConflictValue obtains the conflicting row value from an iterator.
// Column indices start at 0. The returned value is valid until the iterator is
// finalized.
//
// https://www.sqlite.org/session/sqlite3changeset_conflict.html
func (iter *ChangesetIterator) ConflictValue(col int) (Value, error) {
if iter.ptr == 0 {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: iterator finalized")
}
ppValue, err := malloc(iter.tls, ptrSize)
if err != nil {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: %v", err)
}
defer libc.Xfree(iter.tls, ppValue)
res := ResultCode(lib.Xsqlite3changeset_conflict(iter.tls, iter.ptr, int32(col), ppValue))
if err := reserr(res); err != nil {
return Value{}, fmt.Errorf("sqlite: get changeset iterator value: %w", err)
}
return Value{
tls: iter.tls,
ptrOrType: *(*uintptr)(unsafe.Pointer(ppValue)),
}, nil
}
// ForeignKeyConflicts returns the number of foreign key constraint violations.
//
// https://www.sqlite.org/session/sqlite3changeset_fk_conflicts.html
func (iter *ChangesetIterator) ForeignKeyConflicts() (int, error) {
pnOut, err := malloc(iter.tls, types.Size_t(unsafe.Sizeof(int32(0))))
if err != nil {
return 0, fmt.Errorf("sqlite: get number of foreign key conflicts: %v", err)
}
defer libc.Xfree(iter.tls, pnOut)
res := ResultCode(lib.Xsqlite3changeset_fk_conflicts(iter.tls, iter.ptr, pnOut))
if err := reserr(res); err != nil {
return 0, fmt.Errorf("sqlite: get number of foreign key conflicts: %w", err)
}
return int(*(*int32)(unsafe.Pointer(pnOut))), nil
}
// PrimaryKey returns a map of columns that make up the primary key.
//
// https://www.sqlite.org/session/sqlite3changeset_pk.html
func (iter *ChangesetIterator) PrimaryKey() ([]bool, error) {
pabPK, err := malloc(iter.tls, ptrSize)
if err != nil {
return nil, fmt.Errorf("sqlite: get primary key columns: %v", err)
}
defer libc.Xfree(iter.tls, pabPK)
pnCol, err := malloc(iter.tls, types.Size_t(unsafe.Sizeof(int32(0))))
if err != nil {
return nil, fmt.Errorf("sqlite: get primary key columns: %v", err)
}
defer libc.Xfree(iter.tls, pnCol)
res := ResultCode(lib.Xsqlite3changeset_pk(iter.tls, iter.ptr, pabPK, pnCol))
if err := reserr(res); err != nil {
return nil, fmt.Errorf("sqlite: get primary key columns: %w", err)
}
c := libc.GoBytes(*(*uintptr)(unsafe.Pointer(pabPK)), int(*(*int32)(unsafe.Pointer(pnCol))))
cols := make([]bool, len(c))
for i := range cols {
cols[i] = c[i] != 0
}
return cols, nil
}
// ConcatChangesets concatenates two changesets into a single changeset.
//
// https://www.sqlite.org/session/sqlite3changeset_concat.html
func ConcatChangesets(w io.Writer, changeset1, changeset2 io.Reader) error {
tls := libc.NewTLS()
defer tls.Close()
xInput1, pIn1 := registerStreamReader(changeset1)
defer unregisterStreamReader(pIn1)
xInput2, pIn2 := registerStreamReader(changeset2)
defer unregisterStreamReader(pIn2)
xOutput, pOut := registerStreamWriter(w)
defer unregisterStreamWriter(pOut)
res := ResultCode(lib.Xsqlite3changeset_concat_strm(tls, xInput1, pIn1, xInput2, pIn2, xOutput, pOut))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: concatenate changesets: %w", err)
}
return nil
}
// A Changegroup is an object used to combine two or more changesets or
// patchesets. The zero value is an empty changegroup.
//
// https://www.sqlite.org/session/changegroup.html
type Changegroup struct {
tls *libc.TLS
ptr uintptr
}
// NewChangegroup returns a new changegroup. The caller is responsible for
// calling Clear on the returned changegroup.
//
// https://www.sqlite.org/session/sqlite3changegroup_new.html
//
// Deprecated: Use new(sqlite.Changegroup) instead, which does not require
// calling Clear until Add is called.
func NewChangegroup() (*Changegroup, error) {
cg := new(Changegroup)
if err := cg.init(); err != nil {
return nil, fmt.Errorf("sqlite: %w", err)
}
return cg, nil
}
func (cg *Changegroup) init() error {
if cg.tls == nil {
cg.tls = libc.NewTLS()
}
if cg.ptr == 0 {
pp, err := malloc(cg.tls, ptrSize)
if err != nil {
cg.tls.Close()
cg.tls = nil
return fmt.Errorf("init changegroup: %v", err)
}
defer libc.Xfree(cg.tls, pp)
res := ResultCode(lib.Xsqlite3changegroup_new(cg.tls, pp))
if err := reserr(res); err != nil {
cg.tls.Close()
cg.tls = nil
return fmt.Errorf("init changegroup: %w", err)
}
cg.ptr = *(*uintptr)(unsafe.Pointer(pp))
}
return nil
}
// Clear empties the changegroup and releases any resources associated with
// the changegroup. This method may be called multiple times.
func (cg *Changegroup) Clear() {
if cg == nil {
return
}
if cg.ptr != 0 {
lib.Xsqlite3changegroup_delete(cg.tls, cg.ptr)
cg.ptr = 0
}
if cg.tls != nil {
cg.tls.Close()
cg.tls = nil
}
}
// Add adds all changes within the changeset (or patchset) read from r to
// the changegroup. Once Add has been called, it is the caller's responsibility
// to call Clear.
//
// https://www.sqlite.org/session/sqlite3changegroup_add.html
func (cg *Changegroup) Add(r io.Reader) error {
if err := cg.init(); err != nil {
return fmt.Errorf("sqlite: add to changegroup: %w", err)
}
xInput, pIn := registerStreamReader(r)
defer unregisterStreamReader(pIn)
res := ResultCode(lib.Xsqlite3changegroup_add_strm(cg.tls, cg.ptr, xInput, pIn))
if err := reserr(res); err != nil {
return fmt.Errorf("sqlite: add to changegroup: %w", err)
}
return nil
}
// WriteTo writes the current contents of the changegroup to w.
//
// https://www.sqlite.org/session/sqlite3changegroup_output.html
func (cg *Changegroup) WriteTo(w io.Writer) (n int64, err error) {
// We want to allow uninitialized changegroups to write output without
// forcing the caller to call Clear. In theses cases, we initialize a new
// changegroup that lasts for the length of the WriteTo call.
if cg == nil {
cg = new(Changegroup)
}
if cg.ptr == 0 {
defer cg.Clear()
}
if err := cg.init(); err != nil {
return 0, fmt.Errorf("sqlite: write changegroup: %w", err)
}
wc := &writeCounter{Writer: w}
xOutput, pOut := registerStreamWriter(wc)
defer unregisterStreamWriter(pOut)
res := ResultCode(lib.Xsqlite3changegroup_output_strm(cg.tls, cg.ptr, xOutput, pOut))
if err := reserr(res); err != nil {
return wc.n, fmt.Errorf("sqlite: write changegroup: %w", err)
}
return wc.n, nil
}
// A ConflictHandler function determines the action to take to resolve a
// conflict while applying a changeset.
//
// https://www.sqlite.org/session/sqlite3changeset_apply.html
type ConflictHandler func(ConflictType, *ChangesetIterator) ConflictAction
// ConflictType is an enumeration of changeset conflict types.
//
// https://www.sqlite.org/session/c_changeset_conflict.html
type ConflictType int32
// Conflict types.
const (
ChangesetData = ConflictType(lib.SQLITE_CHANGESET_DATA)
ChangesetNotFound = ConflictType(lib.SQLITE_CHANGESET_NOTFOUND)
ChangesetConflict = ConflictType(lib.SQLITE_CHANGESET_CONFLICT)
ChangesetConstraint = ConflictType(lib.SQLITE_CHANGESET_CONSTRAINT)
ChangesetForeignKey = ConflictType(lib.SQLITE_CHANGESET_FOREIGN_KEY)
)
// String returns the C constant name of the conflict type.
func (code ConflictType) String() string {
switch code {
case ChangesetData:
return "SQLITE_CHANGESET_DATA"
case ChangesetNotFound:
return "SQLITE_CHANGESET_NOTFOUND"
case ChangesetConflict:
return "SQLITE_CHANGESET_CONFLICT"
case ChangesetConstraint:
return "SQLITE_CHANGESET_CONSTRAINT"
case ChangesetForeignKey:
return "SQLITE_CHANGESET_FOREIGN_KEY"
default:
return fmt.Sprintf("ConflictType(%d)", int32(code))
}
}
// ConflictAction is an enumeration of actions that can be taken in response to
// a changeset conflict. The zero value is ChangesetOmit.
//
// https://www.sqlite.org/session/c_changeset_abort.html
type ConflictAction int32
// Conflict actions.
const (
// ChangesetOmit signals that no special action should be taken. The change
// that caused the conflict will not be applied. The session module continues
// to the next change in the changeset.
ChangesetOmit = ConflictAction(lib.SQLITE_CHANGESET_OMIT)
// ChangesetAbort signals that any changes applied so far should be rolled
// back and the call to ApplyChangeset returns an error whose code
// is ResultAbort.
ChangesetAbort = ConflictAction(lib.SQLITE_CHANGESET_ABORT)
// ChangesetReplace signals a different action depending on the conflict type.
//
// If the conflict type is ChangesetData, ChangesetReplace signals the
// conflicting row should be updated or deleted.
//
// If the conflict type is ChangesetConflict, then ChangesetReplace signals
// that the conflicting row should be removed from the database and a second
// attempt to apply the change should be made. If this second attempt fails,
// the original row is restored to the database before continuing.
//
// For all other conflict types, returning ChangesetReplace will cause
// ApplyChangeset to roll back any changes applied so far and return an error
// whose code is ResultMisuse.
ChangesetReplace = ConflictAction(lib.SQLITE_CHANGESET_REPLACE)
)
// String returns the C constant name of the conflict action.
func (code ConflictAction) String() string {
switch code {
case ChangesetOmit:
return "SQLITE_CHANGESET_OMIT"
case ChangesetAbort:
return "SQLITE_CHANGESET_ABORT"
case ChangesetReplace:
return "SQLITE_CHANGESET_REPLACE"
default:
return fmt.Sprintf("ConflictAction(%d)", int32(code))
}
}
var (
streamReaders sync.Map // map[uintptr]io.Reader
streamReadersIDMu sync.Mutex
streamReadersIDs idGen
)
func registerStreamReader(r io.Reader) (xInput, pIn uintptr) {
// The following is a conversion from function value to uintptr. It assumes
// the memory representation described in https://golang.org/s/go11func.
//
// It does this by doing the following in order:
// 1) Create a Go struct containing a pointer to a pointer to sessionStreamInput.
// It is assumed that the pointer to sessionStreamInput will be
// stored in the read-only data section and thus will not move.
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
// 3) Dereference the pointer to uintptr to obtain the function value as a
// uintptr. This is safe as long as function values are passed as pointers.
xInput = *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, uintptr, uintptr) int32
}{sessionStreamInput}))
streamReadersIDMu.Lock()
pIn = streamReadersIDs.next()
streamReadersIDMu.Unlock()
streamReaders.Store(pIn, r)
return
}
func unregisterStreamReader(pIn uintptr) {
streamReaders.Delete(pIn)
streamReadersIDMu.Lock()
streamReadersIDs.reclaim(pIn)
streamReadersIDMu.Unlock()
}
// sessionStreamInput is the callback returned by registerSessionReader used
// for the session streaming APIs.
// https://www.sqlite.org/session/sqlite3changegroup_add_strm.html
func sessionStreamInput(tls *libc.TLS, pIn uintptr, pData uintptr, pnData uintptr) int32 {
rval, _ := streamReaders.Load(pIn)
r, _ := rval.(io.Reader)
if r == nil {
return lib.SQLITE_MISUSE
}
n := int(*(*int32)(unsafe.Pointer(pnData)))
n, err := r.Read(libc.GoBytes(pData, n))
*(*int32)(unsafe.Pointer(pnData)) = int32(n)
if n == 0 && err != io.EOF {
// Readers should not return n == 0 && err == nil. However, as per io.Reader
// docs, we can't treat it as an EOF condition.
return lib.SQLITE_IOERR_READ
}
return lib.SQLITE_OK
}
var (
streamWriters sync.Map // map[uintptr]io.Writer
streamWritersIDMu sync.Mutex
streamWritersIDs idGen
)
func registerStreamWriter(w io.Writer) (xOutput, pOut uintptr) {
// The following is a conversion from function value to uintptr. It assumes
// the memory representation described in https://golang.org/s/go11func.
//
// It does this by doing the following in order:
// 1) Create a Go struct containing a pointer to a pointer to sessionStreamOutput.
// It is assumed that the pointer to sessionStreamOutput will be
// stored in the read-only data section and thus will not move.
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
// 3) Dereference the pointer to uintptr to obtain the function value as a
// uintptr. This is safe as long as function values are passed as pointers.
xOutput = *(*uintptr)(unsafe.Pointer(&struct {
f func(*libc.TLS, uintptr, uintptr, int32) int32
}{sessionStreamOutput}))
streamWritersIDMu.Lock()
pOut = streamWritersIDs.next()
streamWritersIDMu.Unlock()
streamWriters.Store(pOut, w)
return
}
func unregisterStreamWriter(pOut uintptr) {
streamWriters.Delete(pOut)
streamWritersIDMu.Lock()
streamWritersIDs.reclaim(pOut)
streamWritersIDMu.Unlock()
}
// sessionStreamOutput is the callback returned by registerSessionWriter used
// for the session streaming APIs.
// https://www.sqlite.org/session/sqlite3changegroup_add_strm.html
func sessionStreamOutput(tls *libc.TLS, pOut uintptr, pData uintptr, nData int32) int32 {
wval, _ := streamWriters.Load(pOut)
w, _ := wval.(io.Writer)
if w == nil {
return lib.SQLITE_MISUSE
}
_, err := w.Write(libc.GoBytes(pData, int(nData)))
if err != nil {
return lib.SQLITE_IOERR_WRITE
}
return lib.SQLITE_OK
}
type writeCounter struct {
io.Writer
n int64
}
func (wc *writeCounter) Write(p []byte) (int, error) {
n, err := wc.Writer.Write(p)
wc.n += int64(n)
return n, err
}

1260
vendor/zombiezen.com/go/sqlite/sqlite.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

353
vendor/zombiezen.com/go/sqlite/sqlitex/exec.go generated vendored Normal file
View File

@@ -0,0 +1,353 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <rosss@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
// Package sqlitex provides utilities for working with SQLite.
package sqlitex
import (
"fmt"
"io"
"reflect"
"strings"
"zombiezen.com/go/sqlite"
"zombiezen.com/go/sqlite/fs"
)
// ExecOptions is the set of optional arguments executing a statement.
type ExecOptions struct {
// Args is the set of positional arguments to bind to the statement. The first
// element in the slice is ?1. See https://sqlite.org/lang_expr.html for more
// details.
Args []interface{}
// Named is the set of named arguments to bind to the statement. Keys must
// start with ':', '@', or '$'. See https://sqlite.org/lang_expr.html for more
// details.
Named map[string]interface{}
// ResultFunc is called for each result row. If ResultFunc returns an error
// then iteration ceases and Exec returns the error value.
ResultFunc func(stmt *sqlite.Stmt) error
}
// Exec executes an SQLite query.
//
// For each result row, the resultFn is called.
// Result values can be read by resultFn using stmt.Column* methods.
// If resultFn returns an error then iteration ceases and Exec returns
// the error value.
//
// Any args provided to Exec are bound to numbered parameters of the
// query using the Stmt Bind* methods. Basic reflection on args is used
// to map:
//
// integers to BindInt64
// floats to BindFloat
// []byte to BindBytes
// string to BindText
// bool to BindBool
//
// All other kinds are printed using fmt.Sprintf("%v", v) and passed
// to BindText.
//
// Exec is implemented using the Stmt prepare mechanism which allows
// better interactions with Go's type system and avoids pitfalls of
// passing a Go closure to cgo.
//
// As Exec is implemented using Conn.Prepare, subsequent calls to Exec
// with the same statement will reuse the cached statement object.
//
// Typical use:
//
// conn := dbpool.Get()
// defer dbpool.Put(conn)
//
// if err := sqlitex.Exec(conn, "INSERT INTO t (a, b, c, d) VALUES (?, ?, ?, ?);", nil, "a1", 1, 42, 1); err != nil {
// // handle err
// }
//
// var a []string
// var b []int64
// fn := func(stmt *sqlite.Stmt) error {
// a = append(a, stmt.ColumnText(0))
// b = append(b, stmt.ColumnInt64(1))
// return nil
// }
// err := sqlutil.Exec(conn, "SELECT a, b FROM t WHERE c = ? AND d = ?;", fn, 42, 1)
// if err != nil {
// // handle err
// }
func Exec(conn *sqlite.Conn, query string, resultFn func(stmt *sqlite.Stmt) error, args ...interface{}) error {
stmt, err := conn.Prepare(query)
if err != nil {
return annotateErr(err)
}
err = exec(stmt, &ExecOptions{
Args: args,
ResultFunc: resultFn,
})
resetErr := stmt.Reset()
if err == nil {
err = resetErr
}
return err
}
// ExecFS executes the single statement in the given SQL file.
// ExecFS is implemented using Conn.Prepare, so subsequent calls to ExecFS with the
// same statement will reuse the cached statement object.
func ExecFS(conn *sqlite.Conn, fsys fs.FS, filename string, opts *ExecOptions) error {
query, err := readString(fsys, filename)
if err != nil {
return fmt.Errorf("exec: %w", err)
}
stmt, err := conn.Prepare(strings.TrimSpace(query))
if err != nil {
return fmt.Errorf("exec %s: %w", filename, err)
}
err = exec(stmt, opts)
resetErr := stmt.Reset()
if err != nil {
// Don't strip the error query: we already do this inside exec.
return fmt.Errorf("exec %s: %w", filename, err)
}
if resetErr != nil {
return fmt.Errorf("exec %s: %w", filename, err)
}
return nil
}
// ExecTransient executes an SQLite query without caching the
// underlying query.
// The interface is exactly the same as Exec.
//
// It is the spiritual equivalent of sqlite3_exec.
func ExecTransient(conn *sqlite.Conn, query string, resultFn func(stmt *sqlite.Stmt) error, args ...interface{}) (err error) {
var stmt *sqlite.Stmt
var trailingBytes int
stmt, trailingBytes, err = conn.PrepareTransient(query)
if err != nil {
return annotateErr(err)
}
defer func() {
ferr := stmt.Finalize()
if err == nil {
err = ferr
}
}()
if trailingBytes != 0 {
return fmt.Errorf("sqlitex.Exec: query %q has trailing bytes", query)
}
return exec(stmt, &ExecOptions{
Args: args,
ResultFunc: resultFn,
})
}
// ExecTransientFS executes the single statement in the given SQL file without
// caching the underlying query.
func ExecTransientFS(conn *sqlite.Conn, fsys fs.FS, filename string, opts *ExecOptions) error {
query, err := readString(fsys, filename)
if err != nil {
return fmt.Errorf("exec: %w", err)
}
stmt, _, err := conn.PrepareTransient(strings.TrimSpace(query))
if err != nil {
return fmt.Errorf("exec %s: %w", filename, err)
}
defer stmt.Finalize()
err = exec(stmt, opts)
resetErr := stmt.Reset()
if err != nil {
// Don't strip the error query: we already do this inside exec.
return fmt.Errorf("exec %s: %w", filename, err)
}
if resetErr != nil {
return fmt.Errorf("exec %s: %w", filename, err)
}
return nil
}
// PrepareTransientFS prepares an SQL statement from a file that is not cached by
// the Conn. Subsequent calls with the same query will create new Stmts.
// The caller is responsible for calling Finalize on the returned Stmt when the
// Stmt is no longer needed.
func PrepareTransientFS(conn *sqlite.Conn, fsys fs.FS, filename string) (*sqlite.Stmt, error) {
query, err := readString(fsys, filename)
if err != nil {
return nil, fmt.Errorf("prepare: %w", err)
}
stmt, _, err := conn.PrepareTransient(strings.TrimSpace(query))
if err != nil {
return nil, fmt.Errorf("prepare %s: %w", filename, err)
}
return stmt, nil
}
func exec(stmt *sqlite.Stmt, opts *ExecOptions) (err error) {
if opts != nil {
for i, arg := range opts.Args {
setArg(stmt, i+1, reflect.ValueOf(arg))
}
if err := setNamed(stmt, opts.Named); err != nil {
return err
}
}
for {
hasRow, err := stmt.Step()
if err != nil {
return err
}
if !hasRow {
break
}
if opts != nil && opts.ResultFunc != nil {
if err := opts.ResultFunc(stmt); err != nil {
return err
}
}
}
return nil
}
func setArg(stmt *sqlite.Stmt, i int, v reflect.Value) {
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
stmt.BindInt64(i, v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
stmt.BindInt64(i, int64(v.Uint()))
case reflect.Float32, reflect.Float64:
stmt.BindFloat(i, v.Float())
case reflect.String:
stmt.BindText(i, v.String())
case reflect.Bool:
stmt.BindBool(i, v.Bool())
case reflect.Invalid:
stmt.BindNull(i)
default:
if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 {
stmt.BindBytes(i, v.Bytes())
} else {
stmt.BindText(i, fmt.Sprint(v.Interface()))
}
}
}
func setNamed(stmt *sqlite.Stmt, args map[string]interface{}) error {
if len(args) == 0 {
return nil
}
for i, count := 1, stmt.BindParamCount(); i <= count; i++ {
name := stmt.BindParamName(i)
if name == "" {
continue
}
arg, present := args[name]
if !present {
return fmt.Errorf("missing parameter %s", name)
}
setArg(stmt, i, reflect.ValueOf(arg))
}
return nil
}
func annotateErr(err error) error {
// TODO(maybe)
// if err, isError := err.(sqlite.Error); isError {
// if err.Loc == "" {
// err.Loc = "Exec"
// } else {
// err.Loc = "Exec: " + err.Loc
// }
// return err
// }
return fmt.Errorf("sqlutil.Exec: %w", err)
}
// ExecScript executes a script of SQL statements.
//
// The script is wrapped in a SAVEPOINT transaction,
// which is rolled back on any error.
func ExecScript(conn *sqlite.Conn, queries string) (err error) {
defer Save(conn)(&err)
for {
queries = strings.TrimSpace(queries)
if queries == "" {
break
}
var stmt *sqlite.Stmt
var trailingBytes int
stmt, trailingBytes, err = conn.PrepareTransient(queries)
if err != nil {
return err
}
usedBytes := len(queries) - trailingBytes
queries = queries[usedBytes:]
_, err := stmt.Step()
stmt.Finalize()
if err != nil {
return err
}
}
return nil
}
// ExecScriptFS executes a script of SQL statements from a file.
//
// The script is wrapped in a SAVEPOINT transaction, which is rolled back on
// any error.
func ExecScriptFS(conn *sqlite.Conn, fsys fs.FS, filename string, opts *ExecOptions) (err error) {
queries, err := readString(fsys, filename)
if err != nil {
return fmt.Errorf("exec: %w", err)
}
defer Save(conn)(&err)
for {
queries = strings.TrimSpace(queries)
if queries == "" {
return nil
}
stmt, trailingBytes, err := conn.PrepareTransient(queries)
if err != nil {
return fmt.Errorf("exec %s: %w", filename, err)
}
usedBytes := len(queries) - trailingBytes
queries = queries[usedBytes:]
err = exec(stmt, opts)
stmt.Finalize()
if err != nil {
return fmt.Errorf("exec %s: %w", filename, err)
}
}
}
func readString(fsys fs.FS, filename string) (string, error) {
f, err := fsys.Open(filename)
if err != nil {
return "", err
}
content := new(strings.Builder)
_, err = io.Copy(content, f)
f.Close()
if err != nil {
return "", fmt.Errorf("%s: %w", filename, err)
}
return content.String(), nil
}

262
vendor/zombiezen.com/go/sqlite/sqlitex/pool.go generated vendored Normal file
View File

@@ -0,0 +1,262 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <rosss@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
package sqlitex
import (
"context"
"fmt"
"sync"
"zombiezen.com/go/sqlite"
)
// Pool is a pool of SQLite connections.
//
// It is safe for use by multiple goroutines concurrently.
//
// Typically, a goroutine that needs to use an SQLite *Conn
// Gets it from the pool and defers its return:
//
// conn := dbpool.Get(nil)
// defer dbpool.Put(conn)
//
// As Get may block, a context can be used to return if a task
// is cancelled. In this case the Conn returned will be nil:
//
// conn := dbpool.Get(ctx)
// if conn == nil {
// return context.Canceled
// }
// defer dbpool.Put(conn)
type Pool struct {
free chan *sqlite.Conn
closed chan struct{}
mu sync.Mutex
all map[*sqlite.Conn]context.CancelFunc
}
// Open opens a fixed-size pool of SQLite connections.
// A flags value of 0 defaults to:
//
// SQLITE_OPEN_READWRITE
// SQLITE_OPEN_CREATE
// SQLITE_OPEN_WAL
// SQLITE_OPEN_URI
// SQLITE_OPEN_NOMUTEX
func Open(uri string, flags sqlite.OpenFlags, poolSize int) (pool *Pool, err error) {
if uri == ":memory:" {
return nil, strerror{msg: `sqlite: ":memory:" does not work with multiple connections, use "file::memory:?mode=memory"`}
}
p := &Pool{
free: make(chan *sqlite.Conn, poolSize),
closed: make(chan struct{}),
}
defer func() {
// If an error occurred, call Close outside the lock so this doesn't deadlock.
if err != nil {
p.Close()
}
}()
if flags == 0 {
flags = sqlite.OpenReadWrite |
sqlite.OpenCreate |
sqlite.OpenWAL |
sqlite.OpenURI |
sqlite.OpenNoMutex
}
// TODO(maybe)
// sqlitex_pool is also defined in package sqlite
// const sqlitex_pool = sqlite.OpenFlags(0x01000000)
// flags |= sqlitex_pool
p.all = make(map[*sqlite.Conn]context.CancelFunc)
for i := 0; i < poolSize; i++ {
conn, err := sqlite.OpenConn(uri, flags)
if err != nil {
return nil, err
}
p.free <- conn
p.all[conn] = func() {}
}
return p, nil
}
// Get returns an SQLite connection from the Pool.
//
// If no Conn is available, Get will block until at least one Conn is returned
// with Put, or until either the Pool is closed or the context is canceled. If
// no Conn can be obtained, nil is returned.
//
// The provided context is also used to control the execution lifetime of the
// connection. See Conn.SetInterrupt for details.
//
// Applications must ensure that all non-nil Conns returned from Get are
// returned to the same Pool with Put.
//
// Although ctx historically may be nil, this is not a recommended design
// pattern.
func (p *Pool) Get(ctx context.Context) *sqlite.Conn {
if ctx == nil {
ctx = context.Background()
}
select {
case conn := <-p.free:
ctx, cancel := context.WithCancel(ctx)
// TODO(maybe)
// conn.SetTracer(&tracer{ctx: ctx})
conn.SetInterrupt(ctx.Done())
p.mu.Lock()
defer p.mu.Unlock()
p.all[conn] = cancel
return conn
case <-ctx.Done():
case <-p.closed:
}
return nil
}
// Put puts an SQLite connection back into the Pool.
//
// Put will panic if the conn was not originally created by p. Put(nil) is a
// no-op.
//
// Applications must ensure that all non-nil Conns returned from Get are
// returned to the same Pool with Put.
func (p *Pool) Put(conn *sqlite.Conn) {
if conn == nil {
// See https://github.com/zombiezen/go-sqlite/issues/17
return
}
query := conn.CheckReset()
if query != "" {
panic(fmt.Sprintf(
"connection returned to pool has active statement: %q",
query))
}
p.mu.Lock()
cancel, found := p.all[conn]
if found {
p.all[conn] = func() {}
}
p.mu.Unlock()
if !found {
panic("sqlite.Pool.Put: connection not created by this pool")
}
conn.SetInterrupt(nil)
cancel()
p.free <- conn
}
// Close interrupts and closes all the connections in the Pool,
// blocking until all connections are returned to the Pool.
func (p *Pool) Close() (err error) {
close(p.closed)
p.mu.Lock()
n := len(p.all)
cancelList := make([]context.CancelFunc, 0, n)
for conn, cancel := range p.all {
cancelList = append(cancelList, cancel)
p.all[conn] = func() {}
}
p.mu.Unlock()
for _, cancel := range cancelList {
cancel()
}
for closed := 0; closed < n; closed++ {
conn := <-p.free
if err2 := conn.Close(); err == nil {
err = err2
}
}
return
}
type strerror struct {
msg string
}
func (err strerror) Error() string { return err.msg }
// TODO(maybe)
// type tracer struct {
// ctx context.Context
// ctxStack []context.Context
// taskStack []*trace.Task
// }
// func (t *tracer) pctx() context.Context {
// if len(t.ctxStack) != 0 {
// return t.ctxStack[len(t.ctxStack)-1]
// }
// return t.ctx
// }
// func (t *tracer) Push(name string) {
// ctx, task := trace.NewTask(t.pctx(), name)
// t.ctxStack = append(t.ctxStack, ctx)
// t.taskStack = append(t.taskStack, task)
// }
// func (t *tracer) Pop() {
// t.taskStack[len(t.taskStack)-1].End()
// t.taskStack = t.taskStack[:len(t.taskStack)-1]
// t.ctxStack = t.ctxStack[:len(t.ctxStack)-1]
// }
// func (t *tracer) NewTask(name string) sqlite.TracerTask {
// ctx, task := trace.NewTask(t.pctx(), name)
// return &tracerTask{
// ctx: ctx,
// task: task,
// }
// }
// type tracerTask struct {
// ctx context.Context
// task *trace.Task
// region *trace.Region
// }
// func (t *tracerTask) StartRegion(regionType string) {
// if t.region != nil {
// panic("sqlitex.tracerTask.StartRegion: already in region")
// }
// t.region = trace.StartRegion(t.ctx, regionType)
// }
// func (t *tracerTask) EndRegion() {
// t.region.End()
// t.region = nil
// }
// func (t *tracerTask) End() {
// t.task.End()
// }

82
vendor/zombiezen.com/go/sqlite/sqlitex/query.go generated vendored Normal file
View File

@@ -0,0 +1,82 @@
// Copyright 2021 Ross Light
// SPDX-License-Identifier: ISC
package sqlitex
import (
"errors"
"zombiezen.com/go/sqlite"
)
var errNoResults = errors.New("sqlite: statement has no results")
var errMultipleResults = errors.New("sqlite: statement has multiple result rows")
func resultSetup(stmt *sqlite.Stmt) error {
hasRow, err := stmt.Step()
if err != nil {
stmt.Reset()
return err
}
if !hasRow {
stmt.Reset()
return errNoResults
}
return nil
}
func resultTeardown(stmt *sqlite.Stmt) error {
hasRow, err := stmt.Step()
if err != nil {
stmt.Reset()
return err
}
if hasRow {
stmt.Reset()
return errMultipleResults
}
return stmt.Reset()
}
func ResultBool(stmt *sqlite.Stmt) (bool, error) {
res, err := ResultInt64(stmt)
return res != 0, err
}
func ResultInt(stmt *sqlite.Stmt) (int, error) {
res, err := ResultInt64(stmt)
return int(res), err
}
func ResultInt64(stmt *sqlite.Stmt) (int64, error) {
if err := resultSetup(stmt); err != nil {
return 0, err
}
res := stmt.ColumnInt64(0)
if err := resultTeardown(stmt); err != nil {
return 0, err
}
return res, nil
}
func ResultText(stmt *sqlite.Stmt) (string, error) {
if err := resultSetup(stmt); err != nil {
return "", err
}
res := stmt.ColumnText(0)
if err := resultTeardown(stmt); err != nil {
return "", err
}
return res, nil
}
func ResultFloat(stmt *sqlite.Stmt) (float64, error) {
if err := resultSetup(stmt); err != nil {
return 0, err
}
res := stmt.ColumnFloat(0)
if err := resultTeardown(stmt); err != nil {
return 0, err
}
return res, nil
}

51
vendor/zombiezen.com/go/sqlite/sqlitex/rand_id.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
// Copyright (c) 2019 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <rosss@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
package sqlitex
import (
"crypto/rand"
"fmt"
"math/big"
"zombiezen.com/go/sqlite"
)
// InsertRandID executes stmt with a random value in the range [min, max) for $param.
func InsertRandID(stmt *sqlite.Stmt, param string, min, max int64) (int64, error) {
if min < 0 {
return 0, fmt.Errorf("sqlitex.InsertRandID: min (%d) is negative", min)
}
for i := 0; ; i++ {
v, err := rand.Int(rand.Reader, big.NewInt(max-min))
if err != nil {
return 0, fmt.Errorf("sqlitex.InsertRandID: %w", err)
}
id := v.Int64() + min
stmt.Reset()
stmt.SetInt64(param, id)
_, err = stmt.Step()
if err == nil {
return id, nil
}
if i >= 100 || sqlite.ErrCode(err) != sqlite.ResultConstraintPrimaryKey {
return 0, fmt.Errorf("sqlitex.InsertRandID: %w", err)
}
}
}

254
vendor/zombiezen.com/go/sqlite/sqlitex/savepoint.go generated vendored Normal file
View File

@@ -0,0 +1,254 @@
// Copyright (c) 2018 David Crawshaw <david@zentus.com>
// Copyright (c) 2021 Ross Light <rosss@zombiezen.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// SPDX-License-Identifier: ISC
package sqlitex
import (
"fmt"
"runtime"
"strings"
"zombiezen.com/go/sqlite"
)
// Save creates a named SQLite transaction using SAVEPOINT.
//
// On success Savepoint returns a releaseFn that will call either
// RELEASE or ROLLBACK depending on whether the parameter *error
// points to a nil or non-nil error. This is designed to be deferred.
//
// Example:
//
// func doWork(conn *sqlite.Conn) (err error) {
// defer sqlitex.Save(conn)(&err)
//
// // ... do work in the transaction
// }
//
// https://www.sqlite.org/lang_savepoint.html
func Save(conn *sqlite.Conn) (releaseFn func(*error)) {
name := "sqlitex.Save" // safe as names can be reused
var pc [3]uintptr
if n := runtime.Callers(0, pc[:]); n > 0 {
frames := runtime.CallersFrames(pc[:n])
if _, more := frames.Next(); more { // runtime.Callers
if _, more := frames.Next(); more { // savepoint.Save
frame, _ := frames.Next() // caller we care about
if frame.Function != "" {
name = frame.Function
}
}
}
}
releaseFn, err := savepoint(conn, name)
if err != nil {
if sqlite.ErrCode(err) == sqlite.ResultInterrupt {
return func(errp *error) {
if *errp == nil {
*errp = err
}
}
}
panic(err)
}
return releaseFn
}
func savepoint(conn *sqlite.Conn, name string) (releaseFn func(*error), err error) {
if strings.Contains(name, `"`) {
return nil, fmt.Errorf("sqlitex.Savepoint: invalid name: %q", name)
}
if err := Exec(conn, fmt.Sprintf("SAVEPOINT %q;", name), nil); err != nil {
return nil, err
}
// TODO(maybe)
// tracer := conn.Tracer()
// if tracer != nil {
// tracer.Push("TX " + name)
// }
releaseFn = func(errp *error) {
// TODO(maybe)
// if tracer != nil {
// tracer.Pop()
// }
recoverP := recover()
// If a query was interrupted or if a user exec'd COMMIT or
// ROLLBACK, then everything was already rolled back
// automatically, thus returning the connection to autocommit
// mode.
if conn.AutocommitEnabled() {
// There is nothing to rollback.
if recoverP != nil {
panic(recoverP)
}
return
}
if *errp == nil && recoverP == nil {
// Success path. Release the savepoint successfully.
*errp = Exec(conn, fmt.Sprintf("RELEASE %q;", name), nil)
if *errp == nil {
return
}
// Possible interrupt. Fall through to the error path.
if conn.AutocommitEnabled() {
// There is nothing to rollback.
if recoverP != nil {
panic(recoverP)
}
return
}
}
orig := ""
if *errp != nil {
orig = (*errp).Error() + "\n\t"
}
// Error path.
// Always run ROLLBACK even if the connection has been interrupted.
oldDoneCh := conn.SetInterrupt(nil)
defer conn.SetInterrupt(oldDoneCh)
err := Exec(conn, fmt.Sprintf("ROLLBACK TO %q;", name), nil)
if err != nil {
panic(orig + err.Error())
}
err = Exec(conn, fmt.Sprintf("RELEASE %q;", name), nil)
if err != nil {
panic(orig + err.Error())
}
if recoverP != nil {
panic(recoverP)
}
}
return releaseFn, nil
}
// Transaction creates a DEFERRED SQLite transaction.
//
// On success Transaction returns an endFn that will call either
// COMMIT or ROLLBACK depending on whether the parameter *error
// points to a nil or non-nil error. This is designed to be deferred.
//
// https://www.sqlite.org/lang_transaction.html
func Transaction(conn *sqlite.Conn) (endFn func(*error)) {
endFn, err := transaction(conn, "DEFERRED")
if err != nil {
if sqlite.ErrCode(err) == sqlite.ResultInterrupt {
return func(errp *error) {
if *errp == nil {
*errp = err
}
}
}
panic(err)
}
return endFn
}
// ImmediateTransaction creates an IMMEDIATE SQLite transaction.
//
// On success ImmediateTransaction returns an endFn that will call either
// COMMIT or ROLLBACK depending on whether the parameter *error
// points to a nil or non-nil error. This is designed to be deferred.
//
// https://www.sqlite.org/lang_transaction.html
func ImmediateTransaction(conn *sqlite.Conn) (endFn func(*error), err error) {
endFn, err = transaction(conn, "IMMEDIATE")
if err != nil {
return func(*error) {}, err
}
return endFn, nil
}
// ExclusiveTransaction creates an EXCLUSIVE SQLite transaction.
//
// On success ImmediateTransaction returns an endFn that will call either
// COMMIT or ROLLBACK depending on whether the parameter *error
// points to a nil or non-nil error. This is designed to be deferred.
//
// https://www.sqlite.org/lang_transaction.html
func ExclusiveTransaction(conn *sqlite.Conn) (endFn func(*error), err error) {
endFn, err = transaction(conn, "EXCLUSIVE")
if err != nil {
return func(*error) {}, err
}
return endFn, nil
}
func transaction(conn *sqlite.Conn, mode string) (endFn func(*error), err error) {
if err := Exec(conn, "BEGIN "+mode+";", nil); err != nil {
return nil, err
}
endFn = func(errp *error) {
recoverP := recover()
// If a query was interrupted or if a user exec'd COMMIT or
// ROLLBACK, then everything was already rolled back
// automatically, thus returning the connection to autocommit
// mode.
if conn.AutocommitEnabled() {
// There is nothing to rollback.
if recoverP != nil {
panic(recoverP)
}
return
}
if *errp == nil && recoverP == nil {
// Success path. Commit the transaction.
*errp = Exec(conn, "COMMIT;", nil)
if *errp == nil {
return
}
// Possible interrupt. Fall through to the error path.
if conn.AutocommitEnabled() {
// There is nothing to rollback.
if recoverP != nil {
panic(recoverP)
}
return
}
}
orig := ""
if *errp != nil {
orig = (*errp).Error() + "\n\t"
}
// Error path.
// Always run ROLLBACK even if the connection has been interrupted.
oldDoneCh := conn.SetInterrupt(nil)
defer conn.SetInterrupt(oldDoneCh)
err := Exec(conn, "ROLLBACK;", nil)
if err != nil {
panic(orig + err.Error())
}
if recoverP != nil {
panic(recoverP)
}
}
return endFn, nil
}