Example #1
0
//
// DropCluster drops 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.
// If nil is returned, then the action succeeded.
//
func DropCluster(dbc *DBClient, clusterName string) error {
	dbc.buf.Reset()

	clusterID := findClusterWithName(dbc.currDB.Clusters, strings.ToLower(clusterName))
	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 DropClusterById(dbc, clusterID)
		return fmt.Errorf("No cluster with name %s is known in database %s\n", clusterName, dbc.currDB.Name)
	}

	err := writeCommandAndSessionId(dbc, REQUEST_DATACLUSTER_DROP)
	if err != nil {
		return oerror.NewTrace(err)
	}

	err = rw.WriteShort(dbc.buf, clusterID)
	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)
	}

	delStatus, err := rw.ReadByte(dbc.conx)
	if err != nil {
		return oerror.NewTrace(err)
	}
	if delStatus != byte(1) {
		return fmt.Errorf("Drop cluster action failed. Return code from server was not '1', but %d",
			delStatus)
	}

	return nil
}
Example #2
0
//
// FetchClusterDataRange returns the range of record ids for a cluster
//
func FetchClusterDataRange(dbc *DBClient, clusterName string) (begin, end int64, err error) {
	dbc.buf.Reset()

	clusterID := findClusterWithName(dbc.currDB.Clusters, strings.ToLower(clusterName))
	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 FetchClusterRangeById(dbc, clusterID)
		return begin, end,
			fmt.Errorf("No cluster with name %s is known in database %s\n", clusterName, dbc.currDB.Name)
	}

	err = writeCommandAndSessionId(dbc, REQUEST_DATACLUSTER_DATARANGE)
	if err != nil {
		return begin, end, oerror.NewTrace(err)
	}

	err = rw.WriteShort(dbc.buf, clusterID)
	if err != nil {
		return begin, end, oerror.NewTrace(err)
	}

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

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

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

	begin, err = rw.ReadLong(dbc.conx)
	if err != nil {
		return begin, end, oerror.NewTrace(err)
	}

	end, err = rw.ReadLong(dbc.conx)
	return begin, end, err
}
Example #3
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
}
Example #4
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
}
Example #5
0
//
// FetchRecordByRID takes an ORID and reads that record from the database.
// NOTE: for now I'm assuming all records are Documents (they can also be "raw bytes" or "flat data")
// and for some reason I don't understand, multiple records can be returned, so I'm returning
// a slice of ODocument
//
// TODO: may also want to expose options: ignoreCache, loadTombstones bool
// TODO: need to properly handle fetchPlan
func FetchRecordByRID(dbc *DBClient, orid oschema.ORID, fetchPlan string) ([]*oschema.ODocument, error) {
	dbc.buf.Reset()

	err := writeCommandAndSessionId(dbc, REQUEST_RECORD_LOAD)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

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

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

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

	ignoreCache := true // hardcoding for now
	err = rw.WriteBool(dbc.buf, ignoreCache)
	if err != nil {
		return nil, oerror.NewTrace(err)
	}

	loadTombstones := false // hardcoding for now
	err = rw.WriteBool(dbc.buf, loadTombstones)
	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)
	}

	// this query can return multiple records (though I don't understand why)
	// so must do this in a loop
	docs := make([]*oschema.ODocument, 0, 1)
	for {
		payloadStatus, err := rw.ReadByte(dbc.conx)
		if err != nil {
			return nil, oerror.NewTrace(err)
		}

		if payloadStatus == byte(0) {
			break
		}

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

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

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

		ogl.Debugf("rectype:%v, recversion:%v, len(databytes):%v\n", rectype, recversion, len(databytes))

		if rectype == 'd' {
			// we don't know the classname so set empty value
			doc := oschema.NewDocument("")
			doc.RID = orid
			doc.Version = recversion

			// the first byte specifies record serialization version
			// use it to look up serializer
			serde := dbc.RecordSerDes[int(databytes[0])]
			// then strip off the version byte and send the data to the serde
			err = serde.Deserialize(dbc, doc, obuf.NewReadBuffer(databytes[1:]))
			if err != nil {
				return nil, fmt.Errorf("ERROR in Deserialize for rid %v: %v\n", orid, err)
			}
			docs = append(docs, doc)

		} else {
			return nil,
				fmt.Errorf("Only `document` records are currently supported by the client. Record returned was type: %v", rectype)
		}
	}

	return docs, nil
}
Example #6
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
}
Example #7
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
}
Example #8
0
//
// loadConfigRecord loads record #0:0 for the current database, caching
// some of the information returned into OStorageConfiguration
//
func loadConfigRecord(dbc *DBClient) (schemaRID string, err error) {
	// The config record comes back as type 'b' (raw bytes), which should
	// just be converted to a string then tokenized by the pipe char

	dbc.buf.Reset()
	var (
		clusterId  int16
		clusterPos int64
	)
	err = writeCommandAndSessionId(dbc, REQUEST_RECORD_LOAD)
	if err != nil {
		return schemaRID, err
	}

	clusterId = 0
	err = rw.WriteShort(dbc.buf, clusterId)
	if err != nil {
		return schemaRID, err
	}

	clusterPos = 0
	err = rw.WriteLong(dbc.buf, clusterPos)
	if err != nil {
		return schemaRID, err
	}

	fetchPlan := "*:-1 index:0"
	err = rw.WriteString(dbc.buf, fetchPlan)
	if err != nil {
		return schemaRID, err
	}

	ignoreCache := true
	err = rw.WriteBool(dbc.buf, ignoreCache)
	if err != nil {
		return schemaRID, err
	}

	loadTombstones := true // based on Java client code
	err = rw.WriteBool(dbc.buf, loadTombstones)
	if err != nil {
		return schemaRID, err
	}

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

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

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

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

	if payloadStatus == byte(0) {
		return schemaRID, errors.New("Payload status for #0:0 load was 0. No config data returned.")
	}

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

	// this is the record version - don't see a reason to check or cache it right now
	_, err = rw.ReadInt(dbc.conx)
	if err != nil {
		return schemaRID, err
	}

	databytes, err := rw.ReadBytes(dbc.conx)
	if err != nil {
		return schemaRID, err
	}

	if rectype != 'b' {
		if err != nil {
			return schemaRID, fmt.Errorf("Expected rectype %d, but was: %d", 'b', rectype)
		}
	}

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

	if payloadStatus != byte(0) {
		return schemaRID,
			errors.New("Second Payload status for #0:0 load was not 0. More than one record returned unexpectedly")
	}

	err = parseConfigRecord(dbc.currDB, string(databytes))
	if err != nil {
		return schemaRID, err
	}

	schemaRID = dbc.currDB.StorageCfg.schemaRID
	return schemaRID, err
}
Example #9
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
}
Example #10
0
//
// 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
}