Example #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)
	}
}
Example #2
0
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
}
Example #3
0
//
// 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
}