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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -9
  3. data/README.md +48 -0
  4. data/lib/neo4j-core.rb +23 -0
  5. data/lib/neo4j-core/helpers.rb +8 -0
  6. data/lib/neo4j-core/query.rb +23 -20
  7. data/lib/neo4j-core/query_clauses.rb +18 -32
  8. data/lib/neo4j-core/query_find_in_batches.rb +3 -1
  9. data/lib/neo4j-core/version.rb +1 -1
  10. data/lib/neo4j-embedded/cypher_response.rb +4 -0
  11. data/lib/neo4j-embedded/embedded_database.rb +3 -5
  12. data/lib/neo4j-embedded/embedded_node.rb +4 -4
  13. data/lib/neo4j-embedded/embedded_session.rb +21 -10
  14. data/lib/neo4j-embedded/embedded_transaction.rb +4 -10
  15. data/lib/neo4j-server/cypher_node.rb +5 -4
  16. data/lib/neo4j-server/cypher_relationship.rb +3 -3
  17. data/lib/neo4j-server/cypher_response.rb +4 -0
  18. data/lib/neo4j-server/cypher_session.rb +31 -22
  19. data/lib/neo4j-server/cypher_transaction.rb +23 -15
  20. data/lib/neo4j-server/resource.rb +3 -4
  21. data/lib/neo4j/core/cypher_session.rb +17 -9
  22. data/lib/neo4j/core/cypher_session/adaptors.rb +116 -33
  23. data/lib/neo4j/core/cypher_session/adaptors/bolt.rb +331 -0
  24. data/lib/neo4j/core/cypher_session/adaptors/bolt/chunk_writer_io.rb +76 -0
  25. data/lib/neo4j/core/cypher_session/adaptors/bolt/pack_stream.rb +288 -0
  26. data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +60 -29
  27. data/lib/neo4j/core/cypher_session/adaptors/has_uri.rb +63 -0
  28. data/lib/neo4j/core/cypher_session/adaptors/http.rb +123 -119
  29. data/lib/neo4j/core/cypher_session/responses.rb +17 -2
  30. data/lib/neo4j/core/cypher_session/responses/bolt.rb +135 -0
  31. data/lib/neo4j/core/cypher_session/responses/embedded.rb +46 -11
  32. data/lib/neo4j/core/cypher_session/responses/http.rb +49 -40
  33. data/lib/neo4j/core/cypher_session/transactions.rb +33 -0
  34. data/lib/neo4j/core/cypher_session/transactions/bolt.rb +36 -0
  35. data/lib/neo4j/core/cypher_session/transactions/embedded.rb +32 -0
  36. data/lib/neo4j/core/cypher_session/transactions/http.rb +52 -0
  37. data/lib/neo4j/core/instrumentable.rb +2 -2
  38. data/lib/neo4j/core/label.rb +182 -0
  39. data/lib/neo4j/core/node.rb +8 -3
  40. data/lib/neo4j/core/relationship.rb +12 -4
  41. data/lib/neo4j/entity_equality.rb +1 -1
  42. data/lib/neo4j/session.rb +4 -5
  43. data/lib/neo4j/transaction.rb +108 -72
  44. data/neo4j-core.gemspec +6 -6
  45. metadata +34 -40
@@ -1,12 +1,11 @@
1
1
  module Neo4j
2
2
  module Embedded
3
- class EmbeddedTransaction
3
+ class EmbeddedTransaction < Neo4j::Transaction::Base
4
4
  attr_reader :root_tx
5
- include Neo4j::Transaction::Instance
6
5
 
7
- def initialize(root_tx)
8
- @root_tx = root_tx
9
- register_instance
6
+ def initialize(session)
7
+ super
8
+ @root_tx = @session.begin_tx
10
9
  end
11
10
 
12
11
  def acquire_read_lock(entity)
@@ -17,11 +16,6 @@ module Neo4j
17
16
  @root_tx.acquire_write_lock(entity)
18
17
  end
19
18
 
20
-
21
- def inspect
22
- "EmbeddedTransaction [nested: #{@pushed_nested} failed?: #{failure?} active: #{Neo4j::Transaction.current == self}]"
23
- end
24
-
25
19
  def delete
26
20
  @root_tx.failure
27
21
  @root_tx.close
@@ -32,7 +32,7 @@ module Neo4j
32
32
  # (see Neo4j::Node#create_rel)
