// // 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 valuesToStrings(args []driver.Value) []string { strargs := make([]string, len(args)) for i, valarg := range args { ogl.Debugf("valarg: %T: %v; isValue=%v\n", valarg, valarg, driver.IsValue(valarg)) // DEBUG switch valarg.(type) { case string: strargs[i] = valarg.(string) case int64: strargs[i] = strconv.FormatInt(valarg.(int64), 10) case float64: strargs[i] = strconv.FormatFloat(valarg.(float64), 'f', -1, 10) case bool: strargs[i] = strconv.FormatBool(valarg.(bool)) case []byte: strargs[i] = string(valarg.([]byte)) case time.Time: strargs[i] = valarg.(time.Time).String() // TODO: this is probably not the format we want -> fix it later default: _, file, line, _ := runtime.Caller(0) ogl.Warn(fmt.Sprintf("Unexpected type in ogonoriConn#Exec: %T. (%s:%d)", valarg, file, line)) } } return strargs }
// // SQLQuery // // TODO: right now I return the entire resultSet as an array, thus all loaded into memory // it would be better to have obinary.dbCommands provide an iterator based model // that only needs to read a "row" (ODocument) at a time // Perhaps SQLQuery() -> iterator/cursor // SQLQueryGetAll() -> []*ODocument ?? // func SQLQuery(dbc *DBClient, sql string, fetchPlan string, params ...string) ([]*oschema.ODocument, error) { dbc.buf.Reset() err := writeCommandAndSessionId(dbc, REQUEST_COMMAND) if err != nil { return nil, oerror.NewTrace(err) } mode := byte('s') // synchronous only supported for now err = rw.WriteByte(dbc.buf, mode) if err != nil { return nil, oerror.NewTrace(err) } // need a separate buffer to write the command-payload to, so // we can calculate its length before writing it to main dbc.buf commandBuf := new(bytes.Buffer) err = rw.WriteStrings(commandBuf, "q", sql) // q for query if err != nil { return nil, oerror.NewTrace(err) } // non-text-limit (-1 = use limit from query text) err = rw.WriteInt(commandBuf, -1) if err != nil { return nil, oerror.NewTrace(err) } // fetch plan err = rw.WriteString(commandBuf, fetchPlan) if err != nil { return nil, oerror.NewTrace(err) } serializedParams, err := serializeSimpleSQLParams(dbc, params) if err != nil { return nil, oerror.NewTrace(err) } if serializedParams != nil { rw.WriteBytes(commandBuf, serializedParams) } serializedCmd := commandBuf.Bytes() // command-payload-length and command-payload err = rw.WriteBytes(dbc.buf, serializedCmd) if err != nil { return nil, oerror.NewTrace(err) } // send to the OrientDB server finalBytes := dbc.buf.Bytes() _, err = dbc.conx.Write(finalBytes) if err != nil { return nil, oerror.NewTrace(err) } /* ---[ Read Response ]--- */ err = readStatusCodeAndSessionId(dbc) if err != nil { return nil, oerror.NewTrace(err) } resType, err := rw.ReadByte(dbc.conx) if err != nil { return nil, oerror.NewTrace(err) } resultType := int32(resType) var docs []*oschema.ODocument if resultType == 'n' { // NOTE: OStorageRemote in Java client just sets result to null and moves on ogl.Warn("Result type in SQLQuery is 'n' -> what to do? nothing ???") // DEBUG } else if resultType == 'r' { ogl.Warn("NOTE NOTE NOTE: this path has NOT YET BEEN TESTED") // DEBUG doc, err := readSingleRecord(dbc) if err != nil { return nil, oerror.NewTrace(err) } docs = append(docs, doc) } else if resultType == 'l' { docs, err = readResultSet(dbc) if err != nil { return nil, oerror.NewTrace(err) } } else { // TODO: I've not yet tested this route of code -> how do so? ogl.Warn(">> Not yet supported") ogl.Fatal(fmt.Sprintf("NOTE NOTE NOTE: testing the resultType == '%v' (else) route of code -- "+ "remove this note and test it!!", string(resultType))) } // any additional records are "supplementary" - from the fetchPlan these // need to be hydrated into ODocuments and then put into the primary Docs if dbc.binaryProtocolVersion >= int16(17) { // copied from the OrientDB 2.x Java client end, err := rw.ReadByte(dbc.conx) if err != nil { return nil, oerror.NewTrace(err) } if end != byte(0) { mapRIDToDoc, err := readSupplementaryRecords(dbc) if err != nil { return nil, oerror.NewTrace(err) } addSupplementaryRecsToPrimaryRecs(docs, mapRIDToDoc) } } return docs, nil }