neo4j-ruby-driver 6.2.1.beta.1 → 6.2.1.beta.3

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/neo4j/driver/bolt/connection.rb +72 -35
  3. data/lib/neo4j/driver/bolt/handshake.rb +14 -10
  4. data/lib/neo4j/driver/bolt/message/failure.rb +8 -8
  5. data/lib/neo4j/driver/bolt/message/record.rb +1 -1
  6. data/lib/neo4j/driver/bolt/message.rb +1 -1
  7. data/lib/neo4j/driver/bolt/pool.rb +2 -2
  8. data/lib/neo4j/driver/bolt/protocol/v44.rb +1 -1
  9. data/lib/neo4j/driver/bolt/record_buffer.rb +15 -8
  10. data/lib/neo4j/driver/bolt/wire.rb +4 -4
  11. data/lib/neo4j/driver/direct/connection_provider.rb +5 -1
  12. data/lib/neo4j/driver/driver.rb +2 -2
  13. data/lib/neo4j/driver/exceptions/no_such_record_exception.rb +1 -1
  14. data/lib/neo4j/driver/internal/duration_normalizer.rb +4 -2
  15. data/lib/neo4j/driver/internal/validator.rb +7 -3
  16. data/lib/neo4j/driver/packstream/markers.rb +11 -11
  17. data/lib/neo4j/driver/packstream/packer.rb +5 -3
  18. data/lib/neo4j/driver/packstream/unpacker.rb +6 -4
  19. data/lib/neo4j/driver/record.rb +9 -15
  20. data/lib/neo4j/driver/result.rb +12 -12
  21. data/lib/neo4j/driver/routing/load_balancer.rb +29 -13
  22. data/lib/neo4j/driver/routing/routing_table.rb +1 -1
  23. data/lib/neo4j/driver/session.rb +72 -70
  24. data/lib/neo4j/driver/summary/database_info.rb +1 -1
  25. data/lib/neo4j/driver/summary/plan.rb +1 -1
  26. data/lib/neo4j/driver/summary/result_summary.rb +2 -0
  27. data/lib/neo4j/driver/transaction.rb +13 -10
  28. data/lib/neo4j/driver/types/duration.rb +6 -6
  29. data/lib/neo4j/driver/types/entity.rb +40 -0
  30. data/lib/neo4j/driver/types/local_date_time.rb +46 -21
  31. data/lib/neo4j/driver/types/local_time.rb +13 -5
  32. data/lib/neo4j/driver/types/node.rb +5 -14
  33. data/lib/neo4j/driver/types/offset_time.rb +16 -9
  34. data/lib/neo4j/driver/types/path.rb +2 -2
  35. data/lib/neo4j/driver/types/point.rb +4 -4
  36. data/lib/neo4j/driver/types/relationship.rb +6 -15
  37. data/lib/neo4j/driver/types/temporal_value.rb +3 -2
  38. data/lib/neo4j/driver/types/unbound_relationship.rb +5 -10
  39. data/lib/neo4j/driver/version.rb +1 -1
  40. data/lib/neo4j/driver.rb +0 -1
  41. metadata +4 -72
@@ -18,8 +18,8 @@ module Neo4j
18
18
  on_summary: nil, on_release: nil)
19
19
  @connection = connection
20
20
  @keys = keys
21
- @buffer = buffer # Bolt::RecordBuffer, filled by the reader
22
- @handler = handler # the StreamHandler registered for this result's PULLs
21
+ @buffer = buffer # Bolt::RecordBuffer, filled by the reader
22
+ @handler = handler # the StreamHandler registered for this result's PULLs
23
23
  @query_text = query_text
24
24
  @parameters = parameters
25
25
  @run_metadata = run_metadata
@@ -35,17 +35,17 @@ module Neo4j
35
35
  # leaves the connection FAILED, so more wire traffic is invalid. nil for
36
36
  # auto-commit results (no enclosing transaction).
37
37
  @terminated_error = terminated_error
