neo4j-ruby-driver 6.0.3.alpha.0 → 6.2.1.beta.2
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 +1091 -0
- data/lib/neo4j/driver/bolt/handshake.rb +236 -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 +143 -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 +262 -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/no_such_record_exception.rb +1 -1
- 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 -39
- 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 +16 -2
- data/lib/neo4j/driver/net.rb +29 -0
- data/lib/neo4j/driver/packstream/markers.rb +49 -0
- data/lib/neo4j/driver/packstream/packer.rb +335 -0
- data/lib/neo4j/driver/packstream/structure.rb +27 -0
- data/lib/neo4j/driver/packstream/unpacker.rb +170 -0
- data/lib/neo4j/driver/record.rb +43 -0
- data/lib/neo4j/driver/result.rb +282 -0
- data/lib/neo4j/driver/routing/load_balancer.rb +706 -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 +577 -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 +215 -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 +344 -0
- data/lib/neo4j/driver/types/duration.rb +76 -0
- data/lib/neo4j/driver/types/entity.rb +40 -0
- data/lib/neo4j/driver/types/local_date_time.rb +77 -8
- data/lib/neo4j/driver/types/local_time.rb +50 -7
- data/lib/neo4j/driver/types/node.rb +18 -0
- data/lib/neo4j/driver/types/offset_time.rb +78 -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 +32 -0
- data/lib/neo4j/driver/types/temporal_value.rb +44 -0
- data/lib/neo4j/driver/types/unbound_relationship.rb +24 -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 +15 -0
- data/lib/neo4j-ruby-driver_loader.rb +34 -20
- metadata +115 -441
- 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,344 @@
|
|
|
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
|
+
if terminated?
|
|
92
|
+
raise Exceptions::TransactionTerminatedException,
|
|
93
|
+
'Cannot run more queries in this transaction, it has been rolled back'
|
|
94
|
+
end
|
|
95
|
+
unless @open
|
|
96
|
+
# Mirror the Java/JRuby messages so the closed-state reason
|
|
97
|
+
# (committed vs rolled back) is reported the same on both impls.
|
|
98
|
+
raise Exceptions::ClientException,
|
|
99
|
+
"Cannot run more queries in this transaction, it has been #{@committed ? 'committed' : 'rolled back'}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# A new query becomes current; the previous result must now name its qid
|
|
103
|
+
# explicitly on further PULL/DISCARD (the server defaults them to the last
|
|
104
|
+
# opened query). We keep it open and streaming rather than buffering it —
|
|
105
|
+
# that's the qid multiplexing the nested-result tests exercise.
|
|
106
|
+
@current_result&.demote!
|
|
107
|
+
|
|
108
|
+
fetch_size = effective_fetch_size
|
|
109
|
+
|
|
110
|
+
# send/flush are inside the begin block so a transport-level
|
|
111
|
+
# failure surfacing on fetch_response still sets @failed and
|
|
112
|
+
# goes through classify_failure. Connection#send_message and
|
|
113
|
+
# #flush both defer peer-closed errors so a buffered server
|
|
114
|
+
# FAILURE is read before fetch_response can raise its own
|
|
115
|
+
# EOF-driven ServiceUnavailableException — JRuby surfaces
|
|
116
|
+
# EPIPE eagerly, MRI tends to defer it naturally. Without
|
|
117
|
+
# this rescue placement, session.close's subsequent rollback
|
|
118
|
+
# path takes the ROLLBACK-message branch (rather than
|
|
119
|
+
# rollback_via_reset) and re-fails on the dead connection.
|
|
120
|
+
buffer = Bolt::RecordBuffer.new(fetch_size: fetch_size)
|
|
121
|
+
handler = Bolt::StreamHandler.new(buffer)
|
|
122
|
+
run_response =
|
|
123
|
+
begin
|
|
124
|
+
@connection.send_message(@connection.protocol.build_run(query, parameters, {}))
|
|
125
|
+
@connection.send_message(@connection.protocol.build_pull(n: fetch_size), handler)
|
|
126
|
+
@connection.flush
|
|
127
|
+
# Drain the pipelined BEGIN (+telemetry) reply now that RUN+PULL are on
|
|
128
|
+
# the wire — no-op unless this is the pipelined first run. A BEGIN that
|
|
129
|
+
# failed surfaces here and is handled as a run failure below; the tx
|
|
130
|
+
# then rolls back on its way out, resetting the connection.
|
|
131
|
+
ack_begin!
|
|
132
|
+
@connection.fetch_response.assert_success!
|
|
133
|
+
rescue Exceptions::Neo4jException => e
|
|
134
|
+
@failed = true
|
|
135
|
+
# Remember the terminating error: sibling results still open must
|
|
136
|
+
# raise it (not pull) once this RUN fails — the connection is FAILED.
|
|
137
|
+
raise(@terminating_error = @connection.classify_failure(e))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
keys = (run_response.metadata[:fields] || run_response.metadata['fields'] || []).map(&:to_sym)
|
|
141
|
+
|
|
142
|
+
@current_result = Result.new(@connection, keys, buffer: buffer, handler: handler,
|
|
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))
|
|
147
|
+
@open_results << @current_result
|
|
148
|
+
@current_result
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def commit
|
|
152
|
+
# Java/JRuby-aligned messages for the already-closed states.
|
|
153
|
+
raise Exceptions::ClientException, 'Can\'t commit, transaction has been committed' if @committed
|
|
154
|
+
raise Exceptions::ClientException, 'Can\'t commit, transaction has been rolled back' if @rolled_back
|
|
155
|
+
raise Exceptions::ClientException, 'Transaction is already closed' unless @open
|
|
156
|
+
|
|
157
|
+
if terminated?
|
|
158
|
+
rollback_via_reset
|
|
159
|
+
raise Exceptions::TransactionTerminatedException,
|
|
160
|
+
"Transaction can't be committed. It has been rolled back"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
begin
|
|
164
|
+
discard_open_results
|
|
165
|
+
rescue Exceptions::Neo4jException
|
|
166
|
+
rollback_via_reset
|
|
167
|
+
raise
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# send/flush inside the begin block — see Transaction#run for
|
|
171
|
+
# the rationale on JRuby vs MRI socket-write timing.
|
|
172
|
+
response =
|
|
173
|
+
begin
|
|
174
|
+
@connection.send_message(Bolt::Message.commit)
|
|
175
|
+
@connection.flush
|
|
176
|
+
@connection.fetch_response.assert_success!
|
|
177
|
+
rescue Exceptions::Neo4jException => e
|
|
178
|
+
@failed = true
|
|
179
|
+
# Classify first so a security failure flags the connection
|
|
180
|
+
# for discard before rollback_via_reset releases it.
|
|
181
|
+
classified = @connection.classify_failure(e)
|
|
182
|
+
rollback_via_reset
|
|
183
|
+
raise classified
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
@committed = true
|
|
187
|
+
@open = false
|
|
188
|
+
|
|
189
|
+
bookmarks = response.metadata[:bookmark]
|
|
190
|
+
@session.update_bookmarks(bookmarks) if bookmarks
|
|
191
|
+
release_connection
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def rollback
|
|
195
|
+
raise Exceptions::ClientException, 'Transaction is already closed' unless @open
|
|
196
|
+
|
|
197
|
+
# A pipelined executeQuery tx whose query never ran (e.g. local validation
|
|
198
|
+
# failed before RUN/PULL were sent) left BEGIN's reply unread — and a
|
|
199
|
+
# pipelining server may withhold it until RUN/PULL arrive, which now never
|
|
200
|
+
# will. RESET rolls the tx back and drains any pending reply without a
|
|
201
|
+
# blocking read that could deadlock; there are no open results to discard.
|
|
202
|
+
return rollback_via_reset unless @begin_acked
|
|
203
|
+
|
|
204
|
+
# A terminated tx left the connection FAILED: don't drain open results
|
|
205
|
+
# (that would send PULL/DISCARD the server rejects) — just RESET.
|
|
206
|
+
if terminated?
|
|
207
|
+
rollback_via_reset
|
|
208
|
+
return
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
begin
|
|
212
|
+
discard_open_results
|
|
213
|
+
rescue Exceptions::Neo4jException
|
|
214
|
+
# Failures surfaced while draining a pending result are expected
|
|
215
|
+
# during rollback — the tx is being discarded anyway. @failed is
|
|
216
|
+
# set; RESET path below will clean up the connection.
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
if @failed
|
|
220
|
+
rollback_via_reset
|
|
221
|
+
return
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
begin
|
|
225
|
+
@connection.send_message(Bolt::Message.rollback)
|
|
226
|
+
@connection.flush
|
|
227
|
+
@connection.fetch_response.assert_success!
|
|
228
|
+
rescue Exceptions::ServiceUnavailableException, Exceptions::SessionExpiredException
|
|
229
|
+
# Rolling back on a broken/dead connection is a no-op — the
|
|
230
|
+
# server discards the tx when the link dies. Swallow these so
|
|
231
|
+
# session.close's rollback path stays clean.
|
|
232
|
+
rescue Exceptions::Neo4jException => e
|
|
233
|
+
# A server FAILURE on ROLLBACK (e.g. DatabaseUnavailable) is a
|
|
234
|
+
# real error: the connection is now in FAILED state. RESET it
|
|
235
|
+
# back to READY, then surface the failure through the routing
|
|
236
|
+
# classifier — same as commit/run — so routing side effects
|
|
237
|
+
# (e.g. deactivate on DatabaseUnavailable) fire and the surfaced
|
|
238
|
+
# type is consistent. No-op for direct connections.
|
|
239
|
+
@connection.reset!
|
|
240
|
+
raise @connection.classify_failure(e)
|
|
241
|
+
ensure
|
|
242
|
+
@rolled_back = true
|
|
243
|
+
@open = false
|
|
244
|
+
release_connection
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def close
|
|
249
|
+
rollback if @open && !@committed
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def open?
|
|
253
|
+
@open
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def failed?
|
|
257
|
+
@failed
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
private
|
|
261
|
+
|
|
262
|
+
# Read the deferred BEGIN acknowledgement (and the telemetry SUCCESS
|
|
263
|
+
# pipelined ahead of it). Idempotent: called once — eagerly in #initialize
|
|
264
|
+
# for a normal tx, or after the first RUN+PULL flush for a pipelined
|
|
265
|
+
# executeQuery (and defensively before ROLLBACK if that query never ran).
|
|
266
|
+
def ack_begin!
|
|
267
|
+
return if @begin_acked
|
|
268
|
+
|
|
269
|
+
@begin_acked = true
|
|
270
|
+
|
|
271
|
+
if @telemetry_sent
|
|
272
|
+
@connection.fetch_response.assert_success!
|
|
273
|
+
# The server acknowledged telemetry; a managed-tx retry won't re-send it.
|
|
274
|
+
@telemetry_ack&.call
|
|
275
|
+
end
|
|
276
|
+
begin_response = @connection.fetch_response.assert_success!
|
|
277
|
+
# A home-db BEGIN that sent db=nil comes back with the resolved name.
|
|
278
|
+
@on_begin&.call(begin_response)
|
|
279
|
+
begin_response
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# The transaction is terminated once a server failure has hit it — either
|
|
283
|
+
# a tx method caught it (@failed) or any open result failed during the
|
|
284
|
+
# user's own iteration (its failure hasn't passed through a tx method).
|
|
285
|
+
def terminated? = @failed || @open_results.any?(&:failed?)
|
|
286
|
+
|
|
287
|
+
# The error that terminated this tx, or nil. A RUN/commit failure records
|
|
288
|
+
# it directly; a result that failed mid-iteration (the user's own PULL)
|
|
289
|
+
# carries it on the result. Passed to each result as its terminated_error
|
|
290
|
+
# so a sibling raises it instead of pulling on a FAILED connection.
|
|
291
|
+
def terminating_error = @terminating_error || @open_results.find(&:failed?)&.failure
|
|
292
|
+
|
|
293
|
+
# See Session#effective_fetch_size. Transactions inherit the session
|
|
294
|
+
# options at open, so the same default rules apply.
|
|
295
|
+
def effective_fetch_size
|
|
296
|
+
size = @options[:fetch_size]
|
|
297
|
+
size.nil? ? 1000 : size
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# At tx end (commit/rollback) every still-open result goes out of scope,
|
|
301
|
+
# so discard each — DISCARD abandons remaining records (essential when a
|
|
302
|
+
# result is unbounded) rather than streaming them into memory, and leaves
|
|
303
|
+
# each raising ResultConsumedException on later access. Demoted results
|
|
304
|
+
# DISCARD by their qid; the current one omits it (targets the last query).
|
|
305
|
+
def discard_open_results
|
|
306
|
+
@open_results.each do |result|
|
|
307
|
+
begin
|
|
308
|
+
result.consume
|
|
309
|
+
rescue Exceptions::Neo4jException => e
|
|
310
|
+
@failed = true
|
|
311
|
+
# A wire error during PULL streaming (e.g. a reader connection
|
|
312
|
+
# interrupted mid-stream) raises ServiceUnavailable straight from
|
|
313
|
+
# fetch_response, not via Result#on_failure — so it never saw the
|
|
314
|
+
# routing classifier. Run it through here so a routed connection
|
|
315
|
+
# failure surfaces as SessionExpired (idempotent if already classified).
|
|
316
|
+
raise @connection.classify_failure(e)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# consume is a no-op when the result was already drained by the user;
|
|
320
|
+
# surface any stored failure so callers can react.
|
|
321
|
+
@failed = true if result.failed?
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Recover a failed transaction by asking the server to RESET the
|
|
326
|
+
# connection. RESET transitions the server from FAILED back to READY
|
|
327
|
+
# and implicitly rolls back the open transaction.
|
|
328
|
+
def rollback_via_reset
|
|
329
|
+
# Skip RESET on a connection being discarded (auth failure: the
|
|
330
|
+
# server closes it, RESET would just error); release then honors
|
|
331
|
+
# the discard flag so it isn't pooled.
|
|
332
|
+
@connection.reset! unless @connection.auth_failed
|
|
333
|
+
@rolled_back = true
|
|
334
|
+
@open = false
|
|
335
|
+
release_connection
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def release_connection
|
|
339
|
+
@on_release&.call
|
|
340
|
+
@on_release = nil # idempotent
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
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 = (::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
|
+
|
|
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
|
|
@@ -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
|
|
@@ -3,17 +3,86 @@
|
|
|
3
3
|
module Neo4j
|
|
4
4
|
module Driver
|
|
5
5
|
module Types
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
# Bolt LocalDateTime — a wall-clock datetime with no timezone
|
|
7
|
+
# (java.time.LocalDateTime). Stored as the wire-format pair
|
|
8
|
+
# (epoch_seconds, nanoseconds), where epoch_seconds encodes the
|
|
9
|
+
# wall-clock components as if they were UTC — so the value reads the
|
|
10
|
+
# same regardless of the host TZ.
|
|
11
|
+
#
|
|
12
|
+
# There is deliberately no #to_time: a zone-less value cannot become a
|
|
13
|
+
# zoned Ruby Time without inventing a zone, and every serious datetime
|
|
14
|
+
# library refuses that implicit conversion (Java's LocalDateTime forces
|
|
15
|
+
# .atZone/.atOffset; Python yields a naive datetime). Read the fields via
|
|
16
|
+
# the component getters instead.
|
|
17
|
+
class LocalDateTime < TemporalValue
|
|
18
|
+
PARSE_FORMATS = [
|
|
19
|
+
'%Y-%m-%d %H:%M:%S.%N',
|
|
20
|
+
'%Y-%m-%dT%H:%M:%S.%N',
|
|
21
|
+
'%Y-%m-%d %H:%M:%S',
|
|
22
|
+
'%Y-%m-%dT%H:%M:%S',
|
|
23
|
+
'%Y-%m-%d %H:%M'
|
|
24
|
+
].freeze
|
|
9
25
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
26
|
+
attr_reader :epoch_seconds, :nanoseconds
|
|
27
|
+
|
|
28
|
+
def self.significant_fields = %i[epoch_seconds nanoseconds]
|
|
29
|
+
|
|
30
|
+
def initialize(epoch_seconds, nanoseconds)
|
|
31
|
+
@epoch_seconds = epoch_seconds
|
|
32
|
+
@nanoseconds = nanoseconds
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.from_epoch(epoch_seconds, nanoseconds) = new(epoch_seconds, nanoseconds)
|
|
36
|
+
|
|
37
|
+
# Store the Time's wall clock (its date and time-of-day *in its own
|
|
38
|
+
# zone*), dropping the zone. We read that wall clock off the instant
|
|
39
|
+
# using the offset the Time already carries — we never invent a zone,
|
|
40
|
+
# so from_time(t) and from_time(t.utc) differ (they show different
|
|
41
|
+
# clocks). epoch_seconds encodes it as-if-UTC per the class contract.
|
|
42
|
+
def self.from_time(time)
|
|
43
|
+
new(time.to_i + time.utc_offset, time.nsec)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.parse(string)
|
|
47
|
+
# Strip trailing timezone if present — naive datetime ignores it.
|
|
48
|
+
naive = string.sub(/[Z+-]\d{2}:?\d{2}?$/, '')
|
|
49
|
+
time = PARSE_FORMATS.lazy.filter_map do |fmt|
|
|
50
|
+
::Time.strptime(naive, fmt)
|
|
51
|
+
rescue StandardError
|
|
52
|
+
nil
|
|
53
|
+
end.first
|
|
54
|
+
raise ArgumentError, "Invalid LocalDateTime format: #{string}" unless time
|
|
55
|
+
|
|
56
|
+
from_time(time)
|
|
13
57
|
end
|
|
14
58
|
|
|
15
|
-
|
|
16
|
-
|
|
59
|
+
def year = wall_clock.year
|
|
60
|
+
def month = wall_clock.month
|
|
61
|
+
def day = wall_clock.day
|
|
62
|
+
def hour = wall_clock.hour
|
|
63
|
+
def minute = wall_clock.min
|
|
64
|
+
def second = wall_clock.sec
|
|
65
|
+
def nanosecond = @nanoseconds
|
|
66
|
+
|
|
67
|
+
# Add a numeric or ActiveSupport::Duration. A wall clock has no DST,
|
|
68
|
+
# so this is plain second arithmetic; sub-second precision preserved.
|
|
69
|
+
def +(other)
|
|
70
|
+
total = (@epoch_seconds * NANOS_PER_SECOND) + @nanoseconds +
|
|
71
|
+
(other.to_f * NANOS_PER_SECOND).round
|
|
72
|
+
self.class.new(total / NANOS_PER_SECOND, total % NANOS_PER_SECOND)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def to_s
|
|
76
|
+
wall_clock.strftime('%Y-%m-%d %H:%M:%S.%N')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
# epoch_seconds read back as its wall clock. UTC by construction, so
|
|
82
|
+
# the components never depend on the host TZ.
|
|
83
|
+
def wall_clock
|
|
84
|
+
::Time.at(@epoch_seconds, @nanoseconds.to_i, :nanosecond, in: 'UTC')
|
|
85
|
+
end
|
|
17
86
|
end
|
|
18
87
|
end
|
|
19
88
|
end
|
|
@@ -3,16 +3,59 @@
|
|
|
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
|
+
# Build from a Ruby Time, taking its displayed wall clock (so the
|
|
20
|
+
# host zone / .utc of the Time decides which time-of-day is captured).
|
|
21
|
+
# Mirrors LocalDateTime.from_time.
|
|
22
|
+
def self.from_time(time)
|
|
23
|
+
new(time.hour * NANOS_PER_HOUR + time.min * NANOS_PER_MINUTE +
|
|
24
|
+
time.sec * NANOS_PER_SECOND + time.nsec)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Accepts "12:34:56.123456789" or any string containing such a
|
|
28
|
+
# time component (so a full datetime works too — we just take the
|
|
29
|
+
# time fields).
|
|
30
|
+
def self.parse(string)
|
|
31
|
+
match = string.match(/(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?/)
|
|
32
|
+
raise ArgumentError, "Invalid LocalTime format: #{string}" unless match
|
|
33
|
+
|
|
34
|
+
new(parse_nanos(match[1].to_i, match[2].to_i, (match[3] || 0).to_i, match[4] || '0'))
|
|
13
35
|
end
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
def self.parse_nanos(hour, minute, second, fraction)
|
|
38
|
+
hour * NANOS_PER_HOUR +
|
|
39
|
+
minute * NANOS_PER_MINUTE +
|
|
40
|
+
second * NANOS_PER_SECOND +
|
|
41
|
+
fraction.ljust(9, '0')[0..8].to_i
|
|
42
|
+
end
|
|
43
|
+
private_class_method :parse_nanos
|
|
44
|
+
|
|
45
|
+
def hour = (@nanoseconds / NANOS_PER_HOUR) % 24
|
|
46
|
+
def minute = (@nanoseconds / NANOS_PER_MINUTE) % 60
|
|
47
|
+
def second = (@nanoseconds / NANOS_PER_SECOND) % 60
|
|
48
|
+
def nanosecond = @nanoseconds % NANOS_PER_SECOND
|
|
49
|
+
|
|
50
|
+
# Add a numeric or ActiveSupport::Duration. Sub-second precision
|
|
51
|
+
# preserved by going through to_f (NOT to_i, which dropped 0.5s).
|
|
52
|
+
def +(other)
|
|
53
|
+
self.class.new(@nanoseconds + (other.to_f * NANOS_PER_SECOND).round)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_s
|
|
57
|
+
format('%02d:%02d:%02d.%09d', hour, minute, second, nanosecond)
|
|
58
|
+
end
|
|
16
59
|
end
|
|
17
60
|
end
|
|
18
61
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Neo4j
|
|
4
|
+
module Driver
|
|
5
|
+
module Types
|
|
6
|
+
# Represents a Node in the Neo4j graph. Mirrors
|
|
7
|
+
# org.neo4j.driver.types.Node — an Entity with a set of labels.
|
|
8
|
+
class Node < Entity
|
|
9
|
+
attr_reader :labels
|
|
10
|
+
|
|
11
|
+
def initialize(id, labels, properties, element_id = nil)
|
|
12
|
+
super(id, properties, element_id)
|
|
13
|
+
@labels = labels
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|