Skip to content

Commit

Permalink
Merge pull request #2355 from cockroachdb/vivek/timeparam
Browse files Browse the repository at this point in the history
Add Timestamp sql parameter.
  • Loading branch information
vivekmenezes committed Sep 6, 2015
2 parents cac6f1d + f443712 commit c14db08
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 103 deletions.
12 changes: 5 additions & 7 deletions sql/driver/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,9 @@ func (c *conn) Query(stmt string, args []driver.Value) (*rows, error) {
case string:
param.StringVal = &value
case time.Time:
// TODO(vivek): pass in time as an input that can be interpreted
// by the server.
time, err := value.MarshalBinary()
if err != nil {
return nil, err
}
param.BytesVal = time
// Send absolute time devoid of time-zone.
t := Datum_Timestamp{Sec: value.Unix(), Nsec: uint32(value.Nanosecond())}
param.TimeVal = &t
}
params = append(params, param)
}
Expand Down Expand Up @@ -141,6 +137,8 @@ func (c *conn) send(args Request) (*rows, error) {
t[j] = datum.BytesVal
} else if datum.StringVal != nil {
t[j] = []byte(*datum.StringVal)
} else if datum.TimeVal != nil {
t[j] = time.Unix((*datum.TimeVal).Sec, int64((*datum.TimeVal).Nsec)).UTC()
}
if !driver.IsScanValue(t[j]) {
panic(fmt.Sprintf("unsupported type %T returned by database", t[j]))
Expand Down
86 changes: 51 additions & 35 deletions sql/driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ package driver_test
import (
"database/sql"
"fmt"
"log"
"testing"
"time"

"github.com/cockroachdb/cockroach/server"
"github.com/cockroachdb/cockroach/util/leaktest"
Expand Down Expand Up @@ -64,7 +66,11 @@ func asResultSlice(src [][]string) resultSlice {
for i, subSlice := range src {
result[i] = make([]*string, len(subSlice))
for j := range subSlice {
result[i][j] = &subSlice[j]
if subSlice[j] == "<NULL>" {
result[i][j] = nil
} else {
result[i][j] = &subSlice[j]
}
}
}
return result
Expand Down Expand Up @@ -137,69 +143,79 @@ func TestPlaceholders(t *testing.T) {
s, db := setup(t)
defer cleanup(s, db)

timeVal := time.Date(2015, time.August, 30, 3, 34, 45, 345670000, time.UTC)
intervalVal, err := time.ParseDuration("34h2s")
if err != nil {
log.Fatal(err)
}

if _, err := db.Exec(`CREATE DATABASE t`); err != nil {
t.Fatal(err)
}
if _, err := db.Exec(`CREATE TABLE t.kv (k CHAR PRIMARY KEY, v CHAR)`); err != nil {
if _, err := db.Exec(`CREATE TABLE t.alltypes (a BIGINT PRIMARY KEY, b FLOAT, c TEXT, d BOOLEAN, e TIMESTAMP, f DATE, g INTERVAL)`); err != nil {
t.Fatal(err)
}
if _, err := db.Exec(`INSERT INTO t.kv VALUES ($1, $2), ($3, $4)`, "a", "b", "c", nil); err != nil {
// Insert values for all the different types.
if _, err := db.Exec(`INSERT INTO t.alltypes (a, b, c, d, e, f, g) VALUES ($1, $2, $3, $4, $5, $5::DATE, $6::INTERVAL)`, 123, 3.4, "blah", true, timeVal, intervalVal); err != nil {
t.Fatal(err)
}

if rows, err := db.Query("SELECT * FROM t.kv"); err != nil {
// Insert a row with NULL values
if _, err := db.Exec(`INSERT INTO t.alltypes (a, b, c, d, e, f, g) VALUES ($1, $2, $3, $4, $5, $6, $7)`, 456, nil, nil, nil, nil, nil, nil); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE a IN ($1)", 123); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE b IN ($1)", 3.4); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE c IN ($1)", "blah"); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE d IN ($1)", true); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE e IN ($1)", timeVal); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE f IN ($1::DATE)", timeVal); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE g IN ($1::INTERVAL)", intervalVal); err != nil {
t.Fatal(err)
}
if rows, err := db.Query("SELECT * FROM t.alltypes"); err != nil {
t.Fatal(err)
} else {
results := readAll(t, rows)
expectedResults := asResultSlice([][]string{
{"k", "v"},
{"a", "b"},
{"c", ""},
{"a", "b", "c", "d", "e", "f", "g"},
{"123", "3.4", "blah", "true", "2015-08-30 03:34:45.34567+00:00", "2015-08-30", "34h0m2s"},
{"456", "<NULL>", "<NULL>", "<NULL>", "<NULL>", "<NULL>", "<NULL>"},
})
expectedResults[2][1] = nil
if err := verifyResults(expectedResults, results); err != nil {
t.Fatal(err)
}
}

if _, err := db.Exec(`DELETE FROM t.kv WHERE k IN ($1)`, "c"); err != nil {
// Delete a row using a placeholder param.
if _, err := db.Exec(`DELETE FROM t.alltypes WHERE a IN ($1)`, 123); err != nil {
t.Fatal(err)
}

if rows, err := db.Query("SELECT * FROM t.kv"); err != nil {
if rows, err := db.Query("SELECT * FROM t.alltypes"); err != nil {
t.Fatal(err)
} else {
results := readAll(t, rows)
expectedResults := asResultSlice([][]string{
{"k", "v"},
{"a", "b"},
{"a", "b", "c", "d", "e", "f", "g"},
{"456", "<NULL>", "<NULL>", "<NULL>", "<NULL>", "<NULL>", "<NULL>"},
})
if err := verifyResults(expectedResults, results); err != nil {
t.Fatal(err)
}
}

if _, err := db.Exec(`CREATE TABLE t.alltypes (a BIGINT PRIMARY KEY, b FLOAT, c TEXT, d BOOLEAN)`); err != nil {
t.Fatal(err)
}
if _, err := db.Exec(`INSERT INTO t.alltypes (a, b, c, d) VALUES ($1, $2, $3, $4)`, 123, 3.4, "blah", true); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE a IN ($1)", 123); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE b IN ($1)", 3.4); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE c IN ($1)", "blah"); err != nil {
t.Fatal(err)
}
if _, err := db.Query("SELECT a, b FROM t.alltypes WHERE d IN ($1)", true); err != nil {
t.Fatal(err)
}
}

func TestinConnectionSettings(t *testing.T) {
func TestConnectionSettings(t *testing.T) {
defer leaktest.AfterTest(t)
s := server.StartTestServer(nil)
url := "https://root@" + s.ServingAddr() + "?certs=test_certs"
Expand Down
54 changes: 5 additions & 49 deletions sql/driver/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ package driver
import (
"fmt"
"strconv"

"github.com/cockroachdb/cockroach/sql/parser"
"time"
)

const (
// Endpoint is the URL path prefix which accepts incoming
// HTTP requests for the SQL API.
Endpoint = "/sql/"

timestampWithOffsetZoneFormat = "2006-01-02 15:04:05.999999999-07:00"
)

func (d Datum) String() string {
Expand All @@ -48,6 +49,8 @@ func (d Datum) String() string {
return string(t)
case *string:
return *t
case *Datum_Timestamp:
return time.Unix((*t).Sec, int64((*t).Nsec)).UTC().Format(timestampWithOffsetZoneFormat)
default:
panic(fmt.Sprintf("unexpected type %T", t))
}
Expand All @@ -62,50 +65,3 @@ func (Request) Method() Method {
func (Request) CreateReply() Response {
return Response{}
}

// GetParameters returns the Params slice as a `parameters`.
func (r Request) GetParameters() Parameters {
return Parameters(r.Params)
}

// Parameters implements the parser.Args interface.
type Parameters []Datum

// Arg implements the parser.Args interface.
func (p Parameters) Arg(name string) (parser.Datum, bool) {
if len(name) == 0 {
// This shouldn't happen unless the parser let through an invalid parameter
// specification.
panic(fmt.Sprintf("invalid empty parameter name"))
}
if ch := name[0]; ch < '0' || ch > '9' {
// TODO(pmattis): Add support for named parameters (vs the numbered
// parameter support below).
return nil, false
}
i, err := strconv.ParseInt(name, 10, 0)
if err != nil {
return nil, false
}
if i < 1 || int(i) > len(p) {
return nil, false
}
arg := p[i-1].GetValue()
if arg == nil {
return parser.DNull, true
}
switch t := arg.(type) {
case *bool:
return parser.DBool(*t), true
case *int64:
return parser.DInt(*t), true
case *float64:
return parser.DFloat(*t), true
case []byte:
return parser.DString(t), true
case *string:
return parser.DString(*t), true
default:
panic(fmt.Sprintf("unexpected type %T", t))
}
}
Loading

0 comments on commit c14db08

Please sign in to comment.