em-pg-client-helper 1.3.0 → 2.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f507ec3c80d6ed2c58575898e2132bfa3e05ac36
4
- data.tar.gz: 5c311e9a44d3274689c4c6f12dfc028a3e721328
3
+ metadata.gz: 54910da339d3b8be844ddddb6b373dbec7ca04b5
4
+ data.tar.gz: efb8474eb8d13f6020116b4923dfea5f4793059c
5
5
  SHA512:
6
- metadata.gz: 6b39ee8f0cdce4de503b1793ca26f13b0bd493cd53b80390a95a65018252af5d30c837f733013fb07678a8aab32caa62fceab8d0f6a26b8c70494df3185c2173
7
- data.tar.gz: 0af1627ff7f3bac097818c5a91f5c71467f32aadfb18fef17dd939db9e360e1a20a8559a68421b5ae24f0d74153907ca9af62c3d3d5347ebf18fda5662239cf0
6
+ metadata.gz: c39f44d77f9238e333d22fd9ed3a6970f46c45a783d3414f733baa61a912479524032c962fdacd0e085e56f38c0615d3dba3e30fcbe66198168ab657a1c113ba
7
+ data.tar.gz: 012405e9483038a357080c994b20ce3ce0793379e557b28f04331a34d70a07a2871afeadc98f1c6303d5d3397073fb61db0ff7b4a4c8826f8fff2cb7aee82055
@@ -76,10 +76,10 @@ module PG::EM::Client::Helper
76
76
  sqls = sqldb.sqls
77
77
 
78
78
  if sqls.empty?
79
- sqls = [ret.sql] rescue []
79
+ sqls = [ret.sql] rescue ret
80
80
  end
81
81
 
82
- if sqls.empty?
82
+ if sqls.nil? or sqls.empty?
83
83
  raise PG::EM::Client::Helper::BadSequelError,
84
84
  "Your block did not generate an SQL statement"
85
85
  end
@@ -195,6 +195,58 @@ module PG::EM::Client::Helper
195
195
  db.exec_defer(*insert_sql(tbl, params))
196
196
  end
197
197
 
198
+ # Efficiently perform a "bulk" insert of multiple rows.
199
+ #
200
+ # When you have a large quantity of data to insert into a table, you don't
201
+ # want to do it one row at a time -- that's *really* inefficient. On the
202
+ # other hand, if you do one giant multi-row insert statement, the insert
203
+ # will fail if *any* of the rows causes a constraint failure. What to do?
204
+ #
205
+ # Well, here's our answer: try to insert all the records at once. If that
206
+ # fails with a constraint violation, then split the set of records in half
207
+ # and try to bulk insert each of those halves. Recurse in this fashion until
208
+ # you only have one record to insert.
209
+ #
210
+ # @param db [PG::EM::Client, PG::EM::ConnectionPool] the connection against
211
+ # which the insert wil be run.
212
+ #
213
+ # @param tbl [#to_sym] the name of the table into which you wish to insert
214
+ # your data.
215
+ #
216
+ # @param columns [Array<#to_sym>] the columns into which each record of data
217
+ # will be inserted.
218
+ #
219
+ # @param rows [Array<Array<Object>>] the values to insert. Each entry in
220
+ # the outermost array is a row of data; the elements of each of these inner
221
+ # arrays corresponds to the column in the same position in the `columns`
222
+ # array. **NOTE**: we don't do any checking to make sure you're giving
223
+ # us the correct list of values for each row. Thus, if you give us a
224
+ # row array that has too few, or too many, entries, the database will puke.
225
+ #
226
+ # @return [EM::Deferrable] the deferrable in which the query is being called;
227
+ # once the bulk insert completes successfully, the deferrable will succeed
228
+ # with the number of rows that were successfully inserted. If the insert
229
+ # could not be completed, the deferrable will fail (`#errback`) with the
230
+ # exception.
231
+ #
232
+ # @since 2.0.0
233
+ #
234
+ def db_bulk_insert(db, tbl, columns, rows, &blk)
235
+ EM::Completion.new.tap do |df|
236
+ df.callback(&blk) if blk
237
+
238
+ db_transaction(db) do |txn|
239
+ txn.bulk_insert(tbl, columns, rows) do |count|
240
+ txn.commit do
241
+ df.succeed(count)
242
+ end
243
+ end
244
+ end.errback do |ex|
245
+ df.fail(ex)
246
+ end
247
+ end
248
+ end
249
+
198
250
  # @!macro upsert_params
