forked from modernc-org/sqlite
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdriver.go
More file actions
272 lines (263 loc) · 14 KB
/
Copy pathdriver.go
File metadata and controls
272 lines (263 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// Copyright 2025 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sqlite // import "modernc.org/sqlite"
import (
"database/sql/driver"
"fmt"
"modernc.org/sqlite/vtab"
)
// Driver implements database/sql/driver.Driver.
//
// Registration functions and methods must be called before the first call to
// Open.
//
// Most code has no use for this type. sql.Open("sqlite", dsn) and
// [NewConnector] both go through the driver this package registers as
// "sqlite", which carries everything registered with [RegisterFunction],
// [RegisterScalarFunction], [RegisterDeterministicScalarFunction],
// [RegisterCollationUtf8], [RegisterConnectionHook] and
// [vtab.RegisterModule].
//
// A Driver a caller constructs is not equivalent to that one. Its fields are
// unexported, so it starts out with no functions, collations or connection
// hooks, and the only way to give it any is [Driver.RegisterConnectionHook];
// the package-level registration functions always apply to the registered
// driver, never to a constructed one. Connections it opens therefore run
// without the package-level functions and collations -- and where such a
// registration overrides a SQLite built-in of the same name, they run with
// SQLite's built-in in force instead. Virtual table modules are the one
// exception: they are held process-globally and reach every Driver.
//
// Constructing one is supported for the private-hook pattern: a driver
// registered under a name of its own with sql.Register, so that its connection
// hooks apply to its own connections rather than to every connection in the
// process. Prefer sql.Open or NewConnector for anything else.
type Driver struct {
// user defined functions that are added to every new connection on Open
udfs map[string]*userDefinedFunction
// collations that are added to every new connection on Open
collations map[string]*collation
// connection hooks are called after a connection is opened
connectionHooks []ConnectionHookFn
// modules holds registered virtual table modules that should be added to
// every new connection on Open.
modules map[string]vtab.Module
}
var d = &Driver{
udfs: make(map[string]*userDefinedFunction, 0),
collations: make(map[string]*collation, 0),
connectionHooks: make([]ConnectionHookFn, 0),
modules: make(map[string]vtab.Module, 0),
}
func newDriver() *Driver { return d }
// Open returns a new connection to the database. The name is a string in a
// driver-specific format.
//
// Open may return a cached connection (one previously closed), but doing so is
// unnecessary; the sql package maintains a pool of idle connections for
// efficient re-use.
//
// The returned connection is only used by one goroutine at a time.
//
// The name may be a filename, e.g., "/tmp/mydata.sqlite", or a URI, in which
// case it may include a '?' followed by one or more query parameters.
// For example, "file:///tmp/mydata.sqlite?_pragma=foreign_keys(1)&_time_format=sqlite".
// The supported query parameters are:
//
// _pragma: Each value will be run as a "PRAGMA ..." statement (with the PRAGMA
// keyword added for you). May be specified more than once, '&'-separated. For more
// information on supported PRAGMAs see: https://www.sqlite.org/pragma.html
//
// The following shorthand keys set common PRAGMAs for easier DSN compatibility
// when migrating from github.com/mattn/go-sqlite3. Each value is validated
// against the same set github.com/mattn/go-sqlite3 accepts (case-insensitive);
// an unrecognized value fails the connection with an error instead of being
// silently ignored. The keys are applied in a fixed order, independent of the
// order they appear in the DSN: _busy_timeout and _auto_vacuum first (auto_vacuum
// must be set before the database is first written), then the _pragma values,
// then the remaining keys, and _query_only last. Where a shorthand key and a
// _pragma set the same PRAGMA, whichever is applied later in that order wins. If
// a key and its alias are both supplied, the alias (the second name below) wins,
// matching github.com/mattn/go-sqlite3; supplying the alias with an empty value
// therefore suppresses the PRAGMA rather than deferring to the primary key.
// Accepted values:
//
// _busy_timeout, _timeout -> PRAGMA busy_timeout (an integer)
// _foreign_keys, _fk -> PRAGMA foreign_keys (0 1 false true no yes off on)
// _journal_mode, _journal -> PRAGMA journal_mode (DELETE TRUNCATE PERSIST MEMORY WAL OFF)
// _synchronous, _sync -> PRAGMA synchronous (0 OFF 1 NORMAL 2 FULL 3 EXTRA)
// _auto_vacuum, _vacuum -> PRAGMA auto_vacuum (0 NONE 1 FULL 2 INCREMENTAL)
// _query_only -> PRAGMA query_only (0 1 false true no yes off on)
//
// All DSN parameters that can be validated are validated before any of them is
// applied, so a DSN carrying a typo fails without having executed the PRAGMAs
// that precede it -- a rejected DSN does not leave the database converted to WAL
// or with auto_vacuum already set.
//
// Unlike these validated shorthand keys, each _pragma value is executed verbatim
// (with PRAGMA prepended) and is not validated, so a DSN that includes _pragma
// must come from a trusted source. It is also the one case that can still fail
// partway: a bad _pragma is only rejected by SQLite as it runs, after any
// earlier _pragma in the list has taken effect.
//
// _time_format: The name of a format to use when writing time values to the database.
// The currently supported values are (1) "sqlite" for YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM
// (format 4 from https://www.sqlite.org/lang_datefunc.html#time_values with sub-second
// precision and timezone specifier) and (2) "datetime" for YYYY-MM-DD HH:MM:SS
// (format 3, matching the output of SQLite's datetime() function).
// If this parameter is not specified, then the default String() format will be used.
//
// _time_integer_format: The name of a integer format to use when writing time values.
// By default, the time is stored as string and the format can be set with _time_format
// parameter. If _time_integer_format is set, the time will be stored as an integer and
// the integer value will depend on the integer format.
// If you decide to set both _time_format and _time_integer_format, the time will be
// converted as integer and the _time_format value will be ignored.
// Currently the supported value are "unix","unix_milli", "unix_micro" and "unix_nano",
// which corresponds to seconds, milliseconds, microseconds or nanoseconds
// since unixepoch (1 January 1970 00:00:00 UTC).
//
// _inttotime: Enable conversion of time column (DATE, DATETIME,TIMESTAMP) from integer
// to time if the field contain integer (int64).
//
// _texttotime: Enable ColumnTypeScanType to report time.Time instead of string
// for TEXT columns declared as DATE, DATETIME, TIME, or TIMESTAMP. It also
// best-effort upgrades date-shaped TEXT values from columns SQLite reports with
// an empty declared type (aggregates and expressions such as MAX(d) or
// upper(d), subqueries, and typeless real columns) to time.Time, since the
// declared-type test cannot catch those (#248). When that upgrade fires, a Scan
// into interface{} yields a time.Time where it previously yielded a string, and
// a Scan into *string receives the value reformatted to RFC3339Nano rather than
// the raw stored text. A value that does not parse as a time is delivered
// unchanged as the original string.
//
// _timezone: A timezone to use for all time reads and writes, such as "UTC".
// The value is parsed by time.LoadLocation.
// Writes will convert to the timezone before formatting as a string;
// it does not impact _inttotime integer values, as they always use UTC.
// Reads will interpret timezone-less strings as being in this timezone.
// Values that are in a known timezone, such as a string with a timezone specifier
// or an integer with _inttotime (specified to be in UTC), will be converted to this timezone.
//
// _txlock: The locking behavior to use when beginning a transaction. May be
// "deferred" (the default), "immediate", or "exclusive" (case insensitive). See:
// https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
//
// _dqs: Opt-in toggle for SQLite's double-quoted string literal
// compatibility quirk on the connection. Accepts the values strconv.ParseBool
// understands ("0"/"1", "false"/"true", "f"/"t", case-insensitive). When
// absent or set to a true value, SQLite's built-in behavior is unchanged:
// a double-quoted identifier that fails to resolve is silently
// re-interpreted as a string literal. When set to a false value,
// SQLITE_DBCONFIG_DQS_DDL and SQLITE_DBCONFIG_DQS_DML are both turned
// off via sqlite3_db_config so that mistakes hidden by the legacy
// fallback surface as a parse error instead. See:
// https://www.sqlite.org/quirks.html#dblquote and
// https://gitlab.com/cznic/sqlite/-/issues/61
//
// _defensive: Opt-in toggle for SQLite's defensive connection mode, which
// disables the SQL-level features that let ordinary statements deliberately
// corrupt the database file. Accepts the values strconv.ParseBool understands
// ("0"/"1", "false"/"true", "f"/"t", case-insensitive). When absent or set to
// a false value SQLite's default is unchanged. When set to a true value the
// driver calls sqlite3_db_config(SQLITE_DBCONFIG_DEFENSIVE) immediately after
// sqlite3_open_v2 and before any other parameter is applied, so the PRAGMAs
// this driver runs, the _pragma list, and every statement the caller prepares
// are all subject to it. The parameter is parsed before sqlite3_open_v2, so an
// invalid value fails the connection without creating the database file, and
// it must appear at most once: a repeated _defensive is an error rather than
// letting the first value silently win.
//
// On such a connection PRAGMA writable_schema=ON, PRAGMA journal_mode=OFF and
// PRAGMA schema_version=N become silent no-ops, and writes to a virtual
// table's shadow tables (fts5's _data, _idx and so on) and to sqlite_dbpage
// fail with "table ... may not be modified". Reading those tables, ordinary
// use of the virtual tables that own them, and VACUUM are unaffected.
//
// Because journal_mode=OFF is one of the operations defensive mode suppresses,
// a true _defensive combined with _journal_mode=OFF (or _journal=OFF) is
// rejected rather than silently honouring neither. The unvalidated _pragma
// list is the exception, as always: _pragma=journal_mode(OFF) alongside
// _defensive=1 runs and is silently ignored by SQLite.
//
// Defensive mode is a hardening measure, not a sandbox for hostile database
// files. It is one of several steps SQLite recommends for that purpose (see
// https://www.sqlite.org/security.html); this build compiles with neither
// SQLITE_TRUSTED_SCHEMA=0 nor SQLITE_DQS=0 and the driver exposes no
// authorizer, so _defensive=1 alone does not make opening an untrusted file
// safe. It is also a property of the connection, not of the database: another
// connection to the same file, opened without the parameter, is unrestricted.
// See: https://www.sqlite.org/c3ref/c_dbconfig_defensive.html
//
// _error_rc: Opt-in error-string reporting mode for synthesised errors.
// Accepts the values strconv.ParseBool understands ("0"/"1",
// "false"/"true", "f"/"t", case-insensitive). When absent or set to a
// false value, the legacy "errstr: errmsg (rc)" form is preserved
// byte-for-byte: the canonical sqlite3_errstr(rc) and the connection's
// sqlite3_errmsg(db) are concatenated even when the latter belongs to a
// different operation, which can read as misleading on open-time
// failures such as SQLITE_CANTOPEN reporting "out of memory". When set
// to a true value, the appended errmsg is suppressed if
// sqlite3_extended_errcode(db) is inconsistent with the operation rc
// (full match first, primary code as fallback); in that case the
// canonical errstr(rc) is used alone. The Code() returned by the
// driver's *Error is unchanged in either mode. The parameter is parsed
// before sqlite3_open_v2 so open-time errors are covered. See
// https://gitlab.com/cznic/sqlite/-/issues/230.
//
// vfs: The name of the SQLite VFS to open the database with. Note the absent
// underscore prefix: this is the same parameter SQLite recognizes in a file:
// URI, and its value is passed on as the sqlite3_open_v2 zVfs argument. It
// selects any VFS registered with SQLite, in particular one returned by
// [modernc.org/sqlite/vfs.New], which exposes a Go fs.FS as a read-only VFS.
// When absent or empty the default VFS is used. Supplying the parameter more
// than once with values that differ is an error.
func (d *Driver) Open(name string) (conn driver.Conn, err error) {
if dmesgs {
defer func() {
dmesg("name %q: (driver.Conn %p, err %v)", name, conn, err)
}()
}
c, err := newConn(name)
if err != nil {
return nil, err
}
for _, udf := range d.udfs {
if err = c.createFunctionInternal(udf); err != nil {
c.Close()
return nil, err
}
}
for _, coll := range d.collations {
if err = c.createCollationInternal(coll); err != nil {
c.Close()
return nil, err
}
}
for _, connHookFn := range d.connectionHooks {
if err = connHookFn(c, name); err != nil {
c.Close()
return nil, fmt.Errorf("connection hook: %w", err)
}
}
// Register any vtab modules with this connection.
// Note: vtab module registration applies to new connections only. If a
// module is registered after a connection has been opened, that existing
// connection will not see the module; open a new connection to use it.
if err := c.registerModules(); err != nil {
c.Close()
return nil, err
}
return c, nil
}
// RegisterConnectionHook registers a function to be called after each connection
// is opened. This is called after all the connection has been set up.
//
// The hook applies only to connections opened by d. To register one on the
// driver this package registers as "sqlite", and so on the connections
// sql.Open and [NewConnector] hand out, use the package-level
// [RegisterConnectionHook].
func (d *Driver) RegisterConnectionHook(fn ConnectionHookFn) {
d.connectionHooks = append(d.connectionHooks, fn)
}