Exemple #1
0
//
// writeEmbeddedMap serializes the EMBEDDEDMAP type. Currently, OrientDB only uses string
// types for the map keys, so that is an assumption of this method as well.
//
func (serde ORecordSerializerV0) writeEmbeddedMap(buf *obuf.WriteBuf, m oschema.OEmbeddedMap) error {
	// number of entries in the map
	err := varint.EncodeAndWriteVarInt32(buf, int32(m.Len()))
	if err != nil {
		return oerror.NewTrace(err)
	}

	ptrPos := make([]int, 0, m.Len()) // position in buf where data ptr int needs to be written

	// TODO: do the map entries have to be written in any particular order?  I will assume no for now
	keys, vals, types := m.All()

	/* ---[ write embedded map header ]--- */
	for i, k := range keys {
		// key type
		err = rw.WriteByte(buf, byte(oschema.STRING))
		if err != nil {
			return oerror.NewTrace(err)
		}

		// write the key value
		err = varint.WriteString(buf, k)
		if err != nil {
			return oerror.NewTrace(err)
		}

		ptrPos = append(ptrPos, buf.Len())
		buf.Skip(4) // placeholder integer for data ptr

		dataType := types[i]
		if dataType == oschema.UNKNOWN {
			dataType = getDataType(vals[i]) // TODO: not sure this is necessary
		}
		// write data type of the data
		err = rw.WriteByte(buf, byte(dataType))
		if err != nil {
			return oerror.NewTrace(err)
		}
	}

	/* ---[ write embedded map data values ]--- */
	for i := 0; i < len(vals); i++ {
		currPos := buf.Len()
		buf.Seek(uint(ptrPos[i]))
		err = rw.WriteInt(buf, int32(currPos))
		if err != nil {
			return oerror.NewTrace(err)
		}
		buf.Seek(uint(currPos))
		err = serde.writeDataValue(buf, vals[i], types[i])
		if err != nil {
			return oerror.NewTrace(err)
		}
	}

	return nil
}
Exemple #2
0
//
// Serialize takes an ODocument and serializes it to bytes in accordance
// with the OrientDB binary serialization spec and writes them to the
// bytes.Buffer passed in.
//
func (serde ORecordSerializerV0) Serialize(dbc *DBClient, doc *oschema.ODocument) ([]byte, error) {
	// temporarily set state for the duration of this Serialize call
	// dbc is allowed to be nil for reentrant (recursive) calls -- in which
	// case serde.dbc should already be set (not-nil)
	if dbc != nil {
		if serde.dbc != nil {
			return nil, errors.New("Attempted to set dbc again in Serialize when it is already set")
		}
		serde.dbc = dbc
		defer func() {
			serde.dbc = nil
		}()
	} else if serde.dbc == nil {
		return nil, errors.New("dbc *DBClient passed into Serialize was null and dbc had not already been set in Serializer state")
	}

	// need to create a new buffer for the serialized record for ptr value calculations,
	// since the incoming buffer (`buf`) already has a lot of stuff written to it (session-id, etc)
	// that are NOT part of the serialized record
	serdebuf := obuf.NewWriteBuffer(80) // holds only the serialized value

	// write the serialization version in so that the buffer size math works
	err := rw.WriteByte(serdebuf, 0)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	err = serde.serializeDocument(serdebuf, doc)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	return serdebuf.Bytes(), nil
}
Exemple #3
0
//
// The link map allow to have as key the types:
// STRING,SHORT,INTEGER,LONG,BYTE,DATE,DECIMAL,DATETIME,DATA,FLOAT,DOUBLE
// the serialization of the linkmap is a list of entry
//
// +----------------------------+
// | values:link_map_entry[]    |
// +----------------------------+
//
// link_map_entry structure
//
// +--------------+------------------+------------+
// | keyType:byte | keyValue:byte[]  | link:LINK  |
// +--------------+------------------+------------+
//
// keyType -  is the type of the key, can be only one of the listed type.
// keyValue - the value of the key serialized with the serializer of the type
// link -     the link value stored with the formant of a LINK
//
// TODO: right now only supporting string keys, but need to support the
//       datatypes listed above (also for EmbeddedMaps)
//
func (serde ORecordSerializerV0) writeLinkMap(buf *obuf.WriteBuf, m map[string]*oschema.OLink) error {
	// number of entries in the map
	err := varint.EncodeAndWriteVarInt32(buf, int32(len(m)))
	if err != nil {
		return oerror.NewTrace(err)
	}

	for k, v := range m {
		// keyType
		err = rw.WriteByte(buf, byte(oschema.STRING))
		if err != nil {
			return oerror.NewTrace(err)
		}
		// keyValue
		err = varint.WriteString(buf, k)
		if err != nil {
			return oerror.NewTrace(err)
		}
		// link
		err = serde.writeLink(buf, v)
		if err != nil {
			return oerror.NewTrace(err)
		}
	}

	return nil
}
//
// DatabaseExists is a server-level command, so must be preceded by calling
// ConnectToServer, otherwise an authorization error will be returned.
// The storageType param must be either PersistentStorageType or VolatileStorageType.
//
func DatabaseExists(dbc *DBClient, dbname string, storageType constants.StorageType) (bool, error) {
	dbc.buf.Reset()

	if dbc.sessionId == NoSessionID {
		return false, oerror.SessionNotInitialized{}
	}

	// cmd
	err := rw.WriteByte(dbc.buf, REQUEST_DB_EXIST)
	if err != nil {
		return false, err
	}

	// session id
	err = rw.WriteInt(dbc.buf, dbc.sessionId)
	if err != nil {
		return false, err
	}

	// database name, storage-type
	err = rw.WriteStrings(dbc.buf, dbname, string(storageType))
	if err != nil {
		return false, err
	}

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

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

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

	err = readAndValidateSessionId(dbc.conx, dbc.sessionId)
	if err != nil {
		return false, err
	}

	if status == RESPONSE_STATUS_ERROR {
		serverExceptions, err := rw.ReadErrorResponse(dbc.conx)
		if err != nil {
			return false, err
		}
		return false, fmt.Errorf("Server Error(s): %v", serverExceptions)
	}

	// the answer to the query
	dbexists, err := rw.ReadBool(dbc.conx)
	if err != nil {
		return false, err
	}

	return dbexists, nil
}
//
// CreateDatabase will create a `remote` database of the type and storageType specified.
// dbType must be type DocumentDBType or GraphDBType.
// storageType must type PersistentStorageType or VolatileStorageType.
//
func CreateDatabase(dbc *DBClient, dbname string, dbtype constants.DatabaseType, storageType constants.StorageType) error {
	dbc.buf.Reset()

	/* ---[ precondition checks ]--- */

	// TODO: may need to change this to serverSessionid (can the "sessionId" be used for both server connections and db conx?)
	if dbc.sessionId == NoSessionID {
		return oerror.SessionNotInitialized{}
	}

	/* ---[ build request and send to server ]--- */

	// cmd
	err := rw.WriteByte(dbc.buf, REQUEST_DB_CREATE)
	if err != nil {
		return err
	}

	// session id
	err = rw.WriteInt(dbc.buf, dbc.sessionId)
	if err != nil {
		return err
	}

	err = rw.WriteStrings(dbc.buf, dbname, string(dbtype), string(storageType))
	if err != nil {
		return err
	}

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

	/* ---[ read response from server ]--- */

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

	err = readAndValidateSessionId(dbc.conx, dbc.sessionId)
	if err != nil {
		return err
	}

	if status == RESPONSE_STATUS_ERROR {
		serverExceptions, err := rw.ReadErrorResponse(dbc.conx)
		if err != nil {
			return err
		}
		return fmt.Errorf("Server Error(s): %v", serverExceptions)
	}

	return nil
}
//
// DropDatabase drops the specified database. The caller must provide
// both the name and the type of the database.  The type should either:
//
//     obinary.DocumentDBType
//     obinary.GraphDBType
//
// This is a "server" command, so you must have already called
// ConnectToServer before calling this function.
//
func DropDatabase(dbc *DBClient, dbname string, dbtype constants.DatabaseType) error {
	dbc.buf.Reset()

	if dbc.sessionId == NoSessionID {
		return oerror.SessionNotInitialized{}
	}

	// cmd
	err := rw.WriteByte(dbc.buf, REQUEST_DB_DROP)
	if err != nil {
		return err
	}

	// session id
	err = rw.WriteInt(dbc.buf, dbc.sessionId)
	if err != nil {
		return err
	}

	// database name, storage-type
	err = rw.WriteStrings(dbc.buf, dbname, string(dbtype))
	if err != nil {
		return err
	}

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

	/* ---[ read response from server ]--- */

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

	err = readAndValidateSessionId(dbc.conx, dbc.sessionId)
	if err != nil {
		return err
	}

	if status == RESPONSE_STATUS_ERROR {
		serverExceptions, err := rw.ReadErrorResponse(dbc.conx)
		if err != nil {
			return err
		}
		return fmt.Errorf("Server Error(s): %v", serverExceptions)
	}

	return nil
}
Exemple #7
0
func writeCommandAndSessionId(dbc *DBClient, cmd byte) error {
	if dbc.sessionId == NoSessionID {
		return oerror.SessionNotInitialized{}
	}

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

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

	return nil
}
Exemple #8
0
//
// Serialization format for EMBEDDEDLIST and EMBEDDEDSET
// +-------------+------------+-------------------+
// |size:varInt  | type:Otype | items:item_data[] |
// +-------------+------------+-------------------+
//
// The item_data data structure is:
// +------------------+--------------+
// | data_type:OType  | data:byte[]  |
// +------------------+--------------+
//
func (serde ORecordSerializerV0) serializeEmbeddedCollection(buf *obuf.WriteBuf, ls oschema.OEmbeddedList) error {
	err := varint.EncodeAndWriteVarInt32(buf, int32(ls.Len()))
	if err != nil {
		return oerror.NewTrace(err)
	}

	// following the lead of the Java driver, you don't specify the type of the list overall
	// (I tried to and it doesn't work, at least with OrientDB-2.0.1)
	err = rw.WriteByte(buf, byte(oschema.ANY))
	if err != nil {
		return oerror.NewTrace(err)
	}

	for _, val := range ls.Values() {
		buf.WriteByte(byte(ls.Type()))
		err = serde.writeDataValue(buf, val, ls.Type())
		if err != nil {
			return oerror.NewTrace(err)
		}
	}

	return nil
}
Exemple #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
}
Exemple #10
0
func deleteByRID(dbc *DBClient, rid string, recVersion int32, async bool) error {
	dbc.buf.Reset()

	orid := oschema.NewORIDFromString(rid)

	err := writeCommandAndSessionId(dbc, REQUEST_RECORD_DELETE)
	if err != nil {
		return err
	}

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

	err = rw.WriteLong(dbc.buf, orid.ClusterPos)
	if err != nil {
		return err
	}

	err = rw.WriteInt(dbc.buf, recVersion)
	if err != nil {
		return err
	}

	// sync mode ; 0 = synchronous; 1 = asynchronous
	var syncMode byte
	if async {
		syncMode = byte(1)
	}
	err = rw.WriteByte(dbc.buf, syncMode)
	if err != nil {
		return err
	}

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

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

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

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

	// status 1 means record was deleted;
	// status 0 means record was not deleted (either failed or didn't exist)
	if payloadStatus == byte(0) {
		return fmt.Errorf("Server reports record %s was not deleted. Either failed or did not exist.",
			rid)
	}

	return nil
}
Exemple #11
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
}
Exemple #12
0
func getClusterCount(dbc *DBClient, countTombstones bool, clusterNames []string) (count int64, err error) {
	dbc.buf.Reset()

	clusterIDs := make([]int16, len(clusterNames))
	for i, name := range clusterNames {
		clusterID := findClusterWithName(dbc.currDB.Clusters, strings.ToLower(name))
		if clusterID < 0 {
			// TODO: This is problematic - someone else may add the cluster not through this
			//       driver session and then this would fail - so options:
			//       1) do a lookup of all clusters on the DB
			//       2) provide a FetchClusterCountById(dbc, clusterID)
			return int64(0),
				fmt.Errorf("No cluster with name %s is known in database %s\n",
					name, dbc.currDB.Name)
		}
		clusterIDs[i] = clusterID
	}

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

	// specify number of clusterIDs being sent and then write the clusterIDs
	err = rw.WriteShort(dbc.buf, int16(len(clusterIDs)))
	if err != nil {
		return int64(0), oerror.NewTrace(err)
	}

	for _, cid := range clusterIDs {
		err = rw.WriteShort(dbc.buf, cid)
		if err != nil {
			return int64(0), oerror.NewTrace(err)
		}
	}

	// count-tombstones
	var ct byte
	if countTombstones {
		ct = byte(1)
	}
	err = rw.WriteByte(dbc.buf, ct) // presuming that 0 means "false"
	if err != nil {
		return int64(0), oerror.NewTrace(err)
	}

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

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

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

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

	return nrecs, err
}
//
// RequestDBList works like the "list databases" command from the OrientDB client.
// The result is put into a map, where the key of the map is the name of the
// database and the value is the type concatenated with the path, like so:
//
//     key:  cars
//     val:  plocal:/path/to/orientdb-community-2.0.1/databases/cars
//
func RequestDBList(dbc *DBClient) (map[string]string, error) {
	dbc.buf.Reset()

	if dbc.sessionId == NoSessionID {
		return nil, oerror.SessionNotInitialized{}
	}

	// cmd
	err := rw.WriteByte(dbc.buf, REQUEST_DB_LIST)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	// session id
	err = rw.WriteInt(dbc.buf, dbc.sessionId)
	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)
	}

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

	err = readAndValidateSessionId(dbc.conx, dbc.sessionId)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	if status == RESPONSE_STATUS_ERROR {
		serverExceptions, err := rw.ReadErrorResponse(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}
		return nil, fmt.Errorf("Server Error(s): %v", serverExceptions)
	}

	// the bytes returned as a serialized EMBEDDEDMAP, so send it to the SerDe
	responseBytes, err := rw.ReadBytes(dbc.conx)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	serde := dbc.RecordSerDes[int(responseBytes[0])]
	buf := obuf.NewReadBuffer(responseBytes[1:])
	doc := oschema.NewDocument("")
	err = serde.Deserialize(dbc, doc, buf)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	m := make(map[string]string)
	fldMap := doc.GetField("databases").Value.(map[string]interface{})
	for k, v := range fldMap {
		m[k] = v.(string)
	}

	return m, nil
}
Exemple #14
0
//
// writeDataValue is part of the Serialize functionality
// TODO: change name to writeSingleValue ?
//
func (serde ORecordSerializerV0) writeDataValue(buf *obuf.WriteBuf, value interface{}, datatype oschema.ODataType) (err error) {
	switch datatype {
	case oschema.STRING:
		err = varint.WriteString(buf, value.(string))
		ogl.Debugf("DEBUG STR: -writeDataVal val: %v\n", value.(string)) // DEBUG

	case oschema.BOOLEAN:
		err = rw.WriteBool(buf, value.(bool))
		ogl.Debugf("DEBUG BOOL: -writeDataVal val: %v\n", value.(bool)) // DEBUG

	case oschema.INTEGER:
		var i32val int32
		i32val, err = toInt32(value)
		if err == nil {
			err = varint.EncodeAndWriteVarInt32(buf, i32val)         // TODO: are serialized integers ALWAYS varint encoded?
			ogl.Debugf("DEBUG INT: -writeDataVal val: %v\n", i32val) // DEBUG
		}

	case oschema.SHORT:
		// TODO: needs toInt16 conversion fn
		err = varint.EncodeAndWriteVarInt32(buf, int32(value.(int16)))
		ogl.Debugf("DEBUG SHORT: -writeDataVal val: %v\n", value.(int16)) // DEBUG

	case oschema.LONG:
		var i64val int64
		i64val, err = toInt64(value)
		if err == nil {
			err = varint.EncodeAndWriteVarInt64(buf, i64val)          // TODO: are serialized longs ALWAYS varint encoded?
			ogl.Debugf("DEBUG LONG: -writeDataVal val: %v\n", i64val) // DEBUG
		}

	case oschema.FLOAT:
		var f32 float32
		f32, err = toFloat32(value)
		if err == nil {
			err = rw.WriteFloat(buf, f32)
		}
		ogl.Debugf("DEBUG FLOAT: -writeDataVal val: %v\n", value) // DEBUG

	case oschema.DOUBLE:
		var f64 float64
		f64, err = toFloat64(value)
		if err == nil {
			err = rw.WriteDouble(buf, f64)
		}
		ogl.Debugf("DEBUG DOUBLE: -writeDataVal val: %v\n", value.(float64)) // DEBUG

	case oschema.DATETIME:
		err = writeDateTime(buf, value)
		ogl.Debugf("DEBUG DATETIME: -writeDataVal val: %v\n", value) // DEBUG

	case oschema.DATE:
		err = writeDate(buf, value)
		ogl.Debugf("DEBUG DATE: -writeDataVal val: %v\n", value) // DEBUG

	case oschema.BINARY:
		err = varint.WriteBytes(buf, value.([]byte))
		ogl.Debugf("DEBUG BINARY: -writeDataVal val: %v\n", value.([]byte)) // DEBUG

	case oschema.EMBEDDED:
		err = serde.serializeDocument(buf, value.(*oschema.ODocument))
		ogl.Debugf("DEBUG EMBEDDED: -writeDataVal val: %v\n", value) // DEBUG

	case oschema.EMBEDDEDLIST:
		err = serde.serializeEmbeddedCollection(buf, value.(oschema.OEmbeddedList))
		ogl.Debugf("DEBUG EMBD-LIST: -writeDataVal val: %v\n", value) // DEBUG

	case oschema.EMBEDDEDSET:
		err = serde.serializeEmbeddedCollection(buf, value.(oschema.OEmbeddedList))
		ogl.Debugf("DEBUG EMBD-SET: -writeDataVal val: %v\n", value) // DEBUG

	case oschema.EMBEDDEDMAP:
		err = serde.writeEmbeddedMap(buf, value.(oschema.OEmbeddedMap))
		ogl.Debugf("DEBUG EMBEDDEDMAP:  val %v\n", value.(oschema.OEmbeddedMap))

	case oschema.LINK:
		err = serde.writeLink(buf, value.(*oschema.OLink))
		ogl.Debugf("DEBUG LINK:  val %v\n", value) // DEBUG

	case oschema.LINKLIST:
		err = serde.writeLinkList(buf, value.([]*oschema.OLink))
		ogl.Debugf("DEBUG LINKLIST:  val %v\n", value) // DEBUG

	case oschema.LINKSET:
		err = serde.writeLinkList(buf, value.([]*oschema.OLink))
		ogl.Debugf("DEBUG LINKSET:  val %v\n", value) // DEBUG

	case oschema.LINKMAP:
		err = serde.writeLinkMap(buf, value.(map[string]*oschema.OLink))
		ogl.Debugf("DEBUG LINKMAP:  val %v\n", value) // DEBUG

	case oschema.BYTE:
		err = rw.WriteByte(buf, value.(byte))
		ogl.Debugf("DEBUG BYTE: -writeDataVal val: %v\n", value.(byte)) // DEBUG

	case oschema.DECIMAL:
		// TODO: impl me -> Java client uses BigDecimal for this
		panic("ORecordSerializerV0#writeDataValue DECIMAL NOT YET IMPLEMENTED")

	case oschema.CUSTOM:
		// TODO: impl me
		panic("ORecordSerializerV0#writeDataValue CUSTOM NOT YET IMPLEMENTED")
	case oschema.LINKBAG:
		panic("ORecordSerializerV0#writeDataValue LINKBAG NOT YET IMPLEMENTED")
	default:
		// ANY and TRANSIENT are do nothing ops
	}
	return err
}
Exemple #15
0
//
// In Progress attempt to rewrite writeSerializedRecord and related fns
// using a seekable/skipping WriteBuf
//
func (serde ORecordSerializerV0) writeSerializedRecord(wbuf *obuf.WriteBuf, doc *oschema.ODocument) (err error) {
	nfields := len(doc.FieldNames())
	ptrPos := make([]int, 0, nfields) // position in buf where data ptr int needs to be written

	currDB := serde.dbc.GetCurrDB()
	oclass, ok := currDB.Classes[doc.Classname]

	docFields := doc.GetFields()
	for _, fld := range docFields {
		var oprop *oschema.OProperty
		if ok {
			oprop = oclass.Properties[fld.Name]
		}

		// FROM THE JAVA CLIENT:
		// if (properties[i] != null) {
		//   OVarIntSerializer.write(bytes, (properties[i].getId() + 1) * -1);
		//   if (properties[i].getType() != OType.ANY)
		//     pos[i] = bytes.alloc(OIntegerSerializer.INT_SIZE);
		//   else
		//     pos[i] = bytes.alloc(OIntegerSerializer.INT_SIZE + 1);   // TODO: why does ANY required an additional byte?
		// } else {
		//   writeString(bytes, entry.getKey());
		//   pos[i] = bytes.alloc(OIntegerSerializer.INT_SIZE + 1);

		if oprop != nil {
			// if property is known in the global properties, then
			// just write its encoded id
			varint.EncodeAndWriteVarInt32(wbuf, encodeFieldIDForHeader(oprop.ID))
			ptrPos = append(ptrPos, wbuf.Len())
			wbuf.Skip(4)
			// Note: no need to write property type when writing property ID

		} else {
			// property Name
			err = varint.WriteString(wbuf, fld.Name)
			if err != nil {
				return oerror.NewTrace(err)
			}
			ptrPos = append(ptrPos, wbuf.Len())
			wbuf.Skip(4)

			// property Type
			err = rw.WriteByte(wbuf, byte(fld.Type))
			if err != nil {
				return oerror.NewTrace(err)
			}
		}
	}
	wbuf.WriteByte(0) // End of Header sentinel

	// now write out the data values
	for i, fld := range docFields {
		currPos := wbuf.Len()
		wbuf.Seek(uint(ptrPos[i]))
		err = rw.WriteInt(wbuf, int32(currPos))
		if err != nil {
			return oerror.NewTrace(err)
		}
		wbuf.Seek(uint(currPos))
		err = serde.writeDataValue(wbuf, fld.Value, fld.Type)
		if err != nil {
			return oerror.NewTrace(err)
		}
	}

	return nil
}
Exemple #16
0
//
// SQLCommand executes SQL commands that are not queries. Any SQL statement
// that does not being with "SELECT" should be sent here.  All SELECT
// statements should go to the SQLQuery function.
//
// Commands can be optionally paramterized using ?, such as:
//
//     INSERT INTO Foo VALUES(a, b, c) (?, ?, ?)
//
// The values for the placeholders (currently) must be provided as strings.
//
// Constraints (for now):
// 1. cmds with only simple positional parameters allowed
// 2. cmds with lists of parameters ("complex") NOT allowed
// 3. parameter types allowed: string only for now
//
// SQL commands in OrientDB tend to return one of two types - a string return value or
// one or more documents. The meaning are specific to the type of query.
//
// ----------------
// For example:
// ----------------
//  for a DELETE statement:
//    retval = number of rows deleted (as a string)
//    docs = empty list
//
//  for an INSERT statement:
//    n = ?
//    docs = ?
//
//  for an CREATE CLASS statement:
//    retval = cluster id of the class (TODO: or it might be number of classes in cluster)
//    docs = empty list
//
//  for an DROP CLASS statement:
//    retval = "true" if successful, "" if class didn't exist (technically it returns null)
//    docs = empty list
//
func SQLCommand(dbc *DBClient, sql string, params ...string) (retval string, docs []*oschema.ODocument, err 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)

	// "classname" (command-type, really) and the sql command
	err = rw.WriteStrings(commandBuf, "c", sql) // c for command(non-idempotent)
	if err != nil {
		return "", nil, oerror.NewTrace(err)
	}

	// SQLCommand
	//  (text:string)
	//  (has-simple-parameters:boolean)
	//  (simple-paremeters:bytes[])  -> serialized Map (EMBEDDEDMAP??)
	//  (has-complex-parameters:boolean)
	//  (complex-parameters:bytes[])  -> serialized Map (EMBEDDEDMAP??)

	serializedParams, err := serializeSimpleSQLParams(dbc, params)
	if err != nil {
		return "", nil, oerror.NewTrace(err)
	}

	// has-simple-parameters
	err = rw.WriteBool(commandBuf, serializedParams != nil)
	if err != nil {
		return "", nil, oerror.NewTrace(err)
	}

	if serializedParams != nil {
		rw.WriteBytes(commandBuf, serializedParams)
	}

	// FIXME: no complex parameters yet since I don't understand what they are
	// has-complex-paramters => HARDCODING FALSE FOR NOW
	err = rw.WriteBool(commandBuf, false)
	if err != nil {
		return "", nil, oerror.NewTrace(err)
	}

	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
	_, 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)
	}

	// for synchronous commands the remaining content is an array of form:
	// [(synch-result-type:byte)[(synch-result-content:?)]]+
	// so the final value will by byte(0) to indicate the end of the array
	// and we must use a loop here

	for {
		resType, err := rw.ReadByte(dbc.conx)
		if err != nil {
			return "", nil, oerror.NewTrace(err)
		}
		// This implementation assumes that SQLCommand can never have "supplementary records"
		// from an extended fetchPlan
		if resType == byte(0) {
			break
		}

		resultType := rune(resType)
		ogl.Debugf("resultType for SQLCommand: %v (%s)\n", resultType, string(rune(resultType)))

		if resultType == 'n' { // null result
			// do nothing - anything need to be done here?

		} else if resultType == 'r' { // single record
			doc, err := readSingleRecord(dbc)
			if err != nil {
				return "", nil, oerror.NewTrace(err)
			}

			ogl.Debugf("r>doc = %v\n", doc) // DEBUG
			if doc != nil {
				docs = make([]*oschema.ODocument, 1)
				docs[0] = doc
			}

		} else if resultType == 'l' { // collection of records
			ogl.Debugln("... resultType l")
			collectionDocs, err := readResultSet(dbc)
			if err != nil {
				return "", nil, oerror.NewTrace(err)
			}

			if docs == nil {
				docs = collectionDocs
			} else {
				docs = append(docs, collectionDocs...)
			}

		} else if resultType == 'a' { // serialized type
			serializedRec, err := rw.ReadBytes(dbc.conx)
			if err != nil {
				return "", nil, oerror.NewTrace(err)
			}
			// TODO: for now I'm going to assume that this always just returns a string
			//       need a use case that violates this assumption
			retval = string(serializedRec)
			if err != nil {
				return "", nil, oerror.NewTrace(err)
			}

		} else {
			_, file, line, _ := runtime.Caller(0)
			// TODO: I've not yet tested this route of code -> how do so?
			ogl.Warnf(">> Got back resultType %v (%v): Not yet supported: line:%d; file:%s\n",
				resultType, string(rune(resultType)), line, file)
			// TODO: returning here is NOT the correct long-term behavior
			return "", nil, fmt.Errorf("Got back resultType %v (%v): Not yet supported: line:%d; file:%s\n",
				resultType, string(rune(resultType)), line, file)
		}
	}

	return retval, docs, err
}
Exemple #17
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
}
//
// 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
}