199
251
  #
200
252
  # @param tbl [#to_s] The name of the table on which to operate.
@@ -7,6 +7,13 @@
7
7
  class PG::EM::Client::Helper::DeferrableGroup
8
8
  include ::EventMachine::Deferrable
9
9
 
10
+ # Raised when an attempt is made to add a deferrable to a group on
11
+ # which {#close} has already been called.
12
+ #
13
+ # @since 2.0.0
14
+ #
15
+ class ClosedError < StandardError; end
16
+
10
17
  # Create a new deferrable group.
11
18
  #
12
19
  def initialize
@@ -23,15 +30,15 @@ class PG::EM::Client::Helper::DeferrableGroup
23
30
  #
24
31
  # @return [EM::Deferrable] the same deferrable.
25
32
  #
26
- # @raise [RuntimeError] if you attempt to add a deferrable after the
27
- # group has been closed (that is, the `#close` method has been called),
28
- # indicating that the deferrable group doesn't have any more deferrables
29
- # to add.
33
+ # @raise [PG::EM::Client::Helper::DeferrableGroup::ClosedError] if you
34
+ # attempt to add a deferrable after the group has been closed (that is,
35
+ # the `#close` method has been called), indicating that the deferrable
36
+ # group doesn't have any more deferrables to add.
30
37
  #
31
38
  def add(df)
32
39
  if @closed
33
- raise RuntimeError,
34
- "This deferrable group is closed."
40
+ raise ClosedError,
41
+ "This deferrable group is closed"
35
42
  end
36
43
 
37
44
  @outstanding << df
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  # Represents a database transaction, and contains all of the methods which
2
4
  # can be used to execute queries within the transaction connection.
3
5
  #
@@ -5,6 +7,13 @@ class PG::EM::Client::Helper::Transaction
5
7
  include ::PG::EM::Client::Helper
6
8
  include ::EventMachine::Deferrable
7
9
 
10
+ # Raised when you attempt to execute a query in a transaction which has
11
+ # been finished (either by COMMIT or ROLLBACK).
12
+ #
13
+ # @since 2.0.0
14
+ #
15
+ class ClosedError < StandardError; end
16
+
8
17
  # Create a new transaction. You shouldn't have to call this yourself;
9
18
  # `db_transaction` should create one and pass it to your block.
10
19
  #
@@ -21,12 +30,12 @@ class PG::EM::Client::Helper::Transaction
21
30
  # @raise [ArgumentError] If an unknown isolation level is specified.
22
31
  #
23
32
  def initialize(conn, opts = {}, &blk)
24
- @conn = conn
25
- @opts = opts
26
- # This can be `nil` if the txn is in progress, or it will be
27
- # true or false to indicate success/failure of the txn
28
- @committed = nil
29
- @retryable = opts[:retry]
33
+ @conn = conn
34
+ @opts = opts
35
+ @finished = nil
36
+ @retryable = opts[:retry]
37
+ @autorollback_on_error = true
38
+ @savepoint_stack = []
30
39
 
31
40
  DeferrableGroup.new do |dg|
32
41
  @dg = dg
@@ -59,7 +68,7 @@ class PG::EM::Client::Helper::Transaction
59
68
  end
60
69
  end
61
70
  end.callback do
62
- rollback(RuntimeError.new("txn.commit was not called")) unless @committed
71
+ rollback(RuntimeError.new("txn.commit was not called")) unless @finished
63
72
  self.succeed
64
73
  end.errback do |ex|
65
74
  if @retryable and [PG::TRSerializationFailure].include?(ex.class)
@@ -81,16 +90,16 @@ class PG::EM::Client::Helper::Transaction
81
90
  # exception will be raised.
82
91
  #
83
92
  def commit
84
- if @committed.nil?
93
+ unless @finished
85
94
  trace_query("COMMIT")
