Beispiel #1
0
//
// Example data section for tree-based LinkBag
//
//                ( --------------------- collectionPointer ----------------------- )  (---size:int--)  (-changes-)
//                (----- fileId:long ----)  ( ---pageIndex:long--- ) (pageOffset:int)
//     TREEBASED             30                         0                 2048                -1             0
//         0,      0, 0, 0, 0, 0, 0, 0, 30,   0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 8, 0,     -1, -1, -1, -1,  0, 0, 0, 0,
//
func readTreeBasedLinkBag(buf io.Reader) (*oschema.OLinkBag, error) {
	fileID, err := rw.ReadLong(buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	pageIdx, err := rw.ReadLong(buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	pageOffset, err := rw.ReadInt(buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	// TODO: need to know how to handle the size and changes stuff => advanced feature not needed yet
	size, err := rw.ReadInt(buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	_, err = rw.ReadInt(buf) // changes // TODO: is changes always an int32?
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	return oschema.NewTreeOLinkBag(fileID, pageIdx, pageOffset, size), nil
}
Beispiel #2
0
//
// readSingleDocument is called by readSingleRecord when it has determined that the server
// has sent a docuemnt ('d'), not flat data ('f') or raw bytes ('b').
// It should be called *after* the single byte below on the first line has been already
// read and determined to be 'd'.  The rest the stream (NOT including the EOT byte) will
// be read.  The serialized document will be turned into an oschema.ODocument.
//
//     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
//
func readSingleDocument(dbc *DBClient) (*oschema.ODocument, error) {
	clusterId, err := rw.ReadShort(dbc.conx)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

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

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

	recBytes, err := rw.ReadBytes(dbc.conx)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}
	rid := oschema.ORID{ClusterID: clusterId, ClusterPos: clusterPos}
	doc, err := createDocumentFromBytes(rid, recVersion, recBytes, dbc)
	ogl.Debugf("::single record doc:::::: %v\n", doc)
	return doc, err
}
Beispiel #3
0
func readStatusCodeAndSessionId(dbc *DBClient) error {
	status, err := rw.ReadByte(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}

	sessionId, err := rw.ReadInt(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}
	if sessionId != dbc.sessionId {
		// FIXME: use of fmt.Errorf is an anti-pattern
		return fmt.Errorf("sessionId from server (%v) does not match client sessionId (%v)",
			sessionId, dbc.sessionId)
	}

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

	return nil
}
Beispiel #4
0
func readEmbeddedLinkBag(rdr io.Reader) (*oschema.OLinkBag, error) {
	bs := make([]byte, 1)
	n, err := rdr.Read(bs)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}
	if n != 1 {
		return nil, oerror.IncorrectNetworkRead{Expected: 1, Actual: n}
	}

	if bs[0] == 1 {
		uuid, err := readLinkBagUUID(rdr)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}
		ogl.Debugf("read uuid %v - now what?\n", uuid)

	} else {
		// if b wasn't zero, then there's no UUID and b was the first byte of an int32
		// specifying the size of the embedded bag collection
		// TODO: I'm not sure this is the right thing - the OrientDB is pretty hazy on how this works
		switch rdr.(type) {
		case *bytes.Buffer:
			buf := rdr.(*bytes.Buffer)
			buf.UnreadByte()

		case *obuf.ReadBuf:
			buf := rdr.(*obuf.ReadBuf)
			buf.UnreadByte()

		default:
			panic("Unknown type of buffer in binserde#readEmbeddedLinkBag")
		}
	}

	bagsz, err := rw.ReadInt(rdr)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}
	links := make([]*oschema.OLink, bagsz)

	for i := int32(0); i < bagsz; i++ {
		clusterID, err := rw.ReadShort(rdr)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		clusterPos, err := rw.ReadLong(rdr)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		orid := oschema.ORID{ClusterID: clusterID, ClusterPos: clusterPos}
		links[i] = &oschema.OLink{RID: orid}
	}

	return oschema.NewOLinkBag(links), nil
}
func readAndValidateSessionId(rdr io.Reader, currentSessionId int32) error {
	sessionId, err := rw.ReadInt(rdr)
	if err != nil {
		return err
	}
	if sessionId != currentSessionId {
		return fmt.Errorf("sessionId from server (%v) does not match client sessionId (%v)",
			sessionId, currentSessionId)
	}
	return nil
}
Beispiel #6
0
//
// readEmbeddedMap handles the EMBEDDEDMAP type. Currently, OrientDB only uses string
// types for the map keys, so that is an assumption of this method as well.
//
// TODO: change return type to (*oschema.OEmbeddedMap, error) {  ???
func (serde ORecordSerializerV0) readEmbeddedMap(buf *obuf.ReadBuf) (map[string]interface{}, error) {
	numRecs, err := varint.ReadVarIntAndDecode32(buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	nrecs := int(numRecs)
	m := make(map[string]interface{}) // final map to be returned

	// data structures for reading the map header section, which gives key names and
	// value types (and value ptrs, but I don't need those for the way I parse the data)
	keynames := make([]string, nrecs)
	valtypes := make([]oschema.ODataType, nrecs)

	// read map headers
	for i := 0; i < nrecs; i++ {
		keytype, err := rw.ReadByte(buf)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}
		if keytype != byte(oschema.STRING) {
			panic(fmt.Sprintf("ReadEmbeddedMap got a key datatype %v - but it should be 7 (string)", keytype))
		}
		keynames[i], err = varint.ReadString(buf)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		_, err = rw.ReadInt(buf) // pointer - throwing away
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		b, err := rw.ReadByte(buf)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}
		valtypes[i] = oschema.ODataType(b)
	}

	// read map values
	for i := 0; i < nrecs; i++ {
		val, err := serde.readDataValue(buf, valtypes[i])
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		m[keynames[i]] = val
	}

	return m, nil
}
Beispiel #7
0
//
// Large LinkBags (aka RidBags) are stored on the server. To look up their
// size requires a call to the database.  The size is returned.  Note that the
// Size field of the linkBag is NOT updated.  That is left for the caller to
// decide whether to do.
//
func FetchSizeOfRemoteLinkBag(dbc *DBClient, linkBag *oschema.OLinkBag) (int, error) {
	dbc.buf.Reset()

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

	err = writeLinkBagCollectionPointer(dbc.buf, linkBag)
	if err != nil {
		return 0, oerror.NewTrace(err)
	}

	// changes => TODO: right now not supporting any change -> just writing empty changes
	err = rw.WriteBytes(dbc.buf, []byte{0, 0, 0, 0})
	if err != nil {
		return 0, oerror.NewTrace(err)
	}

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

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

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

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

	return int(size), nil
}
Beispiel #8
0
//
// readResultSet should be called for collections (resultType = 'l')
// from a SQLQuery call.
//
func readResultSet(dbc *DBClient) ([]*oschema.ODocument, error) {
	// for Collection
	// next val is: (collection-size:int)
	// and then each record is serialized according to format:
	// (0:short)(record-type:byte)(cluster-id:short)(cluster-position:long)(record-version:int)(record-content:bytes)

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

	rsize := int(resultSetSize)
	docs := make([]*oschema.ODocument, rsize)

	for i := 0; i < rsize; i++ {
		// TODO: move code below to readRecordInResultSet
		// this apparently should always be zero for serialized records -> not sure it's meaning
		zero, err := rw.ReadShort(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}
		if zero != int16(0) {
			return nil, fmt.Errorf("ERROR: readResultSet: expected short value of 0 but is %d", zero)
		}

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

		// TODO: may need to check recType here => not sure that clusterId, clusterPos and version follow next if
		//       type is 'b' (raw bytes) or 'f' (flat record)
		//       see the readSingleDocument method (and probably call that one instead?)
		clusterId, err := rw.ReadShort(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

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

		recVersion, err := rw.ReadInt(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}
		if recType == byte('d') { // Document
			var doc *oschema.ODocument
			rid := oschema.ORID{ClusterID: clusterId, ClusterPos: clusterPos}
			recBytes, err := rw.ReadBytes(dbc.conx)
			if err != nil {
				return nil, oerror.NewTrace(err)
			}
			doc, err = createDocumentFromBytes(rid, recVersion, recBytes, dbc)
			if err != nil {
				return nil, oerror.NewTrace(err)
			}
			docs[i] = doc

		} else {
			_, file, line, _ := runtime.Caller(0)
			return nil, fmt.Errorf("%v: %v: Record type %v is not yet supported", file, line+1, recType)
		}
	} // end for loop

	// end, err := rw.ReadByte(dbc.conx)
	// if err != nil {
	// 	return nil, oerror.NewTrace(err)
	// }
	// if end != byte(0) {
	// 	return nil, fmt.Errorf("Final Byte read from collection result set was not 0, but was: %v", end)
	// }
	return docs, nil
}
Beispiel #9
0
//
// UpdateRecord should be used update an existing record in the OrientDB database.
// It does the REQUEST_RECORD_UPDATE OrientDB cmd (network binary protocol)
//
func UpdateRecord(dbc *DBClient, doc *oschema.ODocument) error {
	dbc.buf.Reset()

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

	if doc.RID.ClusterID < 0 || doc.RID.ClusterPos < 0 {
		return errors.New("Document is not updateable - has negative RID values")
	}

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

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

	// update-content flag
	err = rw.WriteBool(dbc.buf, true)
	if err != nil {
		return oerror.NewTrace(err)
	}

	// serialized-doc
	serde := dbc.RecordSerDes[int(dbc.serializationVersion)]

	// this writes the serialized record to dbc.buf
	serializedBytes, err := serde.Serialize(dbc, doc)
	if err != nil {
		return oerror.NewTrace(err)
	}

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

	// record version
	err = rw.WriteInt(dbc.buf, doc.Version)
	if err != nil {
		return oerror.NewTrace(err)
	}

	// record-type: document
	err = rw.WriteByte(dbc.buf, byte('d')) // TODO: how support 'b' (raw bytes) & 'f' (flat data)?
	if err != nil {
		return oerror.NewTrace(err)
	}

	// mode: synchronous
	err = rw.WriteByte(dbc.buf, 0x0)
	if err != nil {
		return oerror.NewTrace(err)
	}

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

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

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

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

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

	if nCollChanges != 0 {
		// if > 0, then have to deal with RidBag mgmt:
		// [(uuid-most-sig-bits:long)(uuid-least-sig-bits:long)(updated-file-id:long)(updated-page-index:long)(updated-page-offset:int)]
		panic("CreateRecord: Found case where number-collection-changes is not zero -> log case and impl code to handle")
	}

	return nil
}
Beispiel #10
0
//
// FetchEntriesOfRemoteLinkBag fills in the links of an OLinkBag that is remote
// (tree-based) rather than embedded.  This function will fill in the links
// of the passed in OLinkBag, rather than returning the new links. The Links
// will have RIDs only, not full Records (ODocuments).  If you then want the
// Records filled in, call the ResolveLinks function.
//
func FetchEntriesOfRemoteLinkBag(dbc *DBClient, linkBag *oschema.OLinkBag, inclusive bool) error {
	var (
		firstLink *oschema.OLink
		linkSerde binserde.OBinaryTypeSerializer
		err       error
	)

	firstLink, err = FetchFirstKeyOfRemoteLinkBag(dbc, linkBag)
	if err != nil {
		return oerror.NewTrace(err)
	}

	dbc.buf.Reset()

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

	err = writeLinkBagCollectionPointer(dbc.buf, linkBag)
	if err != nil {
		return oerror.NewTrace(err)
	}

	typeByte := byte(9)
	linkSerde = binserde.TypeSerializers[typeByte] // the OLinkSerializer

	linkBytes, err := linkSerde.Serialize(firstLink)
	if err != nil {
		return oerror.NewTrace(err)
	}

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

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

	// copied from Java client OSBTreeBonsaiRemote#fetchEntriesMajor
	if dbc.binaryProtocolVersion >= 21 {
		err = rw.WriteInt(dbc.buf, 128)
	}

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

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

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

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

	// all the rest of the response from the server in in this byte slice so
	// we can reset the dbc.buf and reuse it to deserialize the serialized links
	dbc.buf.Reset()
	// ignoring error since doc says this method panics rather than return
	// non-nil error
	n, _ := dbc.buf.Write(linkEntryBytes)
	if n != len(linkEntryBytes) {
		return fmt.Errorf("Unexpected error when writing bytes to bytes.Buffer")
	}

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

	var result interface{}
	nr := int(nrecs)
	// loop over all the serialized links
	for i := 0; i < nr; i++ {
		result, err = linkSerde.Deserialize(dbc.buf)
		if err != nil {
			return oerror.NewTrace(err)
		}
		linkBag.AddLink(result.(*oschema.OLink))

		// FIXME: for some reason the server returns a serialized link
		//        followed by an integer (so far always a 1 in my expts).
		//        Not sure what to do with this int, so ignore for now
		intval, err := rw.ReadInt(dbc.buf)
		if err != nil {
			return oerror.NewTrace(err)
		}
		if intval != int32(1) {
			ogl.Warnf("DEBUG: Found a use case where the val pair of a link was not 1: %d\n", intval)
		}
	}

	return nil
}
Beispiel #11
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
}
Beispiel #12
0
//
// OpenDatabase sends the REQUEST_DB_OPEN command to the OrientDB server to
// open the db in read/write mode.  The database name and type are required, plus
// username and password.  Database type should be one of the obinary constants:
// DocumentDBType or GraphDBType.
//
func OpenDatabase(dbc *DBClient, dbname string, dbtype constants.DatabaseType, username, passw string) error {
	buf := dbc.buf
	buf.Reset()

	// first byte specifies request type
	err := rw.WriteByte(buf, REQUEST_DB_OPEN)
	if err != nil {
		return oerror.NewTrace(err)
	}

	// session-id - send a negative number to create a new server-side conx
	err = rw.WriteInt(buf, RequestNewSession)
	if err != nil {
		return oerror.NewTrace(err)
	}

	err = rw.WriteStrings(buf, DriverName, DriverVersion)
	if err != nil {
		return oerror.NewTrace(err)
	}

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

	// dbclient id - send as null, but cannot be null if clustered config
	// TODO: change to use dbc.clusteredConfig once that is added
	err = rw.WriteNull(buf)
	if err != nil {
		return oerror.NewTrace(err)
	}

	// serialization-impl
	err = rw.WriteString(buf, dbc.serializationType)
	if err != nil {
		return oerror.NewTrace(err)
	}

	// token-session  // TODO: hardcoded as false for now -> change later based on ClientOptions settings
	err = rw.WriteBool(buf, false)
	if err != nil {
		return oerror.NewTrace(err)
	}

	// dbname, dbtype, username, password
	err = rw.WriteStrings(buf, dbname, string(dbtype), username, passw)
	if err != nil {
		return oerror.NewTrace(err)
	}

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

	/* ---[ read back response ]--- */

	// first byte indicates success/error
	status, err := rw.ReadByte(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}

	dbc.currDB = NewDatabase(dbname, dbtype)

	// the first int returned is the session id sent - which was the `RequestNewSession` sentinel
	sessionValSent, err := rw.ReadInt(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}
	if sessionValSent != RequestNewSession {
		return errors.New("Unexpected Error: Server did not return expected session-request-val that was sent")
	}

	// if status returned was ERROR, then the rest of server data is the exception info
	if status != RESPONSE_STATUS_OK {
		exceptions, err := rw.ReadErrorResponse(dbc.conx)
		if err != nil {
			return oerror.NewTrace(err)
		}
		return fmt.Errorf("Server Error(s): %v", exceptions)
	}

	// for the REQUEST_DB_OPEN case, another int is returned which is the new sessionId
	sessionId, err := rw.ReadInt(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}
	dbc.sessionId = sessionId

	// next is the token, which may be null
	tokenBytes, err := rw.ReadBytes(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}
	dbc.token = tokenBytes

	// array of cluster info in this db // TODO: do we need to retain all this in memory?
	numClusters, err := rw.ReadShort(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}

	clusters := make([]OCluster, 0, numClusters)

	for i := 0; i < int(numClusters); i++ {
		clusterName, err := rw.ReadString(dbc.conx)
		if err != nil {
			return oerror.NewTrace(err)
		}
		clusterId, err := rw.ReadShort(dbc.conx)
		if err != nil {
			return oerror.NewTrace(err)
		}
		clusters = append(clusters, OCluster{Name: clusterName, Id: clusterId})
	}
	dbc.currDB.Clusters = clusters

	// cluster-config - bytes - null unless running server in clustered config
	// TODO: treating this as an opaque blob for now
	clusterCfg, err := rw.ReadBytes(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}
	dbc.currDB.ClustCfg = clusterCfg

	// orientdb server release - throwing away for now // TODO: need this?
	_, err = rw.ReadString(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}

	//
	/* ---[ load #0:0 - config record ]--- */
	schemaRIDStr, err := loadConfigRecord(dbc)
	if err != nil {
		return oerror.NewTrace(err)
	}
	clusterID, clusterPos, err := parseRid(schemaRIDStr)
	if err != nil {
		return oerror.NewTrace(err)
	}

	//
	/* ---[ load #0:1 - schema record ]--- */
	err = loadSchema(dbc, oschema.ORID{ClusterID: clusterID, ClusterPos: clusterPos})
	if err != nil {
		return oerror.NewTrace(err)
	}

	return nil
}
Beispiel #13
0
//
// loadConfigRecord loads record #0:0 for the current database, caching
// some of the information returned into OStorageConfiguration
//
func loadConfigRecord(dbc *DBClient) (schemaRID string, err error) {
	// The config record comes back as type 'b' (raw bytes), which should
	// just be converted to a string then tokenized by the pipe char

	dbc.buf.Reset()
	var (
		clusterId  int16
		clusterPos int64
	)
	err = writeCommandAndSessionId(dbc, REQUEST_RECORD_LOAD)
	if err != nil {
		return schemaRID, err
	}

	clusterId = 0
	err = rw.WriteShort(dbc.buf, clusterId)
	if err != nil {
		return schemaRID, err
	}

	clusterPos = 0
	err = rw.WriteLong(dbc.buf, clusterPos)
	if err != nil {
		return schemaRID, err
	}

	fetchPlan := "*:-1 index:0"
	err = rw.WriteString(dbc.buf, fetchPlan)
	if err != nil {
		return schemaRID, err
	}

	ignoreCache := true
	err = rw.WriteBool(dbc.buf, ignoreCache)
	if err != nil {
		return schemaRID, err
	}

	loadTombstones := true // based on Java client code
	err = rw.WriteBool(dbc.buf, loadTombstones)
	if err != nil {
		return schemaRID, err
	}

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

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

	err = readStatusCodeAndSessionId(dbc)
	if err != nil {
		return schemaRID, err
	}

	payloadStatus, err := rw.ReadByte(dbc.conx)
	if err != nil {
		return schemaRID, err
	}

	if payloadStatus == byte(0) {
		return schemaRID, errors.New("Payload status for #0:0 load was 0. No config data returned.")
	}

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

	// this is the record version - don't see a reason to check or cache it right now
	_, err = rw.ReadInt(dbc.conx)
	if err != nil {
		return schemaRID, err
	}

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

	if rectype != 'b' {
		if err != nil {
			return schemaRID, fmt.Errorf("Expected rectype %d, but was: %d", 'b', rectype)
		}
	}

	payloadStatus, err = rw.ReadByte(dbc.conx)
	if err != nil {
		return schemaRID, err
	}

	if payloadStatus != byte(0) {
		return schemaRID,
			errors.New("Second Payload status for #0:0 load was not 0. More than one record returned unexpectedly")
	}

	err = parseConfigRecord(dbc.currDB, string(databytes))
	if err != nil {
		return schemaRID, err
	}

	schemaRID = dbc.currDB.StorageCfg.schemaRID
	return schemaRID, err
}
Beispiel #14
0
//
// ConnectToServer logs into the OrientDB server with the appropriate
// admin privileges in order to execute server-level commands (as opposed
// to database-level commands). This must be called to establish a server
// session before any other server-level commands. The username and password
// required are for the server (admin) not any particular database.
//
func ConnectToServer(dbc *DBClient, adminUser, adminPassw string) error {
	buf := dbc.buf
	buf.Reset()

	// first byte specifies request type
	err := rw.WriteByte(buf, REQUEST_CONNECT)
	if err != nil {
		return err
	}

	// session-id - send a negative number to create a new server-side conx
	err = rw.WriteInt(buf, RequestNewSession)
	if err != nil {
		return err
	}

	err = rw.WriteStrings(buf, DriverName, DriverVersion)
	if err != nil {
		return err
	}

	err = rw.WriteShort(buf, dbc.binaryProtocolVersion)
	if err != nil {
		return err
	}

	// dbclient id - send as null, but cannot be null if clustered config
	// TODO: change to use dbc.clusteredConfig once that is added
	err = rw.WriteNull(buf)
	if err != nil {
		return err
	}

	// serialization-impl
	err = rw.WriteString(buf, dbc.serializationType)
	if err != nil {
		return err
	}

	// token-session  // TODO: hardcoded as false for now -> change later based on ClientOptions settings
	err = rw.WriteBool(buf, false)
	if err != nil {
		return err
	}

	// admin username, password
	err = rw.WriteStrings(buf, adminUser, adminPassw)
	if err != nil {
		return err
	}

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

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

	// first byte indicates success/error
	status, err := rw.ReadByte(dbc.conx)
	if err != nil {
		return err
	}

	// the first int returned is the session id sent - which was the `RequestNewSession` sentinel
	sessionValSent, err := rw.ReadInt(dbc.conx)
	if err != nil {
		return err
	}
	if sessionValSent != RequestNewSession {
		return errors.New("Unexpected Error: Server did not return expected session-request-val that was sent")
	}

	// if status returned was ERROR, then the rest of server data is the exception info
	if status != RESPONSE_STATUS_OK {
		exceptions, err := rw.ReadErrorResponse(dbc.conx)
		if err != nil {
			return err
		}
		return fmt.Errorf("Server Error(s): %v", exceptions)
	}

	// for the REQUEST_CONNECT case, another int is returned which is the new sessionId
	sessionId, err := rw.ReadInt(dbc.conx)
	if err != nil {
		return err
	}
	// TODO: this assumes you can only have one sessionId - but perhaps can have a server sessionid
	//       and one or more database sessions open at the same time ?????
	dbc.sessionId = sessionId

	tokenBytes, err := rw.ReadBytes(dbc.conx)
	if err != nil {
		return err
	}
	dbc.token = tokenBytes
	return nil
}
Beispiel #15
0
func readHeader(buf io.Reader) (header, error) {
	hdr := header{
		properties: make([]headerProperty, 0, 8),
		dataPtrs:   make([]int32, 0, 8),
	}

	for {
		decoded, err := varint.ReadVarIntAndDecode32(buf)
		if err != nil {
			_, _, line, _ := runtime.Caller(0)
			return header{}, fmt.Errorf("Error in binserde.readHeader (line %d): %v", line-2, err)
		}

		if decoded == 0 { // 0 marks end of header
			break

		} else if decoded > 0 {
			// have a property, not a document, so the number is a zigzag encoded length
			// for a string (property name)

			// read property name
			data := make([]byte, int(decoded))
			n, err := buf.Read(data)
			if err != nil {
				return header{}, oerror.NewTrace(err)
			}
			if len(data) != n {
				return header{}, oerror.IncorrectNetworkRead{Expected: len(data), Actual: n}
			}
			// hdr.propertyNames = append(hdr.propertyNames, string(data))

			// read data pointer
			ptr, err := rw.ReadInt(buf)
			if err != nil {
				return header{}, oerror.NewTrace(err)
			}

			// read data type
			bsDataType := make([]byte, 1)
			n, err = buf.Read(bsDataType)
			if err != nil {
				return header{}, oerror.NewTrace(err)
			}
			if n != 1 {
				return header{}, oerror.IncorrectNetworkRead{Expected: 1, Actual: n}
			}

			hdrProp := headerProperty{name: data, typ: oschema.ODataType(bsDataType[0])}
			hdr.properties = append(hdr.properties, hdrProp)
			hdr.dataPtrs = append(hdr.dataPtrs, ptr)

		} else {
			// have a document, not a property, so the number is an encoded property id,
			// convert to (positive) property-id
			propertyID := decodeFieldIDInHeader(decoded)

			ptr, err := rw.ReadInt(buf)
			if err != nil {
				return header{}, oerror.NewTrace(err)
			}

			hdrProp := headerProperty{id: propertyID}
			hdr.properties = append(hdr.properties, hdrProp)
			hdr.dataPtrs = append(hdr.dataPtrs, ptr)
		}
	}
	return hdr, nil
}