Exemple #1
0
// Rollback rolls back and closes the transaction
func (t *boltTx) Rollback() error {
	if t.closed {
		return errors.New("Transaction already closed")
	}
	if t.conn.statement != nil {
		if err := t.conn.statement.Close(); err != nil {
			return errors.Wrap(err, "An error occurred closing open rows in transaction Rollback")
		}
	}

	successInt, pullInt, err := t.conn.sendRunPullAllConsumeSingle("ROLLBACK", nil)
	if err != nil {
		return errors.Wrap(err, "An error occurred rolling back transaction")
	}

	success, ok := successInt.(messages.SuccessMessage)
	if !ok {
		return errors.New("Unrecognized response type rolling back transaction: %#v", success)
	}

	log.Infof("Got success message rolling back transaction: %#v", success)

	pull, ok := pullInt.(messages.SuccessMessage)
	if !ok {
		return errors.New("Unrecognized response type pulling transaction: %#v", pull)
	}

	log.Infof("Got success message pulling transaction: %#v", pull)

	t.conn.transaction = nil
	t.closed = true
	return err
}
func (s *boltStmt) QueryPipeline(params ...map[string]interface{}) (PipelineRows, error) {
	if s.closed {
		return nil, errors.New("Neo4j Bolt statement already closed")
	}
	if s.rows != nil {
		return nil, errors.New("Another query is already open")
	}

	if len(params) != len(s.queries) {
		return nil, errors.New("Must pass same number of params as there are queries")
	}

	for i, query := range s.queries {
		err := s.conn.sendRunPullAll(query, params[i])
		if err != nil {
			return nil, errors.Wrap(err, "Error running query:\n\n%s\n\nWith Params:\n%#v", query, params[i])
		}
	}

	log.Info("Successfully ran all pipeline queries")

	resp, err := s.conn.consume()
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred consuming initial pipeline command")
	}

	success, ok := resp.(messages.SuccessMessage)
	if !ok {
		return nil, errors.New("Got unexpected return message when consuming initial pipeline command: %#v", resp)
	}

	s.rows = newPipelineRows(s, success.Metadata, 0)
	return s.rows, nil
}
// Read out the object bytes to decode
func (d Decoder) read() (*bytes.Buffer, error) {
	output := &bytes.Buffer{}
	for {
		lengthBytes := make([]byte, 2)
		if numRead, err := d.r.Read(lengthBytes); numRead != 2 {
			return nil, errors.Wrap(err, "Couldn't read expected bytes for message length. Read: %d Expected: 2.", numRead)
		}

		// Chunk header contains length of current message
		messageLen := binary.BigEndian.Uint16(lengthBytes)
		if messageLen == 0 {
			// If the length is 0, the chunk is done.
			return output, nil
		}

		data, err := d.readData(messageLen)
		if err != nil {
			return output, errors.Wrap(err, "An error occurred reading message data")
		}

		numWritten, err := output.Write(data)
		if numWritten < len(data) {
			return output, errors.New("Didn't write full data on output. Expected: %d Wrote: %d", len(data), numWritten)
		} else if err != nil {
			return output, errors.Wrap(err, "Error writing data to output")
		}
	}
}
func (c *boltConn) createConn() (net.Conn, error) {

	var err error
	c.url, err = c.parseURL()
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred parsing the conn URL")
	}

	var conn net.Conn
	if c.useTLS {
		config, err := c.tlsConfig()
		if err != nil {
			return nil, errors.Wrap(err, "An error occurred setting up TLS configuration")
		}
		conn, err = tls.Dial("tcp", c.url.Host, config)
		if err != nil {
			return nil, errors.Wrap(err, "An error occurred dialing to neo4j")
		}
	} else {
		conn, err = net.DialTimeout("tcp", c.url.Host, c.timeout)
		if err != nil {
			return nil, errors.Wrap(err, "An error occurred dialing to neo4j")
		}
	}

	return conn, nil
}
func (c *boltConn) ackFailure(failure messages.FailureMessage) error {
	log.Infof("Acknowledging Failure: %#v", failure)

	ack := messages.NewAckFailureMessage()
	err := encoding.NewEncoder(c, c.chunkSize).Encode(ack)
	if err != nil {
		return errors.Wrap(err, "An error occurred encoding ack failure message")
	}

	for {
		respInt, err := encoding.NewDecoder(c).Decode()
		if err != nil {
			return errors.Wrap(err, "An error occurred decoding ack failure message response")
		}

		switch resp := respInt.(type) {
		case messages.IgnoredMessage:
			log.Infof("Got ignored message when acking failure: %#v", resp)
			continue
		case messages.SuccessMessage:
			log.Infof("Got success message when acking failure: %#v", resp)
			return nil
		case messages.FailureMessage:
			log.Errorf("Got failure message when acking failure: %#v", resp)
			return c.reset()
		default:
			log.Errorf("Got unrecognized response from acking failure: %#v", resp)
			err := c.Close()
			if err != nil {
				log.Errorf("An error occurred closing the session: %s", err)
			}
			return errors.New("Got unrecognized response from acking failure: %#v. CLOSING SESSION!", resp)
		}
	}
}
// Close closes the rows
func (r *boltRows) Close() error {
	if r.closed {
		return nil
	}

	if !r.consumed {
		// Discard all messages if not consumed
		respInt, err := r.statement.conn.sendDiscardAllConsume()
		if err != nil {
			return errors.Wrap(err, "An error occurred discarding messages on row close")
		}

		switch resp := respInt.(type) {
		case messages.SuccessMessage:
			log.Infof("Got success message: %#v", resp)
		default:
			return errors.New("Unrecognized response type discarding all rows: Value: %#v", resp)
		}

	} else if !r.finishedConsume {
		// If this is a pipeline statement, we need to "consume all" multiple times
		numConsume := 1
		if r.statement.queries != nil {
			numQueries := len(r.statement.queries)
			if numQueries > 0 {
				// So, every pipeline statement has two successes
				// but by the time you get to the row object, one has
				// been consumed. Hence we need to clear out the
				// rest of the messages on close by taking the current
				// index * 2 but removing the first success
				numConsume = ((numQueries - r.pipelineIndex) * 2) - 1
			}
		}

		// Clear out all unconsumed messages if we
		// never finished consuming them.
		_, _, err := r.statement.conn.consumeAllMultiple(numConsume)
		if err != nil {
			return errors.Wrap(err, "An error occurred clearing out unconsumed stream")
		}
	}

	r.closed = true
	r.statement.rows = nil

	if r.closeStatement {
		return r.statement.Close()
	}
	return nil
}
func (s *boltStmt) ExecPipeline(params ...map[string]interface{}) ([]Result, error) {
	if s.closed {
		return nil, errors.New("Neo4j Bolt statement already closed")
	}
	if s.rows != nil {
		return nil, errors.New("Another query is already open")
	}

	if len(params) != len(s.queries) {
		return nil, errors.New("Must pass same number of params as there are queries")
	}

	for i, query := range s.queries {
		err := s.conn.sendRunPullAll(query, params[i])
		if err != nil {
			return nil, errors.Wrap(err, "Error running exec query:\n\n%s\n\nWith Params:\n%#v", query, params[i])
		}
	}

	log.Info("Successfully ran all pipeline queries")

	results := make([]Result, len(s.queries))
	for i := range s.queries {
		runResp, err := s.conn.consume()
		if err != nil {
			return nil, errors.Wrap(err, "An error occurred getting result of exec command: %#v", runResp)
		}

		success, ok := runResp.(messages.SuccessMessage)
		if !ok {
			return nil, errors.New("Unexpected response when getting exec query result: %#v", runResp)
		}

		_, pullResp, err := s.conn.consumeAll()
		if err != nil {
			return nil, errors.Wrap(err, "An error occurred getting result of exec discard command: %#v", pullResp)
		}

		success, ok = pullResp.(messages.SuccessMessage)
		if !ok {
			return nil, errors.New("Unexpected response when getting exec query discard result: %#v", pullResp)
		}

		results[i] = newResult(success.Metadata)

	}

	return results, nil
}
// Read reads the data from the underlying connection
func (c *boltConn) Read(b []byte) (n int, err error) {
	if err := c.conn.SetReadDeadline(time.Now().Add(c.timeout)); err != nil {
		return 0, errors.Wrap(err, "An error occurred setting read deadline")
	}

	n, err = c.conn.Read(b)

	if log.GetLevel() >= log.TraceLevel {
		log.Tracef("Read %d bytes from stream:\n\n%s\n", n, sprintByteHex(b))
	}

	if err != nil && err != io.EOF {
		err = errors.Wrap(err, "An error occurred reading from stream")
	}
	return n, err
}
// Write writes the data to the underlying connection
func (c *boltConn) Write(b []byte) (n int, err error) {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.timeout)); err != nil {
		return 0, errors.Wrap(err, "An error occurred setting write deadline")
	}

	n, err = c.conn.Write(b)

	if log.GetLevel() >= log.TraceLevel {
		log.Tracef("Wrote %d of %d bytes to stream:\n\n%s\n", len(b), n, sprintByteHex(b[:n]))
	}

	if err != nil {
		err = errors.Wrap(err, "An error occurred writing to stream")
	}
	return n, err
}
// Begin begins a new transaction with the Neo4J Database
func (c *boltConn) Begin() (driver.Tx, error) {
	if c.transaction != nil {
		return nil, errors.New("An open transaction already exists")
	}
	if c.statement != nil {
		return nil, errors.New("Cannot open a transaction when you already have an open statement")
	}
	if c.closed {
		return nil, errors.New("Connection already closed")
	}

	successInt, pullInt, err := c.sendRunPullAllConsumeSingle("BEGIN", nil)
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred beginning transaction")
	}

	success, ok := successInt.(messages.SuccessMessage)
	if !ok {
		return nil, errors.New("Unrecognized response type beginning transaction: %#v", success)
	}

	log.Infof("Got success message beginning transaction: %#v", success)

	success, ok = pullInt.(messages.SuccessMessage)
	if !ok {
		return nil, errors.New("Unrecognized response type pulling transaction:  %#v", success)
	}

	log.Infof("Got success message pulling transaction: %#v", success)

	return newTx(c), nil
}
func (c *boltConn) sendRun(query string, args map[string]interface{}) error {
	log.Infof("Sending RUN message: query %s (args: %#v)", query, args)
	runMessage := messages.NewRunMessage(query, args)
	if err := encoding.NewEncoder(c, c.chunkSize).Encode(runMessage); err != nil {
		return errors.Wrap(err, "An error occurred running query")
	}

	return nil
}
// encodeInt encodes a nil object to the stream
func (e Encoder) encodeInt(val int64) error {
	var err error
	switch {
	case val >= math.MinInt64 && val < math.MinInt32:
		// Write as INT_64
		if _, err = e.Write([]byte{Int64Marker}); err != nil {
			return err
		}
		err = binary.Write(e, binary.BigEndian, val)
	case val >= math.MinInt32 && val < math.MinInt16:
		// Write as INT_32
		if _, err = e.Write([]byte{Int32Marker}); err != nil {
			return err
		}
		err = binary.Write(e, binary.BigEndian, int32(val))
	case val >= math.MinInt16 && val < math.MinInt8:
		// Write as INT_16
		if _, err = e.Write([]byte{Int16Marker}); err != nil {
			return err
		}
		err = binary.Write(e, binary.BigEndian, int16(val))
	case val >= math.MinInt8 && val < -16:
		// Write as INT_8
		if _, err = e.Write([]byte{Int8Marker}); err != nil {
			return err
		}
		err = binary.Write(e, binary.BigEndian, int8(val))
	case val >= -16 && val <= math.MaxInt8:
		// Write as TINY_INT
		err = binary.Write(e, binary.BigEndian, int8(val))
	case val > math.MaxInt8 && val <= math.MaxInt16:
		// Write as INT_16
		if _, err = e.Write([]byte{Int16Marker}); err != nil {
			return err
		}
		err = binary.Write(e, binary.BigEndian, int16(val))
	case val > math.MaxInt16 && val <= math.MaxInt32:
		// Write as INT_32
		if _, err = e.Write([]byte{Int32Marker}); err != nil {
			return err
		}
		err = binary.Write(e, binary.BigEndian, int32(val))
	case val > math.MaxInt32 && val <= math.MaxInt64:
		// Write as INT_64
		if _, err = e.Write([]byte{Int64Marker}); err != nil {
			return err
		}
		err = binary.Write(e, binary.BigEndian, val)
	default:
		return errors.New("Int too long to write: %d", val)
	}
	if err != nil {
		return errors.Wrap(err, "An error occured writing an int to bolt")
	}
	return err
}
func (c *boltConn) sendInit() (interface{}, error) {
	log.Infof("Sending INIT Message. ClientID: %s User: %s Password: %s", ClientID, c.user, c.password)

	initMessage := messages.NewInitMessage(ClientID, c.user, c.password)
	if err := encoding.NewEncoder(c, c.chunkSize).Encode(initMessage); err != nil {
		return nil, errors.Wrap(err, "An error occurred sending init message")
	}

	return c.consume()
}
func (c *boltConn) sendDiscardAll() error {
	log.Infof("Sending DISCARD_ALL message")

	discardAllMessage := messages.NewDiscardAllMessage()
	err := encoding.NewEncoder(c, c.chunkSize).Encode(discardAllMessage)
	if err != nil {
		return errors.Wrap(err, "An error occurred encoding discard all query")
	}

	return nil
}
func (c *boltConn) sendPullAll() error {
	log.Infof("Sending PULL_ALL message")

	pullAllMessage := messages.NewPullAllMessage()
	err := encoding.NewEncoder(c, c.chunkSize).Encode(pullAllMessage)
	if err != nil {
		return errors.Wrap(err, "An error occurred encoding pull all query")
	}

	return nil
}
// flush finishes the encoding stream by flushing it to the writer
func (e Encoder) flush() error {
	length := e.buf.Len()
	if length > 0 {
		if err := binary.Write(e.w, binary.BigEndian, uint16(length)); err != nil {
			return errors.Wrap(err, "An error occured writing length bytes during flush")
		}

		if _, err := e.buf.WriteTo(e.w); err != nil {
			return errors.Wrap(err, "An error occured writing message bytes during flush")
		}
	}

	_, err := e.w.Write(EndMessage)
	if err != nil {
		return errors.Wrap(err, "An error occurred ending encoding message")
	}
	e.buf.Reset()

	return nil
}
// encodeFloat encodes a nil object to the stream
func (e Encoder) encodeFloat(val float64) error {
	if _, err := e.Write([]byte{FloatMarker}); err != nil {
		return err
	}

	err := binary.Write(e, binary.BigEndian, val)
	if err != nil {
		return errors.Wrap(err, "An error occured writing a float to bolt")
	}

	return err
}
func (d Decoder) readData(messageLen uint16) ([]byte, error) {
	output := make([]byte, messageLen)
	var totalRead uint16
	for totalRead < messageLen {
		data := make([]byte, messageLen-totalRead)
		numRead, err := d.r.Read(data)
		if err != nil {
			return nil, errors.Wrap(err, "An error occurred reading from stream")
		} else if numRead == 0 {
			return nil, errors.Wrap(err, "Couldn't read expected bytes for message. Read: %d Expected: %d.", totalRead, messageLen)
		}

		for idx, b := range data {
			output[uint16(idx)+totalRead] = b
		}

		totalRead += uint16(numRead)
	}

	return output, nil
}
// newBoltConn Creates a new bolt connection
func newBoltConn(connStr string, driver *boltDriver) (*boltConn, error) {

	c := createBoltConn(connStr)
	c.driver = driver

	err := c.initialize()
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred initializing connection")
	}

	return c, nil
}
// encodeMessageStructure encodes a nil object to the stream
func (e Encoder) encodeStructure(val structures.Structure) error {

	fields := val.AllFields()
	length := len(fields)
	switch {
	case length <= 15:
		if _, err := e.Write([]byte{byte(TinyStructMarker + length)}); err != nil {
			return err
		}
	case length > 15 && length <= math.MaxUint8:
		if _, err := e.Write([]byte{Struct8Marker}); err != nil {
			return err
		}
		if err := binary.Write(e, binary.BigEndian, int8(length)); err != nil {
			return err
		}
	case length > math.MaxUint8 && length <= math.MaxUint16:
		if _, err := e.Write([]byte{Struct16Marker}); err != nil {
			return err
		}
		if err := binary.Write(e, binary.BigEndian, int16(length)); err != nil {
			return err
		}
	default:
		return errors.New("Structure too long to write: %+v", val)
	}

	_, err := e.Write([]byte{byte(val.Signature())})
	if err != nil {
		return errors.Wrap(err, "An error occurred writing to encoder a struct field")
	}

	for _, field := range fields {
		if err := e.encode(field); err != nil {
			return errors.Wrap(err, "An error occurred encoding a struct field")
		}
	}

	return nil
}
func (c *boltConn) parseURL() (*url.URL, error) {
	user := ""
	password := ""
	url, err := url.Parse(c.connStr)
	if err != nil {
		return url, errors.Wrap(err, "An error occurred parsing bolt URL")
	} else if strings.ToLower(url.Scheme) != "bolt" {
		return url, errors.New("Unsupported connection string scheme: %s. Driver only supports 'bolt' scheme.", url.Scheme)
	}

	if url.User != nil {
		c.user = url.User.Username()
		var isSet bool
		c.password, isSet = url.User.Password()
		if !isSet {
			return url, errors.New("Must specify password when passing user")
		}
	}

	timeout := url.Query().Get("timeout")
	if timeout != "" {
		timeoutInt, err := strconv.Atoi(timeout)
		if err != nil {
			return url, errors.New("Invalid format for timeout: %s.  Must be integer", timeout)
		}

		c.timeout = time.Duration(timeoutInt) * time.Second
	}

	useTLS := url.Query().Get("tls")
	c.useTLS = strings.HasPrefix(strings.ToLower(useTLS), "t") || useTLS == "1"

	if c.useTLS {
		c.certFile = url.Query().Get("tls_cert_file")
		c.keyFile = url.Query().Get("tls_key_file")
		c.caCertFile = url.Query().Get("tls_ca_cert_file")
		noVerify := url.Query().Get("tls_no_verify")
		c.tlsNoVerify = strings.HasPrefix(strings.ToLower(noVerify), "t") || noVerify == "1"
	}

	log.Trace("Bolt Host: ", url.Host)
	log.Trace("Timeout: ", c.timeout)
	log.Trace("User: "******"Password: "******"TLS: ", c.useTLS)
	log.Trace("TLS No Verify: ", c.tlsNoVerify)
	log.Trace("Cert File: ", c.certFile)
	log.Trace("Key File: ", c.keyFile)
	log.Trace("CA Cert File: ", c.caCertFile)

	return url, nil
}
func (c *boltConn) reset() error {
	log.Info("Resetting session")

	reset := messages.NewResetMessage()
	err := encoding.NewEncoder(c, c.chunkSize).Encode(reset)
	if err != nil {
		return errors.Wrap(err, "An error occurred encoding reset message")
	}

	for {
		respInt, err := encoding.NewDecoder(c).Decode()
		if err != nil {
			return errors.Wrap(err, "An error occurred decoding reset message response")
		}

		switch resp := respInt.(type) {
		case messages.IgnoredMessage:
			log.Infof("Got ignored message when resetting session: %#v", resp)
			continue
		case messages.SuccessMessage:
			log.Infof("Got success message when resetting session: %#v", resp)
			return nil
		case messages.FailureMessage:
			log.Errorf("Got failure message when resetting session: %#v", resp)
			err = c.Close()
			if err != nil {
				log.Errorf("An error occurred closing the session: %s", err)
			}
			return errors.New("Error resetting session: %#v. CLOSING SESSION!", resp)
		default:
			log.Errorf("Got unrecognized response from resetting session: %#v", resp)
			err = c.Close()
			if err != nil {
				log.Errorf("An error occurred closing the session: %s", err)
			}
			return errors.New("Got unrecognized response from resetting session: %#v. CLOSING SESSION!", resp)
		}
	}
}
// Close closes the connection
// Driver may allow for pooling in the future, keeping connections alive
func (c *boltConn) Close() error {

	if c.closed {
		return nil
	}

	if c.transaction != nil {
		if err := c.transaction.Rollback(); err != nil {
			return err
		}
	}

	if c.statement != nil {
		if err := c.statement.Close(); err != nil {
			return err
		}
	}

	if c.transaction != nil {
		if err := c.transaction.Rollback(); err != nil {
			return errors.Wrap(err, "Error rolling back transaction when closing connection")
		}
	}

	if c.poolDriver != nil {
		// If using connection pooling, don't close connection, just reclaim it
		c.poolDriver.reclaim(c)
		return nil
	}

	err := c.conn.Close()
	c.closed = true
	if err != nil {
		return errors.Wrap(err, "An error occurred closing the connection")
	}

	return nil
}
// write writes to the writer.  Buffers the writes using chunkSize.
func (e Encoder) Write(p []byte) (n int, err error) {

	n, err = e.buf.Write(p)
	if err != nil {
		err = errors.Wrap(err, "An error occurred writing to encoder temp buffer")
		return n, err
	}

	length := e.buf.Len()
	for length >= int(e.chunkSize) {
		if err := binary.Write(e.w, binary.BigEndian, e.chunkSize); err != nil {
			return 0, errors.Wrap(err, "An error occured writing chunksize")
		}

		numWritten, err := e.w.Write(e.buf.Next(int(e.chunkSize)))
		if err != nil {
			err = errors.Wrap(err, "An error occured writing a chunk")
		}

		return numWritten, err
	}

	return n, nil
}
func (c *boltConn) handShake() error {

	numWritten, err := c.Write(handShake)
	if numWritten != 20 {
		log.Errorf("Couldn't write expected bytes for magic preamble + supported versions. Written: %d. Expected: 4", numWritten)
		if err != nil {
			err = errors.Wrap(err, "An error occurred writing magic preamble + supported versions")
		}
		return err
	}

	numRead, err := c.Read(c.serverVersion)
	if numRead != 4 {
		log.Errorf("Could not read server version response. Read %d bytes. Expected 4 bytes. Output: %s", numRead, c.serverVersion)
		if err != nil {
			err = errors.Wrap(err, "An error occurred reading server version")
		}
		return err
	} else if bytes.Equal(c.serverVersion, noVersionSupported) {
		return errors.New("Server responded with no supported version")
	}

	return nil
}
// NextPipeline gets the next row result
// When the rows are completed, returns the success metadata and the next
// set of rows.
// When all rows are completed, returns io.EOF
func (r *boltRows) NextPipeline() ([]interface{}, map[string]interface{}, PipelineRows, error) {
	if r.closed {
		return nil, nil, nil, errors.New("Rows are already closed")
	}

	respInt, err := r.statement.conn.consume()
	if err != nil {
		return nil, nil, nil, err
	}

	switch resp := respInt.(type) {
	case messages.SuccessMessage:
		log.Infof("Got success message: %#v", resp)

		if r.pipelineIndex == len(r.statement.queries)-1 {
			r.finishedConsume = true
			return nil, nil, nil, err
		}

		successResp, err := r.statement.conn.consume()
		if err == io.EOF {
		} else if err != nil {
			return nil, nil, nil, errors.Wrap(err, "An error occurred getting next set of rows from pipeline command: %#v", successResp)
		}

		success, ok := successResp.(messages.SuccessMessage)
		if !ok {
			return nil, nil, nil, errors.New("Unexpected response getting next set of rows from pipeline command: %#v", successResp)
		}

		r.statement.rows = newPipelineRows(r.statement, success.Metadata, r.pipelineIndex+1)
		r.statement.rows.closeStatement = r.closeStatement
		return nil, success.Metadata, r.statement.rows, nil

	case messages.RecordMessage:
		log.Infof("Got record message: %#v", resp)
		return resp.Fields, nil, nil, nil
	default:
		return nil, nil, nil, errors.New("Unrecognized response type getting next pipeline row: %#v", resp)
	}
}
func (d Decoder) decodeStruct(buffer *bytes.Buffer, size int) (interface{}, error) {

	signature, err := buffer.ReadByte()
	if err != nil {
		return nil, errors.Wrap(err, "An error occurred reading struct signature byte")
	}

	switch signature {
	case graph.NodeSignature:
		return d.decodeNode(buffer)
	case graph.RelationshipSignature:
		return d.decodeRelationship(buffer)
	case graph.PathSignature:
		return d.decodePath(buffer)
	case graph.UnboundRelationshipSignature:
		return d.decodeUnboundRelationship(buffer)
	case messages.RecordMessageSignature:
		return d.decodeRecordMessage(buffer)
	case messages.FailureMessageSignature:
		return d.decodeFailureMessage(buffer)
	case messages.IgnoredMessageSignature:
		return d.decodeIgnoredMessage(buffer)
	case messages.SuccessMessageSignature:
		return d.decodeSuccessMessage(buffer)
	case messages.AckFailureMessageSignature:
		return d.decodeAckFailureMessage(buffer)
	case messages.DiscardAllMessageSignature:
		return d.decodeDiscardAllMessage(buffer)
	case messages.PullAllMessageSignature:
		return d.decodePullAllMessage(buffer)
	case messages.ResetMessageSignature:
		return d.decodeResetMessage(buffer)
	default:
		return nil, errors.New("Unrecognized type decoding struct with signature %x", signature)
	}
}
func (d Decoder) decode(buffer *bytes.Buffer) (interface{}, error) {

	marker, err := buffer.ReadByte()
	if err != nil {
		return nil, errors.Wrap(err, "Error reading marker")
	}

	// Here we have to get the marker as an int to check and see
	// if it's a TINYINT
	var markerInt int8
	err = binary.Read(bytes.NewBuffer([]byte{marker}), binary.BigEndian, &markerInt)
	if err != nil {
		return nil, errors.Wrap(err, "Error reading marker as int8 from bolt message")
	}

	switch {

	// NIL
	case marker == NilMarker:
		return nil, nil

	// BOOL
	case marker == TrueMarker:
		return true, nil
	case marker == FalseMarker:
		return false, nil

	// INT
	case markerInt >= -16 && markerInt <= 127:
		return int64(int8(marker)), nil
	case marker == Int8Marker:
		var out int8
		err := binary.Read(buffer, binary.BigEndian, &out)
		return int64(out), err
	case marker == Int16Marker:
		var out int16
		err := binary.Read(buffer, binary.BigEndian, &out)
		return int64(out), err
	case marker == Int32Marker:
		var out int32
		err := binary.Read(buffer, binary.BigEndian, &out)
		return int64(out), err
	case marker == Int64Marker:
		var out int64
		err := binary.Read(buffer, binary.BigEndian, &out)
		return int64(out), err

	// FLOAT
	case marker == FloatMarker:
		var out float64
		err := binary.Read(buffer, binary.BigEndian, &out)
		return out, err

	// STRING
	case marker >= TinyStringMarker && marker <= TinyStringMarker+0x0F:
		size := int(marker) - int(TinyStringMarker)
		if size == 0 {
			return "", nil
		}
		return string(buffer.Next(size)), nil
	case marker == String8Marker:
		var size int8
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading string size")
		}
		return string(buffer.Next(int(size))), nil
	case marker == String16Marker:
		var size int16
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading string size")
		}
		return string(buffer.Next(int(size))), nil
	case marker == String32Marker:
		var size int32
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading string size")
		}
		return string(buffer.Next(int(size))), nil

	// SLICE
	case marker >= TinySliceMarker && marker <= TinySliceMarker+0x0F:
		size := int(marker) - int(TinySliceMarker)
		return d.decodeSlice(buffer, size)
	case marker == Slice8Marker:
		var size int8
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading slice size")
		}
		return d.decodeSlice(buffer, int(size))
	case marker == Slice16Marker:
		var size int16
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading slice size")
		}
		return d.decodeSlice(buffer, int(size))
	case marker == Slice32Marker:
		var size int32
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading slice size")
		}
		return d.decodeSlice(buffer, int(size))

	// MAP
	case marker >= TinyMapMarker && marker <= TinyMapMarker+0x0F:
		size := int(marker) - int(TinyMapMarker)
		return d.decodeMap(buffer, size)
	case marker == Map8Marker:
		var size int8
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading map size")
		}
		return d.decodeMap(buffer, int(size))
	case marker == Map16Marker:
		var size int16
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading map size")
		}
		return d.decodeMap(buffer, int(size))
	case marker == Map32Marker:
		var size int32
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading map size")
		}
		return d.decodeMap(buffer, int(size))

	// STRUCTURES
	case marker >= TinyStructMarker && marker <= TinyStructMarker+0x0F:
		size := int(marker) - int(TinyStructMarker)
		return d.decodeStruct(buffer, size)
	case marker == Struct8Marker:
		var size int8
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading struct size")
		}
		return d.decodeStruct(buffer, int(size))
	case marker == Struct16Marker:
		var size int16
		if err := binary.Read(buffer, binary.BigEndian, &size); err != nil {
			return nil, errors.Wrap(err, "An error occurred reading struct size")
		}
		return d.decodeStruct(buffer, int(size))

	default:
		return nil, errors.New("Unrecognized marker byte!: %x", marker)
	}

}