chore: ⬆️ updated deps
This commit is contained in:
+135
-17
@@ -29,7 +29,9 @@ type conn struct {
|
||||
|
||||
writeTimeFormat string
|
||||
beginMode string
|
||||
loc *time.Location
|
||||
intToTime bool
|
||||
textToTime bool
|
||||
integerTimeFormat string
|
||||
}
|
||||
|
||||
@@ -62,6 +64,7 @@ func newConn(dsn string) (*conn, error) {
|
||||
sqlite3.SQLITE_OPEN_URI,
|
||||
)
|
||||
if err != nil {
|
||||
c.tls.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -86,12 +89,18 @@ func (c *conn) parseTime(s string) (interface{}, bool) {
|
||||
return v, true
|
||||
}
|
||||
|
||||
ts := strings.TrimSuffix(s, "Z")
|
||||
ts, hadZ := strings.CutSuffix(s, "Z")
|
||||
|
||||
for _, f := range parseTimeFormats {
|
||||
t, err := time.Parse(f, ts)
|
||||
var t time.Time
|
||||
var err error
|
||||
if c.loc != nil && !hadZ {
|
||||
t, err = time.ParseInLocation(f, ts, c.loc)
|
||||
} else {
|
||||
t, err = time.Parse(f, ts)
|
||||
}
|
||||
if err == nil {
|
||||
return t, true
|
||||
return c.applyTimezone(t), true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +110,8 @@ func (c *conn) parseTime(s string) (interface{}, bool) {
|
||||
// Attempt to parse s as a time string produced by t.String(). If x > 0 it's
|
||||
// the index of substring "m=" within s. Return (s, false) if s is
|
||||
// not recognized as a valid time encoding.
|
||||
// This intentionally uses time.Parse, not time.ParseInLocation,
|
||||
// because the format already contains timezone information (-0700 MST).
|
||||
func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) {
|
||||
s := s0
|
||||
if x > 0 {
|
||||
@@ -108,19 +119,28 @@ func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) {
|
||||
}
|
||||
s = strings.TrimSpace(s)
|
||||
if t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", s); err == nil {
|
||||
return t, true
|
||||
return c.applyTimezone(t), true
|
||||
}
|
||||
|
||||
return s0, false
|
||||
}
|
||||
|
||||
func (c *conn) applyTimezone(t time.Time) time.Time {
|
||||
if c.loc == nil {
|
||||
return t
|
||||
}
|
||||
return t.In(c.loc)
|
||||
}
|
||||
|
||||
// writeTimeFormats are the names and formats supported
|
||||
// by the `_time_format` DSN query param.
|
||||
var writeTimeFormats = map[string]string{
|
||||
"sqlite": parseTimeFormats[0],
|
||||
"sqlite": parseTimeFormats[0],
|
||||
"datetime": "2006-01-02 15:04:05",
|
||||
}
|
||||
|
||||
func (c *conn) formatTime(t time.Time) string {
|
||||
t = c.applyTimezone(t)
|
||||
// Before configurable write time formats were supported,
|
||||
// time.Time.String was used. Maintain that default to
|
||||
// keep existing driver users formatting times the same.
|
||||
@@ -215,6 +235,71 @@ func (c *conn) columnName(pstmt uintptr, n int) (string, error) {
|
||||
return libc.GoString(p), nil
|
||||
}
|
||||
|
||||
// ColumnInfo returns column information for query.
|
||||
// It does not execute query.
|
||||
//
|
||||
// For output columns that are expressions, function calls, or constants —
|
||||
// or otherwise do not resolve to a single column — the DatabaseName,
|
||||
// TableName, and OriginName fields of the corresponding ColumnInfo are
|
||||
// empty, per the sqlite3 contract.
|
||||
//
|
||||
// Sample usage:
|
||||
//
|
||||
// err := conn.Raw(func(driverConn any) error {
|
||||
// ci, ok := driverConn.(interface{ ColumnInfo(query string) ([]sqlite.ColumnInfo, error) })
|
||||
// if !ok {
|
||||
// return fmt.Errorf("driver does not support ColumnInfo")
|
||||
// }
|
||||
// info, err := ci.ColumnInfo(query)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // use info
|
||||
// return nil
|
||||
// })
|
||||
func (c *conn) ColumnInfo(query string) (_ []ColumnInfo, err error) {
|
||||
p, err := libc.CString(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.free(p)
|
||||
|
||||
psql := p
|
||||
pstmt, err := c.prepareV2(&psql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pstmt == 0 {
|
||||
// Empty or comment-only query: no columns to describe.
|
||||
return nil, nil
|
||||
}
|
||||
defer func() {
|
||||
if e := c.finalize(pstmt); err == nil {
|
||||
err = e
|
||||
}
|
||||
}()
|
||||
|
||||
n, err := c.columnCount(pstmt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := make([]ColumnInfo, n)
|
||||
for i := range n {
|
||||
name, err := c.columnName(pstmt, i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info[i] = ColumnInfo{
|
||||
Name: name,
|
||||
DeclType: c.columnDeclType(pstmt, i),
|
||||
DatabaseName: libc.GoString(sqlite3.Xsqlite3_column_database_name(c.tls, pstmt, int32(i))),
|
||||
TableName: libc.GoString(sqlite3.Xsqlite3_column_table_name(c.tls, pstmt, int32(i))),
|
||||
OriginName: libc.GoString(sqlite3.Xsqlite3_column_origin_name(c.tls, pstmt, int32(i))),
|
||||
}
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// C documentation
|
||||
//
|
||||
// int sqlite3_column_count(sqlite3_stmt *pStmt);
|
||||
@@ -291,9 +376,7 @@ func (c *conn) bind(pstmt uintptr, n int, args []driver.NamedValue) (allocs []ui
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range allocs {
|
||||
c.free(v)
|
||||
}
|
||||
c.freeAllocs(allocs)
|
||||
allocs = nil
|
||||
}()
|
||||
|
||||
@@ -600,7 +683,18 @@ func (c *conn) openV2(name, vfsName string, flags int32) (uintptr, error) {
|
||||
}
|
||||
|
||||
if rc := sqlite3.Xsqlite3_open_v2(c.tls, s, p, flags, vfs); rc != sqlite3.SQLITE_OK {
|
||||
return 0, c.errstr(rc)
|
||||
dbh := *(*uintptr)(unsafe.Pointer(p))
|
||||
// Per SQLite docs, sqlite3_open_v2 may allocate a handle even on
|
||||
// failure. The error message is stored on that handle, and it must
|
||||
// be closed to avoid leaking resources.
|
||||
var err error
|
||||
if dbh != 0 {
|
||||
err = errstrForDB(c.tls, rc, dbh)
|
||||
sqlite3.Xsqlite3_close_v2(c.tls, dbh)
|
||||
} else {
|
||||
err = c.errstr(rc)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return *(*uintptr)(unsafe.Pointer(p)), nil
|
||||
@@ -620,18 +714,31 @@ func (c *conn) free(p uintptr) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *conn) freeAllocs(allocs []uintptr) {
|
||||
for _, v := range allocs {
|
||||
c.free(v)
|
||||
}
|
||||
}
|
||||
|
||||
// C documentation
|
||||
//
|
||||
// const char *sqlite3_errstr(int);
|
||||
func (c *conn) errstr(rc int32) error {
|
||||
p := sqlite3.Xsqlite3_errstr(c.tls, rc)
|
||||
str := libc.GoString(p)
|
||||
p = sqlite3.Xsqlite3_errmsg(c.tls, c.db)
|
||||
return errstrForDB(c.tls, rc, c.db)
|
||||
}
|
||||
|
||||
func errstrForDB(tls *libc.TLS, rc int32, db uintptr) error {
|
||||
pStr := sqlite3.Xsqlite3_errstr(tls, rc)
|
||||
str := libc.GoString(pStr)
|
||||
var s string
|
||||
if rc == sqlite3.SQLITE_BUSY {
|
||||
s = " (SQLITE_BUSY)"
|
||||
}
|
||||
switch msg := libc.GoString(p); {
|
||||
if db == 0 {
|
||||
return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)}
|
||||
}
|
||||
pMsg := sqlite3.Xsqlite3_errmsg(tls, db)
|
||||
switch msg := libc.GoString(pMsg); {
|
||||
case msg == str:
|
||||
return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)}
|
||||
default:
|
||||
@@ -913,15 +1020,22 @@ func (c *conn) Serialize() (v []byte, err error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Deserialize restore a database from the content returned by Serialize.
|
||||
// Deserialize restores a database from the content returned by Serialize.
|
||||
func (c *conn) Deserialize(buf []byte) (err error) {
|
||||
bufLen := len(buf)
|
||||
pBuf := c.tls.Alloc(bufLen) // free will be done if it fails or on close, must not be freed here
|
||||
if bufLen == 0 {
|
||||
return fmt.Errorf("sqlite: empty buffer passed to Deserialize")
|
||||
}
|
||||
pBuf := sqlite3.Xsqlite3_malloc64(c.tls, uint64(bufLen))
|
||||
if pBuf == 0 {
|
||||
return fmt.Errorf("sqlite: cannot allocate %d bytes for deserialize", bufLen)
|
||||
}
|
||||
|
||||
copy((*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen], buf)
|
||||
|
||||
zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0)
|
||||
if zSchema == 0 {
|
||||
sqlite3.Xsqlite3_free(c.tls, pBuf)
|
||||
return fmt.Errorf("failed to get main db name")
|
||||
}
|
||||
|
||||
@@ -978,8 +1092,12 @@ func (c *conn) backup(remoteConn *conn, restore bool) (_ *Backup, finalErr error
|
||||
pBackup = sqlite3.Xsqlite3_backup_init(c.tls, remoteConn.db, dstSchema, c.db, srcSchema)
|
||||
}
|
||||
if pBackup <= 0 {
|
||||
rc := sqlite3.Xsqlite3_errcode(c.tls, remoteConn.db)
|
||||
return nil, c.errstr(rc)
|
||||
destDb := remoteConn.db
|
||||
if restore {
|
||||
destDb = c.db
|
||||
}
|
||||
rc := sqlite3.Xsqlite3_errcode(c.tls, destDb)
|
||||
return nil, errstrForDB(c.tls, rc, destDb)
|
||||
}
|
||||
|
||||
return &Backup{srcConn: c, dstConn: remoteConn, pBackup: pBackup}, nil
|
||||
|
||||
Reference in New Issue
Block a user