38
- @failure = nil # this result's own classified stream failure, if any
39
- @on_summary = on_summary # called with the built Summary when the stream ends in SUCCESS
40
- @on_release = on_release # called once when the connection is no longer needed
41
- @records = [] # records pulled into memory by #buffer (not by iteration)
38
+ @failure = nil # this result's own classified stream failure, if any
39
+ @on_summary = on_summary # called with the built Summary when the stream ends in SUCCESS
40
+ @on_release = on_release # called once when the connection is no longer needed
41
+ @records = [] # records pulled into memory by #buffer (not by iteration)
42
42
  @had_record = false # any RECORD arrived on this stream — drives the GQL outcome status
43
43
  @summary = nil
44
- @consumed = false # stream fully drained (terminal seen)
45
- @discarded = false # records explicitly released; further access raises
46
- @failed = false # stream ended with a server FAILURE; connection needs RESET
44
+ @consumed = false # stream fully drained (terminal seen)
45
+ @discarded = false # records explicitly released; further access raises
46
+ @failed = false # stream ended with a server FAILURE; connection needs RESET
47
47
  @cancelling = false # consume(): abandon the rest — DISCARD the next batch instead of PULL
48
- @list_mode = false # to_a/list: pull all remaining in one PULL {n: -1}, ignoring fetch_size
48
+ @list_mode = false # to_a/list: pull all remaining in one PULL {n: -1}, ignoring fetch_size
49
49
  @peeked_record = nil
50
50
  end
51
51
 
@@ -98,11 +98,11 @@ module Neo4j
98
98
  record
99
99
  end
100
100
 
101
- def each(&block)
101
+ def each(&)
102
102
  raise Exceptions::ResultConsumedException if @discarded
103
103
  return to_enum(:each) unless block_given?
104
104
 
105
- block.call(self.next) while has_next?
105
+ yield(self.next) while has_next?
106
106
  end
107
107
 
108
108
  def to_a
@@ -40,9 +40,9 @@ module Neo4j
40
40
  @clock = clock
41
41
  @domain_name_resolver = domain_name_resolver
42
42
  @routing_context = parse_routing_context(uri)
43
- @pools = {} # ServerAddress => ConnectionPool::TimedStack
44
- @routing_tables = {} # database (str or nil) => RoutingTable
45
- @cursor = Hash.new(0) # round-robin per (database, role)
43
+ @pools = {} # ServerAddress => ConnectionPool::TimedStack
44
+ @routing_tables = {} # database (str or nil) => RoutingTable
45
+ @cursor = Hash.new(0) # round-robin per (database, role)
46
46
  # Per-server authorization-expired generation counters (see
47
47
  # Connection#auth_epoch and Direct::ConnectionProvider). Bumped for a
48
48
  # server when one of its connections reports AuthorizationExpired, so
@@ -79,8 +79,8 @@ module Neo4j
79
79
  @ssr_lock.synchronize do
80
80
  if conn.ssr_enabled?
81
81
  @ssr_with -= 1 if @ssr_with.positive?
82
- else
83
- @ssr_without -= 1 if @ssr_without.positive?
82
+ elsif @ssr_without.positive?
83
+ @ssr_without -= 1
84
84
  end
85
85
  end
86
86
  end
@@ -193,7 +193,8 @@ module Neo4j
193
193
  # routing driver surfaced the wrong error type.
194
194
  raise Exceptions::IllegalStateException, 'Driver is closed' if @closed
195
195
 
196
- resolved = ensure_routing_table_is_fresh(nil, :read, bookmarks: bookmarks, imp_user: imp_user, auth: auth).database
196
+ resolved = ensure_routing_table_is_fresh(nil, :read, bookmarks: bookmarks, imp_user: imp_user,
197
+ auth: auth).database
197
198
  # Remember the authoritative name (Optimization:HomeDatabaseCache) so a
198
199
  # later same-identity session can guess it and skip discovery.
199
200
  cache_home_db(imp_user, auth, resolved)
@@ -236,7 +237,9 @@ module Neo4j
236
237
  # server to re-auth. Provider-side, so it runs regardless of who
