simple-sql 0.5.9 → 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 913129df1de6aab1bd350652782ea671e56f6a396dd8c6a2e7a8cebc972fab0e
4
- data.tar.gz: 6aca39154c90cdfa0cb089c3358afa0f3c28ab261c837c99536c1fa39c3569f9
3
+ metadata.gz: 4f76ba8aa94983b8943cc358fbf15e9b66939b68b10e19d15336edc62fbc3c35
4
+ data.tar.gz: a5947013464489d1b5c9ce70dfc359c737fe59f7fffaac4cb8512ccb4c040653
5
5
  SHA512:
6
- metadata.gz: '019f7c7b5c0b9364858b0af3a4cbf4ccffc69ae961610cbe5b552d4d646a2df0870c0e20d990dd77a36821a2c6395d05687464093b030cef7bbdc7115c9af3bb'
7
- data.tar.gz: 4cf3ec7ff055cb174ca25043c46f714086483e73908d30daf723df062ed809c747500251555823eba676d6e4c747bdb80a2cb34c303e1ed904d4ef752e4f619d
6
+ metadata.gz: 38839f6a20c97edb9ee06a432db292c2cc620fdc0d01cb794a0caaf2e8bb542651f33850b9937b8e8e8ccd8139050a3c39a9d44dcef349ac8ccca9462025c26c
7
+ data.tar.gz: b8663891f150b32cf7f13550a231eb7fc962c95ec2dbcd9b1f1605bebe32319b6d9b7e7556b6d089ac3c18f698cf4c9e31e8a17417ba00d8beaa503e7ddc06e4
@@ -18,7 +18,7 @@ module Simple
18
18
  extend self
19
19
 
20
20
  extend Forwardable
21
- delegate [:ask, :all, :each, :exec, :locked, :print, :transaction, :wait_for_notify, :estimate_cost] => :default_connection
21
+ delegate [:ask, :all, :each, :exec, :locked, :lock!, :print, :transaction, :wait_for_notify, :estimate_cost] => :default_connection
22
22
  delegate [:reflection] => :default_connection
23
23
  delegate [:duplicate] => :default_connection
24
24
  delegate [:insert] => :default_connection
@@ -5,6 +5,7 @@ require_relative "connection/raw_connection"
5
5
  require_relative "connection/active_record_connection"
6
6
 
7
7
  require_relative "connection/base"
8
+ require_relative "connection/lock"
8
9
  require_relative "connection/scope"
9
10
  require_relative "connection/reflection"
10
11
  require_relative "connection/insert"
@@ -64,9 +64,8 @@ class Simple::SQL::Connection
64
64
  def print(sql, *args, into: nil)
65
65
  raise ArgumentError, "You cannot call Simple::SQL.print with into: #{into.inspect}" unless into.nil?
66
66
 
67
- require "table_print"
68
67
  records = all sql, *args, into: Hash
69
- tp records
68
+ Simple::SQL::Helpers::Printer.print records
70
69
  records
71
70
  end
72
71
 
@@ -91,19 +90,6 @@ class Simple::SQL::Connection
91
90
  explanation.first.dig "Plan", "Total Cost"
92
91
  end
93
92
 
94
- # Executes a block, usually of db insert code, while holding an
95
- # advisory lock.
96
- #
97
- # Examples:
98
- #
99
- # - <tt>Simple::SQL.locked(4711) { puts 'do work while locked' }
100
- def locked(lock_id)
101
- ask("SELECT pg_advisory_lock(#{lock_id})")
102
- yield
103
- ensure
104
- ask("SELECT pg_advisory_unlock(#{lock_id})")
105
- end
106
-
107
93
  private
108
94
 
109
95
  Result = ::Simple::SQL::Result