86
95
  @conn.exec_defer("COMMIT", []).tap do |df|
87
96
  @dg.add(df)
88
97
  end.callback do
89
- @committed = true
98
+ @finished = true
90
99
  @dg.close
91
- end.errback do |ex|
92
- @committed = false
93
- @dg.fail(ex)
100
+ yield if block_given?
101
+ end.errback do
102
+ @finished = true
94
103
  @dg.close
95
104
  end
96
105
  end
@@ -102,15 +111,81 @@ class PG::EM::Client::Helper::Transaction
102
111
  # event of a database error or other exception.
103
112
  #
104
113
  def rollback(ex)
105
- if @committed.nil?
106
- exec("ROLLBACK") do
107
- @committed = false
108
- @dg.fail(ex)
114
+ unless @finished
115
+ if @savepoint_stack.empty?
116
+ exec("ROLLBACK") do
117
+ @finished = true
118
+ @dg.fail(ex)
119
+ @dg.close
120
+ end
121
+ else
122
+ sp = @savepoint_stack.pop
123
+ exec("ROLLBACK TO \"#{sp[:savepoint]}\"")
124
+ sp[:deferrable].fail(ex)
109
125
  @dg.close
126
+ @dg = sp[:parent_deferrable_group]
110
127
  end
111
128
  end
112
129
  end
113
130
 
131
+ # Manage the "rollback on the failure of a single query" behaviour.
132
+ #
133
+ # The default behaviour of a transaction, when a query fails, is for
134
+ # the transaction to automatically be rolled back and the rest of the
135
+ # statements to not be executed. In **ALMOST** every case, this is the
136
+ # correct behaviour. However, there are some corner cases in which you
137
+ # want to be able to avoid this behaviour, and will manually react to
138
+ # the transaction failure in some way. In that case, you can set this
139
+ # to `false` and the transaction will not automatically fail.
140
+ #
141
+ # Given that pretty much the only thing you can do when a query fails,
142
+ # other than abort the transaction, is to rollback to a savepoint, you
143
+ # might want to look at {#savepoint} before you try using this.
144
+ #
145
+ # @since 2.0.0
146
+ #
147
+ attr_accessor :autorollback_on_error
148
+
149
+ # Setup a "savepoint" within the transaction.
150
+ #
151
+ # A savepoint is, as the name suggests, kinda like a "saved game", in an
152
+ # SQL transaction. If a query fails within a transaction, normally all
153
+ # you can do is rollback and abort the entire transaction. Savepoints
154
+ # give you another option: roll back to the savepoint, and try again.
155
+ #
156
+ # So, that's what this method does. Inside of the block passed to
157
+ # `#savepoint`, if any query fails, instead of rolling back the entire
158
+ # transaction, we instead only rollback to the savepoint, and execution
159
+ # continues by executing the `errback` callbacks defined on the savepoint
160
+ # deferrable.
161
+ #
162
+ # @return [EM::Deferrable]
163
+ #
164
+ # @since 2.0.0
165
+ #
166
+ def savepoint(&blk)
167
+ savepoint = SecureRandom.uuid
168
+ parent_dg = @dg
169
+ DeferrableGroup.new do |dg|
170
+ @dg = dg
171
+
172
+ dg.callback do
173
+ @dg = parent_dg
174
+ @dg.close
175
+ end
176
+
177
+ exec("SAVEPOINT \"#{savepoint}\"").tap do |df|
178
+ @savepoint_stack << { :savepoint => savepoint,
179
+ :deferrable => df,
180
+ :parent_deferrable_group => parent_dg
181
+ }
182
+
183
+ df.callback(&blk) if blk
184
+ end
185
+ end
186
+ end
187
+
188
+
114
189
  # Generate SQL statements via Sequel, and run the result against the
115
190
  # database. Very chic.
116
191
  #
@@ -131,6 +206,67 @@ class PG::EM::Client::Helper::Transaction
131
206
  exec(*insert_sql(tbl, params), &blk)
132
207
  end
133
208
 