237
238
  # owns the token; the manager notification is skipped for a
238
239
  # per-session identity.
239
- @refresh_lock.synchronize { @auth_epochs[address] += 1 } if error.is_a?(Exceptions::AuthorizationExpiredException)
240
+ if error.is_a?(Exceptions::AuthorizationExpiredException)
241
+ @refresh_lock.synchronize { @auth_epochs[address] += 1 }
242
+ end
240
243
  return false if session_scoped
241
244
 
242
245
  @auth_manager.handle_security_exception(token, error)
@@ -316,7 +319,13 @@ module Neo4j
316
319
  def close
317
320
  @refresh_lock.synchronize do
318
321
  @closed = true
319
- @pools.each_value { |pool| pool.shutdown { |conn| conn.close rescue nil } }
322
+ @pools.each_value do |pool|
323
+ pool.shutdown do |conn|
324
+ conn.close
325
+ rescue StandardError
326
+ nil
327
+ end
328
+ end
320
329
  @pools.clear
321
330
  @routing_tables.clear
322
331
  end
@@ -368,7 +377,11 @@ module Neo4j
368
377
  @refresh_lock.synchronize do
369
378
  @routing_tables.each_value { |table| table.forget(address) }
370
379
  pool = @pools.delete(address)
371
- pool&.shutdown { |conn| conn.close rescue nil }
380
+ pool&.shutdown do |conn|
381
+ conn.close
382
+ rescue StandardError
383
+ nil
384
+ end
372
385
  end
373
386
  end
374
387
 