33
33
  def create_rel(type, other_node, props = nil)
34
34
  q = @session.query.match(:a, :b).where(a: {neo_id: neo_id}, b: {neo_id: other_node.neo_id})
35
- .create("(a)-[r:`#{type}`]->(b)").break.set(r: props).return(r: :neo_id)
35
+ .create("(a)-[r:`#{type}`]->(b)").break.set(r: props).return(r: :neo_id)
36
36
 
37
37
  id = @session._query_or_fail(q, true)
38
38
 
@@ -138,8 +138,8 @@ module Neo4j
138
138
  @session._query_or_fail(query, false)
139
139
  end
140
140
 
141
- alias_method :delete, :del
142
- alias_method :destroy, :del
141
+ alias delete del
142
+ alias destroy del
143
143
 
144
144
  # (see Neo4j::Node#exist?)
145
145
  def exist?
@@ -193,10 +193,11 @@ module Neo4j
193
193
 
194
194
  private
195
195
 
196
+ DEFAULT_RELATIONSHIP_ARROW_DIRECTION = :both
196
197
  def relationship_arrow(match)
197
198
  rel_spec = match[:type] ? "[r:`#{match[:type]}`]" : '[r]'
198
199
 
199
- case match[:dir] || :both
200
+ case match[:dir] || DEFAULT_RELATIONSHIP_ARROW_DIRECTION
200
201
  when :outgoing then "-#{rel_spec}->"
201
202
  when :incoming then "<-#{rel_spec}-"
202
203
  when :both then "-#{rel_spec}-"
@@ -19,7 +19,7 @@ module Neo4j
19
19
  def ==(other)
20
20
  other.class == self.class && other.neo_id == neo_id
21
21
  end
22
- alias_method :eql?, :==
22
+ alias eql? ==
23
23
 
24
24
  attr_reader :id
25
25
 
@@ -115,8 +115,8 @@ module Neo4j
115
115
  def del
116
116
  @session._query("#{match_start} DELETE n", neo_id: neo_id)
117
117
  end
118
- alias_method :delete, :del
119
- alias_method :destroy, :del
118
+ alias delete del
119
+ alias destroy del
120
120
 
121
121
  def exist?
122
122
  response = @session._query("#{match_start} RETURN n", neo_id: neo_id)
@@ -155,6 +155,10 @@ module Neo4j
155
155
  !!@error
156
156
  end
157
157
 
158
+ def raise_if_cypher_error!
159
+ raise_cypher_error if error?
160
+ end
161
+
158
162
  RETRYABLE_ERROR_STATUSES = %w(DeadlockDetectedException AcquireLockTimeoutException ExternalResourceFailureException UnknownFailureException)
159
163
  def retryable_error?
160
164
  return unless error?
@@ -9,7 +9,7 @@ module Neo4j
9
9
  class CypherSession < Neo4j::Session
10
10
  include Resource
11
11
 
12
- alias_method :super_query, :query
12
+ alias super_query query
13
13
  attr_reader :connection
14
14
 
15
15
  def initialize(data_url, connection)
@@ -27,7 +27,7 @@ module Neo4j
27
27
  conn = Faraday.new(url, init_params) do |b|
28
28
  b.request :basic_auth, params[:basic_auth][:username], params[:basic_auth][:password] if params[:basic_auth]
29
29
  b.request :multi_json
30
- # b.response :logger
30
+ # b.response :logger, ::Logger.new(STDOUT), bodies: true
31
31
 
32
32
  b.response :multi_json, symbolize_keys: true, content_type: 'application/json'
33
33
  # b.use Faraday::Response::RaiseError
@@ -90,14 +90,25 @@ module Neo4j
90
90
  init_resource_data(data_resource, data_url)
91
91
  end
92
92
 
93
- def close
94
- super
95
- Neo4j::Transaction.unregister_current
93
+ def self.transaction_class
94
+ Neo4j::Server::CypherTransaction
96
95
  end
97
96
 
98
- def begin_tx
99
- Neo4j::Transaction.current ? Neo4j::Transaction.current.push_nested! : wrap_resource(@connection)
100
- Neo4j::Transaction.current
97
+ # Duplicate of CypherSession::Adaptor::Base#transaction
98
+ def transaction
99
+ return self.class.transaction_class.new(self) if !block_given?
100
+
101
+ begin
102
+ tx = transaction
103
+
104
+ yield tx
105
+ rescue Exception => e # rubocop:disable Lint/RescueException
106
+ tx.mark_failed
107
+
108
+ raise e
109
+ ensure
110
+ tx.close
111
+ end
101
112
  end
