Пример #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
// TODO: what datatypes can the params be? => right now allowing only string
func serializeSimpleSQLParams(dbc *DBClient, params []string) ([]byte, error) {
	// Java client uses Map<Object, Object>
	// Entry: {0=Honda, 1=Accord}, so positional params start with 0
	// OSQLQuery#serializeQueryParameters(Map<O,O> params)
	//   creates an ODocument
	//   params.put("params", convertToRIDsIfPossible(params))
	//   the convertToRIDsIfPossible is the one that handles Set vs. Map vs. ... vs. else -> primitive which is what simple strings are
	//  then the serialization is done via ODocument#toStream -> ORecordSerializer#toStream
	//    serializeClass(document)  => returns null
	//    only field name in the document is "params"
	//    when the embedded map comes in {0=Honda, 1=Accord}, it calls writeSingleValue

	if len(params) == 0 {
		return nil, nil
	}

	doc := oschema.NewDocument("")

	// the params must be serialized as an embedded map of form:
	// {params => {0=>paramVal1, 1=>paramVal2}}
	// which in ogonori is a Field with:
	//   Field.Name = params
	//   Field.Value = {0=>paramVal1, 1=>paramVal2}} (map[string]interface{})

	paramsMap := oschema.NewEmbeddedMapWithCapacity(2)
	for i, pval := range params {
		paramsMap.Put(strconv.Itoa(i), pval, oschema.STRING)
	}
	doc.FieldWithType("params", paramsMap, oschema.EMBEDDEDMAP)

	ogl.Debugf("DOC XX: %v\n", doc)

	// buf := new(bytes.Buffer)
	// err := buf.WriteByte(dbc.serializationVersion)
	// if err != nil {
	// 	return nil, oerror.NewTrace(err)
	// }
	serde := dbc.RecordSerDes[int(dbc.serializationVersion)]
	serializedBytes, err := serde.Serialize(dbc, doc)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	ogl.Debugf("serialized params: %v\n", serializedBytes)

	return serializedBytes, nil

	// ------------------------
	// final byte type = network.readByte();
	//  switch (type) {
	//  case 'n':
	//    result = null;
	//    break;

	//  case 'r':
	//    result = OChannelBinaryProtocol.readIdentifiable(network);
	//    if (result instanceof ORecord)
	//      database.getLocalCache().updateRecord((ORecord) result);
	//    break;

	//  case 'l':
	//    final int tot = network.readInt();
	//    final Collection<OIdentifiable> list = new ArrayList<OIdentifiable>(tot);
	//    for (int i = 0; i < tot; ++i) {
	//      final OIdentifiable resultItem = OChannelBinaryProtocol.readIdentifiable(network);
	//      if (resultItem instanceof ORecord)
	//        database.getLocalCache().updateRecord((ORecord) resultItem);
	//      list.add(resultItem);
	//    }
	//    result = list;
	//    break;

	//  case 'a':  // 'a' means "serialized result"
	//    final String value = new String(network.readBytes());
	//    result = ORecordSerializerStringAbstract.fieldTypeFromStream(null, ORecordSerializerStringAbstract.getType(value),
	//        value);
	//    break;

	//  default:
	//    OLogManager.instance().warn(this, "Received unexpected result from query: %d", type);
	//  }

	// return nil, nil
}
Пример #4
0
//
// FetchRecordByRID takes an ORID and reads that record from the database.
// NOTE: for now I'm assuming all records are Documents (they can also be "raw bytes" or "flat data")
// and for some reason I don't understand, multiple records can be returned, so I'm returning
// a slice of ODocument
//
// TODO: may also want to expose options: ignoreCache, loadTombstones bool
// TODO: need to properly handle fetchPlan
func FetchRecordByRID(dbc *DBClient, orid oschema.ORID, fetchPlan string) ([]*oschema.ODocument, error) {
	dbc.buf.Reset()

	err := writeCommandAndSessionId(dbc, REQUEST_RECORD_LOAD)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	err = rw.WriteShort(dbc.buf, orid.ClusterID)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	err = rw.WriteLong(dbc.buf, orid.ClusterPos)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	err = rw.WriteString(dbc.buf, fetchPlan)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	ignoreCache := true // hardcoding for now
	err = rw.WriteBool(dbc.buf, ignoreCache)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	loadTombstones := false // hardcoding for now
	err = rw.WriteBool(dbc.buf, loadTombstones)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	// send to the OrientDB server
	_, err = dbc.conx.Write(dbc.buf.Bytes())
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	/* ---[ Read Response ]--- */

	err = readStatusCodeAndSessionId(dbc)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	// this query can return multiple records (though I don't understand why)
	// so must do this in a loop
	docs := make([]*oschema.ODocument, 0, 1)
	for {
		payloadStatus, err := rw.ReadByte(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		if payloadStatus == byte(0) {
			break
		}

		rectype, err := rw.ReadByte(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		recversion, err := rw.ReadInt(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		databytes, err := rw.ReadBytes(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		ogl.Debugf("rectype:%v, recversion:%v, len(databytes):%v\n", rectype, recversion, len(databytes))

		if rectype == 'd' {
			// we don't know the classname so set empty value
			doc := oschema.NewDocument("")
			doc.RID = orid
			doc.Version = recversion

			// the first byte specifies record serialization version
			// use it to look up serializer
			serde := dbc.RecordSerDes[int(databytes[0])]
			// then strip off the version byte and send the data to the serde
			err = serde.Deserialize(dbc, doc, obuf.NewReadBuffer(databytes[1:]))
			if err != nil {
				return nil, fmt.Errorf("ERROR in Deserialize for rid %v: %v\n", orid, err)
			}
			docs = append(docs, doc)

		} else {
			return nil,
				fmt.Errorf("Only `document` records are currently supported by the client. Record returned was type: %v", rectype)
		}
	}

	return docs, nil
}
Пример #5
0
//
// RequestDBList works like the "list databases" command from the OrientDB client.
// The result is put into a map, where the key of the map is the name of the
// database and the value is the type concatenated with the path, like so:
//
//     key:  cars
//     val:  plocal:/path/to/orientdb-community-2.0.1/databases/cars
//
func RequestDBList(dbc *DBClient) (map[string]string, error) {
	dbc.buf.Reset()

	if dbc.sessionId == NoSessionID {
		return nil, oerror.SessionNotInitialized{}
	}

	// cmd
	err := rw.WriteByte(dbc.buf, REQUEST_DB_LIST)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	// session id
	err = rw.WriteInt(dbc.buf, dbc.sessionId)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	// send to the OrientDB server
	_, err = dbc.conx.Write(dbc.buf.Bytes())
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	status, err := rw.ReadByte(dbc.conx)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	err = readAndValidateSessionId(dbc.conx, dbc.sessionId)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	if status == RESPONSE_STATUS_ERROR {
		serverExceptions, err := rw.ReadErrorResponse(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}
		return nil, fmt.Errorf("Server Error(s): %v", serverExceptions)
	}

	// the bytes returned as a serialized EMBEDDEDMAP, so send it to the SerDe
	responseBytes, err := rw.ReadBytes(dbc.conx)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	serde := dbc.RecordSerDes[int(responseBytes[0])]
	buf := obuf.NewReadBuffer(responseBytes[1:])
	doc := oschema.NewDocument("")
	err = serde.Deserialize(dbc, doc, buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	m := make(map[string]string)
	fldMap := doc.GetField("databases").Value.(map[string]interface{})
	for k, v := range fldMap {
		m[k] = v.(string)
	}

	return m, nil
}
Пример #6
0
//
// readDataValue reads the next data section from `buf` according
// to the type of the property (property.Typ) and updates the OField object
// to have the value.
//
func (serde ORecordSerializerV0) readDataValue(buf *obuf.ReadBuf, datatype oschema.ODataType) (interface{}, error) {
	var (
		val interface{}
		err error
	)
	switch datatype {
	case oschema.BOOLEAN:
		val, err = rw.ReadBool(buf)
		ogl.Debugf("DEBUG BOOL: +readDataVal val: %v\n", val) // DEBUG

	case oschema.INTEGER:
		var i64 int64
		i64, err = varint.ReadVarIntAndDecode64(buf)
		if err == nil {
			val = int32(i64)
		}
		ogl.Debugf("DEBUG INT: +readDataVal val: %v\n", val) // DEBUG

	case oschema.SHORT:
		var i32 int32
		i32, err = varint.ReadVarIntAndDecode32(buf)
		if err == nil {
			val = int16(i32)
		}
		ogl.Debugf("DEBUG SHORT: +readDataVal val: %v\n", val) // DEBUG

	case oschema.LONG:
		val, err = varint.ReadVarIntAndDecode64(buf)
		ogl.Debugf("DEBUG LONG: +readDataVal val: %v\n", val) // DEBUG

	case oschema.FLOAT:
		val, err = rw.ReadFloat(buf)
		ogl.Debugf("DEBUG FLOAT: +readDataVal val: %v\n", val) // DEBUG

	case oschema.DOUBLE:
		val, err = rw.ReadDouble(buf)
		ogl.Debugf("DEBUG DOUBLE: +readDataVal val: %v\n", val) // DEBUG

	case oschema.DATETIME:
		// OrientDB DATETIME is precise to the second
		val, err = serde.readDateTime(buf)
		ogl.Debugf("DEBUG DATEIME: +readDataVal val: %v\n", val) // DEBUG

	case oschema.DATE:
		// OrientDB DATE is precise to the day
		val, err = serde.readDate(buf)
		ogl.Debugf("DEBUG DATE: +readDataVal val: %v\n", val) // DEBUG

	case oschema.STRING:
		val, err = varint.ReadString(buf)
		ogl.Debugf("DEBUG STR: +readDataVal val: %v\n", val) // DEBUG

	case oschema.BINARY:
		val, err = varint.ReadBytes(buf)
		ogl.Debugf("DEBUG BINARY: +readDataVal val: %v\n", val) // DEBUG

	case oschema.EMBEDDED:
		doc := oschema.NewDocument("")
		err = serde.Deserialize(nil, doc, buf)
		val = interface{}(doc)
		// ogl.Debugf("DEBUG EMBEDDEDREC: +readDataVal val: %v\n", val) // DEBUG

	case oschema.EMBEDDEDLIST:
		val, err = serde.readEmbeddedCollection(buf)
		// ogl.Debugf("DEBUG EMBD-LIST: +readDataVal val: %v\n", val) // DEBUG

	case oschema.EMBEDDEDSET:
		val, err = serde.readEmbeddedCollection(buf) // TODO: may need to create a set type as well
		// ogl.Debugf("DEBUG EMBD-SET: +readDataVal val: %v\n", val) // DEBUG

	case oschema.EMBEDDEDMAP:
		val, err = serde.readEmbeddedMap(buf)
		// ogl.Debugf("DEBUG EMBD-MAP: +readDataVal val: %v\n", val) // DEBUG

	case oschema.LINK:
		// a link is two int64's (cluster:record) - we translate it here to a string RID
		val, err = serde.readLink(buf)
		ogl.Debugf("DEBUG LINK: +readDataVal val: %v\n", val) // DEBUG

	case oschema.LINKLIST, oschema.LINKSET:
		val, err = serde.readLinkList(buf)
		ogl.Debugf("DEBUG LINK LIST/SET: +readDataVal val: %v\n", val) // DEBUG

	case oschema.LINKMAP:
		val, err = serde.readLinkMap(buf)
		ogl.Debugf("DEBUG LINKMap: +readDataVal val: %v\n", val) // DEBUG

	case oschema.BYTE:
		val, err = rw.ReadByte(buf)
		ogl.Debugf("DEBUG BYTE: +readDataVal val: %v\n", val) // DEBUG

	case oschema.LINKBAG:
		val, err = serde.readLinkBag(buf)
		ogl.Debugf("DEBUG LINKBAG: +readDataVal val: %v\n", val) // DEBUG

	case oschema.CUSTOM:
		// TODO: impl me -> how? when is this used?
		panic("ORecordSerializerV0#readDataValue CUSTOM NOT YET IMPLEMENTED")
	case oschema.DECIMAL:
		// TODO: impl me -> Java client uses BigDecimal for this
		panic("ORecordSerializerV0#readDataValue DECIMAL NOT YET IMPLEMENTED")
	default:
		// ANY and TRANSIENT are do nothing ops
	}

	return val, err
}