simple-sql 0.5.9 → 0.5.10

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
  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