102
113
 
103
114
  def create_node(props = nil, labels = [])
@@ -118,7 +129,7 @@ module Neo4j
118
129
  def load_relationship(neo_id)
119
130
  query.unwrapped.optional_match('(n)-[r]-()').where(r: {neo_id: neo_id}).pluck(:r).first
120
131
  rescue Neo4j::Session::CypherError => cypher_error
121
- return nil if cypher_error.message.match(/not found$/)
132
+ return nil if cypher_error.message =~ /not found$/
122
133
 
123
134
  raise cypher_error
124
135
  end
@@ -154,12 +165,9 @@ module Neo4j
154
165
 
155
166
  def query(*args)
156
167
  if [[String], [String, Hash]].include?(args.map(&:class))
157
- query = args[0]
158
- params = args[1]
159
-
160
- response = _query(query, params)
168
+ response = _query(*args)
161
169
  response.raise_error if response.error?
162
- response.to_node_enumeration(query)
170
+ response.to_node_enumeration(args[0])
163
171
  else
164
172
  options = args[0] || {}
165
173
  Neo4j::Core::Query.new(options.merge(session: self))
@@ -199,19 +207,16 @@ module Neo4j
199
207
  end
200
208
 
201
209
  def _query_entity_data(query, id = nil, params = {})
202
- _query(query, params).tap do |response|
203
- response.raise_error if response.error?
204
- end.entity_data(id)
210
+ _query(query, params).tap(&:raise_if_cypher_error!).entity_data(id)
205
211
  end
206
212
 
207
213
  def _query(query, params = {}, options = {})
208
214
  query, params = query_and_params(query, params)
209
215
 
210
- curr_tx = Neo4j::Transaction.current
211
216
  ActiveSupport::Notifications.instrument('neo4j.cypher_query', params: params, context: options[:context],
212
217
  cypher: query, pretty_cypher: options[:pretty_cypher]) do
213
- if curr_tx
214
- curr_tx._query(query, params)
218
+ if current_transaction
219
+ current_transaction._query(query, params)
215
220
  else
216
221
  query = params.nil? ? {'query' => query} : {'query' => query, 'params' => params}
217
222
  response = @connection.post(resource_url(:cypher), query)
@@ -225,7 +230,7 @@ module Neo4j
225
230
 
226
231
  Enumerator.new do |yielder|
227
232
  response.data.each do |data|
228
- if Neo4j::Transaction.current
233
+ if current_transaction
229
234
  data[:row].each do |id|
230
235
  yielder << CypherNode.new(self, id).wrapper
231
236
  end
@@ -236,12 +241,16 @@ module Neo4j
236
241
  end
237
242
  end
238
243
 
244
+ def current_transaction
245
+ Neo4j::Transaction.current_for(self)
246
+ end
247
+
239
248
  EMPTY = ''
240
249
  NEWLINE_W_SPACES = "\n "
241
250
  def self.log_with
242
251
  ActiveSupport::Notifications.subscribe('neo4j.cypher_query') do |_, start, finish, _id, payload|
243
252
  ms = (finish - start) * 1000
244
- params_string = (payload[:params] && payload[:params].size > 0 ? "| #{payload[:params].inspect}" : EMPTY)
253
+ params_string = (payload[:params] && !payload[:params].empty? ? "| #{payload[:params].inspect}" : EMPTY)
245
254
  cypher = payload[:pretty_cypher] ? NEWLINE_W_SPACES + payload[:pretty_cypher].gsub(/\n/, NEWLINE_W_SPACES) : payload[:cypher]
246
255
 
247
256
  yield(" #{ANSI::CYAN}#{payload[:context] || 'CYPHER'}#{ANSI::CLEAR} #{ANSI::YELLOW}#{ms.round}ms#{ANSI::CLEAR} #{cypher} #{params_string}")
@@ -7,16 +7,20 @@ module Neo4j
7
7
  # * `close` is called to end the transaction. It calls `commit` or `delete`.
8
8
  #
