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 }
// 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) } }
func (d Decoder) decodeNode(buffer *bytes.Buffer) (graph.Node, error) { node := graph.Node{} nodeIdentityInt, err := d.decode(buffer) if err != nil { return node, err } node.NodeIdentity = nodeIdentityInt.(int64) labelInt, err := d.decode(buffer) if err != nil { return node, err } labelIntSlice, ok := labelInt.([]interface{}) if !ok { return node, errors.New("Expected: Labels []string, but got %T %+v", labelInt, labelInt) } node.Labels, err = sliceInterfaceToString(labelIntSlice) if err != nil { return node, err } propertiesInt, err := d.decode(buffer) if err != nil { return node, err } node.Properties, ok = propertiesInt.(map[string]interface{}) if !ok { return node, errors.New("Expected: Properties map[string]interface{}, but got %T %+v", propertiesInt, propertiesInt) } return node, nil }
// 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 (d Decoder) decodeUnboundRelationship(buffer *bytes.Buffer) (graph.UnboundRelationship, error) { rel := graph.UnboundRelationship{} relIdentityInt, err := d.decode(buffer) if err != nil { return rel, err } rel.RelIdentity = relIdentityInt.(int64) var ok bool typeInt, err := d.decode(buffer) if err != nil { return rel, err } rel.Type, ok = typeInt.(string) if !ok { return rel, errors.New("Expected: Type string, but got %T %+v", typeInt, typeInt) } propertiesInt, err := d.decode(buffer) if err != nil { return rel, err } rel.Properties, ok = propertiesInt.(map[string]interface{}) if !ok { return rel, errors.New("Expected: Properties map[string]interface{}, but got %T %+v", propertiesInt, propertiesInt) } return rel, nil }
// 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 }
// Write to the net conn, recording the interaction func (r *recorder) Write(b []byte) (n int, err error) { if r.Conn != nil { numWritten, err := r.Conn.Write(b) if numWritten > 0 { r.record(b[:numWritten], true) } if err != nil { r.recordErr(err, true) } return numWritten, err } if r.currentEvent >= len(r.events) { return 0, errors.New("Trying to write past all of the events in the recorder! %#v", r) } event := r.events[r.currentEvent] if !event.IsWrite { return 0, errors.New("Recorder expected Write, got Read! %#v, Event: %#v", r, event) } for i := 0; i < len(b); i++ { if len(event.Event) == 0 { return i, errors.New("Attempted to write past current event in recorder! %#v, Event: %#v", r, event) } event.Event = event.Event[1:] } if len(event.Event) == 0 { r.currentEvent++ } return len(b), nil }
// 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 }
// PreparePipeline prepares a new pipeline statement for a query. func (c *boltConn) PreparePipeline(queries ...string) (PipelineStmt, error) { if c.statement != nil { return nil, errors.New("An open statement already exists") } if c.closed { return nil, errors.New("Connection already closed") } c.statement = newPipelineStmt(queries, c) return c.statement, nil }
func (c *boltConn) prepare(query string) (*boltStmt, error) { if c.statement != nil { return nil, errors.New("An open statement already exists") } if c.closed { return nil, errors.New("Connection already closed") } c.statement = newStmt(query, c) return c.statement, 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) ExecPipeline(queries []string, params ...map[string]interface{}) ([]Result, error) { if c.statement != nil { return nil, errors.New("An open statement already exists") } if c.closed { return nil, errors.New("Connection already closed") } stmt := newPipelineStmt(queries, c) defer stmt.Close() return stmt.ExecPipeline(params...) }
// Exec executes a query that returns no rows. See sql/driver.Stmt. // You must bolt encode a map to pass as []bytes for the driver value func (c *boltConn) Exec(query string, args []driver.Value) (driver.Result, error) { if c.statement != nil { return nil, errors.New("An open statement already exists") } if c.closed { return nil, errors.New("Connection already closed") } stmt := newStmt(query, c) defer stmt.Close() return stmt.Exec(args) }
// ExecNeo executes a query that returns no rows. Implements a Neo-friendly alternative to sql/driver. func (c *boltConn) ExecNeo(query string, params map[string]interface{}) (Result, error) { if c.statement != nil { return nil, errors.New("An open statement already exists") } if c.closed { return nil, errors.New("Connection already closed") } stmt := newStmt(query, c) defer stmt.Close() return stmt.ExecNeo(params) }
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 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") } } }
// RowsAffected returns the number of nodes+rels created/deleted. For reasons of limitations // on the API, we cannot tell how many nodes+rels were updated, only how many properties were // updated. If this changes in the future, number updated will be added to the output of this // interface. func (r boltResult) RowsAffected() (int64, error) { stats, ok := r.metadata["stats"].(map[string]interface{}) if !ok { return -1, errors.New("Unrecognized type for stats metadata: %#v", r.metadata) } var rowsAffected int64 nodesCreated, ok := stats["nodes-created"] if ok { rowsAffected += nodesCreated.(int64) } relsCreated, ok := stats["relationships-created"] if ok { rowsAffected += relsCreated.(int64) } nodesDeleted, ok := stats["nodes-deleted"] if ok { rowsAffected += nodesDeleted.(int64) } relsDeleted, ok := stats["relationships-deleted"] if ok { rowsAffected += relsDeleted.(int64) } return rowsAffected, 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) } } }
// Encode encodes an object to the stream func (e Encoder) encode(iVal interface{}) error { var err error if iVal == nil { return e.encodeNil() } rv := reflect.ValueOf(iVal) switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: err = e.encodeInt(int64(rv.Int())) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: err = e.encodeInt(int64(rv.Uint())) case reflect.Float32, reflect.Float64: err = e.encodeFloat(rv.Float()) case reflect.Bool: err = e.encodeBool(rv.Bool()) case reflect.String: err = e.encodeString(rv.String()) case reflect.Slice: ret := make([]interface{}, rv.Len()) for i := 0; i < rv.Len(); i++ { ret[i] = rv.Index(i).Interface() } err = e.encodeSlice(ret) case reflect.Map: if rv.Type().Key().Kind() == reflect.String { iv := rv.Interface() val, ok := iv.(map[string]interface{}) if ok { err = e.encodeMap(val) } } else { err = errors.New("Unsupported kind of map: %T, %+v", rv, rv) } case reflect.Struct: val, ok := iVal.(structures.Structure) if ok { err = e.encodeStructure(val) } else { err = errors.New("Unsupported Struct: %T, %+v", rv, rv) } default: return errors.New("Unrecognized type when encoding data for Bolt transport: %T %+v", rv, rv) } return err }
// 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) QueryPipeline(queries []string, params ...map[string]interface{}) (PipelineRows, error) { if c.statement != nil { return nil, errors.New("An open statement already exists") } if c.closed { return nil, errors.New("Connection already closed") } c.statement = newPipelineStmt(queries, c) rows, err := c.statement.QueryPipeline(params...) if err != nil { return nil, err } // Since we're not exposing the statement, // tell the rows to close it when they are closed rows.(*boltRows).closeStatement = true return rows, nil }
func sliceInterfaceToNode(from []interface{}) ([]graph.Node, error) { to := make([]graph.Node, len(from)) for idx, item := range from { toItem, ok := item.(graph.Node) if !ok { return nil, errors.New("Expected Node value. Got %T %+v", item, item) } to[idx] = toItem } return to, nil }
func sliceInterfaceToUnboundRelationship(from []interface{}) ([]graph.UnboundRelationship, error) { to := make([]graph.UnboundRelationship, len(from)) for idx, item := range from { toItem, ok := item.(graph.UnboundRelationship) if !ok { return nil, errors.New("Expected UnboundRelationship value. Got %T %+v", item, item) } to[idx] = toItem } return to, nil }
func sliceInterfaceToString(from []interface{}) ([]string, error) { to := make([]string, len(from)) for idx, item := range from { toItem, ok := item.(string) if !ok { return nil, errors.New("Expected string value. Got %T %+v", item, item) } to[idx] = toItem } return to, 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) decodePath(buffer *bytes.Buffer) (graph.Path, error) { path := graph.Path{} nodesInt, err := d.decode(buffer) if err != nil { return path, err } nodesIntSlice, ok := nodesInt.([]interface{}) if !ok { return path, errors.New("Expected: Nodes []Node, but got %T %+v", nodesInt, nodesInt) } path.Nodes, err = sliceInterfaceToNode(nodesIntSlice) if err != nil { return path, err } relsInt, err := d.decode(buffer) if err != nil { return path, err } relsIntSlice, ok := relsInt.([]interface{}) if !ok { return path, errors.New("Expected: Relationships []Relationship, but got %T %+v", relsInt, relsInt) } path.Relationships, err = sliceInterfaceToUnboundRelationship(relsIntSlice) if err != nil { return path, err } seqInt, err := d.decode(buffer) if err != nil { return path, err } seqIntSlice, ok := seqInt.([]interface{}) if !ok { return path, errors.New("Expected: Sequence []int, but got %T %+v", seqInt, seqInt) } path.Sequence, err = sliceInterfaceToInt(seqIntSlice) return path, err }
// Close the net conn, outputting the recording func (r *recorder) Close() error { if r.Conn != nil { err := r.flush() if err != nil { return err } return r.Conn.Close() } else if len(r.events) > 0 { if r.currentEvent != len(r.events) { return errors.New("Didn't read all of the events in the recorder on close! %#v", r) } if len(r.events[len(r.events)-1].Event) != 0 { return errors.New("Left data in an event in the recorder on close! %#v", r) } return nil } return nil }
func (d Decoder) decodeSuccessMessage(buffer *bytes.Buffer) (messages.SuccessMessage, error) { metadataInt, err := d.decode(buffer) if err != nil { return messages.SuccessMessage{}, err } metadata, ok := metadataInt.(map[string]interface{}) if !ok { return messages.SuccessMessage{}, errors.New("Expected: Metadata map[string]interface{}, but got %T %+v", metadataInt, metadataInt) } return messages.NewSuccessMessage(metadata), nil }
func (d Decoder) decodeRecordMessage(buffer *bytes.Buffer) (messages.RecordMessage, error) { fieldsInt, err := d.decode(buffer) if err != nil { return messages.RecordMessage{}, err } fields, ok := fieldsInt.([]interface{}) if !ok { return messages.RecordMessage{}, errors.New("Expected: Fields []interface{}, but got %T %+v", fieldsInt, fieldsInt) } return messages.NewRecordMessage(fields), 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) } } }