@@ -0,0 +1,86 @@
1
+ require "digest/crc32"
2
+
3
+ class Simple::SQL::Connection
4
+ # Executes a block, usually of db insert code, while holding an
5
+ # advisory lock.
6
+ #
7
+ # This code is deprecated; one should use lock! instead.
8
+ #
9
+ # Examples:
10
+ #
11
+ # - <tt>Simple::SQL.locked(4711) { puts 'do work while locked' }
12
+ def locked(lock_id)
13
+ lock! lock_id
14
+ yield
15
+ end
16
+
17
+ # Returns true if we are inside a transaction.
18
+ def in_transaction?
19
+ # This works because each SELECT is run either inside an existing transaction
20
+ # or inside a temporary, for this statement only, transaction which gets
21
+ # deleted after the statement - and which results in different txids.
22
+ txid1 = ask "select txid_current()"
23
+ txid2 = ask "select txid_current()"
24
+ txid1 == txid2
25
+ end
26
+
27
+ # Acquire an advisory lock.
28
+ #
29
+ # This uses the pg_*_xact_lock locks - that once acquired, cannot be released,
30
+ # but will release automatically once the transaction ends. This is safer than
31
+ # the pg_advisory_lock group of functions.
32
+ #
33
+ # For ease of use the key argument can also be a string - in which case we use
34
+ # a hash function to derive a key value. Usage example:
35
+ #
36
+ # Simple::SQL.lock! "products", 12
37
+ # Simple::SQL.ask "UPDATE products SET ... WHERE id=12"
38
+ #
39
+ # Note that passing in a timeout value sets timeouts for all lock! invocations
40
+ # in this transaction.
41
+ def lock!(key, key2 = nil, timeout: nil)
42
+ unless in_transaction?
43
+ raise "You cannot use lock! outside of a transaction"
44
+ end
45
+
46
+ if key.is_a?(String)
47
+ # calculate a 31 bit checksum < 0
48
+ key = Digest::CRC32.hexdigest(key).to_i(16) # get a 32-bit stable checksum
49
+ key &= ~0x80000000 # reset bit 31
50
+ key = -key # make it negative
51
+ end
52
+
53
+ if timeout
54
+ lock_w_timeout(key, key2, timeout)
55
+ else
56
+ lock_wo_timeout(key, key2)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def lock_wo_timeout(key, key2)
63
+ ask("SET LOCAL lock_timeout TO DEFAULT")
64
+
65
+ if key2
66
+ ask("SELECT pg_advisory_xact_lock($1, $2)", key, key2)
67
+ else
68
+ ask("SELECT pg_advisory_xact_lock($1)", key)
69
+ end
70
+ end
71
+
72
+ def lock_w_timeout(key, key2, timeout)
73
+ expect! timeout => 0..3600
74
+
75
+ timeout_string = "%dms" % (timeout * 1000)
76
+ ask("SET LOCAL lock_timeout = '#{timeout_string}'")
77
+
78
+ if key2
79
+ return if ask("SELECT pg_try_advisory_xact_lock($1, $2)", key, key2)
80
+ else
81
+ return if ask("SELECT pg_try_advisory_xact_lock($1)", key)
82
+ end
83
+
84
+ raise "Cannot get lock w/key #{key.inspect} and key2 #{key2.inspect} within #{timeout} seconds"
85
+ end
86
+ end
@@ -1,6 +1,7 @@
1
1
  module ::Simple::SQL::Helpers
2
2
  end
3
3
 
4
+ require_relative "helpers/printer.rb"
4
5
  require_relative "helpers/decoder.rb"
5
6
  require_relative "helpers/encoder.rb"
6
7
  require_relative "helpers/row_converter.rb"
@@ -0,0 +1,56 @@
1
+ # rubocop:disable Metrics/AbcSize
2
+
3
+ # private
4
+ module Simple::SQL::Helpers::Printer
5
+ extend self
6
+
7
+ def print(records)
8
+ if table_print?
9
+ tp records
10
+ else
11
+ lp records
12
+ end
13
+ end
14
+
15
+ def lp(records)
16
+ return if records.empty?
17
+
18
+ keys = records.first.keys
19
+ return if keys.empty?
20
+
21
+ rows = []
22
+ rows << keys
23
+
24
+ records.each do |rec|
25
+ rows << rec.values_at(*keys).map(&:to_s)
26
+ end
27
+
28
+ max_lengths = rows.inject([0] * keys.count) do |ary, row|
29
+ ary.zip(row.map(&:length)).map(&:max)
30
+ end
31
+
32
+ rows.each_with_index do |row, idx|
33
+ parts = row.zip(max_lengths).map do |value, max_length|
34
+ " %-#{max_length}s " % value
35
+ end
36
+
37
+ STDERR.puts parts.join("|")
38
+
39
+ if idx == 0
40
+ STDERR.puts parts.join("|").gsub(/[^|]/, "-")
41
+ end
42
+ end
43
+ end
44
+
45
+ def table_print?
46
+ load_table_print unless instance_variable_defined? :@table_print
47
+ @table_print
48
+ end
49
+
50
+ def load_table_print
51
+ require "table_print"
52
+ @table_print = true
53
+ rescue LoadError
54
+ @table_print = false
55
+ end
56
+ end
@@ -1,5 +1,5 @@
1
1
  module Simple