9
9
  # If a transaction is created and then closed without performing any queries, an OpenStruct is returned that behaves like a successfully closed query.
10
- class CypherTransaction
11
- include Neo4j::Transaction::Instance
10
+ class CypherTransaction < Neo4j::Transaction::Base
12
11
  include Resource
13
12
 
14
- attr_reader :commit_url, :query_url, :base_url, :connection
13
+ attr_reader :commit_url, :query_url
15
14
 
16
- def initialize(url, session_connection)
17
- @base_url = url
18
- @connection = session_connection
19
- register_instance
15
+ def connection
16
+ @session.connection
17
+ end
18
+
19
+ def base_url
20
+ require 'uri'
21
+ URI(@session.instance_variable_get('@resource_url')).tap do |uri|
22
+ uri.path = ''
23
+ end.to_s
20
24
  end
21
25
 
22
26
  ROW_REST = %w(row REST)
@@ -31,28 +35,34 @@ module Neo4j
31
35
  end
32
36
 
33
37
  def start(body)
34
- request(:post, @base_url, 201, body).tap do |response|
38
+ request(:post, start_url, 201, body).tap do |response|
35
39
  @commit_url = response.body[:commit]
36
40
  @query_url = response.headers[:Location]
37
41
 
38
42
  fail "NO ENDPOINT URL #{connection} : HEAD: #{response.headers.inspect}" if !@query_url || @query_url.empty?
39
43
 
40
- init_resource_data(response.body, @base_url)
44
+ init_resource_data(response.body, base_url)
41
45
  end
42
46
  end
43
47
 
48
+ def start_url
49
+ @session.resource_data.fetch(:transaction) || base_url
50
+ end
51
+
44
52
  def query(body)
45
53
  request(:post, @query_url, 200, body)
46
54
  end
47
55
 
56
+ EMPTY_RESPONSE = OpenStruct.new(status: 200, body: '')
57
+
48
58
  def delete
49
- return empty_response if !@commit_url || expired?
59
+ return EMPTY_RESPONSE if !@commit_url || expired?
50
60
 
51
61
  request(:delete, @query_url, 200, nil, resource_headers)
52
62
  end
53
63
 
54
64
  def commit
55
- return empty_response if !@commit_url || expired?
65
+ return EMPTY_RESPONSE if !@commit_url || expired?
56
66
 
57
67
  request(:post, @commit_url, 200, nil, resource_headers)
58
68
  end
@@ -66,11 +76,9 @@ module Neo4j
66
76
  end
67
77
 
68
78
  def create_cypher_response(response)
69
- first_result = response.body[:results][0]
70
-
71
79
  CypherResponse.new(response, true).tap do |cypher_response|
72
80
  if response.body[:errors].empty?
73
- cypher_response.set_data(first_result)
81
+ cypher_response.set_data(response.body[:results][0])
74
82
  else
75
83
  first_error = response.body[:errors].first
76
84
  tx_cleanup!(first_error)
@@ -81,7 +89,7 @@ module Neo4j
81
89
 
82
90
  def tx_cleanup!(first_error)
83
91
  autoclosed!
84
- mark_expired if first_error[:message].match(/Unrecognized transaction id/)
92
+ mark_expired if first_error[:message] =~ /Unrecognized transaction id/
85
93
  end
86
94
 
87
95
  def empty_response
@@ -16,14 +16,13 @@ module Neo4j
16
16
  self
17
17
  end
18
18
 
19
- def wrap_resource(connection = Neo4j::Session.current)
20
- url = resource_url(:transaction)
21
- CypherTransaction.new(url, connection)
19
+ def wrap_resource(connection)
20
+ CypherTransaction.new(resource_url(:transaction), connection)
22
21
  end
23
22
 
24
23
  def resource_url(key = nil)
25
24
  return @resource_url if key.nil?
26
- @resource_data.fetch key
25
+ resource_data.fetch key
27
26
  rescue KeyError
28
27
  raise "No resource key '#{key}', available #{@resource_data.keys.inspect}"
29
28
  end
@@ -1,7 +1,8 @@
1
-
2
1
  module Neo4j
3
2
  module Core
4
3
  class CypherSession
4
+ attr_reader :adaptor
5
+
5
6
  def initialize(adaptor)
6
7
  fail ArgumentError, "Invalid adaptor: #{adaptor.inspect}" if !adaptor.is_a?(Adaptors::Base)
