ソースを参照

marshalling: treat cql time type as time.Duration (#1286)

After discussions it was proposed to treat time as Go time.Duration.
This makes it consistent with what time is in the spec despite it's name.
Henrik Johansson 6 年 前
コミット
58370d57e4
3 ファイル変更115 行追加6 行削除
  1. 6 0
      helpers.go
  2. 55 6
      marshal.go
  3. 54 0
      marshal_test.go

+ 6 - 0
helpers.go

@@ -26,6 +26,8 @@ func goType(t TypeInfo) reflect.Type {
 		return reflect.TypeOf(*new(string))
 	case TypeBigInt, TypeCounter:
 		return reflect.TypeOf(*new(int64))
+	case TypeTime:
+		return reflect.TypeOf(*new(time.Duration))
 	case TypeTimestamp:
 		return reflect.TypeOf(*new(time.Time))
 	case TypeBlob:
@@ -93,6 +95,8 @@ func getCassandraBaseType(name string) Type {
 		return TypeInt
 	case "tinyint":
 		return TypeTinyInt
+	case "time":
+		return TypeTime
 	case "timestamp":
 		return TypeTimestamp
 	case "uuid":
@@ -231,6 +235,8 @@ func getApacheCassandraType(class string) Type {
 		return TypeSmallInt
 	case "ByteType":
 		return TypeTinyInt
+	case "TimeType":
+		return TypeTime
 	case "DateType", "TimestampType":
 		return TypeTimestamp
 	case "UUIDType", "LexicalUUIDType":

+ 55 - 6
marshal.go

@@ -82,7 +82,9 @@ func Marshal(info TypeInfo, value interface{}) ([]byte, error) {
 		return marshalDouble(info, value)
 	case TypeDecimal:
 		return marshalDecimal(info, value)
-	case TypeTimestamp, TypeTime:
+	case TypeTime:
+		return marshalTime(info, value)
+	case TypeTimestamp:
 		return marshalTimestamp(info, value)
 	case TypeList, TypeSet:
 		return marshalList(info, value)
@@ -146,7 +148,9 @@ func Unmarshal(info TypeInfo, data []byte, value interface{}) error {
 		return unmarshalDouble(info, data, value)
 	case TypeDecimal:
 		return unmarshalDecimal(info, data, value)
-	case TypeTimestamp, TypeTime:
+	case TypeTime:
+		return unmarshalTime(info, data, value)
+	case TypeTimestamp:
 		return unmarshalTimestamp(info, data, value)
 	case TypeList, TypeSet:
 		return unmarshalList(info, data, value)
@@ -1090,6 +1094,30 @@ func encBigInt2C(n *big.Int) []byte {
 	return nil
 }
 
+func marshalTime(info TypeInfo, value interface{}) ([]byte, error) {
+	switch v := value.(type) {
+	case Marshaler:
+		return v.MarshalCQL(info)
+	case unsetColumn:
+		return nil, nil
+	case int64:
+		return encBigInt(v), nil
+	case time.Duration:
+		return encBigInt(v.Nanoseconds()), nil
+	}
+
+	if value == nil {
+		return nil, nil
+	}
+
+	rv := reflect.ValueOf(value)
+	switch rv.Type().Kind() {
+	case reflect.Int64:
+		return encBigInt(rv.Int()), nil
+	}
+	return nil, marshalErrorf("can not marshal %T into %s", value, info)
+}
+
 func marshalTimestamp(info TypeInfo, value interface{}) ([]byte, error) {
 	switch v := value.(type) {
 	case Marshaler:
@@ -1104,8 +1132,6 @@ func marshalTimestamp(info TypeInfo, value interface{}) ([]byte, error) {
 		}
 		x := int64(v.UTC().Unix()*1e3) + int64(v.UTC().Nanosecond()/1e6)
 		return encBigInt(x), nil
-	case time.Duration:
-		return encBigInt(v.Nanoseconds()), nil
 	}
 
 	if value == nil {
@@ -1120,6 +1146,31 @@ func marshalTimestamp(info TypeInfo, value interface{}) ([]byte, error) {
 	return nil, marshalErrorf("can not marshal %T into %s", value, info)
 }
 
+func unmarshalTime(info TypeInfo, data []byte, value interface{}) error {
+	switch v := value.(type) {
+	case Unmarshaler:
+		return v.UnmarshalCQL(info, data)
+	case *int64:
+		*v = decBigInt(data)
+		return nil
+	case *time.Duration:
+		*v = time.Duration(decBigInt(data))
+		return nil
+	}
+
+	rv := reflect.ValueOf(value)
+	if rv.Kind() != reflect.Ptr {
+		return unmarshalErrorf("can not unmarshal into non-pointer %T", value)
+	}
+	rv = rv.Elem()
+	switch rv.Type().Kind() {
+	case reflect.Int64:
+		rv.SetInt(decBigInt(data))
+		return nil
+	}
+	return unmarshalErrorf("can not unmarshal %s into %T", info, value)
+}
+
 func unmarshalTimestamp(info TypeInfo, data []byte, value interface{}) error {
 	switch v := value.(type) {
 	case Unmarshaler:
@@ -1137,8 +1188,6 @@ func unmarshalTimestamp(info TypeInfo, data []byte, value interface{}) error {
 		nsec := (x - sec*1000) * 1000000
 		*v = time.Unix(sec, nsec).In(time.UTC)
 		return nil
-	case *time.Duration:
-		*v = time.Duration(decBigInt(data))
 	}
 
 	rv := reflect.ValueOf(value)

+ 54 - 0
marshal_test.go

@@ -314,6 +314,20 @@ var marshalTests = []struct {
 		nil,
 		nil,
 	},
+	{
+		NativeType{proto: 4, typ: TypeTime},
+		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
+		time.Duration(int64(1376387523000)),
+		nil,
+		nil,
+	},
+	{
+		NativeType{proto: 4, typ: TypeTime},
+		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
+		int64(1376387523000),
+		nil,
+		nil,
+	},
 	{
 		NativeType{proto: 2, typ: TypeTimestamp},
 		[]byte("\x00\x00\x01\x40\x77\x16\xe1\xb8"),
@@ -1217,6 +1231,46 @@ func TestMarshalPointer(t *testing.T) {
 	}
 }
 
+func TestMarshalTime(t *testing.T) {
+	durationS := "1h10m10s"
+	duration, _ := time.ParseDuration(durationS)
+	expectedData := encBigInt(duration.Nanoseconds())
+	var marshalTimeTests = []struct {
+		Info  TypeInfo
+		Data  []byte
+		Value interface{}
+	}{
+		{
+			NativeType{proto: 4, typ: TypeTime},
+			expectedData,
+			duration.Nanoseconds(),
+		},
+		{
+			NativeType{proto: 4, typ: TypeTime},
+			expectedData,
+			duration,
+		},
+		{
+			NativeType{proto: 4, typ: TypeTime},
+			expectedData,
+			&duration,
+		},
+	}
+
+	for i, test := range marshalTimeTests {
+		t.Log(i, test)
+		data, err := Marshal(test.Info, test.Value)
+		if err != nil {
+			t.Errorf("marshalTest[%d]: %v", i, err)
+			continue
+		}
+		if !bytes.Equal(data, test.Data) {
+			t.Errorf("marshalTest[%d]: expected %x (%v), got %x (%v) for time %s", i,
+				test.Data, decInt(test.Data), data, decInt(data), test.Value)
+		}
+	}
+}
+
 func TestMarshalTimestamp(t *testing.T) {
 	var marshalTimestampTests = []struct {
 		Info  TypeInfo