2
2
  module SQL
3
- VERSION = "0.5.9"
3
+ VERSION = "0.5.10"
4
4
  end
5
5
  end
@@ -31,6 +31,8 @@ Gem::Specification.new do |gem|
31
31
  gem.add_dependency 'pg', '~> 0.20'
32
32
  gem.add_dependency 'expectation', '~> 1'
33
33
 
34
+ gem.add_dependency 'digest-crc', '~> 0'
35
+
34
36
  # optional gems (required by some of the parts)
35
37
 
36
38
  # development gems
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+
3
+ describe "Simple::SQL.lock" do
4
+ it "raises an error if not inside a transaction" do
5
+ expect { Simple::SQL.lock!(1) }.to raise_error("You cannot use lock! outside of a transaction")
6
+ end
7
+
8
+ def number_of_locks
9
+ SQL.ask "SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'"
10
+ end
11
+
12
+ it "locks with 2 ints" do
13
+ SQL.transaction do
14
+ expect { SQL.lock!(1, 2) }.to change { number_of_locks }.by(1)
15
+ end
16
+ end
17
+
18
+ it "locks with 1 int" do
19
+ SQL.transaction do
20
+ expect { SQL.lock!(1) }.to change { number_of_locks }.by(1)
21
+ end
22
+ end
23
+
24
+ it "locks converts a string key into an int key" do
25
+ SQL.transaction do
26
+ expect { SQL.lock!("foo", 1) }.to change { number_of_locks }.by(1)
27
+ expect { SQL.lock!("foo") }.to change { number_of_locks }.by(1)
28
+ end
29
+ end
30
+
31
+ it "accepts a timeout value" do
32
+ SQL.transaction do
33
+ SQL.lock!("foo", timeout: 0.000)
34
+ end
35
+ end
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.9
4
+ version: 0.5.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-04-22 00:00:00.000000000 Z
12
+ date: 2019-06-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg_array_parser
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '1'
56
+ - !ruby/object:Gem::Dependency
57
+ name: digest-crc
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: activerecord
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +220,7 @@ files:
206
220
  - lib/simple/sql/connection/base.rb
207
221
  - lib/simple/sql/connection/duplicate.rb
208
222
  - lib/simple/sql/connection/insert.rb
223
+ - lib/simple/sql/connection/lock.rb
209
224
  - lib/simple/sql/connection/raw_connection.rb
210
225
  - lib/simple/sql/connection/reflection.rb
211
226
  - lib/simple/sql/connection/scope.rb
@@ -221,6 +236,7 @@ files:
221
236
  - lib/simple/sql/helpers/decoder.rb
222
237
  - lib/simple/sql/helpers/encoder.rb
223
238
  - lib/simple/sql/helpers/immutable.rb
239
+ - lib/simple/sql/helpers/printer.rb
224
240
  - lib/simple/sql/helpers/row_converter.rb
225
241
  - lib/simple/sql/logging.rb
226
242
  - lib/simple/sql/result.rb
@@ -245,6 +261,7 @@ files:
245
261
  - spec/simple/sql/duplicate_unique_spec.rb
246
262
  - spec/simple/sql/each_spec.rb
247
263
  - spec/simple/sql/insert_spec.rb
264
+ - spec/simple/sql/lock_spec.rb
248
265
  - spec/simple/sql/logging_spec.rb
249
266
  - spec/simple/sql/reflection_spec.rb
250
267
  - spec/simple/sql/result_count_spec.rb
@@ -296,6 +313,7 @@ test_files:
296
313
  - spec/simple/sql/duplicate_unique_spec.rb
297
314
  - spec/simple/sql/each_spec.rb
298
315
  - spec/simple/sql/insert_spec.rb
316
+ - spec/simple/sql/lock_spec.rb
299
317
  - spec/simple/sql/logging_spec.rb
300
318
  - spec/simple/sql/reflection_spec.rb
301
319
  - spec/simple/sql/result_count_spec.rb