7
8
 
@@ -10,22 +11,29 @@ module Neo4j
10
11
  @adaptor.connect
11
12
  end
12
13
 
14
+ def transaction_class
15
+ Neo4j::Core::CypherSession::Transactions::Base
16
+ end
17
+
18
+ %w(
19
+ version
20
+ ).each do |method, &_block|
21
+ define_method(method) do |*args, &block|
22
+ @adaptor.send(method, *args, &block)
23
+ end
24
+ end
25
+
13
26
  %w(
14
27
  query
15
28
  queries
16
29
 
17
- start_transaction
18
- end_transaction
19
30
  transaction
20
- transaction_started?
21
31
 
22
- version
23
-
24
- indexes_for_label
25
- uniqueness_constraints_for_label
32
+ indexes
33
+ constraints
26
34
  ).each do |method, &_block|
27
35
  define_method(method) do |*args, &block|
28
- @adaptor.send(method, *args, &block)
36
+ @adaptor.send(method, self, *args, &block)
29
37
  end
30
38
  end
31
39
  end
@@ -1,19 +1,61 @@
1
1
  require 'neo4j/core/cypher_session'
2
2
  require 'neo4j/core/instrumentable'
3
+ require 'neo4j/core/label'
3
4
 
4
5
  module Neo4j
5
6
  module Core
6
7
  class CypherSession
8
+ class CypherError < StandardError
9
+ attr_reader :code, :message, :stack_trace
10
+
11
+ def self.new_from(code, message, stack_trace = nil)
12
+ @code = code
13
+ @message = message
14
+ @stack_trace = stack_trace
15
+
16
+ msg = <<-ERROR
17
+ Cypher error:
18
+ #{ANSI::CYAN}#{code}#{ANSI::CLEAR}: #{message}
19
+ #{stack_trace}
20
+ ERROR
21
+
22
+ error_class_from(code).new(msg)
23
+ end
24
+
25
+ def self.error_class_from(code)
26
+ case code
27
+ when /(ConstraintValidationFailed|ConstraintViolation)/
28
+ SchemaErrors::ConstraintValidationFailedError
29
+ else
30
+ CypherError
31
+ end
32
+ end
33
+ end
34
+ module SchemaErrors
35
+ class ConstraintValidationFailedError < CypherError; end
36
+ end
37
+
7
38
  module Adaptors
8
39
  MAP = {}
9
40
 
10
41
  class Base
11
42
  include Neo4j::Core::Instrumentable
12
43
 
44
+ gem, version = if defined?(::Neo4j::ActiveNode)
45
+ ['neo4j', ::Neo4j::VERSION]
46
+ else
47
+ ['neo4j-core', ::Neo4j::Core::VERSION]
48
+ end
49
+
50
+
51
+ USER_AGENT_STRING = "#{gem}-gem/#{version} (https://github.com/neo4jrb/#{gem})"
52
+
13
53
  def connect(*_args)
14
54
  fail '#connect not implemented!'
15
55
  end
16
56
 
57
+ attr_accessor :wrap_level
58
+
17
59
  Query = Struct.new(:cypher, :parameters, :pretty_cypher, :context)
18
60
 
19
61
  class QueryBuilder
@@ -43,57 +85,71 @@ module Neo4j
43
85
  end
44
86
  end
45
87
 
46
- def query(*args)
47
- queries { append(*args) }[0]
88
+ def query(session, *args)
89
+ options = (args.size == 3 || (args[0].is_a?(::Neo4j::Core::Query) && args.size == 2)) ? args.pop : {}
90
+
91
+ queries(session, options) { append(*args) }[0]
48
92
  end
49
93
 
50
- def queries(&block)
94
+ def queries(session, options = {}, &block)
51
95
  query_builder = QueryBuilder.new
52
96
 
53
97
  query_builder.instance_eval(&block)
54
98
 
55
- query_set(query_builder.queries)
99
+ new_or_current_transaction(session, options[:transaction]) do |tx|
100
+ query_set(tx, query_builder.queries, {commit: !options[:transaction]}.merge(options))
101
+ end
56
102
  end
57
103
 
58
- def query_set(_queries)
59
- fail '#queries not implemented!'
104
+ [:query_set,
105
+ :version,
106
+ :indexes,
107
+ :constraints,
108
+ :connected?].each do |method|
109
+ define_method(method) do |*_args|
110
+ fail "##{method} method not implemented on adaptor!"
111
+ end
60
112
  end
