Commit 9c5a8b15 authored by gwenn's avatar gwenn

Add support to Time type in Scan methods.

parent 6394bba8
...@@ -37,7 +37,7 @@ func NewBackup(dst *Conn, dstDbName string, src *Conn, srcDbName string) (*Backu ...@@ -37,7 +37,7 @@ func NewBackup(dst *Conn, dstDbName string, src *Conn, srcDbName string) (*Backu
sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname) sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname)
if sb == nil { if sb == nil {
return nil, dst.error(C.sqlite3_errcode(dst.db)) return nil, dst.error(C.sqlite3_errcode(dst.db), "backup init failed")
} }
return &Backup{sb, dst, src}, nil return &Backup{sb, dst, src}, nil
} }
...@@ -52,7 +52,7 @@ type Backup struct { ...@@ -52,7 +52,7 @@ type Backup struct {
// (See http://sqlite.org/c3ref/backup_finish.html#sqlite3backupstep) // (See http://sqlite.org/c3ref/backup_finish.html#sqlite3backupstep)
func (b *Backup) Step(npage int) error { func (b *Backup) Step(npage int) error {
rv := C.sqlite3_backup_step(b.sb, C.int(npage)) rv := C.sqlite3_backup_step(b.sb, C.int(npage))
if rv == C.SQLITE_OK || Errno(rv) == ErrBusy || Errno(rv) == ErrLocked { if rv == C.SQLITE_OK || Errno(rv) == ErrBusy || Errno(rv) == ErrLocked { // TODO Trace busy/locked errors
return nil return nil
} }
return Errno(rv) return Errno(rv)
......
package sqlite package sqlite_test
import ( import (
. "github.com/gwenn/gosqlite"
"testing" "testing"
"time" "time"
) )
...@@ -16,3 +17,42 @@ func TestJulianDay(t *testing.T) { ...@@ -16,3 +17,42 @@ func TestJulianDay(t *testing.T) {
t.Errorf("%#v <> %#v", now, r) t.Errorf("%#v <> %#v", now, r)
} }
} }
func TestScan(t *testing.T) {
db := open(t)
defer db.Close()
var dt time.Time
err := db.OneValue("SELECT date('now')", &dt)
checkNoError(t, err, "Error reading date: %#v")
if dt.IsZero() {
t.Error("Unexpected zero date")
}
var tm time.Time
err = db.OneValue("SELECT time('now')", &tm)
checkNoError(t, err, "Error reading date: %#v")
if tm.IsZero() {
t.Error("Unexpected zero time")
}
var dtm time.Time
err = db.OneValue("SELECT strftime('%Y-%m-%dT%H:%M:%f', 'now')", &dtm)
checkNoError(t, err, "Error reading date: %#v")
if dtm.IsZero() {
t.Error("Unexpected zero datetime")
}
var jd time.Time
err = db.OneValue("SELECT CAST(strftime('%J', 'now') AS NUMERIC)", &jd)
checkNoError(t, err, "Error reading date: %#v")
if jd.IsZero() {
t.Error("Unexpected zero julian day")
}
var unix time.Time
err = db.OneValue("SELECT CAST(strftime('%s', 'now') AS NUMERIC)", &unix)
checkNoError(t, err, "Error reading date: %#v")
if unix.IsZero() {
t.Error("Unexpected zero julian day")
}
}
...@@ -41,7 +41,7 @@ func (c *connImpl) Exec(query string, args []driver.Value) (driver.Result, error ...@@ -41,7 +41,7 @@ func (c *connImpl) Exec(query string, args []driver.Value) (driver.Result, error
if err := c.c.Exec(query, tmp...); err != nil { if err := c.c.Exec(query, tmp...); err != nil {
return nil, err return nil, err
} }
return c, nil // FIXME RowAffected/ddlSuccess return c, nil // FIXME RowAffected/noRows
} }
// TODO How to know that the last Stmt did an INSERT? // TODO How to know that the last Stmt did an INSERT?
...@@ -95,7 +95,7 @@ func (s *stmtImpl) Exec(args []driver.Value) (driver.Result, error) { ...@@ -95,7 +95,7 @@ func (s *stmtImpl) Exec(args []driver.Value) (driver.Result, error) {
if err := s.s.exec(); err != nil { if err := s.s.exec(); err != nil {
return nil, err return nil, err
} }
return s, nil // FIXME RowAffected/ddlSuccess return s, nil // FIXME RowAffected/noRows
} }
// TODO How to know that this Stmt did an INSERT? // TODO How to know that this Stmt did an INSERT?
......
...@@ -44,6 +44,7 @@ import ( ...@@ -44,6 +44,7 @@ import (
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"time"
"unsafe" "unsafe"
) )
...@@ -293,7 +294,7 @@ func OpenVfs(filename string, vfsname string, flags ...OpenFlag) (*Conn, error) ...@@ -293,7 +294,7 @@ func OpenVfs(filename string, vfsname string, flags ...OpenFlag) (*Conn, error)
// Set a busy timeout // Set a busy timeout
// (See http://sqlite.org/c3ref/busy_timeout.html) // (See http://sqlite.org/c3ref/busy_timeout.html)
func (c *Conn) BusyTimeout(ms int) error { func (c *Conn) BusyTimeout(ms int) error { // TODO time.Duration ?
return c.error(C.sqlite3_busy_timeout(c.db, C.int(ms))) return c.error(C.sqlite3_busy_timeout(c.db, C.int(ms)))
} }
...@@ -690,6 +691,10 @@ func (s *Stmt) BindByIndex(index int, value interface{}) error { ...@@ -690,6 +691,10 @@ func (s *Stmt) BindByIndex(index int, value interface{}) error {
p = &value[0] p = &value[0]
} }
rv = C.my_bind_blob(s.stmt, i, unsafe.Pointer(p), C.int(len(value))) rv = C.my_bind_blob(s.stmt, i, unsafe.Pointer(p), C.int(len(value)))
/*
case time.Time: // At least three representations are possible: string (YYYY-MM-DDTHH:MM:SS.SSS), int64 (unix time), float64 (julian day)
rv = C.sqlite3_bind_text()
*/
case ZeroBlobLength: case ZeroBlobLength:
rv = C.sqlite3_bind_zeroblob(s.stmt, i, C.int(value)) rv = C.sqlite3_bind_zeroblob(s.stmt, i, C.int(value))
default: default:
...@@ -1012,6 +1017,8 @@ func (s *Stmt) ScanByIndex(index int, value interface{}) (bool, error) { ...@@ -1012,6 +1017,8 @@ func (s *Stmt) ScanByIndex(index int, value interface{}) (bool, error) {
} else { } else {
**value = bs **value = bs
} }
case *time.Time: // go fix doesn't like this type!
*value, isNull, err = s.ScanTime(index)
case *interface{}: case *interface{}:
*value = s.ScanValue(index) *value = s.ScanValue(index)
isNull = *value == nil isNull = *value == nil
...@@ -1183,6 +1190,55 @@ func (s *Stmt) ScanBlob(index int) (value []byte, isNull bool) { ...@@ -1183,6 +1190,55 @@ func (s *Stmt) ScanBlob(index int) (value []byte, isNull bool) {
return return
} }
func (s *Stmt) ScanTime(index int) (value time.Time, isNull bool, err error) {
switch s.ColumnType(index) {
case Null:
isNull = true
case Text:
p := C.sqlite3_column_text(s.stmt, C.int(index))
txt := C.GoString((*C.char)(unsafe.Pointer(p)))
var layout string
switch len(txt) {
case 5: // HH:MM
layout = "15:04"
case 8: // HH:MM:SS
layout = "15:04:05"
case 10: // YYYY-MM-DD
layout = "2006-01-02"
case 12: // HH:MM:SS.SSS
layout = "15:04:05.000"
case 16: // YYYY-MM-DDTHH:MM
if txt[10] == 'T' {
layout = "2006-01-02T15:04"
} else {
layout = "2006-01-02 15:04"
}
case 19: // YYYY-MM-DDTHH:MM:SS
if txt[10] == 'T' {
layout = "2006-01-02T15:04:05"
} else {
layout = "2006-01-02 15:04:05"
}
default: // YYYY-MM-DDTHH:MM:SS.SSS or parse error
if len(txt) > 10 && txt[10] == 'T' {
layout = "2006-01-02T15:04:05.000"
} else {
layout = "2006-01-02 15:04:05.000"
}
}
value, err = time.Parse(layout, txt)
case Integer:
unixepoch := int64(C.sqlite3_column_int64(s.stmt, C.int(index)))
value = time.Unix(unixepoch, 0)
case Float:
jd := float64(C.sqlite3_column_double(s.stmt, C.int(index)))
value = JulianDayToUTC(jd)
default:
panic("The column type is not one of SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, or SQLITE_NULL")
}
return
}
// Only lossy conversion is reported as error. // Only lossy conversion is reported as error.
func (s *Stmt) checkTypeMismatch(source, target Type) error { func (s *Stmt) checkTypeMismatch(source, target Type) error {
switch target { switch target {
......
...@@ -53,7 +53,7 @@ func (c *Conn) Trace(f Tracer, udp interface{}) { ...@@ -53,7 +53,7 @@ func (c *Conn) Trace(f Tracer, udp interface{}) {
} }
// See Conn.Profile // See Conn.Profile
type Profiler func(udp interface{}, sql string, nanoseconds uint64) type Profiler func(udp interface{}, sql string, nanoseconds uint64) // TODO time.Duration
type sqliteProfile struct { type sqliteProfile struct {
f Profiler f Profiler
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment