apache / cassandra-gocql-driver

GoCQL Driver for Apache Cassandra®
https://cassandra.apache.org/
Apache License 2.0
2.57k stars 618 forks source link

Always encode BigInt for time.Time #1657

Open jameshartig opened 2 years ago

jameshartig commented 2 years ago

Please answer these questions before submitting your issue. Thanks!

What version of Cassandra are you using?

Yugabyte 2.14

What version of Gocql are you using?

v1.2.1

What version of Go are you using?

Go 1.19

What did you do?

package main

import (
    "fmt"
    "time"

    "github.com/gocql/gocql"
)

func main() {
    cluster := gocql.NewCluster("127.0.0.1:9042")
    db, err := cluster.CreateSession()
    if err != nil {
        panic(err)
    }
    err = db.Query("CREATE KEYSPACE IF NOT EXISTS test WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};").Exec()
    if err != nil {
        panic(err)
    }
    err = db.Query("CREATE TABLE IF NOT EXISTS test.test (a TINYINT, b TIMESTAMP, PRIMARY KEY(a))").Exec()
    if err != nil {
        panic(err)
    }
    d := time.Date(2022, time.September, 20, 10, 0, 0, 0, time.UTC)
    err = db.Query("INSERT INTO test.test (a, b) VALUES (?, ?)", 1, d).Exec()
    if err != nil {
        panic(err)
    }
    var d2 time.Time
    err = db.Query("SELECT b FROM test.test WHERE a = ?", 1).Scan(&d2)
    if err != nil {
        panic(err)
    }
    fmt.Printf("sent: %v\n", d)
    fmt.Printf("got: %v\n", d2)
    d2 = time.Time{}
    err = db.Query("SELECT b FROM test.test WHERE a = ? AND b = ? ALLOW FILTERING", 1, d).Scan(&d2)
    if err != nil {
        panic(err)
    }
    fmt.Printf("sent: %v\n", d)
    fmt.Printf("got: %v\n", d2)
    d2 = time.Time{}
    err = db.Query("SELECT b FROM test.test WHERE a = ? AND b > ? ALLOW FILTERING", 1, time.Time{}).Scan(&d2)
    if err != nil {
        panic(err)
    }
    fmt.Printf("sent: %v\n", d)
    fmt.Printf("got: %v\n", d2)
}

What did you expect to see?

I expect the last query to not fail with not found.

Ran yugabyte with:

docker run --rm -it -p 9042:9042 --name yb yugabytedb/yugabyte:2.14.1.0-b36 bash -c "bin/yugabyted start --tserver_flags 'pgsql_proxy_bind_address=0.0.0.0:5433,cql_proxy_bind_address=0.0.0.0:9042' --daemon=false"

What did you see instead?

Instead it panic's because the last query was not found.


Related: https://github.com/yugabyte/yugabyte-db/issues/14084

I believe this is because gocql encodes empty time.Time values as []byte{} rather than just letting them encode as -62135596800000.

martin-sucha commented 2 years ago

The marshaling as []byte{} was added in https://github.com/gocql/gocql/pull/441 .

Also note that CQL protocol recognizes zero bytes as empty value:

For legacy compatibility reasons, note that most non-string types support "empty" values (i.e. a value with zero length). An empty value is distinct from NULL, which is encoded with a negative length.

However, I don't have any experience with how these empty values (should) behave.

It seems that the Java driver treats the empty value as null. How do the other drivers treat those values?

jameshartig commented 2 years ago

I believe that's how Yugabyte is interpreting the empty bytes so b > NULL is false since you can't compare against NULL.

It looks like the datastax Node.JS driver always encodes an int: https://github.com/datastax/nodejs-driver/blob/fe7b965b6241545f03b6fbbca20fe72c8bc99196/lib/encoder.js#L433