neo4j-ruby-driver 6.0.0.alpha.2 → 6.2.1.beta.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.
- checksums.yaml +4 -4
- data/README.md +113 -411
- data/{ruby → lib}/neo4j/driver/access_mode.rb +2 -1
- data/lib/neo4j/driver/auth_token_managers.rb +31 -0
- data/lib/neo4j/driver/auth_tokens.rb +41 -0
- data/lib/neo4j/driver/auto_commit_retries_mode.rb +18 -0
- data/lib/neo4j/driver/bolt/bolt_version.rb +67 -0
- data/lib/neo4j/driver/bolt/connection.rb +1054 -0
- data/lib/neo4j/driver/bolt/handshake.rb +232 -0
- data/lib/neo4j/driver/bolt/message/failure.rb +139 -0
- data/lib/neo4j/driver/bolt/message/ignored.rb +27 -0
- data/lib/neo4j/driver/bolt/message/record.rb +30 -0
- data/lib/neo4j/driver/bolt/message/success.rb +34 -0
- data/lib/neo4j/driver/bolt/message.rb +112 -0
- data/lib/neo4j/driver/bolt/pool.rb +255 -0
- data/lib/neo4j/driver/bolt/protocol/base.rb +146 -0
- data/lib/neo4j/driver/bolt/protocol/v3.rb +43 -0
- data/lib/neo4j/driver/bolt/protocol/v4.rb +24 -0
- data/lib/neo4j/driver/bolt/protocol/v43.rb +28 -0
- data/lib/neo4j/driver/bolt/protocol/v44.rb +23 -0
- data/lib/neo4j/driver/bolt/protocol/v5.rb +27 -0
- data/lib/neo4j/driver/bolt/protocol/v51.rb +24 -0
- data/lib/neo4j/driver/bolt/protocol/v52.rb +28 -0
- data/lib/neo4j/driver/bolt/protocol/v53.rb +27 -0
- data/lib/neo4j/driver/bolt/protocol/v54.rb +14 -0
- data/lib/neo4j/driver/bolt/protocol/v55.rb +17 -0
- data/lib/neo4j/driver/bolt/protocol/v56.rb +14 -0
- data/lib/neo4j/driver/bolt/protocol/v57.rb +31 -0
- data/lib/neo4j/driver/bolt/protocol/v58.rb +15 -0
- data/lib/neo4j/driver/bolt/protocol/v6.rb +43 -0
- data/lib/neo4j/driver/bolt/protocol/v61.rb +21 -0
- data/lib/neo4j/driver/bolt/protocol_version_handler.rb +39 -0
- data/lib/neo4j/driver/bolt/record_buffer.rb +136 -0
- data/lib/neo4j/driver/bolt/stream_handler.rb +53 -0
- data/lib/neo4j/driver/bolt/tls_config.rb +129 -0
- data/lib/neo4j/driver/bolt/wire.rb +321 -0
- data/lib/neo4j/driver/bookmark.rb +15 -0
- data/lib/neo4j/driver/bookmark_managers.rb +39 -0
- data/lib/neo4j/driver/client_certificate.rb +19 -0
- data/lib/neo4j/driver/client_certificate_manager.rb +37 -0
- data/lib/neo4j/driver/client_certificate_managers.rb +15 -0
- data/lib/neo4j/driver/client_certificates.rb +14 -0
- data/lib/neo4j/driver/direct/connection_provider.rb +258 -0
- data/lib/neo4j/driver/driver.rb +268 -0
- data/lib/neo4j/driver/eager_result.rb +10 -0
- data/lib/neo4j/driver/exceptions/neo4j_exception.rb +16 -7
- data/lib/neo4j/driver/exceptions/protocol_exception.rb +0 -5
- data/lib/neo4j/driver/exceptions/security_retryable_exception.rb +14 -0
- data/lib/neo4j/driver/exceptions/transaction_terminated_exception.rb +13 -0
- data/lib/neo4j/driver/graph_database.rb +29 -0
- data/lib/neo4j/driver/internal/clock.rb +20 -0
- data/lib/neo4j/driver/internal/clock_adapter.rb +21 -0
- data/lib/neo4j/driver/internal/default_bookmark_manager.rb +54 -0
- data/lib/neo4j/driver/internal/deprecator.rb +9 -5
- data/lib/neo4j/driver/internal/driver_factory.rb +128 -0
- data/lib/neo4j/driver/internal/duration_normalizer.rb +14 -41
- data/lib/neo4j/driver/internal/extras.rb +23 -0
- data/lib/neo4j/driver/internal/home_db_cache.rb +57 -0
- data/lib/neo4j/driver/internal/internal_auth_token_manager.rb +37 -0
- data/lib/neo4j/driver/internal/metrics.rb +31 -0
- data/lib/neo4j/driver/internal/security/static_auth_token_manager.rb +25 -0
- data/lib/neo4j/driver/internal/validator.rb +10 -0
- data/lib/neo4j/driver/net.rb +29 -0
- data/lib/neo4j/driver/packstream/markers.rb +49 -0
- data/lib/neo4j/driver/packstream/packer.rb +333 -0
- data/lib/neo4j/driver/packstream/structure.rb +27 -0
- data/lib/neo4j/driver/packstream/unpacker.rb +168 -0
- data/lib/neo4j/driver/record.rb +49 -0
- data/lib/neo4j/driver/result.rb +282 -0
- data/lib/neo4j/driver/routing/load_balancer.rb +690 -0
- data/lib/neo4j/driver/routing/routed_connection.rb +132 -0
- data/lib/neo4j/driver/routing/routing_table.rb +127 -0
- data/lib/neo4j/driver/routing/server_address.rb +25 -0
- data/lib/neo4j/driver/routing_control.rb +13 -0
- data/lib/neo4j/driver/session.rb +575 -0
- data/lib/neo4j/driver/summary/database_info.rb +25 -0
- data/lib/neo4j/driver/summary/gql_notification.rb +38 -0
- data/lib/neo4j/driver/summary/gql_status_object.rb +35 -0
- data/lib/neo4j/driver/summary/notification.rb +46 -0
- data/lib/neo4j/driver/summary/plan.rb +23 -0
- data/lib/neo4j/driver/summary/profile.rb +28 -0
- data/lib/neo4j/driver/summary/query.rb +19 -0
- data/lib/neo4j/driver/summary/query_type.rb +5 -1
- data/lib/neo4j/driver/summary/result_summary.rb +213 -0
- data/lib/neo4j/driver/summary/server_info.rb +33 -0
- data/lib/neo4j/driver/summary/summary_counters.rb +72 -0
- data/lib/neo4j/driver/transaction.rb +341 -0
- data/lib/neo4j/driver/types/duration.rb +76 -0
- data/lib/neo4j/driver/types/local_date_time.rb +52 -8
- data/lib/neo4j/driver/types/local_time.rb +42 -7
- data/lib/neo4j/driver/types/node.rb +27 -0
- data/lib/neo4j/driver/types/offset_time.rb +71 -7
- data/lib/neo4j/driver/types/path.rb +68 -0
- data/lib/neo4j/driver/types/point.rb +38 -18
- data/lib/neo4j/driver/types/relationship.rb +41 -0
- data/lib/neo4j/driver/types/temporal_value.rb +43 -0
- data/lib/neo4j/driver/types/unbound_relationship.rb +29 -0
- data/lib/neo4j/driver/types/unresolvable_zoned_date_time.rb +38 -0
- data/lib/neo4j/driver/types/unsupported_type.rb +25 -0
- data/lib/neo4j/driver/types/uuid.rb +29 -0
- data/{ruby → lib}/neo4j/driver/version.rb +1 -1
- data/lib/neo4j/driver.rb +16 -0
- data/lib/neo4j-ruby-driver_loader.rb +34 -20
- metadata +132 -390
- data/lib/neo4j/driver/internal/bolt_server_address.rb +0 -97
- data/lib/neo4j/driver/synchronizable.rb +0 -23
- data/lib/neo4j/driver/types/time.rb +0 -45
- data/ruby/neo4j/driver/auth_tokens.rb +0 -34
- data/ruby/neo4j/driver/bookmark.rb +0 -21
- data/ruby/neo4j/driver/config.rb +0 -89
- data/ruby/neo4j/driver/graph_database.rb +0 -78
- data/ruby/neo4j/driver/internal/async/connection/bolt_protocol_util.rb +0 -51
- data/ruby/neo4j/driver/internal/async/connection/bootstrap_factory.rb +0 -22
- data/ruby/neo4j/driver/internal/async/connection/channel_attributes.rb +0 -31
- data/ruby/neo4j/driver/internal/async/connection/channel_connected_listener.rb +0 -32
- data/ruby/neo4j/driver/internal/async/connection/channel_connector_impl.rb +0 -83
- data/ruby/neo4j/driver/internal/async/connection/channel_pipeline_builder_impl.rb +0 -22
- data/ruby/neo4j/driver/internal/async/connection/direct_connection.rb +0 -30
- data/ruby/neo4j/driver/internal/async/connection/event_loop_group_factory.rb +0 -83
- data/ruby/neo4j/driver/internal/async/connection/handshake_completed_listener.rb +0 -27
- data/ruby/neo4j/driver/internal/async/connection/handshake_handler.rb +0 -113
- data/ruby/neo4j/driver/internal/async/connection/netty_channel_initializer.rb +0 -57
- data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver.rb +0 -26
- data/ruby/neo4j/driver/internal/async/connection/netty_domain_name_resolver_group.rb +0 -19
- data/ruby/neo4j/driver/internal/async/connection/routing_connection.rb +0 -36
- data/ruby/neo4j/driver/internal/async/connection/stream.rb +0 -12
- data/ruby/neo4j/driver/internal/async/connection/stream_reader.rb +0 -16
- data/ruby/neo4j/driver/internal/async/connection_context.rb +0 -10
- data/ruby/neo4j/driver/internal/async/immutable_connection_context.rb +0 -24
- data/ruby/neo4j/driver/internal/async/inbound/byte_buf_input.rb +0 -30
- data/ruby/neo4j/driver/internal/async/inbound/channel_error_handler.rb +0 -77
- data/ruby/neo4j/driver/internal/async/inbound/chunk_decoder.rb +0 -41
- data/ruby/neo4j/driver/internal/async/inbound/connect_timeout_handler.rb +0 -32
- data/ruby/neo4j/driver/internal/async/inbound/connection_read_timeout_handler.rb +0 -17
- data/ruby/neo4j/driver/internal/async/inbound/inbound_message_dispatcher.rb +0 -171
- data/ruby/neo4j/driver/internal/async/inbound/inbound_message_handler.rb +0 -42
- data/ruby/neo4j/driver/internal/async/inbound/message_decoder.rb +0 -51
- data/ruby/neo4j/driver/internal/async/internal_async_session.rb +0 -92
- data/ruby/neo4j/driver/internal/async/internal_async_transaction.rb +0 -13
- data/ruby/neo4j/driver/internal/async/leak_logging_network_session.rb +0 -34
- data/ruby/neo4j/driver/internal/async/network_connection.rb +0 -194
- data/ruby/neo4j/driver/internal/async/network_session.rb +0 -150
- data/ruby/neo4j/driver/internal/async/outbound/chunk_aware_byte_buf_output.rb +0 -110
- data/ruby/neo4j/driver/internal/async/outbound/outbound_message_handler.rb +0 -39
- data/ruby/neo4j/driver/internal/async/pool/channel.rb +0 -66
- data/ruby/neo4j/driver/internal/async/pool/channel_pool.rb +0 -31
- data/ruby/neo4j/driver/internal/async/pool/channel_tracker.rb +0 -135
- data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +0 -156
- data/ruby/neo4j/driver/internal/async/pool/netty_channel_health_checker.rb +0 -87
- data/ruby/neo4j/driver/internal/async/pool/netty_channel_pool.rb +0 -52
- data/ruby/neo4j/driver/internal/async/pool/network_connection_factory.rb +0 -21
- data/ruby/neo4j/driver/internal/async/pool/pool_settings.rb +0 -34
- data/ruby/neo4j/driver/internal/async/pool/timed_stack.rb +0 -15
- data/ruby/neo4j/driver/internal/async/result_cursors_holder.rb +0 -17
- data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +0 -212
- data/ruby/neo4j/driver/internal/bookmark_holder.rb +0 -9
- data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +0 -48
- data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +0 -14
- data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +0 -122
- data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +0 -10
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +0 -68
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +0 -125
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/round_robin_array_index.rb +0 -13
- data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +0 -31
- data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +0 -147
- data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +0 -43
- data/ruby/neo4j/driver/internal/cluster/routing_context.rb +0 -77
- data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +0 -60
- data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +0 -35
- data/ruby/neo4j/driver/internal/cluster/routing_settings.rb +0 -24
- data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +0 -95
- data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +0 -121
- data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +0 -73
- data/ruby/neo4j/driver/internal/connection_settings.rb +0 -16
- data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +0 -55
- data/ruby/neo4j/driver/internal/cursor/async_result_cursor_only_factory.rb +0 -24
- data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +0 -61
- data/ruby/neo4j/driver/internal/cursor/result_cursor_factory_impl.rb +0 -24
- data/ruby/neo4j/driver/internal/cursor/rx_result_cursor_impl.rb +0 -110
- data/ruby/neo4j/driver/internal/database_name_util.rb +0 -37
- data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +0 -9
- data/ruby/neo4j/driver/internal/default_domain_name_resolver.rb +0 -11
- data/ruby/neo4j/driver/internal/direct_connection_provider.rb +0 -40
- data/ruby/neo4j/driver/internal/driver_factory.rb +0 -126
- data/ruby/neo4j/driver/internal/eager_result_value.rb +0 -5
- data/ruby/neo4j/driver/internal/handlers/begin_tx_response_handler.rb +0 -20
- data/ruby/neo4j/driver/internal/handlers/channel_releasing_reset_response_handler.rb +0 -30
- data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +0 -23
- data/ruby/neo4j/driver/internal/handlers/hello_response_handler.rb +0 -65
- data/ruby/neo4j/driver/internal/handlers/init_response_handler.rb +0 -34
- data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +0 -199
- data/ruby/neo4j/driver/internal/handlers/no_op_response_handler.rb +0 -16
- data/ruby/neo4j/driver/internal/handlers/ping_response_handler.rb +0 -29
- data/ruby/neo4j/driver/internal/handlers/pull_handlers.rb +0 -32
- data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +0 -169
- data/ruby/neo4j/driver/internal/handlers/pulln/basic_pull_response_handler.rb +0 -298
- data/ruby/neo4j/driver/internal/handlers/pulln/fetch_size_util.rb +0 -20
- data/ruby/neo4j/driver/internal/handlers/reset_response_handler.rb +0 -34
- data/ruby/neo4j/driver/internal/handlers/rollback_tx_response_handler.rb +0 -25
- data/ruby/neo4j/driver/internal/handlers/route_message_response_handler.rb +0 -21
- data/ruby/neo4j/driver/internal/handlers/routing_response_handler.rb +0 -79
- data/ruby/neo4j/driver/internal/handlers/run_response_handler.rb +0 -38
- data/ruby/neo4j/driver/internal/handlers/session_pull_response_completion_listener.rb +0 -34
- data/ruby/neo4j/driver/internal/handlers/transaction_pull_response_completion_listener.rb +0 -20
- data/ruby/neo4j/driver/internal/impersonation_util.rb +0 -22
- data/ruby/neo4j/driver/internal/internal_bookmark.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_database_name.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_driver.rb +0 -83
- data/ruby/neo4j/driver/internal/internal_entity.rb +0 -21
- data/ruby/neo4j/driver/internal/internal_node.rb +0 -21
- data/ruby/neo4j/driver/internal/internal_pair.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_path.rb +0 -35
- data/ruby/neo4j/driver/internal/internal_point2_d.rb +0 -9
- data/ruby/neo4j/driver/internal/internal_point3_d.rb +0 -6
- data/ruby/neo4j/driver/internal/internal_record.rb +0 -27
- data/ruby/neo4j/driver/internal/internal_relationship.rb +0 -27
- data/ruby/neo4j/driver/internal/internal_result.rb +0 -49
- data/ruby/neo4j/driver/internal/internal_session.rb +0 -91
- data/ruby/neo4j/driver/internal/internal_transaction.rb +0 -48
- data/ruby/neo4j/driver/internal/logging/channel_activity_logger.rb +0 -29
- data/ruby/neo4j/driver/internal/logging/channel_error_logger.rb +0 -17
- data/ruby/neo4j/driver/internal/logging/prefixed_logger.rb +0 -19
- data/ruby/neo4j/driver/internal/logging/reformatted_logger.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/abstract_message_writer.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/bolt_protocol.rb +0 -32
- data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +0 -48
- data/ruby/neo4j/driver/internal/messaging/common/common_message_reader.rb +0 -51
- data/ruby/neo4j/driver/internal/messaging/common/common_value.rb +0 -33
- data/ruby/neo4j/driver/internal/messaging/common/common_value_packer.rb +0 -104
- data/ruby/neo4j/driver/internal/messaging/common/common_value_unpacker.rb +0 -256
- data/ruby/neo4j/driver/internal/messaging/encode/begin_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/commit_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/discard_all_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/discard_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/goodbye_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/hello_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/init_message_encoder.rb +0 -16
- data/ruby/neo4j/driver/internal/messaging/encode/logoff_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/logon_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/pull_all_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/pull_message_encoder.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/encode/reset_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/rollback_message_encoder.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +0 -24
- data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +0 -22
- data/ruby/neo4j/driver/internal/messaging/encode/run_message_encoder.rb +0 -16
- data/ruby/neo4j/driver/internal/messaging/encode/run_with_metadata_message_encoder.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/request/abstract_streaming_message.rb +0 -25
- data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +0 -25
- data/ruby/neo4j/driver/internal/messaging/request/commit_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/discard_all_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/discard_message.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/request/goodbye_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/hello_message.rb +0 -30
- data/ruby/neo4j/driver/internal/messaging/request/init_message.rb +0 -19
- data/ruby/neo4j/driver/internal/messaging/request/logoff_message.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/request/logon_message.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/request/message_with_metadata.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +0 -26
- data/ruby/neo4j/driver/internal/messaging/request/pull_all_message.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/request/pull_message.rb +0 -22
- data/ruby/neo4j/driver/internal/messaging/request/reset_message.rb +0 -32
- data/ruby/neo4j/driver/internal/messaging/request/rollback_message.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +0 -28
- data/ruby/neo4j/driver/internal/messaging/request/run_message.rb +0 -23
- data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +0 -49
- data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +0 -24
- data/ruby/neo4j/driver/internal/messaging/response/failure_message.rb +0 -40
- data/ruby/neo4j/driver/internal/messaging/response/ignored_message.rb +0 -29
- data/ruby/neo4j/driver/internal/messaging/response/record_message.rb +0 -33
- data/ruby/neo4j/driver/internal/messaging/response/success_message.rb +0 -34
- data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +0 -82
- data/ruby/neo4j/driver/internal/messaging/v3/message_format_v3.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v3/message_writer_v3.rb +0 -27
- data/ruby/neo4j/driver/internal/messaging/v4/bolt_protocol_v4.rb +0 -29
- data/ruby/neo4j/driver/internal/messaging/v4/message_format_v4.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/v4/message_writer_v4.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v41/bolt_protocol_v41.rb +0 -25
- data/ruby/neo4j/driver/internal/messaging/v42/bolt_protocol_v42.rb +0 -13
- data/ruby/neo4j/driver/internal/messaging/v43/bolt_protocol_v43.rb +0 -19
- data/ruby/neo4j/driver/internal/messaging/v43/message_format_v43.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/v43/message_writer_v43.rb +0 -20
- data/ruby/neo4j/driver/internal/messaging/v44/bolt_protocol_v44.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v44/message_format_v44.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +0 -15
- data/ruby/neo4j/driver/internal/messaging/v5/bolt_protocol_v5.rb +0 -21
- data/ruby/neo4j/driver/internal/messaging/v5/message_format_v5.rb +0 -18
- data/ruby/neo4j/driver/internal/messaging/v5/value_unpacker_v5.rb +0 -17
- data/ruby/neo4j/driver/internal/messaging/v51/bolt_protocol_v51.rb +0 -40
- data/ruby/neo4j/driver/internal/messaging/v51/message_format_v51.rb +0 -14
- data/ruby/neo4j/driver/internal/messaging/v51/message_writer_v51.rb +0 -17
- data/ruby/neo4j/driver/internal/metrics/connection_pool_metrics_listener.rb +0 -34
- data/ruby/neo4j/driver/internal/metrics/internal_abstract_metrics.rb +0 -46
- data/ruby/neo4j/driver/internal/metrics/internal_connection_pool_metrics.rb +0 -105
- data/ruby/neo4j/driver/internal/metrics/internal_metrics.rb +0 -82
- data/ruby/neo4j/driver/internal/metrics/internal_metrics_provider.rb +0 -18
- data/ruby/neo4j/driver/internal/metrics/listener_event.rb +0 -17
- data/ruby/neo4j/driver/internal/metrics/metrics_provider.rb +0 -24
- data/ruby/neo4j/driver/internal/metrics/time_recorder_listener_event.rb +0 -15
- data/ruby/neo4j/driver/internal/packstream/byte_array_incompatible_packer.rb +0 -12
- data/ruby/neo4j/driver/internal/packstream/pack_input.rb +0 -47
- data/ruby/neo4j/driver/internal/packstream/pack_output.rb +0 -39
- data/ruby/neo4j/driver/internal/packstream/pack_stream.rb +0 -324
- data/ruby/neo4j/driver/internal/packstream/pack_type.rb +0 -17
- data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +0 -13
- data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +0 -35
- data/ruby/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb +0 -150
- data/ruby/neo4j/driver/internal/revocation_strategy.rb +0 -19
- data/ruby/neo4j/driver/internal/scheme.rb +0 -32
- data/ruby/neo4j/driver/internal/security/internal_auth_token.rb +0 -15
- data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +0 -48
- data/ruby/neo4j/driver/internal/security_setting.rb +0 -66
- data/ruby/neo4j/driver/internal/session_factory_impl.rb +0 -32
- data/ruby/neo4j/driver/internal/spi/connection.rb +0 -19
- data/ruby/neo4j/driver/internal/spi/connection_pool.rb +0 -9
- data/ruby/neo4j/driver/internal/spi/response_handler.rb +0 -23
- data/ruby/neo4j/driver/internal/summary/internal_database_info.rb +0 -7
- data/ruby/neo4j/driver/internal/summary/internal_input_position.rb +0 -11
- data/ruby/neo4j/driver/internal/summary/internal_notification.rb +0 -16
- data/ruby/neo4j/driver/internal/summary/internal_plan.rb +0 -41
- data/ruby/neo4j/driver/internal/summary/internal_profiled_plan.rb +0 -32
- data/ruby/neo4j/driver/internal/summary/internal_result_summary.rb +0 -33
- data/ruby/neo4j/driver/internal/summary/internal_server_info.rb +0 -6
- data/ruby/neo4j/driver/internal/summary/internal_summary_counters.rb +0 -18
- data/ruby/neo4j/driver/internal/svm/netty_substitutions.rb +0 -196
- data/ruby/neo4j/driver/internal/svm/z_lib_substitutions.rb +0 -21
- data/ruby/neo4j/driver/internal/util/certificate_tool.rb +0 -65
- data/ruby/neo4j/driver/internal/util/clock.rb +0 -29
- data/ruby/neo4j/driver/internal/util/error_util.rb +0 -104
- data/ruby/neo4j/driver/internal/util/extract.rb +0 -123
- data/ruby/neo4j/driver/internal/util/format.rb +0 -39
- data/ruby/neo4j/driver/internal/util/futures.rb +0 -99
- data/ruby/neo4j/driver/internal/util/iterables.rb +0 -35
- data/ruby/neo4j/driver/internal/util/lock_util.rb +0 -23
- data/ruby/neo4j/driver/internal/util/metadata_extractor.rb +0 -101
- data/ruby/neo4j/driver/internal/util/mutex.rb +0 -9
- data/ruby/neo4j/driver/internal/util/preconditions.rb +0 -16
- data/ruby/neo4j/driver/internal/util/result_holder.rb +0 -72
- data/ruby/neo4j/driver/internal/util/server_version.rb +0 -60
- data/ruby/neo4j/driver/logging1.rb +0 -51
- data/ruby/neo4j/driver/net/server_address.rb +0 -9
- data/ruby/neo4j/driver/query.rb +0 -48
- data/ruby/neo4j/driver/records.rb +0 -13
- data/ruby/neo4j/driver/transaction_config.rb +0 -50
- data/ruby/neo4j/driver/values.rb +0 -26
- data/ruby/neo4j/driver.rb +0 -30
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Neo4j
|
|
4
|
+
module Driver
|
|
5
|
+
# Represents an explicit transaction
|
|
6
|
+
class Transaction
|
|
7
|
+
attr_reader :connection
|
|
8
|
+
|
|
9
|
+
def initialize(connection, session, bookmarks = [], options = {}, telemetry_api: nil, telemetry_ack: nil,
|
|
10
|
+
pipelined: false, on_begin: nil, on_release: nil)
|
|
11
|
+
@connection = connection
|
|
12
|
+
@session = session
|
|
13
|
+
@options = options
|
|
14
|
+
# executeQuery pipelines BEGIN + RUN + PULL (Optimization:ExecuteQueryPipelining):
|
|
15
|
+
# BEGIN's reply is read only after the first RUN+PULL are flushed, not eagerly.
|
|
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
|
|
19
|
+
@open = true
|
|
20
|
+
@committed = false
|
|
21
|
+
@rolled_back = false
|
|
22
|
+
@failed = false
|
|
23
|
+
@terminating_error = nil # the classified error that terminated this tx (a RUN/commit failure)
|
|
24
|
+
@current_result = nil
|
|
25
|
+
# Every result opened in this tx, in order. Bolt lets multiple stay open
|
|
26
|
+
# and streaming concurrently (qid multiplexing); a new RUN no longer
|
|
27
|
+
# force-buffers the previous one, so we track them all here to discard
|
|
28
|
+
# any still-open at commit/rollback and to spot a mid-stream failure.
|
|
29
|
+
@open_results = []
|
|
30
|
+
|
|
31
|
+
# Begin the transaction
|
|
32
|
+
# Drop blank values so the serialised BEGIN map matches what
|
|
33
|
+
# testkit's stub scripts expect (e.g. `BEGIN {"db": "adb"}`,
|
|
34
|
+
# not `BEGIN {"db": "adb", "tx_metadata": {}}`).
|
|
35
|
+
begin_extra = {
|
|
36
|
+
bookmarks: bookmarks,
|
|
37
|
+
db: options[:database],
|
|
38
|
+
mode: options[:access_mode],
|
|
39
|
+
tx_timeout: options[:timeout],
|
|
40
|
+
tx_metadata: options[:metadata],
|
|
41
|
+
imp_user: options[:impersonated_user]
|
|
42
|
+
}
|
|
43
|
+
begin_extra.reject!(&Internal::Extras::BLANK)
|
|
44
|
+
|
|
45
|
+
# TELEMETRY (api = 0 managed / 1 explicit / 3 executeQuery) pipelined
|
|
46
|
+
# ahead of BEGIN when the server opted in and the driver didn't disable
|
|
47
|
+
# it; its SUCCESS is read first (see #ack_begin!).
|
|
48
|
+
@telemetry_sent = @connection.telemetry(telemetry_api, disabled: options[:telemetry_disabled])
|
|
49
|
+
@telemetry_ack = telemetry_ack
|
|
50
|
+
# Session-level NotificationsConfig rides on BEGIN (5.2+); the tx's own
|
|
51
|
+
# RUNs carry none. nil / pre-5.2 => no notification keys on the wire.
|
|
52
|
+
@connection.send_message(@connection.protocol.build_begin(begin_extra,
|
|
53
|
+
notification_config: options[:notification_config]))
|
|
54
|
+
@connection.flush
|
|
55
|
+
|
|
56
|
+
# Non-pipelined: read BEGIN's reply now, a plain round-trip (unchanged).
|
|
57
|
+
# Pipelined (executeQuery): defer it so the first #run can flush RUN+PULL
|
|
58
|
+
# before we block — a pipelining server withholds BEGIN's SUCCESS until it
|
|
59
|
+
# has all three. #ack_begin! drains it after that flush.
|
|
60
|
+
@begin_acked = false
|
|
61
|
+
ack_begin! unless @pipelined
|
|
62
|
+
rescue Exceptions::Neo4jException => e
|
|
63
|
+
# Classify first so the auth-token manager is notified and the
|
|
64
|
+
# connection is flagged for discard on an auth failure (the server
|
|
65
|
+
# closes it). BEGIN failed → server is in FAILED state; RESET to
|
|
66
|
+
# make it reusable, unless it's being discarded (a security
|
|
67
|
+
# failure: the server closes it and RESET would just error).
|
|
68
|
+
classified = @connection.classify_failure(e)
|
|
69
|
+
@connection.reset! unless @connection.auth_failed
|
|
70
|
+
@open = false
|
|
71
|
+
release_connection
|
|
72
|
+
raise classified
|
|
73
|
+
rescue StandardError
|
|
74
|
+
# Transport-level failure (IO/socket). RESET will likely fail
|
|
75
|
+
# too on a dead connection, but release the lease so it doesn't
|
|
76
|
+
# leak; pool reuse will surface the breakage to the next caller.
|
|
77
|
+
@open = false
|
|
78
|
+
release_connection
|
|
79
|
+
raise
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def run(query, **parameters)
|
|
83
|
+
Internal::Validator.require_query_text!(query)
|
|
84
|
+
# A server failure terminated this transaction; further work is
|
|
85
|
+
# rejected locally (no wire traffic) as a TransactionTerminatedException
|
|
86
|
+
# — a ClientException subclass, matching the Java driver. Covers both a
|
|
87
|
+
# failed RUN (sets @failed) and a result that failed mid-stream during
|
|
88
|
+
# the user's own iteration (@current_result.failed?). Message wording
|
|
89
|
+
# stays "rolled back" to match the Java flavor (a shared integration spec
|
|
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?
|
|
93
|
+
unless @open
|
|
94
|
+
# Mirror the Java/JRuby messages so the closed-state reason
|
|
95
|
+
# (committed vs rolled back) is reported the same on both impls.
|
|
96
|
+
raise Exceptions::ClientException,
|
|
97
|
+
"Cannot run more queries in this transaction, it has been #{@committed ? 'committed' : 'rolled back'}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# A new query becomes current; the previous result must now name its qid
|
|
101
|
+
# explicitly on further PULL/DISCARD (the server defaults them to the last
|
|
102
|
+
# opened query). We keep it open and streaming rather than buffering it —
|
|
103
|
+
# that's the qid multiplexing the nested-result tests exercise.
|
|
104
|
+
@current_result&.demote!
|
|
105
|
+
|
|
106
|
+
fetch_size = effective_fetch_size
|
|
107
|
+
|
|
108
|
+
# send/flush are inside the begin block so a transport-level
|
|
109
|
+
# failure surfacing on fetch_response still sets @failed and
|
|
110
|
+
# goes through classify_failure. Connection#send_message and
|
|
111
|
+
# #flush both defer peer-closed errors so a buffered server
|
|
112
|
+
# FAILURE is read before fetch_response can raise its own
|
|
113
|
+
# EOF-driven ServiceUnavailableException — JRuby surfaces
|
|
114
|
+
# EPIPE eagerly, MRI tends to defer it naturally. Without
|
|
115
|
+
# this rescue placement, session.close's subsequent rollback
|
|
116
|
+
# path takes the ROLLBACK-message branch (rather than
|
|
117
|
+
# rollback_via_reset) and re-fails on the dead connection.
|
|
118
|
+
buffer = Bolt::RecordBuffer.new(fetch_size: fetch_size)
|
|
119
|
+
handler = Bolt::StreamHandler.new(buffer)
|
|
120
|
+
run_response =
|
|
121
|
+
begin
|
|
122
|
+
@connection.send_message(@connection.protocol.build_run(query, parameters, {}))
|
|
123
|
+
@connection.send_message(@connection.protocol.build_pull(n: fetch_size), handler)
|
|
124
|
+
@connection.flush
|
|
125
|
+
# Drain the pipelined BEGIN (+telemetry) reply now that RUN+PULL are on
|
|
126
|
+
# the wire — no-op unless this is the pipelined first run. A BEGIN that
|
|
127
|
+
# failed surfaces here and is handled as a run failure below; the tx
|
|
128
|
+
# then rolls back on its way out, resetting the connection.
|
|
129
|
+
ack_begin!
|
|
130
|
+
@connection.fetch_response.assert_success!
|
|
131
|
+
rescue Exceptions::Neo4jException => e
|
|
132
|
+
@failed = true
|
|
133
|
+
# Remember the terminating error: sibling results still open must
|
|
134
|
+
# raise it (not pull) once this RUN fails — the connection is FAILED.
|
|
135
|
+
raise(@terminating_error = @connection.classify_failure(e))
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
keys = (run_response.metadata[:fields] || run_response.metadata['fields'] || []).map(&:to_sym)
|
|
139
|
+
|
|
140
|
+
@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))
|
|
145
|
+
@open_results << @current_result
|
|
146
|
+
@current_result
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def commit
|
|
150
|
+
# Java/JRuby-aligned messages for the already-closed states.
|
|
151
|
+
raise Exceptions::ClientException, 'Can\'t commit, transaction has been committed' if @committed
|
|
152
|
+
raise Exceptions::ClientException, 'Can\'t commit, transaction has been rolled back' if @rolled_back
|
|
153
|
+
raise Exceptions::ClientException, 'Transaction is already closed' unless @open
|
|
154
|
+
|
|
155
|
+
if terminated?
|
|
156
|
+
rollback_via_reset
|
|
157
|
+
raise Exceptions::TransactionTerminatedException,
|
|
158
|
+
"Transaction can't be committed. It has been rolled back"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
begin
|
|
162
|
+
discard_open_results
|
|
163
|
+
rescue Exceptions::Neo4jException
|
|
164
|
+
rollback_via_reset
|
|
165
|
+
raise
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# send/flush inside the begin block — see Transaction#run for
|
|
169
|
+
# the rationale on JRuby vs MRI socket-write timing.
|
|
170
|
+
response =
|
|
171
|
+
begin
|
|
172
|
+
@connection.send_message(Bolt::Message.commit)
|
|
173
|
+
@connection.flush
|
|
174
|
+
@connection.fetch_response.assert_success!
|
|
175
|
+
rescue Exceptions::Neo4jException => e
|
|
176
|
+
@failed = true
|
|
177
|
+
# Classify first so a security failure flags the connection
|
|
178
|
+
# for discard before rollback_via_reset releases it.
|
|
179
|
+
classified = @connection.classify_failure(e)
|
|
180
|
+
rollback_via_reset
|
|
181
|
+
raise classified
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
@committed = true
|
|
185
|
+
@open = false
|
|
186
|
+
|
|
187
|
+
bookmarks = response.metadata[:bookmark]
|
|
188
|
+
@session.update_bookmarks(bookmarks) if bookmarks
|
|
189
|
+
release_connection
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def rollback
|
|
193
|
+
raise Exceptions::ClientException, 'Transaction is already closed' unless @open
|
|
194
|
+
|
|
195
|
+
# A pipelined executeQuery tx whose query never ran (e.g. local validation
|
|
196
|
+
# failed before RUN/PULL were sent) left BEGIN's reply unread — and a
|
|
197
|
+
# pipelining server may withhold it until RUN/PULL arrive, which now never
|
|
198
|
+
# will. RESET rolls the tx back and drains any pending reply without a
|
|
199
|
+
# blocking read that could deadlock; there are no open results to discard.
|
|
200
|
+
return rollback_via_reset unless @begin_acked
|
|
201
|
+
|
|
202
|
+
# A terminated tx left the connection FAILED: don't drain open results
|
|
203
|
+
# (that would send PULL/DISCARD the server rejects) — just RESET.
|
|
204
|
+
if terminated?
|
|
205
|
+
rollback_via_reset
|
|
206
|
+
return
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
begin
|
|
210
|
+
discard_open_results
|
|
211
|
+
rescue Exceptions::Neo4jException
|
|
212
|
+
# Failures surfaced while draining a pending result are expected
|
|
213
|
+
# during rollback — the tx is being discarded anyway. @failed is
|
|
214
|
+
# set; RESET path below will clean up the connection.
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
if @failed
|
|
218
|
+
rollback_via_reset
|
|
219
|
+
return
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
begin
|
|
223
|
+
@connection.send_message(Bolt::Message.rollback)
|
|
224
|
+
@connection.flush
|
|
225
|
+
@connection.fetch_response.assert_success!
|
|
226
|
+
rescue Exceptions::ServiceUnavailableException, Exceptions::SessionExpiredException
|
|
227
|
+
# Rolling back on a broken/dead connection is a no-op — the
|
|
228
|
+
# server discards the tx when the link dies. Swallow these so
|
|
229
|
+
# session.close's rollback path stays clean.
|
|
230
|
+
rescue Exceptions::Neo4jException => e
|
|
231
|
+
# A server FAILURE on ROLLBACK (e.g. DatabaseUnavailable) is a
|
|
232
|
+
# real error: the connection is now in FAILED state. RESET it
|
|
233
|
+
# back to READY, then surface the failure through the routing
|
|
234
|
+
# classifier — same as commit/run — so routing side effects
|
|
235
|
+
# (e.g. deactivate on DatabaseUnavailable) fire and the surfaced
|
|
236
|
+
# type is consistent. No-op for direct connections.
|
|
237
|
+
@connection.reset!
|
|
238
|
+
raise @connection.classify_failure(e)
|
|
239
|
+
ensure
|
|
240
|
+
@rolled_back = true
|
|
241
|
+
@open = false
|
|
242
|
+
release_connection
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def close
|
|
247
|
+
rollback if @open && !@committed
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def open?
|
|
251
|
+
@open
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def failed?
|
|
255
|
+
@failed
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
private
|
|
259
|
+
|
|
260
|
+
# Read the deferred BEGIN acknowledgement (and the telemetry SUCCESS
|
|
261
|
+
# pipelined ahead of it). Idempotent: called once — eagerly in #initialize
|
|
262
|
+
# for a normal tx, or after the first RUN+PULL flush for a pipelined
|
|
263
|
+
# executeQuery (and defensively before ROLLBACK if that query never ran).
|
|
264
|
+
def ack_begin!
|
|
265
|
+
return if @begin_acked
|
|
266
|
+
@begin_acked = true
|
|
267
|
+
|
|
268
|
+
if @telemetry_sent
|
|
269
|
+
@connection.fetch_response.assert_success!
|
|
270
|
+
# The server acknowledged telemetry; a managed-tx retry won't re-send it.
|
|
271
|
+
@telemetry_ack&.call
|
|
272
|
+
end
|
|
273
|
+
begin_response = @connection.fetch_response.assert_success!
|
|
274
|
+
# A home-db BEGIN that sent db=nil comes back with the resolved name.
|
|
275
|
+
@on_begin&.call(begin_response)
|
|
276
|
+
begin_response
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# The transaction is terminated once a server failure has hit it — either
|
|
280
|
+
# a tx method caught it (@failed) or any open result failed during the
|
|
281
|
+
# user's own iteration (its failure hasn't passed through a tx method).
|
|
282
|
+
def terminated? = @failed || @open_results.any?(&:failed?)
|
|
283
|
+
|
|
284
|
+
# The error that terminated this tx, or nil. A RUN/commit failure records
|
|
285
|
+
# it directly; a result that failed mid-iteration (the user's own PULL)
|
|
286
|
+
# carries it on the result. Passed to each result as its terminated_error
|
|
287
|
+
# so a sibling raises it instead of pulling on a FAILED connection.
|
|
288
|
+
def terminating_error = @terminating_error || @open_results.find(&:failed?)&.failure
|
|
289
|
+
|
|
290
|
+
# See Session#effective_fetch_size. Transactions inherit the session
|
|
291
|
+
# options at open, so the same default rules apply.
|
|
292
|
+
def effective_fetch_size
|
|
293
|
+
size = @options[:fetch_size]
|
|
294
|
+
size.nil? ? 1000 : size
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# At tx end (commit/rollback) every still-open result goes out of scope,
|
|
298
|
+
# so discard each — DISCARD abandons remaining records (essential when a
|
|
299
|
+
# result is unbounded) rather than streaming them into memory, and leaves
|
|
300
|
+
# each raising ResultConsumedException on later access. Demoted results
|
|
301
|
+
# DISCARD by their qid; the current one omits it (targets the last query).
|
|
302
|
+
def discard_open_results
|
|
303
|
+
@open_results.each do |result|
|
|
304
|
+
begin
|
|
305
|
+
result.consume
|
|
306
|
+
rescue Exceptions::Neo4jException => e
|
|
307
|
+
@failed = true
|
|
308
|
+
# A wire error during PULL streaming (e.g. a reader connection
|
|
309
|
+
# interrupted mid-stream) raises ServiceUnavailable straight from
|
|
310
|
+
# fetch_response, not via Result#on_failure — so it never saw the
|
|
311
|
+
# routing classifier. Run it through here so a routed connection
|
|
312
|
+
# failure surfaces as SessionExpired (idempotent if already classified).
|
|
313
|
+
raise @connection.classify_failure(e)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# consume is a no-op when the result was already drained by the user;
|
|
317
|
+
# surface any stored failure so callers can react.
|
|
318
|
+
@failed = true if result.failed?
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Recover a failed transaction by asking the server to RESET the
|
|
323
|
+
# connection. RESET transitions the server from FAILED back to READY
|
|
324
|
+
# and implicitly rolls back the open transaction.
|
|
325
|
+
def rollback_via_reset
|
|
326
|
+
# Skip RESET on a connection being discarded (auth failure: the
|
|
327
|
+
# server closes it, RESET would just error); release then honors
|
|
328
|
+
# the discard flag so it isn't pooled.
|
|
329
|
+
@connection.reset! unless @connection.auth_failed
|
|
330
|
+
@rolled_back = true
|
|
331
|
+
@open = false
|
|
332
|
+
release_connection
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def release_connection
|
|
336
|
+
@on_release&.call
|
|
337
|
+
@on_release = nil # idempotent
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Neo4j
|
|
4
|
+
module Driver
|
|
5
|
+
module Types
|
|
6
|
+
# Bolt Duration. Stores months, days, seconds, nanoseconds as
|
|
7
|
+
# distinct ints — Neo4j keeps these separate because months are
|
|
8
|
+
# variable-length (no fixed conversion to seconds).
|
|
9
|
+
class Duration < TemporalValue
|
|
10
|
+
attr_reader :months, :days, :seconds, :nanoseconds
|
|
11
|
+
|
|
12
|
+
def self.significant_fields = %i[months days seconds nanoseconds]
|
|
13
|
+
|
|
14
|
+
def initialize(months, days, seconds, nanoseconds)
|
|
15
|
+
@months = months.to_i
|
|
16
|
+
@days = days.to_i
|
|
17
|
+
@seconds = seconds.to_i
|
|
18
|
+
@nanoseconds = nanoseconds.to_i
|
|
19
|
+
normalize!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Parse ISO 8601 duration: "P1Y2M3DT4H5M6.789S", "P3M", "PT2H30M"
|
|
23
|
+
def self.parse(string)
|
|
24
|
+
unless string =~ /^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/
|
|
25
|
+
raise ArgumentError, "Invalid ISO 8601 duration format: #{string}"
|
|
26
|
+
end
|
|
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
|
|
34
|
+
|
|
35
|
+
total_months = years * 12 + months
|
|
36
|
+
whole_seconds = seconds_with_fraction.to_i
|
|
37
|
+
fractional_seconds = seconds_with_fraction - whole_seconds
|
|
38
|
+
nanos = (fractional_seconds * NANOS_PER_SECOND).round
|
|
39
|
+
total_seconds = hours * 3600 + minutes * 60 + whole_seconds
|
|
40
|
+
|
|
41
|
+
new(total_months, days, total_seconds, nanos)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def parts
|
|
45
|
+
{ months: @months, days: @days, seconds: @seconds, nanoseconds: @nanoseconds }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# ISO 8601 form. Nanoseconds get a 9-digit zero-padded suffix on
|
|
49
|
+
# seconds so 1s + 5ns prints as "PT1.000000005S", not "PT1.5S".
|
|
50
|
+
def to_s
|
|
51
|
+
if @nanoseconds.zero?
|
|
52
|
+
format('P%dM%dDT%dS', @months, @days, @seconds)
|
|
53
|
+
else
|
|
54
|
+
format('P%dM%dDT%d.%09dS', @months, @days, @seconds, @nanoseconds)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# Normalize nanoseconds overflow/underflow into seconds:
|
|
61
|
+
# -1ns becomes -1s + 999999999ns; 1.5e9 ns becomes 1s + 5e8 ns.
|
|
62
|
+
def normalize!
|
|
63
|
+
if @nanoseconds < 0
|
|
64
|
+
sec_adjust = (@nanoseconds.abs / NANOS_PER_SECOND.to_f).ceil
|
|
65
|
+
@seconds -= sec_adjust
|
|
66
|
+
@nanoseconds += sec_adjust * NANOS_PER_SECOND
|
|
67
|
+
elsif @nanoseconds >= NANOS_PER_SECOND
|
|
68
|
+
sec_adjust = @nanoseconds / NANOS_PER_SECOND
|
|
69
|
+
@seconds += sec_adjust
|
|
70
|
+
@nanoseconds %= NANOS_PER_SECOND
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -3,17 +3,61 @@
|
|
|
3
3
|
module Neo4j
|
|
4
4
|
module Driver
|
|
5
5
|
module Types
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
# Bolt LocalDateTime — wall-clock datetime without timezone.
|
|
7
|
+
# Stored as the wire-format pair (epoch_seconds, nanoseconds), where
|
|
8
|
+
# epoch_seconds is the wall-clock components encoded as if they were
|
|
9
|
+
# UTC. Two LocalDateTimes representing the same wall-clock value
|
|
10
|
+
# always have the same field values regardless of the host TZ.
|
|
11
|
+
class LocalDateTime < TemporalValue
|
|
12
|
+
PARSE_FORMATS = [
|
|
13
|
+
'%Y-%m-%d %H:%M:%S.%N',
|
|
14
|
+
'%Y-%m-%dT%H:%M:%S.%N',
|
|
15
|
+
'%Y-%m-%d %H:%M:%S',
|
|
16
|
+
'%Y-%m-%dT%H:%M:%S',
|
|
17
|
+
'%Y-%m-%d %H:%M'
|
|
18
|
+
].freeze
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
attr_reader :epoch_seconds, :nanoseconds
|
|
21
|
+
|
|
22
|
+
def self.significant_fields = %i[epoch_seconds nanoseconds]
|
|
23
|
+
|
|
24
|
+
def initialize(epoch_seconds, nanoseconds)
|
|
25
|
+
@epoch_seconds = epoch_seconds
|
|
26
|
+
@nanoseconds = nanoseconds
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.from_epoch(epoch_seconds, nanoseconds) = new(epoch_seconds, nanoseconds)
|
|
30
|
+
|
|
31
|
+
def self.from_time(time)
|
|
32
|
+
new(time.to_i, ((time.to_f - time.to_i) * NANOS_PER_SECOND).round)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.parse(string)
|
|
36
|
+
# Strip trailing timezone if present — naive datetime ignores it.
|
|
37
|
+
naive = string.sub(/[Z+\-]\d{2}:?\d{2}?$/, '')
|
|
38
|
+
time = PARSE_FORMATS.lazy.filter_map { |fmt| ::Time.strptime(naive, fmt) rescue nil }.first
|
|
39
|
+
raise ArgumentError, "Invalid LocalDateTime format: #{string}" unless time
|
|
40
|
+
from_time(::Time.utc(*time_components(time)))
|
|
13
41
|
end
|
|
14
42
|
|
|
15
|
-
|
|
16
|
-
|
|
43
|
+
def self.time_components(time)
|
|
44
|
+
[time.year, time.month, time.day, time.hour, time.min, time.sec, time.subsec * 1_000_000]
|
|
45
|
+
end
|
|
46
|
+
private_class_method :time_components
|
|
47
|
+
|
|
48
|
+
def to_time
|
|
49
|
+
::Time.at(@epoch_seconds, @nanoseconds.to_i, :nanosecond)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Add a numeric or ActiveSupport::Duration. Sub-second precision
|
|
53
|
+
# preserved by going through to_f.
|
|
54
|
+
def +(seconds)
|
|
55
|
+
self.class.from_time(to_time + seconds.to_f)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def to_s
|
|
59
|
+
to_time.strftime('%Y-%m-%d %H:%M:%S.%N')
|
|
60
|
+
end
|
|
17
61
|
end
|
|
18
62
|
end
|
|
19
63
|
end
|
|
@@ -3,16 +3,51 @@
|
|
|
3
3
|
module Neo4j
|
|
4
4
|
module Driver
|
|
5
5
|
module Types
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
# Bolt LocalTime — time of day without timezone, stored as
|
|
7
|
+
# nanoseconds since midnight.
|
|
8
|
+
class LocalTime < TemporalValue
|
|
9
|
+
attr_reader :nanoseconds
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
def self.significant_fields = %i[nanoseconds]
|
|
12
|
+
|
|
13
|
+
def initialize(nanoseconds)
|
|
14
|
+
@nanoseconds = nanoseconds % NANOS_PER_DAY
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.from_nanos(nanoseconds) = new(nanoseconds)
|
|
18
|
+
|
|
19
|
+
# Accepts "12:34:56.123456789" or any string containing such a
|
|
20
|
+
# time component (so a full datetime works too — we just take the
|
|
21
|
+
# time fields).
|
|
22
|
+
def self.parse(string)
|
|
23
|
+
match = string.match(/(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?/)
|
|
24
|
+
raise ArgumentError, "Invalid LocalTime format: #{string}" unless match
|
|
25
|
+
|
|
26
|
+
new(parse_nanos(match[1].to_i, match[2].to_i, (match[3] || 0).to_i, match[4] || '0'))
|
|
13
27
|
end
|
|
14
28
|
|
|
15
|
-
|
|
29
|
+
def self.parse_nanos(hour, minute, second, fraction)
|
|
30
|
+
hour * NANOS_PER_HOUR +
|
|
31
|
+
minute * NANOS_PER_MINUTE +
|
|
32
|
+
second * NANOS_PER_SECOND +
|
|
33
|
+
fraction.ljust(9, '0')[0..8].to_i
|
|
34
|
+
end
|
|
35
|
+
private_class_method :parse_nanos
|
|
36
|
+
|
|
37
|
+
def hour = (@nanoseconds / NANOS_PER_HOUR) % 24
|
|
38
|
+
def minute = (@nanoseconds / NANOS_PER_MINUTE) % 60
|
|
39
|
+
def second = (@nanoseconds / NANOS_PER_SECOND) % 60
|
|
40
|
+
def nanosecond = @nanoseconds % NANOS_PER_SECOND
|
|
41
|
+
|
|
42
|
+
# Add a numeric or ActiveSupport::Duration. Sub-second precision
|
|
43
|
+
# preserved by going through to_f (NOT to_i, which dropped 0.5s).
|
|
44
|
+
def +(seconds)
|
|
45
|
+
self.class.new(@nanoseconds + (seconds.to_f * NANOS_PER_SECOND).round)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_s
|
|
49
|
+
format('%02d:%02d:%02d.%09d', hour, minute, second, nanosecond)
|
|
50
|
+
end
|
|
16
51
|
end
|
|
17
52
|
end
|
|
18
53
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Neo4j
|
|
4
|
+
module Driver
|
|
5
|
+
module Types
|
|
6
|
+
# Represents a Node in the Neo4j graph
|
|
7
|
+
class Node
|
|
8
|
+
attr_reader :id, :labels, :properties, :element_id
|
|
9
|
+
|
|
10
|
+
def initialize(id, labels, properties, element_id = nil)
|
|
11
|
+
@id = id
|
|
12
|
+
@labels = labels
|
|
13
|
+
@properties = properties
|
|
14
|
+
@element_id = element_id || id.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def [](key)
|
|
18
|
+
@properties[key.to_s] || @properties[key.to_sym]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def ==(other)
|
|
22
|
+
other.is_a?(Node) && other.id == @id
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -3,16 +3,80 @@
|
|
|
3
3
|
module Neo4j
|
|
4
4
|
module Driver
|
|
5
5
|
module Types
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
# Bolt OffsetTime — time of day with a fixed UTC offset, stored as
|
|
7
|
+
# nanoseconds-since-midnight (in local wall clock) plus offset
|
|
8
|
+
# seconds. Equivalent to java.time.OffsetTime. Was previously
|
|
9
|
+
# called Types::Time, which collided cosmetically with ::Time.
|
|
10
|
+
class OffsetTime < TemporalValue
|
|
11
|
+
attr_reader :nanoseconds, :tz_offset_seconds
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
def self.significant_fields = %i[nanoseconds tz_offset_seconds]
|
|
14
|
+
|
|
15
|
+
def initialize(nanoseconds, tz_offset_seconds)
|
|
16
|
+
@nanoseconds = nanoseconds % NANOS_PER_DAY
|
|
17
|
+
@tz_offset_seconds = tz_offset_seconds
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.from_nanos(nanoseconds, tz_offset_seconds) = new(nanoseconds, tz_offset_seconds)
|
|
21
|
+
|
|
22
|
+
# Accepts "12:34:56.123456789+01:00", "12:34:56Z", or any string
|
|
23
|
+
# containing such a time-with-offset component.
|
|
24
|
+
def self.parse(string)
|
|
25
|
+
match = string.match(/(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?([Z+\-][\d:]*)?/)
|
|
26
|
+
raise ArgumentError, "Invalid OffsetTime format: #{string}" unless match
|
|
27
|
+
|
|
28
|
+
nanos = LocalTime.send(:parse_nanos,
|
|
29
|
+
match[1].to_i, match[2].to_i, (match[3] || 0).to_i, match[4] || '0')
|
|
30
|
+
new(nanos, parse_offset(match[5] || 'Z'))
|
|
13
31
|
end
|
|
14
32
|
|
|
15
|
-
|
|
33
|
+
def self.parse_offset(str)
|
|
34
|
+
return 0 if str == 'Z'
|
|
35
|
+
raise ArgumentError, "Invalid timezone offset: #{str}" unless str =~ /^([+\-])(\d{2}):?(\d{2})?$/
|
|
36
|
+
|
|
37
|
+
sign = $1 == '+' ? 1 : -1
|
|
38
|
+
sign * ($2.to_i * 3600 + ($3 || 0).to_i * 60)
|
|
39
|
+
end
|
|
40
|
+
private_class_method :parse_offset
|
|
41
|
+
|
|
42
|
+
def hour = (@nanoseconds / NANOS_PER_HOUR) % 24
|
|
43
|
+
def minute = (@nanoseconds / NANOS_PER_MINUTE) % 60
|
|
44
|
+
def second = (@nanoseconds / NANOS_PER_SECOND) % 60
|
|
45
|
+
def nanosecond = @nanoseconds % NANOS_PER_SECOND
|
|
46
|
+
|
|
47
|
+
# Add a numeric or ActiveSupport::Duration. Sub-second precision
|
|
48
|
+
# preserved (see LocalTime#+).
|
|
49
|
+
def +(seconds)
|
|
50
|
+
self.class.new(@nanoseconds + (seconds.to_f * NANOS_PER_SECOND).round, @tz_offset_seconds)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_s
|
|
54
|
+
# Apply the sign to the formatted offset as a whole, not to
|
|
55
|
+
# `hours` alone — Ruby integer division for `-12600 / 3600` is
|
|
56
|
+
# -4 (floor toward -∞), which would render -03:30 as -04:30.
|
|
57
|
+
abs = @tz_offset_seconds.abs
|
|
58
|
+
sign = @tz_offset_seconds.negative? ? '-' : '+'
|
|
59
|
+
format('%02d:%02d:%02d.%09d%s%02d:%02d',
|
|
60
|
+
hour, minute, second, nanosecond,
|
|
61
|
+
sign, abs / 3600, (abs % 3600) / 60)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Order by underlying UTC instant. Two OffsetTimes representing
|
|
65
|
+
# the same UTC instant in different offsets compare equal under
|
|
66
|
+
# <=>; == still requires same fields (they're not the same value).
|
|
67
|
+
def <=>(other)
|
|
68
|
+
return nil unless other.is_a?(OffsetTime)
|
|
69
|
+
utc_nanos <=> other.utc_nanos
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
protected
|
|
73
|
+
|
|
74
|
+
# UTC = local − offset. Positive offset means "ahead of UTC", so
|
|
75
|
+
# the UTC instant is *earlier* (smaller nanos). The previous
|
|
76
|
+
# implementation added instead of subtracting, inverting ordering.
|
|
77
|
+
def utc_nanos
|
|
78
|
+
@nanoseconds - (@tz_offset_seconds * NANOS_PER_SECOND)
|
|
79
|
+
end
|
|
16
80
|
end
|
|
17
81
|
end
|
|
18
82
|
end
|