12
vendor/zombiezen.com/go/sqlite/.gitignore
generated
vendored
Normal file
12
vendor/zombiezen.com/go/sqlite/.gitignore
generated
vendored
Normal 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
200
vendor/zombiezen.com/go/sqlite/CHANGELOG.md
generated
vendored
Normal 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
128
vendor/zombiezen.com/go/sqlite/CODE_OF_CONDUCT.md
generated
vendored
Normal 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
13
vendor/zombiezen.com/go/sqlite/CONTRIBUTING.md
generated
vendored
Normal 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
14
vendor/zombiezen.com/go/sqlite/LICENSE
generated
vendored
Normal 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
92
vendor/zombiezen.com/go/sqlite/README.md
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# `zombiezen.com/go/sqlite`
|
||||
|
||||
[][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
318
vendor/zombiezen.com/go/sqlite/auth.go
generated
vendored
Normal 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
319
vendor/zombiezen.com/go/sqlite/blob.go
generated
vendored
Normal 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
102
vendor/zombiezen.com/go/sqlite/blocking_step.go
generated
vendored
Normal 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
86
vendor/zombiezen.com/go/sqlite/doc.go
generated
vendored
Normal 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
5
vendor/zombiezen.com/go/sqlite/fs/doc.go
generated
vendored
Normal 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
22
vendor/zombiezen.com/go/sqlite/fs/fs.go
generated
vendored
Normal 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
14
vendor/zombiezen.com/go/sqlite/fs/fs_go116.go
generated
vendored
Normal 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
603
vendor/zombiezen.com/go/sqlite/func.go
generated
vendored
Normal 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
127
vendor/zombiezen.com/go/sqlite/op_type.go
generated
vendored
Normal 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
78
vendor/zombiezen.com/go/sqlite/openflags.go
generated
vendored
Normal 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
359
vendor/zombiezen.com/go/sqlite/result.go
generated
vendored
Normal 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
915
vendor/zombiezen.com/go/sqlite/session.go
generated
vendored
Normal 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
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
353
vendor/zombiezen.com/go/sqlite/sqlitex/exec.go
generated
vendored
Normal 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
262
vendor/zombiezen.com/go/sqlite/sqlitex/pool.go
generated
vendored
Normal 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
82
vendor/zombiezen.com/go/sqlite/sqlitex/query.go
generated
vendored
Normal 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
51
vendor/zombiezen.com/go/sqlite/sqlitex/rand_id.go
generated
vendored
Normal 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
254
vendor/zombiezen.com/go/sqlite/sqlitex/savepoint.go
generated
vendored
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user