Пример #1
0
//
// 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)
	}
}
Пример #2
0
//
// 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
}
Пример #3
0
//
// 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
}
Пример #4
0
//
// 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
}