209
+ # Efficiently perform a "bulk" insert of multiple rows.
210
+ #
211
+ # When you have a large quantity of data to insert into a table, you don't
212
+ # want to do it one row at a time -- that's *really* inefficient. On the
213
+ # other hand, if you do one giant multi-row insert statement, the insert
214
+ # will fail if *any* of the rows causes a constraint failure. What to do?
215
+ #
216
+ # Well, here's our answer: try to insert all the records at once. If that
217
+ # fails with a constraint violation, then split the set of records in half
218
+ # and try to bulk insert each of those halves. Recurse in this fashion until
219
+ # you only have one record to insert.
220
+ #
221
+ # @param tbl [#to_sym] the name of the table into which you wish to insert
222
+ # your data.
223
+ #
224
+ # @param columns [Array<#to_sym>] the columns into which each record of data
225
+ # will be inserted.
226
+ #
227
+ # @param rows [Array<Array<Object>>] the values to insert. Each entry in
228
+ # the outermost array is a row of data; the elements of each of these inner
229
+ # arrays corresponds to the column in the same position in the `columns`
230
+ # array. **NOTE**: we don't do any checking to make sure you're giving
231
+ # us the correct list of values for each row. Thus, if you give us a
232
+ # row array that has too few, or too many, entries, the database will puke.
233
+ #
234
+ # @yield [Integer] Once the insert has completed, the number of rows that
235
+ # were successfully inserted (that may be less than `rows.length` if
236
+ # there were any constraint failures) will be yielded to the block.
237
+ #
238
+ # @since 2.0.0
239
+ #
240
+ def bulk_insert(tbl, columns, rows, &blk)
241
+ db = Sequel.connect("mock://postgres")
242
+
243
+ # Guh hand-hacked SQL is fugly... but what I'm doing is so utterly
244
+ # niche that Sequel doesn't support it.
245
+ q_tbl = db.literal(tbl.to_sym)
246
+ q_cols = columns.map { |c| db.literal(c.to_sym) }
247
+
248
+ subselect = "SELECT 1 FROM #{q_tbl} AS dst WHERE " +
249
+ q_cols.map { |c| "src.#{c}=dst.#{c}" }.join(" AND ")
250
+
251
+ total_rows_inserted = 0
252
+ DeferrableGroup.new.tap do |dg|
253
+ rows.each_slice(100) do |slice|
254
+ vals = slice.map do |row|
255
+ "(" + row.map { |v| db.literal(v) }.join(", ") + ")"
256
+ end.join(", ")
257
+ q = "INSERT INTO #{q_tbl} (SELECT * FROM (VALUES #{vals}) " +
258
+ "AS src (#{q_cols.join(", ")}) WHERE NOT EXISTS (#{subselect}))"
259
+ df = exec(q) do |res|
260
+ total_rows_inserted += res.cmd_tuples
261
+ end
262
+ dg.add(df)
263
+ end
264
+ dg.callback { dg.succeed(total_rows_inserted) }
265
+ dg.callback(&blk) if blk
266
+ dg.close
267
+ end
268
+ end
269
+
134
270
  # Run an upsert inside a transaction.
135
271
  #
136
272
  # @see {PG::EM::Client::Helper#upsert_sql} for all the parameters.
@@ -157,8 +293,8 @@ class PG::EM::Client::Helper::Transaction
157
293
  # specific query finishes.
158
294
  #
159
295
  def exec(sql, values=[], &blk)
160
- unless @committed.nil?
161
- raise RuntimeError,
296
+ if @finished
297
+ raise ClosedError,
162
298
  "Cannot execute a query in a transaction that has been closed"
163
299
  end
164
300
 
@@ -167,11 +303,13 @@ class PG::EM::Client::Helper::Transaction
167
303
  @dg.add(df)
168
304
  df.callback(&blk) if blk
169
305
  end.errback do |ex|
170
- rollback(ex)
306
+ rollback(ex) if @autorollback_on_error
171
307
  end
172
308
  end
173
309
  alias_method :exec_defer, :exec
174
310
 
311
+ private
312
+
175
313
  # Trace queries as they happen, if `ENV['EM_PG_TXN_TRACE']` is set.
176
314
  #
