File Coverage

lib/Neo4j/Driver/Session.pm
Criterion Covered Total %
statement 92 92 100.0
branch 22 22 100.0
condition 16 16 100.0
subroutine 26 26 100.0
pod 5 6 100.0
total 161 162 100.0


line stmt bran cond sub pod time code
1 20     20   239 use v5.14;
  20         77  
2 20     20   102 use warnings;
  20         26  
  20         1454  
3              
4             package Neo4j::Driver::Session 1.02;
5             # ABSTRACT: Context of work for database interactions
6              
7              
8 20     20   104 use Carp qw(croak);
  20         35  
  20         1615  
9             our @CARP_NOT = qw(
10             Neo4j::Driver
11             );
12 20     20   3676 use Feature::Compat::Try;
  20         2683  
  20         148  
13 20     20   3895 use List::Util qw(min);
  20         35  
  20         1304  
14 20     20   100 use Scalar::Util qw(blessed);
  20         27  
  20         864  
15 20     20   4851 use Time::HiRes ();
  20         12042  
  20         548  
16              
17 20     20   8572 use Neo4j::Driver::Net::Bolt;
  20         49  
  20         752  
18 20     20   12026 use Neo4j::Driver::Net::HTTP;
  20         74  
  20         1271  
19 20     20   10545 use Neo4j::Driver::Transaction;
  20         71  
  20         891  
20 20     20   177 use Neo4j::Error;
  20         43  
  20         23665  
21              
22              
23             sub new {
24             # uncoverable pod (private method)
25 185     185 0 529 my ($class, $driver) = @_;
26            
27 185 100       583 return Neo4j::Driver::Session::Bolt->new($driver) if $driver->config('uri')->scheme eq 'bolt';
28 170         3156 return Neo4j::Driver::Session::HTTP->new($driver);
29             }
30              
31              
32             # Connect and get ServerInfo (via Bolt HELLO or HTTP Discovery API),
33             # then determine the default database name for Neo4j >= 4.
34             sub _connect {
35 176     176   605 my ($self, $database) = @_;
36            
37 176         615 my $neo4j_version = $self->server->agent; # ensure contact with the server has been made
38 174 100       644 $self->{cypher_params_v2} = 0 if $neo4j_version =~ m{^Neo4j/2\.}; # no conversion required
39            
40 174   100     680 $database //= $self->server->_default_database($self->{driver});
41 172         839 $self->{net}->_set_database($database);
42 172         1296 return $self;
43             }
44              
45              
46             sub begin_transaction {
47 26     26 1 23696 my ($self) = @_;
48            
49 26         85 return $self->new_tx->_begin;
50             }
51              
52              
53             sub run {
54 210     210 1 367762 my ($self, $query, @parameters) = @_;
55            
56 210         812 return $self->new_tx->_run_autocommit($query, @parameters);
57             }
58              
59              
60             sub _execute {
61 37     37   86 my ($self, $mode, $func) = @_;
62            
63 37 100       151 croak sprintf "%s->execute_%s() requires subroutine ref", __PACKAGE__, lc $mode unless ref $func eq 'CODE';
64            
65 35   100     172 $self->{retry_sleep} //= 1;
66             my $time_stop = Time::HiRes::time
67 35   100     211 + ($self->{driver}->config('max_transaction_retry_time') // 30); # seconds
68 35         56 my $tries = 0;
69 35         46 while () {
70 41         153 my $tx = $self->new_tx($mode);
71 41     12   211 $tx->{error_handler} = sub { die shift };
  12         149  
72            
73 41         104 try {
74 41         134 $tx->_begin;
75 38         73 $tx->{managed} = 1; # Disallow commit() in $func
76 38 100       140 wantarray ? (my @r = $func->($tx)) : (my $r = $func->($tx));
77 17         67 $tx->{managed} = 0;
78 17         50 $tx->commit;
79            
80 17 100       111 return unless defined wantarray;
81 14 100       31 $r = $r[0] if wantarray;
82 14 100 100     92 warnings::warnif closure => "Result object may not be valid outside the transaction function"
83             if blessed $r && $r->isa('Neo4j::Driver::Result');
84            
85 14 100       469 return wantarray ? @r : $r;
86             }
87             catch ($e) {
88             # The tx may or may not already be closed; we need to make sure
89 24         7042 $tx->{managed} = 0;
90 24         49 try { $tx->rollback } catch ($f) {}
  24         91  
91            
92             # Never retry non-Neo4j errors
93 24 100 100     7527 croak $e unless blessed $e && $e->isa('Neo4j::Error');
94            
95 11 100 100     43 if (! $e->is_retryable || Time::HiRes::time >= $time_stop) {
96 5         243 $self->{driver}->{events}->trigger( error => $e );
97 1         32 return; # in case the event handler doesn't die
98             }
99             }
100            
101             Time::HiRes::sleep min
102 6         121437 $self->{retry_sleep} * (1 << $tries++),
103             $time_stop - Time::HiRes::time;
104             }
105             }
106              
107              
108             sub execute_read {
109 21     21 1 14625 my ($self, $func) = @_;
110            
111 21         106 return $self->_execute( READ => $func );
112             }
113              
114              
115             sub execute_write {
116 16     16 1 10202 my ($self, $func) = @_;
117            
118 16         53 return $self->_execute( WRITE => $func );
119             }
120              
121              
122             sub server {
123 263     263 1 5110 my ($self) = @_;
124            
125 263         762 my $server_info = $self->{driver}->{server_info};
126 263 100       1766 return $server_info if defined $server_info;
127 87         617 return $self->{driver}->{server_info} = $self->{net}->_server;
128             }
129              
130              
131              
132              
133             package # private
134             Neo4j::Driver::Session::Bolt;
135 20     20   177 use parent -norequire => 'Neo4j::Driver::Session';
  20         38  
  20         139  
136              
137              
138             sub new {
139 15     15   344 my ($class, $driver) = @_;
140            
141 15         47 return bless {
142             cypher_params_v2 => $driver->config('cypher_params'),
143             driver => $driver,
144             net => Neo4j::Driver::Net::Bolt->new($driver),
145             }, $class;
146             }
147              
148              
149             sub new_tx {
150 27     27   307 return Neo4j::Driver::Transaction::Bolt->new(@_);
151             }
152              
153              
154              
155              
156             package # private
157             Neo4j::Driver::Session::HTTP;
158 20     20   4594 use parent -norequire => 'Neo4j::Driver::Session';
  20         40  
  20         115  
159              
160              
161             sub new {
162 173     173   3199 my ($class, $driver) = @_;
163            
164 173         470 return bless {
165             cypher_params_v2 => $driver->config('cypher_params'),
166             driver => $driver,
167             net => Neo4j::Driver::Net::HTTP->new($driver),
168             }, $class;
169             }
170              
171              
172             sub new_tx {
173 250     250   1785 return Neo4j::Driver::Transaction::HTTP->new(@_);
174             }
175              
176              
177             1;
178              
179             __END__