@@ -442,7 +455,7 @@ module Neo4j
442
455
  raise Exceptions::ServiceUnavailableException.new(
443
456
  "Unable to retrieve routing information for database #{database.inspect}: " \
444
457
  "tried #{errors.length} router(s) without success" \
445
- "#{last ? " (last: #{last.message})" : ''}",
458
+ "#{" (last: #{last.message})" if last}",
446
459
  suppressed: errors
447
460
  )
448
461
  end
@@ -529,7 +542,11 @@ module Neo4j
529
542
  errors << e
530
543
  # Connection (if any) is presumed dead; deactivate tears
531
544
  # down the pool so the conn's `created` slot is reclaimed.
532
- conn&.close rescue nil
545
+ begin
546
+ conn&.close
547
+ rescue StandardError
548
+ nil
549
+ end
533
550
  deactivate(router)
534
551
  nil
535
552
  rescue Exceptions::Neo4jException => e
@@ -674,7 +691,7 @@ module Neo4j
674
691
 
675
692
  def symbolize(value)
676
693
  case value
677
- when Hash then value.transform_keys(&:to_sym).transform_values { symbolize(it) }
694
+ when Hash then value.transform_keys(&:to_sym).transform_values { symbolize(it) }
678
695
  when Array then value.map { symbolize(it) }
679
696
  else value
680
697
  end
@@ -683,7 +700,6 @@ module Neo4j
683
700
  def max_pool_size
684
701
  @options[:max_connection_pool_size] || Driver::DEFAULT_MAX_POOL_SIZE
685
702
  end
686
-
687
703
  end
688
704
  end
689
705
  end
@@ -109,7 +109,7 @@ module Neo4j
109
109
 
110
110
  def servers_for(access_mode)
111
111
  case access_mode
112
- when :read then @readers
112
+ when :read then @readers
113
113
  when :write then @writers
114
114
  when :route then @routers
115
115
  else raise ArgumentError, "Unknown access mode: #{access_mode.inspect}"
@@ -10,6 +10,7 @@ module Neo4j
10
10
  # hold a connection — only its in-flight operation does.
11
11
  class Session
12
12
  include Internal::DurationNormalizer
13
+
13
14
  def initialize(connection_provider, options = {}, clock: Internal::Clock.new)
14
15
  @connection_provider = connection_provider
15
16
  @options = options
@@ -21,7 +22,7 @@ module Neo4j
21
22
  # The home database this session pinned after its first resolution
22
23
  # (Optimization:HomeDatabaseCache); nil until then / for explicit-db sessions.
23
24
  @pinned_database = nil
24
- @used_guess = false # whether an operation optimistically guessed the home db
25
+ @used_guess = false # whether an operation optimistically guessed the home db
25
26
  @bookmark_manager = options[:bookmark_manager]
26
27
  # The bookmark snapshot we sent on the most recent BEGIN — used
27
28
  # as `previous_bookmarks` when forwarding to the manager so it
@@ -31,12 +32,12 @@ module Neo4j
31
32
  @bookmarks_used_on_begin = nil
32
33
 
33
34
  # Initialize with provided bookmarks if any
34
- if options[:bookmarks]
35
- initial_bookmarks = options[:bookmarks]
36
- initial_bookmarks = [initial_bookmarks] unless initial_bookmarks.is_a?(Enumerable)
37
- initial_bookmarks.each do |bookmark|
38
- @last_bookmarks << (bookmark.is_a?(Bookmark) ? bookmark : Bookmark.from(bookmark))
39
- end
35
+ return unless options[:bookmarks]
36
+
37
+ initial_bookmarks = options[:bookmarks]
38
+ initial_bookmarks = [initial_bookmarks] unless initial_bookmarks.is_a?(Enumerable)
39
+ initial_bookmarks.each do |bookmark|
40
+ @last_bookmarks << (bookmark.is_a?(Bookmark) ? bookmark : Bookmark.from(bookmark))
40
41
  end
41
42
  end
42
43
 
@@ -50,7 +51,10 @@ module Neo4j
50
51
  end
51
52
 
52
53
  raise Exceptions::ClientException, 'Session is closed' unless @open
53
- raise Exceptions::ClientException, 'You cannot run a query directly on a session while a transaction is open' if @transaction&.open?
54
+ if @transaction&.open?
55
+ raise Exceptions::ClientException,
56
+ 'You cannot run a query directly on a session while a transaction is open'
57
+ end
54
58
 
55
59
  drain_current_result
56
60
 
@@ -90,7 +94,7 @@ module Neo4j
90
94
  retries_left = auto_commit_retries_enabled? ? 1 : 0
91
95
 
92
96
  buffer = handler = run_response = nil
93
- telemetry_sent = false # TELEMETRY 2 = auto-commit; sent once, never resent on a retry
97
+ telemetry_sent = false # TELEMETRY 2 = auto-commit; sent once, never resent on a retry
94
98
  telemetry_pending = false # its (opted-in) SUCCESS is still owed
95
99
  loop do
96
100
  # A fresh stream per attempt — a prior attempt's buffer saw the IGNORED.
@@ -160,10 +164,10 @@ module Neo4j
160
164
  keys = (run_response.metadata[:fields] || run_response.metadata['fields'] || []).map(&:to_sym)
161
165
  @current_result = Result.new(
162
166
  connection, keys, buffer: buffer, handler: handler,
163
- query_text: query, parameters: parameters, run_metadata: run_response.metadata,
164
- fetch_size: fetch_size,
165
- on_summary: method(:harvest_auto_commit_bookmark),
166
- on_release: -> { @connection_provider.release(connection) }
167
+ query_text: query, parameters: parameters, run_metadata: run_response.metadata,
168
+ fetch_size: fetch_size,
169
+ on_summary: method(:harvest_auto_commit_bookmark),
170
+ on_release: -> { @connection_provider.release(connection) }
167
171
  )
168
172
  end
169
173
 
@@ -171,7 +175,7 @@ module Neo4j
171
175
  raise Exceptions::ClientException, 'Session is closed' unless @open
172
176
  if @transaction&.open?
173
177
  raise Exceptions::ClientException,
174
- "You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session."
178
+ 'You cannot begin a transaction on a session with an open transaction; either run from within the transaction or use a different session.'
175
179
  end
176
180
 
177
181
  drain_current_result
@@ -201,7 +205,7 @@ module Neo4j
201
205
  # tx.commit to persist changes (matches Java driver semantics).
202
206
  @transaction.rollback if @transaction.open?
203
207
  result
204
- rescue => e
208
+ rescue StandardError => e
205
209
  @transaction.rollback if @transaction.open?
206
210
  raise e
207
211
  ensure
@@ -212,12 +216,12 @@ module Neo4j
212
216
  end
213
217
  end
214
218
 
215
- def execute_read(timeout: nil, metadata: nil, &block)
216
- execute_transaction(AccessMode::READ, timeout:, metadata:, &block)
219
+ def execute_read(timeout: nil, metadata: nil, &)
220
+ execute_transaction(AccessMode::READ, timeout:, metadata:, &)
217
221
  end
218
222
 
219
- def execute_write(timeout: nil, metadata: nil, &block)
220
- execute_transaction(AccessMode::WRITE, timeout:, metadata:, &block)
223
+ def execute_write(timeout: nil, metadata: nil, &)
224
+ execute_transaction(AccessMode::WRITE, timeout:, metadata:, &)
221
225
  end
222
226
 
223
227
  # Close any pending result by asking the server to abandon
@@ -232,7 +236,7 @@ module Neo4j
232
236
 
233
237
  pending_error = nil
234
238
  begin
235
- @transaction&.close # tx releases its own connection
239
+ @transaction&.close # tx releases its own connection
236
240
  if @current_result
237
241
  connection = @current_result.connection
238
242
  begin
@@ -244,7 +248,7 @@ module Neo4j
244
248
  # the server's FAILED state; RESET so the next borrower starts
245
249
  # from a clean READY state.
246
250
  connection.reset! if @current_result.failed?
247
- @current_result.discard! # idempotent; releases the connection
251
+ @current_result.discard! # idempotent; releases the connection
248
252
  end
249
253
  ensure
250
254
  @open = false
@@ -257,9 +261,7 @@ module Neo4j
257
261
  @open
258
262
  end
259
263
 
260
- def last_bookmarks
261
- @last_bookmarks
262
- end
264
+ attr_reader :last_bookmarks
263
265
 
264
266
  def update_bookmarks(bookmarks)
265
267
  # Replace bookmarks (don't accumulate) — matches Java driver behavior.
@@ -447,19 +449,19 @@ module Neo4j
447
449
  result.buffer
448
450
  rescue Exceptions::Neo4jException
449
451
  connection.reset!
450
- result.discard! # idempotent; routes through Result#release_connection
452
+ result.discard! # idempotent; routes through Result#release_connection
451
453
  raise
452
454
  end
453
455
 
454
- if result.failed?
455
- connection.reset!
456
- result.discard!
457
- end
456
+ return unless result.failed?
457
+
458
+ connection.reset!
459
+ result.discard!
460
+
458
461
  # Successful drain: Result already released its connection from on_success.
459
462
  end
460
463
 
461
-
462
- def execute_transaction(access_mode, timeout: nil, metadata: nil, &block)
464
+ def execute_transaction(access_mode, timeout: nil, metadata: nil, &)
463
465
  raise Exceptions::ClientException, 'Session is closed' unless @open
464
466
 
465
467
  # Drain any pending auto-commit result so its connection is released
@@ -495,55 +497,53 @@ module Neo4j
495
497
  ).compact
496
498
 
497
499
  loop do
498
- begin
499
- # api 0 = managed tx; execute_query's session overrides it to 3
500
- # (DRIVER_EXECUTE_QUERY). nil once the server has acked the report.
501
- telemetry_api = telemetry_acked ? nil : (@options[:telemetry_api] || 0)
502
- return run_managed_transaction(op_mode, tx_options, telemetry_api, on_telemetry_ack, &block)
503
- rescue Exceptions::ServiceUnavailableException, Exceptions::SessionExpiredException,
504
- Exceptions::TransientException,
505
- # AuthorizationExpired (server authz-cache expiry) is always
506
- # retryable; SecurityRetryableException is an auth failure the
507
- # token manager has flagged retryable (token refreshed) — both
508
- # warrant another attempt on a fresh connection/identity.
509
- Exceptions::AuthorizationExpiredException, Exceptions::SecurityRetryableException => e
510
- errors << e
511
-
512
- if @clock.realtime - start_time >= max_retry_time
513
- # Retries exhausted re-raise the LAST error with its real
514
- # type (SessionExpired / Transient / ServiceUnavailable)
515
- # rather than flattening everything to ServiceUnavailable;
516
- # Java's transaction executor likewise rethrows the last
517
- # error. testkit asserts on the type (e.g. a routed reader
518
- # interruption must surface as SessionExpired), so the
519
- # generic wrapper masked it. Earlier attempts ride along as
520
- # suppressed.
521
- e.add_suppressed(*errors[0...-1])
522
- raise e
523
- end
524
-
525
- # Exponential backoff: 1s, 2s, 4s, ... matching Java driver defaults
526
- sleep(2 ** (errors.size - 1))
500
+ # api 0 = managed tx; execute_query's session overrides it to 3
501
+ # (DRIVER_EXECUTE_QUERY). nil once the server has acked the report.
502
+ telemetry_api = telemetry_acked ? nil : (@options[:telemetry_api] || 0)
503
+ return run_managed_transaction(op_mode, tx_options, telemetry_api, on_telemetry_ack, &)
504
+ rescue Exceptions::ServiceUnavailableException, Exceptions::SessionExpiredException,
505
+ Exceptions::TransientException,
506
+ # AuthorizationExpired (server authz-cache expiry) is always
507
+ # retryable; SecurityRetryableException is an auth failure the
508
+ # token manager has flagged retryable (token refreshed) — both
509
+ # warrant another attempt on a fresh connection/identity.
510
+ Exceptions::AuthorizationExpiredException, Exceptions::SecurityRetryableException => e
511
+ errors << e
512
+
513
+ if @clock.realtime - start_time >= max_retry_time
514
+ # Retries exhausted — re-raise the LAST error with its real
515
+ # type (SessionExpired / Transient / ServiceUnavailable)
516
+ # rather than flattening everything to ServiceUnavailable;
517
+ # Java's transaction executor likewise rethrows the last
518
+ # error. testkit asserts on the type (e.g. a routed reader
519
+ # interruption must surface as SessionExpired), so the
520
+ # generic wrapper masked it. Earlier attempts ride along as
521
+ # suppressed.
522
+ e.add_suppressed(*errors[0...-1])
523
+ raise e
527
524
  end
525
+
526
+ # Exponential backoff: 1s, 2s, 4s, ... matching Java driver defaults
527
+ sleep(2**(errors.size - 1))
528
528
  end
529
529
  end
530
530
 
531
- def run_managed_transaction(op_mode, tx_options, telemetry_api, on_telemetry_ack, &block)
531
+ def run_managed_transaction(op_mode, tx_options, telemetry_api, on_telemetry_ack, &)
532
532
  if @transaction&.open?
533
533
  raise Exceptions::ClientException,
534
- "You cannot begin a transaction on a session with an open transaction"
534
+ 'You cannot begin a transaction on a session with an open transaction'
535
535
  end
536
536
 
537
537
  # TELEMETRY 0 = managed transaction function; api is nil once acked so a
538
538
  # retry doesn't repeat it.
539
539
  @transaction = open_transaction(op_mode, tx_options, telemetry_api: telemetry_api,
540
- telemetry_ack: on_telemetry_ack)
540
+ telemetry_ack: on_telemetry_ack)
541
541
 