177
315
  def trace_query(q, v=nil)
@@ -0,0 +1,27 @@
1
+ require_relative './spec_helper'
2
+
3
+ describe "PG::EM::Client::Helper#db_bulk_insert" do
4
+ let(:mock_conn) { double(PG::EM::Client) }
5
+
6
+ it "inserts multiple records" do
7
+ expect(dbl = double).to receive(:results).with(2)
8
+
9
+ in_em do
10
+ expect_query("BEGIN")
11
+ expect_query('INSERT INTO "foo" ' +
12
+ '(SELECT * FROM (VALUES (1, \'x\'), (3, \'y\')) ' +
13
+ 'AS src ("bar", "baz") ' +
14
+ 'WHERE NOT EXISTS ' +
15
+ '(SELECT 1 FROM "foo" AS dst ' +
16
+ 'WHERE src."bar"=dst."bar" AND src."baz"=dst."baz"))',
17
+ [], 0.001, :succeed, Struct.new(:cmd_tuples).new(2)
18
+ )
19
+ expect_query("COMMIT")
20
+
21
+ db_bulk_insert(mock_conn, "foo", [:bar, :baz], [[1, "x"], [3, "y"]]) do |count|
22
+ dbl.results(count)
23
+ EM.stop
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,118 @@
1
+ require_relative './spec_helper'
2
+
3
+ describe "PG::EM::Client::Helper::Transaction#savepoint" do
4
+ let(:mock_conn) { double(PG::EM::Client) }
5
+
6
+ it "executes through the savepoint in normal operation" do
7
+ in_em do
8
+ expect(SecureRandom).to receive(:uuid).and_return("faff")
9
+ expect_query("BEGIN")
10
+ expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["wombat"])
11
+ expect_query('SAVEPOINT "faff"')
12
+ expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
13
+ expect_query("COMMIT")
14
+ in_transaction do |txn|
15
+ txn.insert("foo", :bar => 'wombat') do
16
+ txn.savepoint do
17
+ txn.insert("foo", :bar => 'baz') do
18
+ txn.commit
19
+ end
20
+ end.errback do
21
+ txn.rollback # Just to show this *doesn't* happen
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ it "rolls back to the savepoint after a failed INSERT" do
29
+ in_em do
30
+ expect(SecureRandom).to receive(:uuid).and_return("faff")
31
+ expect_query("BEGIN")
32
+ expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["wombat"])
33
+ expect_query('SAVEPOINT "faff"')
34
+ expect_query_failure('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
35
+ expect_query('ROLLBACK TO "faff"')
36
+ expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["wibble"])
37
+ expect_query("COMMIT")
38
+ in_transaction do |txn|
39
+ txn.insert("foo", :bar => 'wombat') do
40
+ txn.savepoint do
41
+ txn.insert("foo", :bar => 'baz') do
42
+ txn.rollback # Just to show this *doesn't* happen
43
+ end
44
+ end.errback do
45
+ txn.insert("foo", :bar => 'wibble') do
46
+ txn.commit
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ it "issues ROLLBACK if query fails outside of savepoint" do
55
+ in_em do
56
+ expect(SecureRandom).to receive(:uuid).and_return("faff")
57
+ expect_query("BEGIN")
58
+ expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["wombat"])
59
+ expect_query('SAVEPOINT "faff"')
60
+ expect_query_failure('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
61
+ # I'd like to be able to verify that the savepoint IDs are the same
62
+ # in both queries, but I'm not sure how. Cross fingers!
63
+ expect_query('ROLLBACK TO "faff"')
64
+ expect_query_failure('INSERT INTO "foo" ("bar") VALUES ($1)', ["wibble"])
65
+ expect_query("ROLLBACK")
66
+ in_transaction do |txn|
67
+ txn.insert("foo", :bar => 'wombat') do
68
+ txn.savepoint do
69
+ txn.insert("foo", :bar => 'baz') do
70
+ txn.rollback # Just to show this *doesn't* happen
71
+ end
72
+ end.errback do
73
+ txn.insert("foo", :bar => 'wibble') do
74
+ txn.commit
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ it "handles nested SAVEPOINTs correctly" do
83
+ in_em do
84
+ expect(SecureRandom).to receive(:uuid).and_return("faff1")
85
+ expect(SecureRandom).to receive(:uuid).and_return("faff2")
86
+ expect_query("BEGIN")
87
+ expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["wombat"])
88
+ expect_query('SAVEPOINT "faff1"')
89
+ expect_query_failure('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
90
+ # I'd like to be able to verify that the savepoint IDs are the same
91
+ # in both queries, but I'm not sure how. Cross fingers!
92
+ expect_query('ROLLBACK TO "faff1"')
93
+
94
+ expect_query('SAVEPOINT "faff2"')
95
+ expect_query_failure('INSERT INTO "foo" ("bar") VALUES ($1)', ["wibble"])
96
+ expect_query('ROLLBACK TO "faff2"')
97
+ expect_query("COMMIT")
98
+
99
+ in_transaction do |txn|
100
+ txn.insert("foo", :bar => 'wombat') do
101
+ txn.savepoint do
102
+ txn.insert("foo", :bar => 'baz') do
103
+ txn.rollback # Just to show this *doesn't* happen
104
+ end
105
+ end.errback do
106
+ txn.savepoint do
107
+ txn.insert("foo", :bar => 'wibble') do
108
+ txn.rollback # Another "shouldn't happen"
109
+ end.errback do
110
+ txn.commit
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -33,6 +33,20 @@ describe "PG::EM::Client::Helper#db_transaction" do
33
33
  end
34
34
  end
35
35
 
36
+ it "fails the transaction if COMMIT fails" do
37
+ dbl = double
38
+ expect(dbl).to receive(:foo)
39
+ expect(dbl).to_not receive(:bar)
40
+
41
+ in_em do
42
+ expect_query("BEGIN")
43
+ expect_query_failure("COMMIT")
44
+ in_transaction do |txn|
45
+ txn.commit
46
+ end.callback { dbl.bar }.errback { dbl.foo }
47
+ end
48
+ end
49
+
36
50
  it "runs a simple INSERT correctly" do
37
51
  in_em do
38
52
  expect_query("BEGIN")
@@ -219,4 +233,21 @@ describe "PG::EM::Client::Helper#db_transaction" do
219
233
  end
220
234
  end
221
235
  end
236
+
237
+ it "doesn't rollback back after a failed INSERT with autorollback = false" do
238
+ in_em do
239
+ expect_query("BEGIN")
240
+ expect_query_failure('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
241
+ expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["wombat"])
242
+ expect_query("COMMIT")
243
+ in_transaction do |txn|
244
+ txn.autorollback_on_error = false
245
+ txn.insert("foo", :bar => 'baz').errback do
246
+ txn.insert("foo", :bar => 'wombat') do
247
+ txn.commit
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
222
253
  end
data/spec/txn_helper.rb CHANGED
@@ -23,9 +23,16 @@ module TxnHelper
23
23
  end
24
24
 
25
25
  def in_em
26
- EM.run do
27
- EM.add_timer(0.5) { EM.stop; raise "test timeout" }
28
- yield
26
+ begin
27
+ Timeout.timeout(0.5) do
28
+ EM.run do
29
+ yield
30
+ end
31
+ end
32
+ rescue Timeout::Error
33
+ EM.stop if EM.reactor_running?
34
+ raise RuntimeError,
35
+ "EM test time exceeded"
29
36
  end
30
37
  end
31
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-pg-client-helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Palmer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-03 00:00:00.000000000 Z
11
+ date: 2015-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-pg-client
@@ -230,7 +230,9 @@ files:
230
230
  - lib/em-pg-client-helper.rb
231
231
  - lib/em-pg-client-helper/deferrable_group.rb
232
232
  - lib/em-pg-client-helper/transaction.rb
233
+ - spec/db_bulk_insert_spec.rb
233
234
  - spec/db_insert_spec.rb
235
+ - spec/db_transaction_savepoint_spec.rb
234
236
  - spec/db_transaction_sequel_spec.rb
235
237
  - spec/db_transaction_spec.rb
236
238
  - spec/db_transaction_upsert_spec.rb