This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch go-type-ser in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 3735d7eb61c8517c0de038217e611214bbebc3d6 Author: Yang Xia <[email protected]> AuthorDate: Thu Jan 23 15:33:56 2025 -0800 update graph binary serializer to 4.0.0 spec --- gremlin-go/driver/graphBinary.go | 630 ++++++++++------------------------ gremlin-go/driver/graphBinary_test.go | 35 +- gremlin-go/driver/serializer.go | 62 +--- gremlin-go/go.mod | 4 + gremlin-go/go.sum | 9 + 5 files changed, 231 insertions(+), 509 deletions(-) diff --git a/gremlin-go/driver/graphBinary.go b/gremlin-go/driver/graphBinary.go index 39c6d2f029..fb6b2f30dd 100644 --- a/gremlin-go/driver/graphBinary.go +++ b/gremlin-go/driver/graphBinary.go @@ -23,6 +23,7 @@ import ( "bytes" "encoding/binary" "fmt" + "github.com/wk8/go-ordered-map/v2" "math" "math/big" "reflect" @@ -38,54 +39,33 @@ type dataType uint8 // dataType defined as constants. const ( - customType dataType = 0x00 - intType dataType = 0x01 - longType dataType = 0x02 - stringType dataType = 0x03 - dateType dataType = 0x04 - timestampType dataType = 0x05 - classType dataType = 0x06 - doubleType dataType = 0x07 - floatType dataType = 0x08 - listType dataType = 0x09 - mapType dataType = 0x0a - setType dataType = 0x0b - uuidType dataType = 0x0c - edgeType dataType = 0x0d - pathType dataType = 0x0e - propertyType dataType = 0x0f - vertexType dataType = 0x11 - vertexPropertyType dataType = 0x12 - barrierType dataType = 0x13 - bindingType dataType = 0x14 - cardinalityType dataType = 0x16 - bytecodeType dataType = 0x15 - columnType dataType = 0x17 - directionType dataType = 0x18 - operatorType dataType = 0x19 - orderType dataType = 0x1a - pickType dataType = 0x1b - popType dataType = 0x1c - lambdaType dataType = 0x1d - pType dataType = 0x1e - scopeType dataType = 0x1f - tType dataType = 0x20 - traverserType dataType = 0x21 - bigDecimalType dataType = 0x22 - bigIntegerType dataType = 0x23 - byteType dataType = 0x24 - byteBuffer dataType = 0x25 - shortType dataType = 0x26 - booleanType dataType = 0x27 - textPType dataType = 0x28 - traversalStrategyType dataType = 0x29 - bulkSetType dataType = 0x2a - mergeType dataType = 0x2e - dtType dataType = 0x2f - metricsType dataType = 0x2c - traversalMetricsType dataType = 0x2d - durationType dataType = 0x81 - nullType dataType = 0xFE + customType dataType = 0x00 + intType dataType = 0x01 + longType dataType = 0x02 + stringType dataType = 0x03 + datetimeType dataType = 0x04 + doubleType dataType = 0x07 + floatType dataType = 0x08 + listType dataType = 0x09 + mapType dataType = 0x0a + setType dataType = 0x0b + uuidType dataType = 0x0c + edgeType dataType = 0x0d + pathType dataType = 0x0e + propertyType dataType = 0x0f + vertexType dataType = 0x11 + vertexPropertyType dataType = 0x12 + directionType dataType = 0x18 + tType dataType = 0x20 + bigDecimalType dataType = 0x22 + bigIntegerType dataType = 0x23 + byteType dataType = 0x24 + byteBuffer dataType = 0x25 + shortType dataType = 0x26 + booleanType dataType = 0x27 + mergeType dataType = 0x2e + durationType dataType = 0x81 + nullType dataType = 0xFE ) var nullBytes = []byte{nullType.getCodeByte(), 0x01} @@ -168,92 +148,45 @@ func mapWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBin return buffer.Bytes(), nil } - v := reflect.ValueOf(value) - keys := v.MapKeys() - err := binary.Write(buffer, binary.BigEndian, int32(len(keys))) - if err != nil { - return nil, err - } - for _, k := range keys { - convKey := k.Convert(v.Type().Key()) - // serialize k - _, err := typeSerializer.write(k.Interface(), buffer) + if val, ok := value.(*orderedmap.OrderedMap[interface{}, interface{}]); ok { + err := binary.Write(buffer, binary.BigEndian, int32(val.Len())) if err != nil { return nil, err } - // serialize v.MapIndex(c_key) - val := v.MapIndex(convKey) - _, err = typeSerializer.write(val.Interface(), buffer) - if err != nil { - return nil, err - } - } - return buffer.Bytes(), nil -} - -func instructionWriter(instructions []instruction, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) error { - // Write {steps_length}, i.e number of steps. - err := binary.Write(buffer, binary.BigEndian, int32(len(instructions))) - if err != nil { - return err - } - - // Write {step_0} to {step_n}. - for _, instruction := range instructions { - // Write {name} of {step_i}. - // Note: {name} follows string writing, therefore write string length followed by actual string. - _, err = typeSerializer.writeValue(instruction.operator, buffer, false) - if err != nil { - return err + for pair := val.Oldest(); pair != nil; pair = pair.Next() { + // serialize k + _, err := typeSerializer.write(pair.Key, buffer) + if err != nil { + return nil, err + } + // serialize v + _, err = typeSerializer.write(pair.Value, buffer) + if err != nil { + return nil, err + } } - - // Write {values_length} of {step_i}. - err = binary.Write(buffer, binary.BigEndian, int32(len(instruction.arguments))) + } else { + v := reflect.ValueOf(value) + keys := v.MapKeys() + err := binary.Write(buffer, binary.BigEndian, int32(len(keys))) if err != nil { - return err + return nil, err } - - // Write {values_0} to {values_n}. - for _, argument := range instruction.arguments { - _, err = typeSerializer.write(argument, buffer) + for _, k := range keys { + convKey := k.Convert(v.Type().Key()) + // serialize k + _, err := typeSerializer.write(k.Interface(), buffer) + if err != nil { + return nil, err + } + // serialize v.MapIndex(c_key) + val := v.MapIndex(convKey) + _, err = typeSerializer.write(val.Interface(), buffer) if err != nil { - return err + return nil, err } } } - return nil -} - -// Format: {steps_length}{step_0}…{step_n}{sources_length}{source_0}…{source_n} -// Where: -// -// {steps_length} is an Int value describing the amount of steps. -// {step_i} is composed of {name}{values_length}{value_0}…{value_n}, where: -// {name} is a String. This is also known as the operator. -// {values_length} is an Int describing the amount values. -// {value_i} is a fully qualified typed value composed of {type_code}{type_info}{value_flag}{value} describing the step argument. -func bytecodeWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { - var bc Bytecode - switch typedVal := value.(type) { - case *GraphTraversal: - bc = *typedVal.Bytecode - case Bytecode: - bc = typedVal - case *Bytecode: - bc = *typedVal - default: - return nil, newError(err0402BytecodeWriterError) - } - - // Write {steps_length} and {step_0} through {step_n}, then {sources_length} and {source_0} through {source_n} - err := instructionWriter(bc.stepInstructions, buffer, typeSerializer) - if err != nil { - return nil, err - } - err = instructionWriter(bc.sourceInstructions, buffer, typeSerializer) - if err != nil { - return nil, err - } return buffer.Bytes(), nil } @@ -366,16 +299,6 @@ func bigDecimalWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *g return bigIntWriter(v.UnscaledValue, buffer, typeSerializer) } -func classWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { - var v GremlinType - if reflect.TypeOf(value).Kind() == reflect.Ptr { - v = *(value.(*GremlinType)) - } else { - v = value.(GremlinType) - } - return stringWriter(v.Fqcn, buffer, typeSerializer) -} - // Format: {Id}{Label}{properties} func vertexWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { v := value.(*Vertex) @@ -385,7 +308,7 @@ func vertexWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graph } // Not fully qualified. - _, err = typeSerializer.writeValue(v.Label, buffer, false) + _, err = typeSerializer.writeValue([1]string{v.Label}, buffer, false) if err != nil { return nil, err } @@ -403,7 +326,7 @@ func edgeWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBi } // Not fully qualified - _, err = typeSerializer.writeValue(e.Label, buffer, false) + _, err = typeSerializer.writeValue([1]string{e.Label}, buffer, false) if err != nil { return nil, err } @@ -465,7 +388,7 @@ func vertexPropertyWriter(value interface{}, buffer *bytes.Buffer, typeSerialize } // Not fully qualified. - _, err = typeSerializer.writeValue(vp.Label, buffer, false) + _, err = typeSerializer.writeValue([1]string{vp.Label}, buffer, false) if err != nil { return nil, err } @@ -502,7 +425,29 @@ func setWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBin func timeWriter(value interface{}, buffer *bytes.Buffer, _ *graphBinaryTypeSerializer) ([]byte, error) { t := value.(time.Time) - err := binary.Write(buffer, binary.BigEndian, t.UnixMilli()) + err := binary.Write(buffer, binary.BigEndian, int32(t.Year())) + if err != nil { + return nil, err + } + err = binary.Write(buffer, binary.BigEndian, byte(t.Month())) + if err != nil { + return nil, err + } + err = binary.Write(buffer, binary.BigEndian, byte(t.Day())) + if err != nil { + return nil, err + } + // construct time of day in nanoseconds + h := t.Hour() + m := t.Minute() + s := t.Second() + ns := (h * 60 * 60 * 1e9) + (m * 60 * 1e9) + (s * 1e9) + t.Nanosecond() + err = binary.Write(buffer, binary.BigEndian, int64(ns)) + if err != nil { + return nil, err + } + _, os := t.Zone() + err = binary.Write(buffer, binary.BigEndian, int32(os)) if err != nil { return nil, err } @@ -534,32 +479,6 @@ func enumWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBi return buffer.Bytes(), err } -// Format: {language}{script}{arguments_length} -func lambdaWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { - lambda := value.(*Lambda) - if lambda.Language == "" { - lambda.Language = "gremlin-groovy" - } - _, err := typeSerializer.writeValue(lambda.Language, buffer, false) - if err != nil { - return nil, err - } - - _, err = typeSerializer.writeValue(lambda.Script, buffer, false) - if err != nil { - return nil, err - } - - // It's hard to know how many parameters there are without extensive string parsing. - // Instead, we can set -1 which means unknown. - err = binary.Write(buffer, binary.BigEndian, int32(-1)) - if err != nil { - return nil, err - } - - return buffer.Bytes(), nil -} - // Format: {strategy_class}{configuration} func traversalStrategyWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { ts := value.(*traversalStrategy) @@ -572,84 +491,8 @@ func traversalStrategyWriter(value interface{}, buffer *bytes.Buffer, typeSerial return mapWriter(ts.configuration, buffer, typeSerializer) } -func pWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { - var v p - if reflect.TypeOf(value).Kind() == reflect.Ptr { - v = *(value.(*p)) - } else { - v = value.(p) - } - _, err := typeSerializer.writeValue(v.operator, buffer, false) - if err != nil { - return nil, err - } - - err = binary.Write(buffer, binary.BigEndian, int32(len(v.values))) - if err != nil { - return nil, err - } - - for _, pValue := range v.values { - _, err := typeSerializer.write(pValue, buffer) - if err != nil { - return nil, err - } - } - return buffer.Bytes(), err -} - -func textPWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { - var v textP - if reflect.TypeOf(value).Kind() == reflect.Ptr { - v = *(value.(*textP)) - } else { - v = value.(textP) - } - _, err := typeSerializer.writeValue(v.operator, buffer, false) - if err != nil { - return nil, err - } - - err = binary.Write(buffer, binary.BigEndian, int32(len(v.values))) - if err != nil { - return nil, err - } - - for _, pValue := range v.values { - _, err := typeSerializer.write(pValue, buffer) - if err != nil { - return nil, err - } - } - return buffer.Bytes(), err -} - -// Format: {key}{value} -func bindingWriter(value interface{}, buffer *bytes.Buffer, typeSerializer *graphBinaryTypeSerializer) ([]byte, error) { - var v Binding - if reflect.TypeOf(value).Kind() == reflect.Ptr { - v = *(value.(*Binding)) - } else { - v = value.(Binding) - } - - // Not fully qualified. - _, err := typeSerializer.writeValue(v.Key, buffer, false) - if err != nil { - return nil, err - } - - _, err = typeSerializer.write(v.Value, buffer) - if err != nil { - return nil, err - } - return buffer.Bytes(), nil -} - func (serializer *graphBinaryTypeSerializer) getType(val interface{}) (dataType, error) { switch val.(type) { - case *Bytecode, Bytecode, *GraphTraversal: - return bytecodeType, nil case string: return stringType, nil case uint, uint64, *big.Int: @@ -678,58 +521,26 @@ func (serializer *graphBinaryTypeSerializer) getType(val interface{}) (dataType, return propertyType, nil case *VertexProperty: return vertexPropertyType, nil - case *Lambda: - return lambdaType, nil - case *traversalStrategy: - return traversalStrategyType, nil case *Path: return pathType, nil case Set: return setType, nil case time.Time: - return dateType, nil + return datetimeType, nil case time.Duration: return durationType, nil - case cardinality: - return cardinalityType, nil - case column: - return columnType, nil case direction: return directionType, nil - case operator: - return operatorType, nil - case order: - return orderType, nil - case pick: - return pickType, nil - case pop: - return popType, nil case t: return tType, nil - case barrier: - return barrierType, nil - case scope: - return scopeType, nil case merge: return mergeType, nil - case dt: - return dtType, nil - case p, Predicate: - return pType, nil - case textP, TextPredicate: - return textPType, nil - case *Binding, Binding: - return bindingType, nil case *BigDecimal, BigDecimal: return bigDecimalType, nil - case *GremlinType, GremlinType: - return classType, nil - case *Metrics, Metrics: - return metricsType, nil - case *TraversalMetrics, TraversalMetrics: - return traversalMetricsType, nil case *ByteBuffer, ByteBuffer: return byteBuffer, nil + case *orderedmap.OrderedMap[interface{}, interface{}], orderedmap.OrderedMap[interface{}, interface{}]: + return mapType, nil default: switch reflect.TypeOf(val).Kind() { case reflect.Map: @@ -926,8 +737,6 @@ func getDefaultValue(dataType dataType) interface{} { switch dataType { case intType, bigIntegerType, longType, shortType, byteType, booleanType, floatType, doubleType: return 0 - case traverserType, stringType: - return "" case uuidType: return uuid.Nil case vertexType: @@ -942,7 +751,7 @@ func getDefaultValue(dataType dataType) interface{} { return Path{} case setType: return SimpleSet{} - case dateType, timestampType: + case datetimeType: return time.Time{} case durationType: return time.Duration(0) @@ -952,15 +761,28 @@ func getDefaultValue(dataType dataType) interface{} { } // Composite -func readList(data *[]byte, i *int) (interface{}, error) { +func readList(data *[]byte, i *int, flag byte) (interface{}, error) { sz := readIntSafe(data, i) var valList []interface{} - for j := int32(0); j < sz; j++ { - val, err := readFullyQualifiedNullable(data, i, true) - if err != nil { - return nil, err + if flag == 0x02 { + for j := int32(0); j < sz; j++ { + val, err := readFullyQualifiedNullable(data, i, true) + if err != nil { + return nil, err + } + bulk := readIntSafe(data, i) + for k := int32(0); k < bulk; k++ { + valList = append(valList, val) + } + } + } else { + for j := int32(0); j < sz; j++ { + val, err := readFullyQualifiedNullable(data, i, true) + if err != nil { + return nil, err + } + valList = append(valList, val) } - valList = append(valList, val) } return valList, nil } @@ -975,32 +797,59 @@ func readByteBuffer(data *[]byte, i *int) (interface{}, error) { return r, nil } -func readMap(data *[]byte, i *int) (interface{}, error) { +func readMap(data *[]byte, i *int, flag byte) (interface{}, error) { sz := readUint32Safe(data, i) - var mapData = make(map[interface{}]interface{}) - for j := uint32(0); j < sz; j++ { - k, err := readFullyQualifiedNullable(data, i, true) - if err != nil { - return nil, err - } - v, err := readFullyQualifiedNullable(data, i, true) - if err != nil { - return nil, err + if flag == 0x02 { + var mapData = orderedmap.New[interface{}, interface{}]() + for j := uint32(0); j < sz; j++ { + k, err := readFullyQualifiedNullable(data, i, true) + if err != nil { + return nil, err + } + v, err := readFullyQualifiedNullable(data, i, true) + if err != nil { + return nil, err + } + if k == nil { + mapData.Set(nil, v) + } else if reflect.TypeOf(k).Comparable() { + mapData.Set(k, v) + } else { + switch reflect.TypeOf(k).Kind() { + case reflect.Map: + mapData.Set(&k, v) + default: + mapData.Set(fmt.Sprint(k), v) + } + } } - if k == nil { - mapData[nil] = v - } else if reflect.TypeOf(k).Comparable() { - mapData[k] = v - } else { - switch reflect.TypeOf(k).Kind() { - case reflect.Map: - mapData[&k] = v - default: - mapData[fmt.Sprint(k)] = v + return mapData, nil + } else { + var mapData = make(map[interface{}]interface{}) + for j := uint32(0); j < sz; j++ { + k, err := readFullyQualifiedNullable(data, i, true) + if err != nil { + return nil, err + } + v, err := readFullyQualifiedNullable(data, i, true) + if err != nil { + return nil, err + } + if k == nil { + mapData[nil] = v + } else if reflect.TypeOf(k).Comparable() { + mapData[k] = v + } else { + switch reflect.TypeOf(k).Kind() { + case reflect.Map: + mapData[&k] = v + default: + mapData[fmt.Sprint(k)] = v + } } } + return mapData, nil } - return mapData, nil } func readMapUnqualified(data *[]byte, i *int) (interface{}, error) { @@ -1028,7 +877,8 @@ func readMapUnqualified(data *[]byte, i *int) (interface{}, error) { } func readSet(data *[]byte, i *int) (interface{}, error) { - list, err := readList(data, i) + // TODO placeholder flag + list, err := readList(data, i, 0x00) if err != nil { return nil, err } @@ -1041,7 +891,15 @@ func readUuid(data *[]byte, i *int) (interface{}, error) { } func timeReader(data *[]byte, i *int) (interface{}, error) { - return time.UnixMilli(readLongSafe(data, i)), nil + year := readIntSafe(data, i) + month := readByteSafe(data, i) + day := readByteSafe(data, i) + ns := readLongSafe(data, i) + offset := readIntSafe(data, i) + // only way to pass offset info, timezone display is fixed to UTC as consequence (offset is calculated properly) + loc := time.FixedZone("UTC", int(offset)) + datetime := time.Date(int(year), time.Month(month), int(day), 0, 0, 0, int(ns), loc) + return datetime, nil } func durationReader(data *[]byte, i *int) (interface{}, error) { @@ -1063,11 +921,11 @@ func vertexReaderReadingProperties(data *[]byte, i *int, readProperties bool) (i if err != nil { return nil, err } - label, err := readUnqualified(data, i, stringType, false) + label, err := readUnqualified(data, i, listType, false) if err != nil { return nil, err } - v.Label = label.(string) + v.Label = label.([]interface{})[0].(string) if readProperties { props, err := readFullyQualifiedNullable(data, i, true) if err != nil { @@ -1090,11 +948,11 @@ func edgeReader(data *[]byte, i *int) (interface{}, error) { if err != nil { return nil, err } - label, err := readUnqualified(data, i, stringType, false) + label, err := readUnqualified(data, i, listType, false) if err != nil { return nil, err } - e.Label = label.(string) + e.Label = label.([]interface{})[0].(string) v, err := vertexReaderReadingProperties(data, i, false) if err != nil { return nil, err @@ -1142,11 +1000,11 @@ func vertexPropertyReader(data *[]byte, i *int) (interface{}, error) { if err != nil { return nil, err } - label, err := readUnqualified(data, i, stringType, false) + label, err := readUnqualified(data, i, listType, false) if err != nil { return nil, err } - vp.Label = label.(string) + vp.Label = label.([]interface{})[0].(string) vp.Value, err = readFullyQualifiedNullable(data, i, true) if err != nil { return nil, err @@ -1186,35 +1044,6 @@ func pathReader(data *[]byte, i *int) (interface{}, error) { return path, err } -// {bulk int}{fully qualified value} -func traverserReader(data *[]byte, i *int) (interface{}, error) { - var err error - traverser := new(Traverser) - traverser.bulk = readLongSafe(data, i) - traverser.value, err = readFullyQualifiedNullable(data, i, true) - if err != nil { - return nil, err - } - return traverser, nil -} - -// {int32 length}{fully qualified item_0}{int64 repetition_0}...{fully qualified item_n}{int64 repetition_n} -func bulkSetReader(data *[]byte, i *int) (interface{}, error) { - sz := int(readIntSafe(data, i)) - var valList []interface{} - for j := 0; j < sz; j++ { - val, err := readFullyQualifiedNullable(data, i, true) - if err != nil { - return nil, err - } - rep := readLongSafe(data, i) - for k := 0; k < int(rep); k++ { - valList = append(valList, val) - } - } - return valList, nil -} - // {type code (always string so ignore)}{nil code (always false so ignore)}{int32 size}{string enum} func enumReader(data *[]byte, i *int) (interface{}, error) { typeCode := readDataType(data, i) @@ -1225,113 +1054,6 @@ func enumReader(data *[]byte, i *int) (interface{}, error) { return readString(data, i) } -// {unqualified key}{fully qualified value} -func bindingReader(data *[]byte, i *int) (interface{}, error) { - b := new(Binding) - val, err := readUnqualified(data, i, stringType, false) - if err != nil { - return nil, err - } - b.Key = val.(string) - - b.Value, err = readFullyQualifiedNullable(data, i, true) - if err != nil { - return nil, err - } - return b, nil -} - -// {id}{name}{duration}{counts}{annotations}{nested_metrics} -func metricsReader(data *[]byte, i *int) (interface{}, error) { - metrics := new(Metrics) - val, err := readUnqualified(data, i, stringType, false) - if err != nil { - return nil, err - } - metrics.Id = val.(string) - - val, err = readUnqualified(data, i, stringType, false) - if err != nil { - return nil, err - } - metrics.Name = val.(string) - - dur, err := readLong(data, i) - if err != nil { - return nil, err - } - metrics.Duration = dur.(int64) - - counts, err := readMap(data, i) - cmap := counts.(map[interface{}]interface{}) - if err != nil { - return nil, err - } - metrics.Counts = make(map[string]int64, len(cmap)) - for k := range cmap { - metrics.Counts[k.(string)] = cmap[k].(int64) - } - - annotations, err := readMap(data, i) - if err != nil { - return nil, err - } - amap := annotations.(map[interface{}]interface{}) - if err != nil { - return nil, err - } - metrics.Annotations = make(map[string]interface{}, len(amap)) - for k := range amap { - metrics.Annotations[k.(string)] = amap[k] - } - - nested, err := readList(data, i) - if err != nil { - return nil, err - } - list := nested.([]interface{}) - metrics.NestedMetrics = make([]Metrics, len(list)) - for i, metric := range list { - metrics.NestedMetrics[i] = metric.(Metrics) - } - - return metrics, nil -} - -// {id}{name}{duration}{counts}{annotations}{nested_metrics} -func traversalMetricsReader(data *[]byte, i *int) (interface{}, error) { - m := new(TraversalMetrics) - dur, err := readLong(data, i) - if err != nil { - return nil, err - } - m.Duration = dur.(int64) - - nested, err := readList(data, i) - if err != nil { - return nil, err - } - list := nested.([]interface{}) - m.Metrics = make([]Metrics, len(list)) - for i, metric := range list { - m.Metrics[i] = *metric.(*Metrics) - } - - return m, nil -} - -// Format: A String containing the fqcn. -func readClass(data *[]byte, i *int) (interface{}, error) { - gremlinType := new(GremlinType) - str, err := readString(data, i) - if err != nil { - return nil, err - } - gremlinType.Fqcn = str.(string) - - return gremlinType, nil -} - func readUnqualified(data *[]byte, i *int, dataTyp dataType, nullable bool) (interface{}, error) { if nullable && readByteSafe(data, i) == valueFlagNull { return getDefaultValue(dataTyp), nil @@ -1351,9 +1073,17 @@ func readFullyQualifiedNullable(data *[]byte, i *int, nullable bool) (interface{ } return nil, nil } else if nullable { - if readByteSafe(data, i) == valueFlagNull { + flag := readByteSafe(data, i) + fmt.Println(flag) + if flag == valueFlagNull { return getDefaultValue(dataTyp), nil } + if dataTyp == listType { + return readList(data, i, flag) + } + if dataTyp == mapType { + return readMap(data, i, flag) + } } deserializer, ok := deserializers[dataTyp] if !ok { diff --git a/gremlin-go/driver/graphBinary_test.go b/gremlin-go/driver/graphBinary_test.go index d8c66d2816..a4d76596ae 100644 --- a/gremlin-go/driver/graphBinary_test.go +++ b/gremlin-go/driver/graphBinary_test.go @@ -24,6 +24,7 @@ import ( "encoding/binary" "fmt" "github.com/stretchr/testify/assert" + "github.com/wk8/go-ordered-map/v2" "golang.org/x/text/language" "math/big" "reflect" @@ -83,16 +84,6 @@ func TestGraphBinaryV1(t *testing.T) { assert.Nil(t, err) assert.Equal(t, str, res) }) - t.Run("read-write GremlinType", func(t *testing.T) { - pos := 0 - var buffer bytes.Buffer - source := &GremlinType{"test fqcn"} - buf, err := classWriter(source, &buffer, nil) - assert.Nil(t, err) - res, err := readClass(&buf, &pos) - assert.Nil(t, err) - assert.Equal(t, source, res) - }) t.Run("read-write bool", func(t *testing.T) { pos := 0 var buffer bytes.Buffer @@ -198,7 +189,7 @@ func TestGraphBinaryV1(t *testing.T) { source := []interface{}{int32(111), "str"} buf, err := listWriter(source, &buffer, nil) assert.Nil(t, err) - res, err := readList(&buf, &pos) + res, err := readList(&buf, &pos, 0x00) assert.Nil(t, err) assert.Equal(t, source, res) }) @@ -228,10 +219,23 @@ func TestGraphBinaryV1(t *testing.T) { source := map[interface{}]interface{}{1: "s1", "s2": 2, nil: nil} buf, err := mapWriter(source, &buffer, nil) assert.Nil(t, err) - res, err := readMap(&buf, &pos) + res, err := readMap(&buf, &pos, 0x00) assert.Nil(t, err) assert.Equal(t, fmt.Sprintf("%v", source), fmt.Sprintf("%v", res)) }) + t.Run("read-write ordered map", func(t *testing.T) { + pos := 0 + var buffer bytes.Buffer + source := orderedmap.New[interface{}, interface{}]() + source.Set(int32(1), "s1") + source.Set(int32(2), "s2") + source.Set(nil, nil) + buf, err := mapWriter(source, &buffer, nil) + assert.Nil(t, err) + res, err := readMap(&buf, &pos, 0x02) + assert.Nil(t, err) + assert.Equal(t, source, res) + }) t.Run("read incomparable map: a map value as the key", func(t *testing.T) { // prepare test data var buf = &bytes.Buffer{} @@ -255,7 +259,7 @@ func TestGraphBinaryV1(t *testing.T) { data := buf.Bytes() i := 0 - result, err := readMap(&data, &i) + result, err := readMap(&data, &i, 0x00) if err != nil { t.Fatalf("readMap failed: %v", err) } @@ -291,7 +295,7 @@ func TestGraphBinaryV1(t *testing.T) { data := buf.Bytes() i := 0 - result, err := readMap(&data, &i) + result, err := readMap(&data, &i, 0x00) if err != nil { t.Fatalf("readMap failed: %v", err) } @@ -310,7 +314,8 @@ func TestGraphBinaryV1(t *testing.T) { assert.Nil(t, err) res, err := timeReader(&buf, &pos) assert.Nil(t, err) - assert.Equal(t, source, res) + // ISO format + assert.Equal(t, source.Format(time.RFC3339Nano), res.(time.Time).Format(time.RFC3339Nano)) }) }) diff --git a/gremlin-go/driver/serializer.go b/gremlin-go/driver/serializer.go index 04a7e468b5..30ffef001a 100644 --- a/gremlin-go/driver/serializer.go +++ b/gremlin-go/driver/serializer.go @@ -228,7 +228,6 @@ func (gs graphBinarySerializer) deserializeMessage(message []byte) (response, er func initSerializers() { serializers = map[dataType]writer{ - bytecodeType: bytecodeWriter, stringType: stringWriter, bigDecimalType: bigDecimalWriter, bigIntegerType: bigIntWriter, @@ -255,35 +254,20 @@ func initSerializers() { err := binary.Write(buffer, binary.BigEndian, value) return buffer.Bytes(), err }, - vertexType: vertexWriter, - edgeType: edgeWriter, - propertyType: propertyWriter, - vertexPropertyType: vertexPropertyWriter, - lambdaType: lambdaWriter, - traversalStrategyType: traversalStrategyWriter, - pathType: pathWriter, - setType: setWriter, - dateType: timeWriter, - durationType: durationWriter, - cardinalityType: enumWriter, - columnType: enumWriter, - directionType: enumWriter, - dtType: enumWriter, - operatorType: enumWriter, - orderType: enumWriter, - pickType: enumWriter, - popType: enumWriter, - tType: enumWriter, - barrierType: enumWriter, - scopeType: enumWriter, - mergeType: enumWriter, - pType: pWriter, - textPType: textPWriter, - bindingType: bindingWriter, - mapType: mapWriter, - listType: listWriter, - byteBuffer: byteBufferWriter, - classType: classWriter, + vertexType: vertexWriter, + edgeType: edgeWriter, + propertyType: propertyWriter, + vertexPropertyType: vertexPropertyWriter, + pathType: pathWriter, + setType: setWriter, + datetimeType: timeWriter, + durationType: durationWriter, + directionType: enumWriter, + tType: enumWriter, + mergeType: enumWriter, + mapType: mapWriter, + listType: listWriter, + byteBuffer: byteBufferWriter, } } @@ -302,34 +286,24 @@ func initDeserializers() { stringType: readString, // Composite - listType: readList, - mapType: readMap, + //listType: readList, + //mapType: readMap, setType: readSet, uuidType: readUuid, byteBuffer: readByteBuffer, - classType: readClass, // Date Time - dateType: timeReader, - timestampType: timeReader, - durationType: durationReader, + datetimeType: timeReader, + durationType: durationReader, // Graph - traverserType: traverserReader, vertexType: vertexReader, edgeType: edgeReader, propertyType: propertyReader, vertexPropertyType: vertexPropertyReader, pathType: pathReader, - bulkSetType: bulkSetReader, tType: enumReader, directionType: enumReader, - dtType: enumReader, - bindingType: bindingReader, - - // Metrics - metricsType: metricsReader, - traversalMetricsType: traversalMetricsReader, // Customer customType: customTypeReader, diff --git a/gremlin-go/go.mod b/gremlin-go/go.mod index 39cb593de2..0123714f30 100644 --- a/gremlin-go/go.mod +++ b/gremlin-go/go.mod @@ -25,11 +25,14 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/nicksnyder/go-i18n/v2 v2.4.1 github.com/stretchr/testify v1.9.0 + github.com/wk8/go-ordered-map/v2 v2.1.8 golang.org/x/text v0.21.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect github.com/cucumber/messages/go/v21 v21.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -37,6 +40,7 @@ require ( github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-memdb v1.3.4 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect diff --git a/gremlin-go/go.sum b/gremlin-go/go.sum index 24bf83a7a5..365be7fe1a 100644 --- a/gremlin-go/go.sum +++ b/gremlin-go/go.sum @@ -1,5 +1,9 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= @@ -30,11 +34,14 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g= github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -54,6 +61,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