542
542
  begin
543
543
  result = yield @transaction
544
544
  @transaction.commit if @transaction.open?
545
545
  result
546
- rescue => e
546
+ rescue StandardError => e
547
547
  @transaction.rollback if @transaction.open?
548
548
  raise e
549
549
  ensure
@@ -563,12 +563,14 @@ module Neo4j
563
563
  bookmarks = current_bookmarks_for_extra
564
564
  tx_options = tx_options.merge(database: begin_db).compact
565
565
  Transaction.new(connection, self, bookmarks, tx_options, telemetry_api: telemetry_api,
566
- telemetry_ack: telemetry_ack,
567
- # executeQuery's session reports telemetry api 3 (DRIVER_EXECUTE_QUERY);
568
- # only that path pipelines BEGIN + RUN + PULL (Optimization:ExecuteQueryPipelining).
569
- pipelined: @options[:telemetry_api] == 3,
570
- on_begin: method(:cache_home_db_from),
571
- on_release: -> { @connection_provider.release(connection) })
566
+ telemetry_ack: telemetry_ack,
567
+ # executeQuery's session reports telemetry api 3 (DRIVER_EXECUTE_QUERY);
568
+ # only that path pipelines BEGIN + RUN + PULL (Optimization:ExecuteQueryPipelining).
569
+ pipelined: @options[:telemetry_api] == 3,
570
+ on_begin: method(:cache_home_db_from),
571
+ on_release: lambda {
572
+ @connection_provider.release(connection)
573
+ })
572
574
  end
