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
sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname)
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
}
......@@ -52,7 +52,7 @@ type Backup struct {
// (See http://sqlite.org/c3ref/backup_finish.html#sqlite3backupstep)
func (b *Backup) Step(npage int) error {
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 Errno(rv)
......
package sqlite
package sqlite_test
import (
. "github.com/gwenn/gosqlite"
"testing"
"time"
)
......@@ -16,3 +17,42 @@ func TestJulianDay(t *testing.T) {
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
if err := c.c.Exec(query, tmp...); err != nil {
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?
......@@ -95,7 +95,7 @@ func (s *stmtImpl) Exec(args []driver.Value) (driver.Result, error) {
if err := s.s.exec(); err != nil {
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?
......
......@@ -44,6 +44,7 @@ import (
"fmt"
"io"
"reflect"
"time"
"unsafe"
)
......@@ -293,7 +294,7 @@ func OpenVfs(filename string, vfsname string, flags ...OpenFlag) (*Conn, error)
// Set a busy timeout
// (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)))
}
......@@ -690,6 +691,10 @@ func (s *Stmt) BindByIndex(index int, value interface{}) error {
p = &value[0]
}
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:
rv = C.sqlite3_bind_zeroblob(s.stmt, i, C.int(value))
default:
......@@ -1012,6 +1017,8 @@ func (s *Stmt) ScanByIndex(index int, value interface{}) (bool, error) {
} else {
**value = bs
}
case *time.Time: // go fix doesn't like this type!
*value, isNull, err = s.ScanTime(index)
case *interface{}:
*value = s.ScanValue(index)
isNull = *value == nil
......@@ -1183,6 +1190,55 @@ func (s *Stmt) ScanBlob(index int) (value []byte, isNull bool) {
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.
func (s *Stmt) checkTypeMismatch(source, target Type) error {
switch target {
......
......@@ -53,7 +53,7 @@ func (c *Conn) Trace(f Tracer, udp interface{}) {
}
// 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 {
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