Example #1
0
// ExecNeo executes a query that returns no rows. Implements a Neo-friendly alternative to sql/driver.
func (s *boltStmt) ExecNeo(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")
	}

	runResp, pullResp, _, err := s.conn.sendRunPullAllConsumeAll(s.query, params)
	if err != nil {
		return nil, err
	}

	success, ok := runResp.(messages.SuccessMessage)
	if !ok {
		return nil, errors.New("Unrecognized response type when running exec query: %#v", success)

	}

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

	success, ok = pullResp.(messages.SuccessMessage)
	if !ok {
		return nil, errors.New("Unrecognized response when discarding exec rows: %#v", success)
	}

	log.Infof("Got discard all success message: %#v", success)

	return newResult(success.Metadata), nil
}
Example #2
0
// 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
}
Example #3
0
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)
		}
	}
}
Example #4
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
}
Example #5
0
// NextNeo gets the next row result
// When the rows are completed, returns the success metadata
// and io.EOF
func (r *boltRows) NextNeo() ([]interface{}, map[string]interface{}, error) {
	if r.closed {
		return nil, nil, errors.New("Rows are already closed")
	}

	if !r.consumed {
		r.consumed = true
		if err := r.statement.conn.sendPullAll(); err != nil {
			r.finishedConsume = true
			return nil, nil, err
		}
	}

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

	switch resp := respInt.(type) {
	case messages.SuccessMessage:
		log.Infof("Got success message: %#v", resp)
		r.finishedConsume = true
		return nil, resp.Metadata, io.EOF
	case messages.RecordMessage:
		log.Infof("Got record message: %#v", resp)
		return resp.Fields, nil, nil
	default:
		return nil, nil, errors.New("Unrecognized response type getting next query row: %#v", resp)
	}
}
Example #6
0
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
}
Example #7
0
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()
}
Example #8
0
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
}
Example #9
0
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
}
Example #10
0
// 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)
	}
}
Example #11
0
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)
		}
	}
}
Example #12
0
// 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
}
Example #13
0
func (c *boltConn) initialize() error {

	// Handle recorder. If there is no conn string, assume we're playing back a recording.
	// If there is a recorder and a conn string, assume we're recording the connection
	// Else, just create the conn normally
	var err error
	if c.connStr == "" && c.driver != nil && c.driver.recorder != nil {
		c.conn = c.driver.recorder
	} else if c.driver != nil && c.driver.recorder != nil {
		c.driver.recorder.Conn, err = c.createConn()
		if err != nil {
			return err
		}
		c.conn = c.driver.recorder
	} else {
		c.conn, err = c.createConn()
		if err != nil {
			return err
		}
	}

	if err := c.handShake(); err != nil {
		if e := c.Close(); e != nil {
			log.Errorf("An error occurred closing connection: %s", e)
		}
		return err
	}

	respInt, err := c.sendInit()
	if err != nil {
		if e := c.Close(); e != nil {
			log.Errorf("An error occurred closing connection: %s", e)
		}
		return err
	}

	switch resp := respInt.(type) {
	case messages.SuccessMessage:
		log.Infof("Successfully initiated Bolt connection: %+v", resp)
		return nil
	default:
		log.Errorf("Got an unrecognized message when initializing connection :%+v", resp)
		if e := c.Close(); e != nil {
			log.Errorf("An error occurred closing connection: %s", e)
		}
		return errors.New("Unrecognized response from the server: %#v", resp)
	}
}
Example #14
0
func (c *boltConn) consumeAll() ([]interface{}, interface{}, error) {
	log.Info("Consuming all responses until success/failure")

	responses := []interface{}{}
	for {
		respInt, err := c.consume()
		if err != nil {
			return nil, respInt, err
		}

		if success, isSuccess := respInt.(messages.SuccessMessage); isSuccess {
			log.Infof("Got success message: %#v", success)
			return responses, success, nil
		}

		responses = append(responses, respInt)
	}
}
Example #15
0
func (s *boltStmt) queryNeo(params map[string]interface{}) (*boltRows, 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")
	}

	respInt, err := s.conn.sendRunConsume(s.query, params)
	if err != nil {
		return nil, err
	}

	resp, ok := respInt.(messages.SuccessMessage)
	if !ok {
		return nil, errors.New("Unrecognized response type running query: %#v", resp)
	}

	log.Infof("Got success message on run query: %#v", resp)
	s.rows = newRows(s, resp.Metadata)
	return s.rows, nil
}
func TestBoltDriverPool_Concurrent(t *testing.T) {
	if neo4jConnStr == "" {
		t.Skip("Cannot run this test when in recording mode")
	}

	var wg sync.WaitGroup
	wg.Add(2)
	driver, err := NewDriverPool(neo4jConnStr, 2)
	if err != nil {
		t.Fatalf("An error occurred opening driver pool: %#v", err)
	}

	one := make(chan bool)
	two := make(chan bool)
	three := make(chan bool)
	four := make(chan bool)
	five := make(chan bool)
	six := make(chan bool)
	seven := make(chan bool)
	go func() {
		defer wg.Done()

		conn, err := driver.OpenPool()
		if err != nil {
			t.Fatalf("An error occurred opening conn: %s", err)
		}
		defer conn.Close()

		data, _, _, err := conn.QueryNeoAll(`MATCH (n) RETURN n`, nil)
		if err != nil {
			t.Fatalf("An error occurred querying neo: %s", err)
		}

		log.Info("1")
		one <- true
		<-two

		if len(data) != 0 {
			t.Fatalf("Expected no data: %#v", data)
		}

		data, _, _, err = conn.QueryNeoAll(`MATCH (n) RETURN n`, nil)
		if err != nil {
			t.Fatalf("An error occurred querying neo: %s", err)
		}

		log.Infof("data: %#v", data)
		if len(data) != 1 {
			t.Fatalf("Expected no data: %#v", data)
		}

		log.Info("3")
		three <- true
		<-four

		data, _, _, err = conn.QueryNeoAll(`MATCH path=(:FOO)-[:BAR]->(:BAZ) RETURN path`, nil)
		if err != nil {
			t.Fatalf("An error occurred querying neo: %s", err)
		}

		if len(data) != 1 {
			t.Fatalf("Expected no data: %#v", data)
		}

		log.Info("5")
		five <- true
		<-six

		data, _, _, err = conn.QueryNeoAll(`MATCH path=(:FOO)-[:BAR]->(:BAZ) RETURN path`, nil)
		if err != nil {
			t.Fatalf("An error occurred querying neo: %s", err)
		}

		if len(data) != 0 {
			t.Fatalf("Expected no data: %#v", data)
		}

		log.Info("7")
		seven <- true
	}()

	go func() {
		<-one
		defer wg.Done()

		conn, err := driver.OpenPool()
		if err != nil {
			t.Fatalf("An error occurred opening conn: %s", err)
		}
		defer conn.Close()

		_, err = conn.ExecNeo(`CREATE (f:FOO)`, nil)
		if err != nil {
			t.Fatalf("An error occurred creating f neo: %s", err)
		}

		log.Info("2")
		two <- true
		<-three

		_, err = conn.ExecNeo(`MATCH (f:FOO) CREATE UNIQUE (f)-[b:BAR]->(c:BAZ)`, nil)
		if err != nil {
			t.Fatalf("An error occurred creating f neo: %s", err)
		}

		log.Info("4")
		four <- true
		<-five

		_, err = conn.ExecNeo(`MATCH (:FOO)-[b:BAR]->(:BAZ) DELETE b`, nil)
		if err != nil {
			t.Fatalf("An error occurred creating f neo: %s", err)
		}

		_, err = conn.ExecNeo(`MATCH (n) DETACH DELETE n`, nil)
		if err != nil {
			t.Fatalf("An error occurred creating f neo: %s", err)
		}

		log.Info("6")
		six <- true
		<-seven

	}()

	wg.Wait()
}