neo4j-ruby-driver 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +8 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +68 -0
  5. data/Rakefile +16 -0
  6. data/ffi/bolt/address.rb +11 -0
  7. data/ffi/bolt/auth.rb +10 -0
  8. data/ffi/bolt/auto_releasable.rb +22 -0
  9. data/ffi/bolt/boolean.rb +9 -0
  10. data/ffi/bolt/bytes.rb +10 -0
  11. data/ffi/bolt/config.rb +10 -0
  12. data/ffi/bolt/connection.rb +44 -0
  13. data/ffi/bolt/connector.rb +17 -0
  14. data/ffi/bolt/dictionary.rb +15 -0
  15. data/ffi/bolt/error.rb +74 -0
  16. data/ffi/bolt/float.rb +9 -0
  17. data/ffi/bolt/integer.rb +9 -0
  18. data/ffi/bolt/library.rb +12 -0
  19. data/ffi/bolt/lifecycle.rb +9 -0
  20. data/ffi/bolt/list.rb +10 -0
  21. data/ffi/bolt/status.rb +15 -0
  22. data/ffi/bolt/string.rb +9 -0
  23. data/ffi/bolt/structure.rb +10 -0
  24. data/ffi/bolt/value.rb +35 -0
  25. data/ffi/bolt/values_private.rb +36 -0
  26. data/ffi/neo4j/driver.rb +45 -0
  27. data/ffi/neo4j/driver/access_mode.rb +10 -0
  28. data/ffi/neo4j/driver/auth_tokens.rb +17 -0
  29. data/ffi/neo4j/driver/config.rb +11 -0
  30. data/ffi/neo4j/driver/graph_database.rb +29 -0
  31. data/ffi/neo4j/driver/internal/async/access_mode_connection.rb +19 -0
  32. data/ffi/neo4j/driver/internal/async/direct_connection.rb +88 -0
  33. data/ffi/neo4j/driver/internal/bookmarks_holder.rb +30 -0
  34. data/ffi/neo4j/driver/internal/direct_connection_provider.rb +27 -0
  35. data/ffi/neo4j/driver/internal/driver_factory.rb +38 -0
  36. data/ffi/neo4j/driver/internal/error_handling.rb +50 -0
  37. data/ffi/neo4j/driver/internal/explicit_transaction.rb +144 -0
  38. data/ffi/neo4j/driver/internal/handlers/pull_all_response_handler.rb +101 -0
  39. data/ffi/neo4j/driver/internal/handlers/response_handler.rb +46 -0
  40. data/ffi/neo4j/driver/internal/handlers/run_response_handler.rb +32 -0
  41. data/ffi/neo4j/driver/internal/handlers/session_pull_all_response_handler.rb +32 -0
  42. data/ffi/neo4j/driver/internal/handlers/transaction_pull_all_response_handler.rb +23 -0
  43. data/ffi/neo4j/driver/internal/internal_driver.rb +41 -0
  44. data/ffi/neo4j/driver/internal/internal_record.rb +22 -0
  45. data/ffi/neo4j/driver/internal/internal_statement_result.rb +52 -0
  46. data/ffi/neo4j/driver/internal/messaging/bolt_protocol.rb +24 -0
  47. data/ffi/neo4j/driver/internal/messaging/v1/bolt_protocol_v1.rb +59 -0
  48. data/ffi/neo4j/driver/internal/messaging/v2/bolt_protocol_v2.rb +16 -0
  49. data/ffi/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +63 -0
  50. data/ffi/neo4j/driver/internal/network_session.rb +124 -0
  51. data/ffi/neo4j/driver/internal/session_factory_impl.rb +28 -0
  52. data/ffi/neo4j/driver/internal/summary/internal_result_summary.rb +67 -0
  53. data/ffi/neo4j/driver/internal/summary/internal_server_info.rb +19 -0
  54. data/ffi/neo4j/driver/internal/summary/internal_summary_counters.rb +23 -0
  55. data/ffi/neo4j/driver/internal/util/metadata_extractor.rb +15 -0
  56. data/ffi/neo4j/driver/internal/value/base_time_value.rb +22 -0
  57. data/ffi/neo4j/driver/internal/value/date_value.rb +25 -0
  58. data/ffi/neo4j/driver/internal/value/duration_value.rb +27 -0
  59. data/ffi/neo4j/driver/internal/value/local_date_time_value.rb +24 -0
  60. data/ffi/neo4j/driver/internal/value/local_time_value.rb +19 -0
  61. data/ffi/neo4j/driver/internal/value/node_value.rb +18 -0
  62. data/ffi/neo4j/driver/internal/value/offset_time_value.rb +25 -0
  63. data/ffi/neo4j/driver/internal/value/path_value.rb +41 -0
  64. data/ffi/neo4j/driver/internal/value/point2_d_value.rb +24 -0
  65. data/ffi/neo4j/driver/internal/value/point3_d_value.rb +24 -0
  66. data/ffi/neo4j/driver/internal/value/relationship_value.rb +18 -0
  67. data/ffi/neo4j/driver/internal/value/structure_value.rb +42 -0
  68. data/ffi/neo4j/driver/internal/value/time_with_zone_id_value.rb +25 -0
  69. data/ffi/neo4j/driver/internal/value/time_with_zone_offset_value.rb +28 -0
  70. data/ffi/neo4j/driver/internal/value/unbound_relationship_value.rb +18 -0
  71. data/ffi/neo4j/driver/internal/value/value_adapter.rb +95 -0
  72. data/ffi/neo4j/driver/statement.rb +14 -0
  73. data/ffi/neo4j/driver/summary/statement_type.rb +14 -0
  74. data/ffi/neo4j/driver/types/entity.rb +17 -0
  75. data/ffi/neo4j/driver/types/node.rb +16 -0
  76. data/ffi/neo4j/driver/types/path.rb +35 -0
  77. data/ffi/neo4j/driver/types/relationship.rb +19 -0
  78. data/lib/loader.rb +18 -0
  79. data/lib/neo4j/driver/auto_closable.rb +32 -0
  80. data/lib/neo4j/driver/exceptions/authentication_exception.rb +10 -0
  81. data/lib/neo4j/driver/exceptions/client_exception.rb +10 -0
  82. data/lib/neo4j/driver/exceptions/database_exception.rb +10 -0
  83. data/lib/neo4j/driver/exceptions/illegal_state_exception.rb +10 -0
  84. data/lib/neo4j/driver/exceptions/neo4j_exception.rb +18 -0
  85. data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +33 -0
  86. data/lib/neo4j/driver/exceptions/protocol_exception.rb +10 -0
  87. data/lib/neo4j/driver/exceptions/security_exception.rb +10 -0
  88. data/lib/neo4j/driver/exceptions/service_unavailable_exception.rb +10 -0
  89. data/lib/neo4j/driver/exceptions/session_expired_exception.rb +10 -0
  90. data/lib/neo4j/driver/exceptions/transient_exception.rb +10 -0
  91. data/lib/neo4j/driver/exceptions/untrusted_server_exception.rb +10 -0
  92. data/lib/neo4j/driver/internal/duration_normalizer.rb +42 -0
  93. data/lib/neo4j/driver/internal/ruby_signature.rb +18 -0
  94. data/lib/neo4j/driver/types/byte_array.rb +17 -0
  95. data/lib/neo4j/driver/types/local_date_time.rb +20 -0
  96. data/lib/neo4j/driver/types/local_time.rb +19 -0
  97. data/lib/neo4j/driver/types/offset_time.rb +19 -0
  98. data/lib/neo4j/driver/types/point.rb +39 -0
  99. data/lib/neo4j/driver/types/time.rb +43 -0
  100. data/lib/neo4j/driver/version.rb +7 -0
  101. data/lib/neo4j_ruby_driver.rb +19 -0
  102. data/neo4j-ruby-driver.gemspec +63 -0
  103. metadata +274 -0
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ class InternalStatementResult
7
+ include Enumerable
8
+
9
+ delegate :consume, :failure, :summary, :finalize, to: :@pull_all_handler
10
+
11
+ def initialize(run_handler, pull_all_handler)
12
+ @run_handler = run_handler
13
+ @pull_all_handler = pull_all_handler
14
+ end
15
+
16
+ def single
17
+ @pull_all_handler.next.tap do |record|
18
+ raise Exceptions::NoSuchRecordException.empty unless record
19
+ raise Exceptions::NoSuchRecordException.too_many if has_next?
20
+ end
21
+ end
22
+
23
+ def next
24
+ @pull_all_handler.next.tap do |record|
25
+ raise Exceptions::NoSuchRecordException.no_more unless record
26
+ end
27
+ end
28
+
29
+ def peek
30
+ @pull_all_handler.peek.tap do |record|
31
+ raise Exceptions::NoSuchRecordException.no_peek_past unless record
32
+ end
33
+ end
34
+
35
+ def has_next?
36
+ @pull_all_handler.peek
37
+ end
38
+
39
+ def each
40
+ yield @pull_all_handler.next while has_next?
41
+ end
42
+
43
+ def keys
44
+ @keys ||= begin
45
+ @pull_all_handler.peek
46
+ @pull_all_handler.statement_keys
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ module Messaging
7
+ module BoltProtocol
8
+ def self.for_version(version)
9
+ case version
10
+ when V1::BoltProtocolV1::VERSION
11
+ V1::BoltProtocolV1::INSTANCE
12
+ when V2::BoltProtocolV2::VERSION
13
+ V2::BoltProtocolV2::INSTANCE
14
+ when V3::BoltProtocolV3::VERSION
15
+ V3::BoltProtocolV3::INSTANCE
16
+ else
17
+ raise Exceptions::ClientException, "Unknown protocol version: #{version}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ module Messaging
7
+ module V1
8
+ class BoltProtocolV1
9
+ VERSION = 1
10
+ INSTANCE = new
11
+
12
+ METADATA_EXTRACTOR = Util::MetadataExtractor.new('result_available_after', 'result_consumed_after')
13
+
14
+ def run_in_auto_commit_transaction(connection, statement, bookmarks_holder, config)
15
+ # bookmarks are ignored for auto-commit transactions in this version of the protocol
16
+
17
+ self.class.tx_config_not_supported if config&.present?
18
+
19
+ self.class.run_statement(connection, statement, nil)
20
+ end
21
+
22
+ def run_in_explicit_transaction(connection, statement, tx)
23
+ self.class.run_statement(connection, statement, tx)
24
+ end
25
+
26
+ class << self
27
+ def run_statement(connection, statement, tx)
28
+ query = statement.text
29
+ params = statement.parameters
30
+
31
+ run_handler = Handlers::RunResponseHandler.new(METADATA_EXTRACTOR)
32
+ pull_all_handler = new_pull_all_handler(statement, run_handler, connection, tx)
33
+
34
+ connection.write_and_flush(query, params, run_handler, pull_all_handler)
35
+ InternalStatementResult.new(run_handler, pull_all_handler)
36
+ end
37
+
38
+ def new_pull_all_handler(statement, run_handler, connection, tx)
39
+ if tx
40
+ Handlers::TransactionPullAllResponseHandler.new(statement, run_handler, connection, tx,
41
+ METADATA_EXTRACTOR)
42
+ else
43
+ Handlers::SessionPullAllResponseHandler.new(statement, run_handler, connection,
44
+ BookmarksHolder::NO_OP, METADATA_EXTRACTOR)
45
+ end
46
+ end
47
+
48
+ def tx_config_not_supported
49
+ raise ClientException,
50
+ 'Driver is connected to the database that does not support transaction configuration. ' \
51
+ 'Please upgrade to neo4j 3.5.0 or later in order to use this functionality'
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ module Messaging
7
+ module V2
8
+ class BoltProtocolV2 < V1::BoltProtocolV1
9
+ VERSION = 2
10
+ INSTANCE = new
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ module Messaging
7
+ module V3
8
+ class BoltProtocolV3
9
+ VERSION = 3
10
+ INSTANCE = new
11
+ METADATA_EXTRACTOR = Util::MetadataExtractor.new('t_first', 't_last')
12
+
13
+ def run_in_auto_commit_transaction(connection, statement, bookmarks_holder, config)
14
+ self.class.run_statement(connection, statement, bookmarks_holder, nil, config)
15
+ end
16
+
17
+ def run_in_explicit_transaction(connection, statement, tx)
18
+ self.class.run_statement(connection, statement, BookmarksHolder::NO_OP, tx, nil)
19
+ end
20
+
21
+ def begin_transaction(connection, bookmarks, config)
22
+ begin_handler = Handlers::ResponseHandler.new(connection)
23
+ connection.begin(bookmarks, config, begin_handler)
24
+ connection.flush if bookmarks.present?
25
+ begin_handler
26
+ end
27
+
28
+ def commit_transaction(connection)
29
+ Handlers::ResponseHandler.new(connection).tap(&connection.method(:commit))
30
+ end
31
+
32
+ def rollback_transaction(connection)
33
+ Handlers::ResponseHandler.new(connection).tap(&connection.method(:rollback))
34
+ end
35
+
36
+ class << self
37
+ def run_statement(connection, statement, boomarks_holder, tx, config)
38
+ query = statement.text
39
+ params = statement.parameters
40
+
41
+ run_handler = Handlers::RunResponseHandler.new(connection, METADATA_EXTRACTOR)
42
+ pull_all_handler = new_pull_all_handler(statement, run_handler, connection, boomarks_holder, tx)
43
+
44
+ connection.write_and_flush(query, params, boomarks_holder, config, run_handler, pull_all_handler)
45
+ InternalStatementResult.new(run_handler, pull_all_handler)
46
+ end
47
+
48
+ def new_pull_all_handler(statement, run_handler, connection, bookmarks_holder, tx)
49
+ if tx
50
+ Handlers::TransactionPullAllResponseHandler.new(statement, run_handler, connection, tx,
51
+ METADATA_EXTRACTOR)
52
+ else
53
+ Handlers::SessionPullAllResponseHandler.new(statement, run_handler, connection, bookmarks_holder,
54
+ METADATA_EXTRACTOR)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ class NetworkSession
7
+ include BookmarksHolder
8
+ include ErrorHandling
9
+ extend AutoClosable
10
+
11
+ auto_closable :begin_transaction
12
+
13
+ def initialize(connection_provider, mode, retry_logic = nil, logging = nil)
14
+ super()
15
+ @open = Concurrent::AtomicBoolean.new(true)
16
+ @connection_provider = connection_provider
17
+ @mode = mode
18
+ # @retry_logic = retry_logic
19
+ @retry_logic = Class.new do
20
+ def retry
21
+ yield
22
+ end
23
+ end.new
24
+ end
25
+
26
+ def run(statement, parameters = {}, config = nil)
27
+ ensure_session_is_open
28
+ ensure_no_open_tx_before_running_query
29
+ acquire_connection(@mode)
30
+ @result = @connection.protocol.run_in_auto_commit_transaction(
31
+ @connection, Statement.new(statement, parameters), self, config
32
+ )
33
+ end
34
+
35
+ def read_transaction(&block)
36
+ transaction(Neo4j::Driver::AccessMode::READ, &block)
37
+ end
38
+
39
+ def write_transaction(&block)
40
+ transaction(Neo4j::Driver::AccessMode::WRITE, &block)
41
+ end
42
+
43
+ def close
44
+ return unless @open.make_false
45
+ begin
46
+ @result&.finalize
47
+ @result&.failure
48
+ ensure
49
+ close_transaction_and_release_connection
50
+ end
51
+ end
52
+
53
+ def release_connection
54
+ @connection&.release
55
+ end
56
+
57
+ def begin_transaction(mode = @mode, config = nil)
58
+ ensure_no_open_tx_before_starting_tx
59
+ acquire_connection(mode)
60
+ @transaction = ExplicitTransaction.new(@connection, self).begin(bookmarks, config)
61
+ end
62
+
63
+ def last_bookmark
64
+ bookmarks&.max
65
+ end
66
+
67
+ private
68
+
69
+ def transaction(mode, config = nil)
70
+ @retry_logic.retry do
71
+ tx = begin_transaction(mode, config)
72
+ result = yield tx
73
+ tx.success
74
+ result
75
+ rescue StandardError => e
76
+ tx&.failure
77
+ raise e
78
+ ensure
79
+ tx&.close
80
+ end
81
+ end
82
+
83
+ def acquire_connection(mode = @mode)
84
+ @result&.failure
85
+ raise Exceptions::IllegalStateException, 'Existing open connection detected' if @connection&.open?
86
+ @connection = @connection_provider.acquire_connection(@mode)
87
+ # old
88
+ # raise Exception, 'existing connection present' if @connection
89
+ #
90
+ # status = Bolt::Status.create
91
+ # @connection = Bolt::Connector.acquire(@connector, mode, status)
92
+ # check_status(status)
93
+ end
94
+
95
+ def close_transaction_and_release_connection
96
+ @transaction&.close
97
+ ensure
98
+ @transaction = nil
99
+ release_connection
100
+ end
101
+
102
+ def ensure_session_is_open
103
+ return if @open.true?
104
+ raise Exceptions::ClientException,
105
+ 'No more interaction with this session are allowed as the current session is already closed.'
106
+ end
107
+
108
+ def ensure_no_open_tx_before_running_query
109
+ ensure_no_open_tx('Statements cannot be run directly on a session with an open transaction; ' \
110
+ 'either run from within the transaction or use a different session.')
111
+ end
112
+
113
+ def ensure_no_open_tx_before_starting_tx
114
+ ensure_no_open_tx('You cannot begin a transaction on a session with an open transaction; ' \
115
+ 'either run from within the transaction or use a different session.')
116
+ end
117
+
118
+ def ensure_no_open_tx(error_message)
119
+ raise Exceptions::ClientException, error_message if @transaction&.open?
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ class SessionFactoryImpl
7
+ attr_reader :connection_provider
8
+ delegate :close, :verify_connectivity, to: :connection_provider
9
+
10
+ def initialize(connection_provider, retry_logic, config)
11
+ @connection_provider = connection_provider
12
+ @retry_logic = retry_logic
13
+ @config = config
14
+ end
15
+
16
+ def new_instance(mode, bookmarks)
17
+ create_session(connection_provider, @retry_logic, mode).tap { |session| session.bookmarks = bookmarks }
18
+ end
19
+
20
+ private
21
+
22
+ def create_session(connection_provider, retry_logic, mode, logging = nil)
23
+ NetworkSession.new(connection_provider, mode, retry_logic, logging)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ module Summary
7
+ class InternalResultSummary
8
+ attr_reader :server, :counters, :statement, :result_available_after, :result_consumed_after
9
+ delegate :notifications, :profile, to: :@metadata
10
+
11
+ def initialize(statement, result_available_after, bolt_connection)
12
+ @statement = statement
13
+ @result_available_after = result_available_after
14
+ @server = InternalServerInfo.new(bolt_connection)
15
+ @metadata = RecursiveOpenStruct.new(
16
+ underscore_keys(Value::ValueAdapter.to_ruby(Bolt::Connection.metadata(bolt_connection))),
17
+ recurse_over_arrays: true
18
+ )
19
+ @result_consumed_after = @metadata.result_consumed_after || @metadata.t_last
20
+ @counters = InternalSummaryCounters.new(@metadata.stats)
21
+ end
22
+
23
+ def notifications
24
+ @metadata.notifications || []
25
+ end
26
+
27
+ def plan
28
+ @metadata.plan || profile
29
+ end
30
+
31
+ alias has_plan? plan
32
+ alias has_profile? profile
33
+
34
+ def method_missing(method)
35
+ @metadata.send(method)
36
+ end
37
+
38
+ private
39
+
40
+ def underscore_keys(arg)
41
+ case arg
42
+ when Array
43
+ arg.map(&method(:underscore_keys))
44
+ when Hash
45
+ arg.map { |key, value| [translate_key(key), underscore_keys(value)] }.to_h
46
+ else
47
+ arg
48
+ end
49
+ end
50
+
51
+ def translate_key(key)
52
+ case key
53
+ when :type
54
+ :statement_type
55
+ when :args
56
+ :arguments
57
+ when :rows
58
+ :records
59
+ else
60
+ key.to_s.underscore.to_sym
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Internal
6
+ module Summary
7
+ class InternalServerInfo
8
+ attr_reader :address, :version
9
+
10
+ def initialize(bolt_connection)
11
+ address = Bolt::Connection.remote_endpoint(bolt_connection)
12
+ @address = %i[host port].map { |method| Bolt::Address.send(method, address).first }.join(':')
13
+ @version = Bolt::Connection.server(bolt_connection).first
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end