예제 #1
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
}
예제 #2
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
}
예제 #3
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)
	}
}
예제 #4
0
func (ols OLinkSerializer) Deserialize(buf *bytes.Buffer) (interface{}, error) {
	clusterID, err := rw.ReadShort(buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

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

	rid := oschema.ORID{ClusterID: clusterID, ClusterPos: clusterPos}
	return &oschema.OLink{RID: rid}, nil
}
예제 #5
0
//
// readRID should be called when a single record (as opposed to a collection of
// records) is returned from a db query/command (REQUEST_COMMAND only ???).
// That is when the server sends back:
//     1) Writing byte (1 byte): 0 [OChannelBinaryServer]   -> SUCCESS
//     2) Writing int (4 bytes): 192 [OChannelBinaryServer] -> session-id
//     3) Writing byte (1 byte): 114 [OChannelBinaryServer] -> 'r'  (single record)
//     4) Writing short (2 bytes): 0 [OChannelBinaryServer] -> full record (not null, not RID only)
// Line 3 can be 'l' or possibly other things. For 'l' call readResultSet.
// Line 4 can be 0=full-record, -2=null, -3=RID only.  For -3, call readRID.  For 0, call this readSingleDocument.
//
func readRID(dbc *DBClient) (oschema.ORID, error) {
	// svr response: (-3:short)(cluster-id:short)(cluster-position:long)
	// TODO: impl me -> in the future this may need to call loadRecord for the RID and return the ODocument
	clusterID, err := rw.ReadShort(dbc.conx)
	if err != nil {
		return oschema.NewORID(), oerror.NewTrace(err)
	}
	clusterPos, err := rw.ReadLong(dbc.conx)
	if err != nil {
		return oschema.NewORID(), oerror.NewTrace(err)
	}

	return oschema.ORID{ClusterID: clusterID, ClusterPos: clusterPos}, nil
}
예제 #6
0
//
// AddCluster adds a cluster to the current database. It is a
// database-level operation, so OpenDatabase must have already
// been called first in order to start a session with the database.
// The clusterID is returned if the command is successful.
//
func AddCluster(dbc *DBClient, clusterName string) (clusterID int16, err error) {
	dbc.buf.Reset()

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

	cname := strings.ToLower(clusterName)

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

	err = rw.WriteShort(dbc.buf, -1) // -1 means generate new cluster id
	if err != nil {
		return int16(0), oerror.NewTrace(err)
	}

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

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

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

	clusterID, err = rw.ReadShort(dbc.conx)
	if err != nil {
		return clusterID, oerror.NewTrace(err)
	}

	dbc.currDB.Clusters = append(dbc.currDB.Clusters, OCluster{cname, clusterID})
	return clusterID, err
}
예제 #7
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
}
예제 #8
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
}