neo4j-core 6.1.6 → 7.0.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +4 -9
- data/README.md +48 -0
- data/lib/neo4j-core.rb +23 -0
- data/lib/neo4j-core/helpers.rb +8 -0
- data/lib/neo4j-core/query.rb +23 -20
- data/lib/neo4j-core/query_clauses.rb +18 -32
- data/lib/neo4j-core/query_find_in_batches.rb +3 -1
- data/lib/neo4j-core/version.rb +1 -1
- data/lib/neo4j-embedded/cypher_response.rb +4 -0
- data/lib/neo4j-embedded/embedded_database.rb +3 -5
- data/lib/neo4j-embedded/embedded_node.rb +4 -4
- data/lib/neo4j-embedded/embedded_session.rb +21 -10
- data/lib/neo4j-embedded/embedded_transaction.rb +4 -10
- data/lib/neo4j-server/cypher_node.rb +5 -4
- data/lib/neo4j-server/cypher_relationship.rb +3 -3
- data/lib/neo4j-server/cypher_response.rb +4 -0
- data/lib/neo4j-server/cypher_session.rb +31 -22
- data/lib/neo4j-server/cypher_transaction.rb +23 -15
- data/lib/neo4j-server/resource.rb +3 -4
- data/lib/neo4j/core/cypher_session.rb +17 -9
- data/lib/neo4j/core/cypher_session/adaptors.rb +116 -33
- data/lib/neo4j/core/cypher_session/adaptors/bolt.rb +331 -0
- data/lib/neo4j/core/cypher_session/adaptors/bolt/chunk_writer_io.rb +76 -0
- data/lib/neo4j/core/cypher_session/adaptors/bolt/pack_stream.rb +288 -0
- data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +60 -29
- data/lib/neo4j/core/cypher_session/adaptors/has_uri.rb +63 -0
- data/lib/neo4j/core/cypher_session/adaptors/http.rb +123 -119
- data/lib/neo4j/core/cypher_session/responses.rb +17 -2
- data/lib/neo4j/core/cypher_session/responses/bolt.rb +135 -0
- data/lib/neo4j/core/cypher_session/responses/embedded.rb +46 -11
- data/lib/neo4j/core/cypher_session/responses/http.rb +49 -40
- data/lib/neo4j/core/cypher_session/transactions.rb +33 -0
- data/lib/neo4j/core/cypher_session/transactions/bolt.rb +36 -0
- data/lib/neo4j/core/cypher_session/transactions/embedded.rb +32 -0
- data/lib/neo4j/core/cypher_session/transactions/http.rb +52 -0
- data/lib/neo4j/core/instrumentable.rb +2 -2
- data/lib/neo4j/core/label.rb +182 -0
- data/lib/neo4j/core/node.rb +8 -3
- data/lib/neo4j/core/relationship.rb +12 -4
- data/lib/neo4j/entity_equality.rb +1 -1
- data/lib/neo4j/session.rb +4 -5
- data/lib/neo4j/transaction.rb +108 -72
- data/neo4j-core.gemspec +6 -6
- metadata +34 -40
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'neo4j/core/cypher_session/transactions'
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
module Core
|
5
|
+
class CypherSession
|
6
|
+
module Transactions
|
7
|
+
class Embedded < Base
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
@java_tx = adaptor.graph_db.begin_tx
|
11
|
+
end
|
12
|
+
|
13
|
+
def commit
|
14
|
+
return if !@java_tx
|
15
|
+
|
16
|
+
@java_tx.success
|
17
|
+
@java_tx.close
|
18
|
+
rescue Java::OrgNeo4jGraphdb::TransactionFailureException => e
|
19
|
+
raise CypherError, e.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete
|
23
|
+
return if !@java_tx
|
24
|
+
|
25
|
+
@java_tx.failure
|
26
|
+
@java_tx.close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'neo4j/core/cypher_session/transactions'
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
module Core
|
5
|
+
class CypherSession
|
6
|
+
module Transactions
|
7
|
+
class HTTP < Base
|
8
|
+
# Should perhaps have transaction adaptors only define #close
|
9
|
+
# commit/delete are, I think, an implementation detail
|
10
|
+
|
11
|
+
def commit
|
12
|
+
adaptor.requestor.request(:post, query_path(true)) if started?
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete
|
16
|
+
adaptor.requestor.request(:delete, query_path) if started?
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_path(commit = false)
|
20
|
+
if id
|
21
|
+
"/db/data/transaction/#{id}"
|
22
|
+
else
|
23
|
+
'/db/data/transaction'
|
24
|
+
end.tap do |path|
|
25
|
+
path << '/commit' if commit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Takes the transaction URL from Neo4j and parses out the ID
|
30
|
+
def apply_id_from_url!(url)
|
31
|
+
root.instance_variable_set('@id', url.match(%r{/(\d+)/?$})[1].to_i) if url
|
32
|
+
# @id = url.match(%r{/(\d+)/?$})[1].to_i if url
|
33
|
+
end
|
34
|
+
|
35
|
+
def started?
|
36
|
+
!!id
|
37
|
+
end
|
38
|
+
|
39
|
+
def id
|
40
|
+
root.instance_variable_get('@id')
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def connection
|
46
|
+
adaptor.connection
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -12,13 +12,13 @@ module Neo4j
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
-
def instrument(name, label, arguments
|
15
|
+
def instrument(name, label, arguments)
|
16
16
|
# defining class methods
|
17
17
|
klass = class << self; self; end
|
18
18
|
klass.instance_eval do
|
19
19
|
define_method("subscribe_to_#{name}") do |&b|
|
20
20
|
ActiveSupport::Notifications.subscribe(label) do |a, start, finish, id, payload|
|
21
|
-
b.call
|
21
|
+
b.call yield(a, start, finish, id, payload)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Core
|
3
|
+
class Label
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name, session)
|
7
|
+
@name = name
|
8
|
+
@session = session
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_index(property, options = {})
|
12
|
+
validate_index_options!(options)
|
13
|
+
properties = property.is_a?(Array) ? property.join(',') : property
|
14
|
+
schema_query("CREATE INDEX ON :`#{@name}`(#{properties})")
|
15
|
+
end
|
16
|
+
|
17
|
+
def drop_index(property, options = {})
|
18
|
+
validate_index_options!(options)
|
19
|
+
schema_query("DROP INDEX ON :`#{@name}`(#{property})")
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates a neo4j constraint on a property
|
23
|
+
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
24
|
+
# @example
|
25
|
+
# label = Neo4j::Label.create(:person, session)
|
26
|
+
# label.create_constraint(:name, {type: :unique}, session)
|
27
|
+
#
|
28
|
+
def create_constraint(property, constraints)
|
29
|
+
cypher = case constraints[:type]
|
30
|
+
when :unique, :uniqueness
|
31
|
+
"CREATE CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
|
32
|
+
else
|
33
|
+
fail "Not supported constraint #{constraints.inspect} for property #{property} (expected :type => :unique)"
|
34
|
+
end
|
35
|
+
schema_query(cypher)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_uniqueness_constraint(property, options = {})
|
39
|
+
create_constraint(property, options.merge(type: :unique))
|
40
|
+
end
|
41
|
+
|
42
|
+
# Drops a neo4j constraint on a property
|
43
|
+
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
44
|
+
# @example
|
45
|
+
# label = Neo4j::Label.create(:person, session)
|
46
|
+
# label.create_constraint(:name, {type: :unique}, session)
|
47
|
+
# label.drop_constraint(:name, {type: :unique}, session)
|
48
|
+
#
|
49
|
+
def drop_constraint(property, constraint)
|
50
|
+
cypher = case constraint[:type]
|
51
|
+
when :unique, :uniqueness
|
52
|
+
"DROP CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
|
53
|
+
else
|
54
|
+
fail "Not supported constraint #{constraint.inspect}"
|
55
|
+
end
|
56
|
+
schema_query(cypher)
|
57
|
+
end
|
58
|
+
|
59
|
+
def drop_uniqueness_constraint(property, options = {})
|
60
|
+
drop_constraint(property, options.merge(type: :unique))
|
61
|
+
end
|
62
|
+
|
63
|
+
def indexes
|
64
|
+
@session.indexes.select do |definition|
|
65
|
+
definition[:label] == @name.to_sym
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.indexes_for(session)
|
70
|
+
session.indexes
|
71
|
+
end
|
72
|
+
|
73
|
+
def drop_indexes
|
74
|
+
indexes.each do |definition|
|
75
|
+
begin
|
76
|
+
@session.query("DROP INDEX ON :`#{definition[:label]}`(#{definition[:properties][0]})")
|
77
|
+
rescue Neo4j::Server::CypherResponse::ResponseError
|
78
|
+
# This will error on each constraint. Ignore and continue.
|
79
|
+
next
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.drop_indexes_for(session)
|
85
|
+
indexes_for(session).each do |definition|
|
86
|
+
begin
|
87
|
+
session.query("DROP INDEX ON :`#{definition[:label]}`(#{definition[:properties][0]})")
|
88
|
+
rescue Neo4j::Server::CypherResponse::ResponseError
|
89
|
+
# This will error on each constraint. Ignore and continue.
|
90
|
+
next
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def index?(property)
|
96
|
+
indexes.any? { |definition| definition[:properties] == [property.to_sym] }
|
97
|
+
end
|
98
|
+
|
99
|
+
def constraints(_options = {})
|
100
|
+
@session.constraints.select do |definition|
|
101
|
+
definition[:label] == @name.to_sym
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def uniqueness_constraints(_options = {})
|
106
|
+
constraints.select do |definition|
|
107
|
+
definition[:type] == :uniqueness
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def drop_uniqueness_constraints
|
112
|
+
uniqueness_constraints.each do |definition|
|
113
|
+
@session.query("DROP CONSTRAINT ON (n:`#{definition[:label]}`) ASSERT n.`#{definition[:properties][0]}` IS UNIQUE")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.drop_uniqueness_constraints_for(session)
|
118
|
+
session.constraints.each do |definition|
|
119
|
+
session.query("DROP CONSTRAINT ON (n:`#{definition[:label]}`) ASSERT n.`#{definition[:properties][0]}` IS UNIQUE")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def constraint?(property)
|
124
|
+
constraints.any? { |definition| definition[:properties] == [property.to_sym] }
|
125
|
+
end
|
126
|
+
|
127
|
+
def uniqueness_constraint?(property)
|
128
|
+
uniqueness_constraints.include?([property])
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.wait_for_schema_changes(session)
|
132
|
+
schema_threads(session).map(&:join)
|
133
|
+
set_schema_threads(session, [])
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
# Store schema threads on the session so that we can easily wait for all
|
139
|
+
# threads on a session regardless of label
|
140
|
+
def schema_threads
|
141
|
+
self.class.schema_threads(@session)
|
142
|
+
end
|
143
|
+
|
144
|
+
def schema_threads=(array)
|
145
|
+
self.class.set_schema_threads(@session, array)
|
146
|
+
end
|
147
|
+
|
148
|
+
class << self
|
149
|
+
def schema_threads(session)
|
150
|
+
session.instance_variable_get('@_schema_threads') || []
|
151
|
+
end
|
152
|
+
|
153
|
+
def set_schema_threads(session, array)
|
154
|
+
session.instance_variable_set('@_schema_threads', array)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Schema queries can run separately from other queries, but they should
|
159
|
+
# be mutually exclusive to each other or we get locking errors
|
160
|
+
# SCHEMA_QUERY_SEMAPHORE = Mutex.new
|
161
|
+
|
162
|
+
# If there is a transaction going on, this could block
|
163
|
+
# So we run in a thread and it will go through at the next opportunity
|
164
|
+
def schema_query(cypher)
|
165
|
+
# Thread.new do
|
166
|
+
# SCHEMA_QUERY_SEMAPHORE.synchronize do
|
167
|
+
|
168
|
+
@session.transaction { |tx| tx.query(cypher, {}) }
|
169
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
170
|
+
puts 'ERROR during schema query:', e.message, e.backtrace
|
171
|
+
|
172
|
+
# end
|
173
|
+
# end.tap { |thread| schema_threads << thread }
|
174
|
+
end
|
175
|
+
|
176
|
+
def validate_index_options!(options)
|
177
|
+
return unless options[:type] && options[:type] != :exact
|
178
|
+
fail "Type #{options[:type]} is not supported"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/lib/neo4j/core/node.rb
CHANGED
@@ -1,20 +1,25 @@
|
|
1
1
|
require 'neo4j/core/wrappable'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
2
3
|
|
3
4
|
module Neo4j
|
4
5
|
module Core
|
5
6
|
class Node
|
6
7
|
attr_reader :id, :labels, :properties
|
7
|
-
|
8
|
+
alias props properties
|
8
9
|
|
9
10
|
include Wrappable
|
10
11
|
|
11
12
|
# Perhaps we should deprecate this?
|
12
|
-
|
13
|
+
alias neo_id id
|
13
14
|
|
14
15
|
def initialize(id, labels, properties = {})
|
15
16
|
@id = id
|
16
17
|
@labels = labels.map(&:to_sym) unless labels.nil?
|
17
|
-
@properties = properties
|
18
|
+
@properties = properties.symbolize_keys
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
other.is_a?(Node) && neo_id == other.neo_id
|
18
23
|
end
|
19
24
|
|
20
25
|
class << self
|
@@ -1,14 +1,22 @@
|
|
1
|
+
require 'neo4j/core/wrappable'
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
|
1
4
|
module Neo4j
|
2
5
|
module Core
|
3
6
|
class Relationship
|
4
|
-
attr_reader :id, :type, :properties
|
7
|
+
attr_reader :id, :type, :properties, :start_node_id, :end_node_id
|
8
|
+
alias props properties
|
9
|
+
alias neo_id id
|
10
|
+
alias rel_type type
|
5
11
|
|
6
12
|
include Wrappable
|
7
13
|
|
8
|
-
def initialize(id, type, properties)
|
14
|
+
def initialize(id, type, properties, start_node_id = nil, end_node_id = nil)
|
9
15
|
@id = id
|
10
16
|
@type = type.to_sym unless type.nil?
|
11
|
-
@properties = properties
|
17
|
+
@properties = properties.symbolize_keys
|
18
|
+
@start_node_id = start_node_id
|
19
|
+
@end_node_id = end_node_id
|
12
20
|
end
|
13
21
|
|
14
22
|
class << self
|
@@ -17,7 +25,7 @@ module Neo4j
|
|
17
25
|
type = nil # unknown
|
18
26
|
properties = properties
|
19
27
|
|
20
|
-
new(id, type, properties)
|
28
|
+
new(id, type, properties, nil, nil)
|
21
29
|
end
|
22
30
|
end
|
23
31
|
end
|
data/lib/neo4j/session.rb
CHANGED
@@ -36,11 +36,6 @@ module Neo4j
|
|
36
36
|
true # TODO
|
37
37
|
end
|
38
38
|
|
39
|
-
# @abstract
|
40
|
-
def begin_tx
|
41
|
-
fail 'not impl.'
|
42
|
-
end
|
43
|
-
|
44
39
|
class CypherError < StandardError
|
45
40
|
attr_reader :error_msg, :error_status, :error_code
|
46
41
|
def initialize(error_msg, error_code, error_status)
|
@@ -77,6 +72,10 @@ module Neo4j
|
|
77
72
|
fail 'not implemented'
|
78
73
|
end
|
79
74
|
|
75
|
+
def transaction_class
|
76
|
+
self.class.transaction_class
|
77
|
+
end
|
78
|
+
|
80
79
|
class << self
|
81
80
|
# Creates a new session to Neo4j.
|
82
81
|
# This will be the default session to be used unless there is already a session created (see #current and #set_current)
|
data/lib/neo4j/transaction.rb
CHANGED
@@ -1,39 +1,86 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
require 'active_support/per_thread_registry'
|
3
|
+
|
1
4
|
module Neo4j
|
2
5
|
module Transaction
|
3
6
|
extend self
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
# Provides a simple API to manage transactions for each session in a thread-safe manner
|
9
|
+
class TransactionsRegistry
|
10
|
+
extend ActiveSupport::PerThreadRegistry
|
11
|
+
|
12
|
+
attr_accessor :transactions_by_session_id
|
13
|
+
end
|
14
|
+
|
15
|
+
class Base
|
16
|
+
attr_reader :session, :root
|
17
|
+
|
18
|
+
def initialize(session, _options = {})
|
19
|
+
@session = session
|
20
|
+
|
21
|
+
Transaction.stack_for(session) << self
|
22
|
+
|
23
|
+
@root = Transaction.stack_for(session).first
|
24
|
+
# Neo4j::Core::Label::SCHEMA_QUERY_SEMAPHORE.lock if root?
|
25
|
+
|
26
|
+
# @parent = session_transaction_stack.last
|
27
|
+
# session_transaction_stack << self
|
10
28
|
end
|
11
29
|
|
12
|
-
|
13
|
-
|
14
|
-
|
30
|
+
def inspect
|
31
|
+
status_string = [:id, :failed?, :active?, :commit_url].map do |method|
|
32
|
+
"#{method}: #{send(method)}" if respond_to?(method)
|
33
|
+
end.compact.join(', ')
|
34
|
+
|
35
|
+
"<#{self.class} [#{status_string}]"
|
15
36
|
end
|
16
|
-
alias_method :failure, :mark_failed
|
17
37
|
|
18
|
-
#
|
19
|
-
def
|
20
|
-
|
38
|
+
# Commits or marks this transaction for rollback, depending on whether #mark_failed has been previously invoked.
|
39
|
+
def close
|
40
|
+
tx_stack = Transaction.stack_for(@session)
|
41
|
+
fail 'Tried closing when transaction stack is empty (maybe you closed too many?)' if tx_stack.empty?
|
42
|
+
fail "Closed transaction which wasn't the most recent on the stack (maybe you forgot to close one?)" if tx_stack.pop != self
|
43
|
+
|
44
|
+
@closed = true
|
45
|
+
|
46
|
+
post_close! if tx_stack.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def delete
|
50
|
+
fail 'not implemented'
|
51
|
+
end
|
52
|
+
|
53
|
+
def commit
|
54
|
+
fail 'not implemented'
|
21
55
|
end
|
22
|
-
alias_method :failure?, :failed?
|
23
56
|
|
24
57
|
def autoclosed!
|
25
58
|
@autoclosed = true if transient_failures_autoclose?
|
26
59
|
end
|
27
60
|
|
28
|
-
def
|
29
|
-
|
61
|
+
def closed?
|
62
|
+
!!@closed
|
30
63
|
end
|
31
64
|
|
32
|
-
|
33
|
-
|
65
|
+
# Marks this transaction as failed,
|
66
|
+
# which means that it will unconditionally be rolled back
|
67
|
+
# when #close is called.
|
68
|
+
# Aliased for legacy purposes.
|
69
|
+
def mark_failed
|
70
|
+
root.mark_failed if root && root != self
|
71
|
+
@failure = true
|
34
72
|
end
|
73
|
+
alias failure mark_failed
|
74
|
+
|
75
|
+
# If it has been marked as failed.
|
76
|
+
# Aliased for legacy purposes.
|
77
|
+
def failed?
|
78
|
+
!!@failure
|
79
|
+
end
|
80
|
+
alias failure? failed?
|
35
81
|
|
36
82
|
def mark_expired
|
83
|
+
@parent.mark_expired if @parent
|
37
84
|
@expired = true
|
38
85
|
end
|
39
86
|
|
@@ -41,43 +88,24 @@ module Neo4j
|
|
41
88
|
!!@expired
|
42
89
|
end
|
43
90
|
|
44
|
-
|
45
|
-
|
46
|
-
@pushed_nested += 1
|
91
|
+
def root?
|
92
|
+
@root == self
|
47
93
|
end
|
48
94
|
|
49
|
-
|
50
|
-
def pop_nested!
|
51
|
-
@pushed_nested -= 1
|
52
|
-
end
|
95
|
+
private
|
53
96
|
|
54
|
-
|
55
|
-
|
56
|
-
# See neo4j java docs.
|
57
|
-
# @param [Neo4j::Node,Neo4j::Relationship] entity
|
58
|
-
# @return [Java::OrgNeo4jKernelImplCoreapi::PropertyContainerLocker]
|
59
|
-
def acquire_read_lock(entity)
|
97
|
+
def transient_failures_autoclose?
|
98
|
+
Gem::Version.new(@session.version) >= Gem::Version.new('2.2.6')
|
60
99
|
end
|
61
100
|
|
62
|
-
|
63
|
-
|
64
|
-
# See neo4j java docs.
|
65
|
-
# @param [Neo4j::Node,Neo4j::Relationship] entity
|
66
|
-
# @return [Java::OrgNeo4jKernelImplCoreapi::PropertyContainerLocker]
|
67
|
-
def acquire_write_lock(entity)
|
101
|
+
def autoclosed?
|
102
|
+
!!@autoclosed
|
68
103
|
end
|
69
104
|
|
70
|
-
|
71
|
-
|
72
|
-
pop_nested!
|
73
|
-
return if @pushed_nested >= 0
|
74
|
-
fail "Can't commit transaction, already committed" if @pushed_nested < -1
|
75
|
-
Neo4j::Transaction.unregister(self)
|
76
|
-
post_close!
|
105
|
+
def active?
|
106
|
+
!closed?
|
77
107
|
end
|
78
108
|
|
79
|
-
private
|
80
|
-
|
81
109
|
def post_close!
|
82
110
|
return if autoclosed?
|
83
111
|
if failed?
|
@@ -89,56 +117,64 @@ module Neo4j
|
|
89
117
|
end
|
90
118
|
|
91
119
|
# @return [Neo4j::Transaction::Instance]
|
92
|
-
def new(
|
93
|
-
|
120
|
+
def new(session = Session.current!)
|
121
|
+
session.transaction
|
94
122
|
end
|
95
123
|
|
96
124
|
# Runs the given block in a new transaction.
|
97
125
|
# @param [Boolean] run_in_tx if true a new transaction will not be created, instead if will simply yield to the given block
|
98
126
|
# @@yield [Neo4j::Transaction::Instance]
|
99
|
-
def run(
|
127
|
+
def run(*args)
|
128
|
+
session, run_in_tx = session_and_run_in_tx_from_args(args)
|
129
|
+
|
100
130
|
fail ArgumentError, 'Expected a block to run in Transaction.run' unless block_given?
|
101
131
|
|
102
132
|
return yield(nil) unless run_in_tx
|
103
133
|
|
104
|
-
tx = Neo4j::Transaction.new
|
134
|
+
tx = Neo4j::Transaction.new(session)
|
105
135
|
yield tx
|
106
136
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
107
|
-
print_exception_cause(e)
|
137
|
+
# print_exception_cause(e)
|
138
|
+
|
108
139
|
tx.mark_failed unless tx.nil?
|
109
|
-
raise
|
140
|
+
raise e
|
110
141
|
ensure
|
111
142
|
tx.close unless tx.nil?
|
112
143
|
end
|
113
144
|
|
114
|
-
#
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
145
|
+
# To support old syntax of providing run_in_tx first
|
146
|
+
# But session first is ideal
|
147
|
+
def session_and_run_in_tx_from_args(args)
|
148
|
+
fail ArgumentError, 'Too many arguments' if args.size > 2
|
149
|
+
|
150
|
+
if args.empty?
|
151
|
+
[Session.current!, true]
|
152
|
+
else
|
153
|
+
result = args.dup
|
154
|
+
if result.size == 1
|
155
|
+
result << ([true, false].include?(args[0]) ? Session.current! : true)
|
156
|
+
end
|
122
157
|
|
123
|
-
|
124
|
-
|
158
|
+
[true, false].include?(result[0]) ? result.reverse : result
|
159
|
+
end
|
125
160
|
end
|
126
161
|
|
127
|
-
|
128
|
-
|
129
|
-
Thread.current[:neo4j_curr_tx] = nil if tx == Thread.current[:neo4j_curr_tx]
|
162
|
+
def current_for(session)
|
163
|
+
stack_for(session).first
|
130
164
|
end
|
131
165
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
fail 'Already running a transaction' if current
|
136
|
-
Thread.current[:neo4j_curr_tx] = tx
|
166
|
+
def stack_for(session)
|
167
|
+
TransactionsRegistry.transactions_by_session_id ||= {}
|
168
|
+
TransactionsRegistry.transactions_by_session_id[session.object_id] ||= []
|
137
169
|
end
|
138
170
|
|
139
|
-
|
140
|
-
|
141
|
-
|
171
|
+
private
|
172
|
+
|
173
|
+
def print_exception_cause(exception)
|
174
|
+
return if !exception.respond_to?(:cause) || !exception.cause.respond_to?(:print_stack_trace)
|
175
|
+
|
176
|
+
Core.logger.info "Java Exception in a transaction, cause: #{exception.cause}"
|
177
|
+
exception.cause.print_stack_trace
|
142
178
|
end
|
143
179
|
end
|
144
180
|
end
|