// // 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 }
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 }
// // 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) } }
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 }
// // 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 }
// // 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 }
// // 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 }
// // 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 }