61
113
 
62
- def start_transaction(*_args)
63
- fail '#start_transaction not implemented!'
64
- end
114
+ # If called without a block, returns a Transaction object
115
+ # which can be used to call query/queries/mark_failed/commit
116
+ # If called with a block, the Transaction object is yielded
117
+ # to the block and `commit` is ensured. Any uncaught exceptions
118
+ # will mark the transaction as failed first
119
+ def transaction(session)
120
+ return self.class.transaction_class.new(session) if !block_given?
65
121
 
66
- def end_transaction(*_args)
67
- fail '#end_transaction not implemented!'
68
- end
122
+ begin
123
+ tx = transaction(session)
69
124
 
70
- def transaction_started?(*_args)
71
- fail '#transaction_started? not implemented!'
72
- end
125
+ yield tx
126
+ rescue => e
127
+ tx.mark_failed
73
128
 
74
- def version(*_args)
75
- fail '#version not implemented!'
129
+ raise e
130
+ ensure
131
+ tx.close
132
+ end
76
133
  end
77
134
 
78
- # Schema inspection methods
79
- def indexes_for_label(*_args)
80
- fail '#indexes_for_label not implemented!'
81
- end
135
+ def logger
136
+ return @logger if @logger
82
137
 
83
- def uniqueness_constraints_for_label(*_args)
84
- fail '#uniqueness_constraints_for_label not implemented!'
138
+ @logger = if @options[:logger]
139
+ @options[:logger]
140
+ else
141
+ Logger.new(logger_location).tap do |logger|
142
+ logger.level = logger_level
143
+ end
144
+ end
85
145
  end
86
146
 
147
+ def setup_queries!(queries, transaction, options = {})
148
+ fail 'Query attempted without a connection' if !connected?
149
+ fail "Invalid transaction object: #{transaction.inspect}" if !transaction.is_a?(self.class.transaction_class)
87
150
 
88
- # Uses #start_transaction and #end_transaction to allow
89
- # execution of queries within a block to be part of a
90
- # full transaction
91
- def transaction
92
- start_transaction
93
-
94
- yield
95
- ensure
96
- end_transaction if transaction_started?
151
+ # context option not yet implemented
152
+ self.class.instrument_queries(queries) unless options[:skip_instrumentation]
97
153
  end
98
154
 
99
155
  EMPTY = ''
@@ -101,7 +157,7 @@ module Neo4j
101
157
 
102
158
  instrument(:query, 'neo4j.core.cypher_query', %w(query)) do |_, _start, _finish, _id, payload|
103
159
  query = payload[:query]
104
- params_string = (query.parameters && query.parameters.size > 0 ? "| #{query.parameters.inspect}" : EMPTY)
160
+ params_string = (query.parameters && !query.parameters.empty? ? "| #{query.parameters.inspect}" : EMPTY)
105
161
  cypher = query.pretty_cypher ? NEWLINE_W_SPACES + query.pretty_cypher.gsub(/\n/, NEWLINE_W_SPACES) : query.cypher
106
162
 
107
163
  " #{ANSI::CYAN}#{query.context || 'CYPHER'}#{ANSI::CLEAR} #{cypher} #{params_string}"
@@ -113,6 +169,33 @@ module Neo4j
113
169
  instrument_query(query) {}
114
170
  end
115
171
  end
172
+
173
+ def transaction_class
174
+ fail '.transaction_class method not implemented on adaptor!'
175
+ end
176
+ end
177
+
178
+ private
179
+
180
+ def new_or_current_transaction(session, tx, &block)
181
+ if tx
182
+ yield(tx)
183
+ else
184
+ transaction(session, &block)
185
+ end
186
+ end
187
+
188
+ def validate_query_set!(transaction, _queries, _options = {})
189
+ fail 'Query attempted without a connection' if !connected?
190
+ fail "Invalid transaction object: #{transaction}" if !transaction.is_a?(self.class.transaction_class)
191
+ end
192
+
193
+ def logger_location
194
+ @options[:logger_location] || STDOUT
195
+ end
196
+
197
+ def logger_level
198
+ @options[:logger_level] || Logger::WARN
116
199
  end
117
200
  end
118
201
  end