// // readSingleRecord should be called to read a single record from the DBClient connection // stream (from a db query/command). In particular, this function should be called // after the resultType has been read from the stream and resultType == 'r' (byte 114). // When this is called the 'r' byte shown below should have already been read. This // function will then read everything else shown here - including the serialized record, // but *NOT* including the byte after the serialized record (which is 0 to indicate // End of Transmission). // // Writing byte (1 byte): 114 [OChannelBinaryServer] <- 'r' (type=single-record) // Writing short (2 bytes): 0 [OChannelBinaryServer] <- 0=full record (-2=null, -3=RID only) // Writing byte (1 byte): 100 [OChannelBinaryServer] <- 'd'=document ('f'=flat data, 'b'=raw bytes) // Writing short (2 bytes): 11 [OChannelBinaryServer] <- cluster-id (RID part 1) // Writing long (8 bytes): 0 [OChannelBinaryServer] <- cluster-pos (RID part 2) // Writing int (4 bytes): 1 [OChannelBinaryServer] <- version // Writing bytes (4+26=30 bytes): [0, 14, 80, 97, 116, ... , 110, 107, 1] <- serialized record // // A new single ODocument pointer is returned. // // TODO: this method needs to determine how to handle 'f' (flat data) and 'b' (raw bytes) // func readSingleRecord(dbc *DBClient) (*oschema.ODocument, error) { var doc *oschema.ODocument resultType, err := rw.ReadShort(dbc.conx) if err != nil { return nil, oerror.NewTrace(err) } if resultType == RecordNull { // null record // do nothing - return the zero values of the return types return nil, nil } else if resultType == RecordRID { orid, err := readRID(dbc) if err != nil { return nil, oerror.NewTrace(err) } doc = oschema.NewDocument("") doc.RID = orid ogl.Warn(fmt.Sprintf("readSingleRecord :: Code path not seen before!!: SQLCommand resulted in RID: %s\n", orid)) // TODO: would now load that record from the DB if the user (Go SQL API) wants it return doc, nil } else if resultType != int16(0) { _, file, line, _ := runtime.Caller(0) return nil, fmt.Errorf("Unexpected resultType in SQLCommand (file: %s; line %d): %d", file, line+1, resultType) } // if get here then have a full record, which can be in one of three formats: // - "flat data" // - "raw bytes" // - "document" recType, err := rw.ReadByte(dbc.conx) if err != nil { return nil, oerror.NewTrace(err) } if recType == byte('d') { return readSingleDocument(dbc) } else if recType == byte('f') { return readFlatDataRecord(dbc) // ??? } else if recType == byte('b') { return readRawBytesRecord(dbc) // ??? } else { _, file, line, _ := runtime.Caller(0) return nil, fmt.Errorf("Unexpected record type. Expected 'd', 'f' or 'b', but was %v (file: %s; line %d)", recType, file, line+1) } }
// // TODO: in the Java version there is a "fill" method on ODocument (ORecord) // to create a record from these entries => maybe move this there? // func createDocumentFromBytes(rid oschema.ORID, recVersion int32, serializedDoc []byte, dbc *DBClient) (*oschema.ODocument, error) { var doc *oschema.ODocument doc = oschema.NewDocument("") // don't know classname yet (in serialized record) doc.RID = rid doc.Version = recVersion // TODO: here need to make a query to look up the schema of the doc if we don't have it already cached // the first byte specifies record serialization version // use it to look up serializer and strip off that byte serde := dbc.RecordSerDes[int(serializedDoc[0])] recBuf := obuf.NewReadBuffer(serializedDoc[1:]) err := serde.Deserialize(dbc, doc, recBuf) if err != nil { return nil, err } return doc, nil }
// // The serialization version (the first byte of the serialized record) should // be stripped off (already read) from the io.Reader being passed in. // func (serde ORecordSerializerV0) Deserialize(dbc *DBClient, doc *oschema.ODocument, buf *obuf.ReadBuf) (err error) { if doc == nil { return errors.New("ODocument reference passed into ORecordSerializerBinaryV0.Deserialize was null") } // temporarily set state for the duration of this Deserialize call // dbc is allowed to be nil for reentrant (recursive) calls -- in which // case serde.dbc should already be set (not-nil) if dbc != nil { if serde.dbc != nil { return errors.New("Attempted to set dbc again in Serialize when it is already set") } serde.dbc = dbc defer func() { serde.dbc = nil }() } else if serde.dbc == nil { return errors.New("dbc *DBClient passed into Deserialize was null and dbc had not already been set in Serializer state") } classname, err := readClassname(buf) if err != nil { return oerror.NewTrace(err) } doc.Classname = classname header, err := readHeader(buf) if err != nil { return oerror.NewTrace(err) } serde.refreshGlobalPropertiesIfRequired(header) for i, prop := range header.properties { var ofield *oschema.OField if len(prop.name) == 0 { globalProp, ok := serde.dbc.GetCurrDB().GlobalProperties[int(prop.id)] if !ok { panic(oerror.ErrStaleGlobalProperties) // TODO: should return this instead } ofield = &oschema.OField{ ID: prop.id, Name: globalProp.Name, Type: globalProp.Type, } } else { ofield = &oschema.OField{ ID: int32(-1), Name: string(prop.name), Type: prop.typ, } } // if data ptr is 0 (NULL), then it has no entry/value in the serialized record if header.dataPtrs[i] != 0 { buf.Seek(uint(header.dataPtrs[i] - 1)) // -1 bcs the lead byte (serialization version) was stripped off val, err := serde.readDataValue(buf, ofield.Type) if err != nil { return err } ofield.Value = val } doc.AddField(ofield.Name, ofield) } return nil }
// // In Progress attempt to rewrite writeSerializedRecord and related fns // using a seekable/skipping WriteBuf // func (serde ORecordSerializerV0) writeSerializedRecord(wbuf *obuf.WriteBuf, doc *oschema.ODocument) (err error) { nfields := len(doc.FieldNames()) ptrPos := make([]int, 0, nfields) // position in buf where data ptr int needs to be written currDB := serde.dbc.GetCurrDB() oclass, ok := currDB.Classes[doc.Classname] docFields := doc.GetFields() for _, fld := range docFields { var oprop *oschema.OProperty if ok { oprop = oclass.Properties[fld.Name] } // FROM THE JAVA CLIENT: // if (properties[i] != null) { // OVarIntSerializer.write(bytes, (properties[i].getId() + 1) * -1); // if (properties[i].getType() != OType.ANY) // pos[i] = bytes.alloc(OIntegerSerializer.INT_SIZE); // else // pos[i] = bytes.alloc(OIntegerSerializer.INT_SIZE + 1); // TODO: why does ANY required an additional byte? // } else { // writeString(bytes, entry.getKey()); // pos[i] = bytes.alloc(OIntegerSerializer.INT_SIZE + 1); if oprop != nil { // if property is known in the global properties, then // just write its encoded id varint.EncodeAndWriteVarInt32(wbuf, encodeFieldIDForHeader(oprop.ID)) ptrPos = append(ptrPos, wbuf.Len()) wbuf.Skip(4) // Note: no need to write property type when writing property ID } else { // property Name err = varint.WriteString(wbuf, fld.Name) if err != nil { return oerror.NewTrace(err) } ptrPos = append(ptrPos, wbuf.Len()) wbuf.Skip(4) // property Type err = rw.WriteByte(wbuf, byte(fld.Type)) if err != nil { return oerror.NewTrace(err) } } } wbuf.WriteByte(0) // End of Header sentinel // now write out the data values for i, fld := range docFields { currPos := wbuf.Len() wbuf.Seek(uint(ptrPos[i])) err = rw.WriteInt(wbuf, int32(currPos)) if err != nil { return oerror.NewTrace(err) } wbuf.Seek(uint(currPos)) err = serde.writeDataValue(wbuf, fld.Value, fld.Type) if err != nil { return oerror.NewTrace(err) } } return nil }