573
575
  end
574
576
  end
@@ -12,7 +12,7 @@ module Neo4j
12
12
  @name =
13
13
  case db_data
14
14
  when String then db_data
15
- when Hash then db_data[:name]
15
+ when Hash then db_data[:name]
16
16
  end
17
17
  end
18
18
 
@@ -16,7 +16,7 @@ module Neo4j
16
16
 
17
17
  # Alias args → arguments to match JRuby's InternalPlan#args helper,
18
18
  # so cross-flavour callers can use either name.
19
- alias_method :args, :arguments
19
+ alias args arguments
20
20
  end
21
21
  end
22
22
  end
@@ -74,6 +74,7 @@ module Neo4j
74
74
 
75
75
  def plan
76
76
  return @plan if defined?(@plan)
77
+
77
78
  @plan =
78
79
  if @metadata[:plan]
79
80
  Plan.new(@metadata[:plan])
@@ -87,6 +88,7 @@ module Neo4j
87
88
 
88
89
  def profile
89
90
  return @profile if defined?(@profile)
91
+
90
92
  @profile = @metadata[:profile] ? Profile.new(@metadata[:profile]) : nil
91
93
  end
92
94
 
@@ -14,13 +14,13 @@ module Neo4j
14
14
  # executeQuery pipelines BEGIN + RUN + PULL (Optimization:ExecuteQueryPipelining):
