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 +4 -4
- data/lib/simple/sql.rb +1 -1
- data/lib/simple/sql/connection.rb +1 -0
- data/lib/simple/sql/connection/base.rb +1 -15
- data/lib/simple/sql/connection/lock.rb +86 -0
- data/lib/simple/sql/helpers.rb +1 -0
- data/lib/simple/sql/helpers/printer.rb +56 -0
- data/lib/simple/sql/version.rb +1 -1
- data/simple-sql.gemspec +2 -0
- data/spec/simple/sql/lock_spec.rb +36 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f76ba8aa94983b8943cc358fbf15e9b66939b68b10e19d15336edc62fbc3c35
|
4
|
+
data.tar.gz: a5947013464489d1b5c9ce70dfc359c737fe59f7fffaac4cb8512ccb4c040653
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38839f6a20c97edb9ee06a432db292c2cc620fdc0d01cb794a0caaf2e8bb542651f33850b9937b8e8e8ccd8139050a3c39a9d44dcef349ac8ccca9462025c26c
|
7
|
+
data.tar.gz: b8663891f150b32cf7f13550a231eb7fc962c95ec2dbcd9b1f1605bebe32319b6d9b7e7556b6d089ac3c18f698cf4c9e31e8a17417ba00d8beaa503e7ddc06e4
|
data/lib/simple/sql.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/simple/sql/helpers.rb
CHANGED
@@ -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
|
data/lib/simple/sql/version.rb
CHANGED
data/simple-sql.gemspec
CHANGED
@@ -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.
|
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
|
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
|