15
15
  # BEGIN's reply is read only after the first RUN+PULL are flushed, not eagerly.
16
16
  @pipelined = pipelined
17
- @on_begin = on_begin # called with the BEGIN reply (home-db cache update)
18
- @on_release = on_release # called once when the connection is no longer needed
17
+ @on_begin = on_begin # called with the BEGIN reply (home-db cache update)
18
+ @on_release = on_release # called once when the connection is no longer needed
19
19
  @open = true
20
20
  @committed = false
21
21
  @rolled_back = false
22
22
  @failed = false
23
- @terminating_error = nil # the classified error that terminated this tx (a RUN/commit failure)
23
+ @terminating_error = nil # the classified error that terminated this tx (a RUN/commit failure)
24
24
  @current_result = nil
25
25
  # Every result opened in this tx, in order. Bolt lets multiple stay open
26
26
  # and streaming concurrently (qid multiplexing); a new RUN no longer
@@ -88,8 +88,10 @@ module Neo4j
88
88
  # the user's own iteration (@current_result.failed?). Message wording
89
89
  # stays "rolled back" to match the Java flavor (a shared integration spec
90
90
  # asserts it on both impls); only the exception class becomes specific.
91
- raise Exceptions::TransactionTerminatedException,
92
- 'Cannot run more queries in this transaction, it has been rolled back' if terminated?
91
+ if terminated?
92
+ raise Exceptions::TransactionTerminatedException,
93
+ 'Cannot run more queries in this transaction, it has been rolled back'
94
+ end
93
95
  unless @open
94
96
  # Mirror the Java/JRuby messages so the closed-state reason
95
97
  # (committed vs rolled back) is reported the same on both impls.
@@ -138,10 +140,10 @@ module Neo4j
138
140
  keys = (run_response.metadata[:fields] || run_response.metadata['fields'] || []).map(&:to_sym)
139
141
 
140
142
  @current_result = Result.new(@connection, keys, buffer: buffer, handler: handler,
141
- query_text: query, parameters: parameters,
142
- run_metadata: run_response.metadata, fetch_size: fetch_size,
143
- qid: run_response.metadata[:qid],
144
- terminated_error: method(:terminating_error))
143
+ query_text: query, parameters: parameters,
144
+ run_metadata: run_response.metadata, fetch_size: fetch_size,
145
+ qid: run_response.metadata[:qid],
146
+ terminated_error: method(:terminating_error))
145
147
  @open_results << @current_result
146
148
  @current_result
147
149
  end
@@ -263,6 +265,7 @@ module Neo4j
263
265
  # executeQuery (and defensively before ROLLBACK if that query never ran).
264
266
  def ack_begin!
265
267
  return if @begin_acked
268
+
266
269
  @begin_acked = true
267
270
 
268
271
  if @telemetry_sent
@@ -334,7 +337,7 @@ module Neo4j
334
337
 
335
338
  def release_connection
336
339
  @on_release&.call
337
- @on_release = nil # idempotent
340
+ @on_release = nil # idempotent
338
341
  end
339
342
  end
340
343
  end
@@ -25,12 +25,12 @@ module Neo4j
25
25
  raise ArgumentError, "Invalid ISO 8601 duration format: #{string}"
26
26
  end
27
27
 
28
- years = ($1 || 0).to_i
29
- months = ($2 || 0).to_i
30
- days = ($3 || 0).to_i
31
- hours = ($4 || 0).to_i
32
- minutes = ($5 || 0).to_i
33
- seconds_with_fraction = ($6 || 0).to_f
28
+ years = (::Regexp.last_match(1) || 0).to_i
29
+ months = (::Regexp.last_match(2) || 0).to_i
30
+ days = (::Regexp.last_match(3) || 0).to_i
31
+ hours = (::Regexp.last_match(4) || 0).to_i
32
+ minutes = (::Regexp.last_match(5) || 0).to_i
33
+ seconds_with_fraction = (::Regexp.last_match(6) || 0).to_f
34
34
 
35
35
  total_months = years * 12 + months
36
36
  whole_seconds = seconds_with_fraction.to_i
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Neo4j
4
+ module Driver
5
+ module Types
6
+ # Base for graph entities — a Node or a Relationship. Holds the shared
7
+ # identity (id / element_id) and property map, mirroring
8
+ # org.neo4j.driver.types.Entity, which the JRuby flavour exposes as
9
+ # Types::Entity. Kept as a real superclass (not just to de-duplicate
10
+ # Node/Relationship) because downstream gems and applications type-check
11
+ # against Neo4j::Driver::Types::Entity.
12
+ class Entity
13
+ attr_reader :id, :element_id, :properties
14
+
15
+ # element_id defaults to the stringified id — the Bolt 4.x wire carries
16
+ # no element_id, and callers always expect a value (matches Java).
17
+ def initialize(id, properties, element_id = nil)
18
+ @id = id
19
+ @properties = properties
20
+ @element_id = element_id || id.to_s
21
+ end
22
+
23
+ # Property lookup. PackStream hydration symbolizes every map key
24
+ # (Unpacker#unpack_map), so properties are symbol-keyed. Coerce the key
25
+ # so a string caller works too — the Java flavour accepts strings.
26
+ def [](key) = @properties[key.to_sym]
27
+
28
+ # Entities compare by identity within their own type (a Node is never
29
+ # equal to a Relationship), matching the Java driver. eql?/hash track
30
+ # == so entities work as Hash/Set keys.
31
+ def ==(other)
32
+ other.is_a?(self.class) && other.id == @id
33
+ end
34
+ alias eql? ==
35
+
36
+ def hash = @id.hash
37
+ end
38
+ end
39
+ end
40
+ end