simple-sql 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/simple/sql/duplicate.rb +74 -26
- data/lib/simple/sql/version.rb +1 -1
- data/spec/simple/sql_duplicate_spec.rb +5 -5
- data/spec/simple/sql_duplicate_unique_spec.rb +37 -0
- data/spec/simple/sql_insert_spec.rb +0 -5
- data/spec/support/001_database.rb +9 -0
- data/spec/support/factories/user_factory.rb +8 -2
- data/spec/support/model/user.rb +3 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f45fc27ee92d2023cdcd3eb2b5a0cfbd1a05672
|
4
|
+
data.tar.gz: 8036ee0f56cd039cf1555f8f3fbe323dbaf76a09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcef39ebaa97f2807ea6812257b22775d70b3ac21620bdfcf508e4a06340c4142791d7bb6a93d7a17a1c40950236aa3431691afe7c3fcd4072cd1b78d199e1fe
|
7
|
+
data.tar.gz: af8c3af9c817824a76e89c2ed5e7cb9856ad87a696da7e87c26c5cad8a0b9d48d5df92841484e8c27a33a48152069be9a22a0e4269ae7bfb8043a00938cac8d5
|
data/Gemfile.lock
CHANGED
data/lib/simple/sql/duplicate.rb
CHANGED
@@ -1,41 +1,89 @@
|
|
1
|
-
# rubocop:disable
|
2
|
-
# rubocop:disable Metrics/AbcSize
|
1
|
+
# rubocop:disable Style/StructInheritance
|
3
2
|
|
4
3
|
module Simple
|
5
4
|
module SQL
|
6
|
-
|
5
|
+
class Fragment < Struct.new(:to_sql)
|
6
|
+
end
|
7
|
+
|
8
|
+
def fragment(str)
|
9
|
+
Fragment.new(str)
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Creates duplicates of record in a table.
|
14
|
+
#
|
15
|
+
# This method handles timestamp columns (these will be set to the current
|
16
|
+
# time) and primary keys (will be set to NULL.) You can pass in overrides
|
17
|
+
# as a third argument for specific columns.
|
18
|
+
#
|
19
|
+
# Parameters:
|
20
|
+
#
|
21
|
+
# - ids: (Integer, Array<Integer>) primary key ids
|
22
|
+
# - overrides: Hash[column_names => SQL::Fragment]
|
23
|
+
#
|
24
|
+
def duplicate(table, ids, overrides = {})
|
7
25
|
ids = Array(ids)
|
8
26
|
return [] if ids.empty?
|
9
27
|
|
10
|
-
|
11
|
-
|
28
|
+
Duplicator.new(table, overrides).call(ids)
|
29
|
+
end
|
30
|
+
|
31
|
+
class Duplicator
|
32
|
+
attr_reader :table_name, :custom_overrides
|
33
|
+
|
34
|
+
def initialize(table_name, overrides)
|
35
|
+
@table_name = table_name
|
36
|
+
@custom_overrides = validated_overrides(overrides)
|
37
|
+
end
|
38
|
+
|
39
|
+
def call(ids)
|
40
|
+
Simple::SQL.all query, ids
|
41
|
+
rescue PG::UndefinedColumn => e
|
42
|
+
raise ArgumentError, e.message
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# This stringify all keys of the overrides hash, and verifies that
|
48
|
+
# all values in there are SQL::Fragments
|
49
|
+
def validated_overrides(overrides)
|
50
|
+
overrides.inject({}) do |hsh, (key, value)|
|
51
|
+
raise ArgumentError, "Unknown value #{value.inspect}" unless value.is_a?(Fragment)
|
52
|
+
hsh.update key.to_s => value.to_sql
|
53
|
+
end
|
54
|
+
end
|
12
55
|
|
13
|
-
|
14
|
-
|
15
|
-
|
56
|
+
def timestamp_overrides
|
57
|
+
Reflection.timestamp_columns(table_name).inject({}) do |hsh, column|
|
58
|
+
hsh.update column => "now() AS #{column}"
|
59
|
+
end
|
60
|
+
end
|
16
61
|
|
17
|
-
|
18
|
-
|
19
|
-
|
62
|
+
def copy_columns
|
63
|
+
(Reflection.columns(table_name) - Reflection.primary_key_columns(table_name)).inject({}) do |hsh, column|
|
64
|
+
hsh.update column => column
|
65
|
+
end
|
66
|
+
end
|
20
67
|
|
21
|
-
#
|
22
|
-
|
23
|
-
|
68
|
+
# build SQL query
|
69
|
+
def query
|
70
|
+
sources = {}
|
24
71
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# with.
|
29
|
-
columns_to_dupe -= except.map(&:to_s)
|
72
|
+
sources.update copy_columns
|
73
|
+
sources.update timestamp_overrides
|
74
|
+
sources.update custom_overrides
|
30
75
|
|
31
|
-
|
32
|
-
|
33
|
-
|
76
|
+
# convert into an Array, to make sure that keys and values aka firsts
|
77
|
+
# and lasts are always in the correct order.
|
78
|
+
sources = sources.to_a
|
34
79
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
80
|
+
<<~SQL
|
81
|
+
INSERT INTO #{table_name}(#{sources.map(&:first).join(', ')})
|
82
|
+
SELECT #{sources.map(&:last).join(', ')}
|
83
|
+
FROM #{table_name}
|
84
|
+
WHERE id = ANY($1) RETURNING id
|
85
|
+
SQL
|
86
|
+
end
|
39
87
|
end
|
40
88
|
end
|
41
89
|
end
|
data/lib/simple/sql/version.rb
CHANGED
@@ -10,24 +10,24 @@ describe "Simple::SQL.duplicate" do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it "does not fail on a non-existing user" do
|
13
|
-
dupe_ids = SQL.duplicate "users",
|
13
|
+
dupe_ids = SQL.duplicate "users", -1
|
14
14
|
|
15
|
-
expect(dupe_ids.length).to eq(
|
16
|
-
expect(SQL.ask("SELECT COUNT(*) FROM users")).to eq(
|
15
|
+
expect(dupe_ids.length).to eq(0)
|
16
|
+
expect(SQL.ask("SELECT COUNT(*) FROM users")).to eq(USER_COUNT)
|
17
17
|
end
|
18
18
|
|
19
19
|
it "duplicates a single user" do
|
20
20
|
dupe_ids = SQL.duplicate "users", source_ids.first
|
21
21
|
|
22
22
|
expect(dupe_ids.length).to eq(1)
|
23
|
-
expect(SQL.ask("SELECT COUNT(*) FROM users")).to eq(
|
23
|
+
expect(SQL.ask("SELECT COUNT(*) FROM users")).to eq(1 + USER_COUNT)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "duplicates many users" do
|
27
27
|
dupe_ids = SQL.duplicate "users", (source_ids + [ -10 ])
|
28
28
|
|
29
29
|
expect(dupe_ids.length).to eq(2)
|
30
|
-
expect(SQL.ask("SELECT COUNT(*) FROM users")).to eq(
|
30
|
+
expect(SQL.ask("SELECT COUNT(*) FROM users")).to eq(2 + USER_COUNT)
|
31
31
|
end
|
32
32
|
|
33
33
|
it "updates the timestamp columns" do
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Simple::SQL.duplicate/unique indices" do
|
4
|
+
let!(:unique_users) { 1.upto(USER_COUNT).map { create(:unique_user) } }
|
5
|
+
|
6
|
+
let!(:source_ids) { SQL.all("SELECT id FROM unique_users") }
|
7
|
+
|
8
|
+
it "cannot duplicate unique_user" do
|
9
|
+
expect {
|
10
|
+
SQL.duplicate "unique_users", source_ids
|
11
|
+
}.to raise_error(PG::UniqueViolation)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "raises an ArgumentError when called with unknown columns" do
|
15
|
+
expect {
|
16
|
+
SQL.duplicate "unique_users", source_ids, foo: SQL.fragment("baz")
|
17
|
+
}.to raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises an ArgumentError when called with invalid overrides" do
|
21
|
+
expect {
|
22
|
+
SQL.duplicate "unique_users", source_ids, first_name: "I am invalid"
|
23
|
+
}.to raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "duplicates unique_users" do
|
27
|
+
overrides = {
|
28
|
+
first_name: SQL.fragment("first_name || '.' || id"),
|
29
|
+
last_name: SQL.fragment("last_name || '.' || id")
|
30
|
+
}
|
31
|
+
|
32
|
+
dupe_ids = SQL.duplicate "unique_users", source_ids, overrides
|
33
|
+
|
34
|
+
expect(dupe_ids.length).to eq(2)
|
35
|
+
expect(SQL.ask("SELECT COUNT(*) FROM unique_users")).to eq(4)
|
36
|
+
end
|
37
|
+
end
|
@@ -15,11 +15,6 @@ describe "Simple::SQL.insert" do
|
|
15
15
|
expect(user.first_name).to eq("foo")
|
16
16
|
expect(user.last_name).to eq("bar")
|
17
17
|
expect(user.created_at).to be_a(Time)
|
18
|
-
|
19
|
-
#
|
20
|
-
# r = SQL.record("SELECT COUNT(*) AS count FROM users")
|
21
|
-
# r = SQL.record("SELECT COUNT(*) AS count FROM users")
|
22
|
-
# expect(r).to eq({count: 2})
|
23
18
|
end
|
24
19
|
end
|
25
20
|
|
@@ -35,4 +35,13 @@ ActiveRecord::Schema.define do
|
|
35
35
|
|
36
36
|
t.timestamps null: true
|
37
37
|
end
|
38
|
+
|
39
|
+
create_table :unique_users, force: true do |t|
|
40
|
+
t.string :first_name
|
41
|
+
t.string :last_name
|
42
|
+
end
|
43
|
+
|
44
|
+
execute <<-SQL
|
45
|
+
CREATE UNIQUE INDEX unique_users_ix1 ON unique_users(first_name, last_name)
|
46
|
+
SQL
|
38
47
|
end
|
@@ -1,8 +1,14 @@
|
|
1
1
|
FactoryGirl.define do
|
2
2
|
factory :user do
|
3
3
|
role_id 123
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
sequence :first_name do |n| "First #{n}" end
|
6
|
+
sequence :last_name do |n| "Last #{n}" end
|
6
7
|
access_level "viewable"
|
7
8
|
end
|
9
|
+
|
10
|
+
factory :unique_user do
|
11
|
+
sequence :first_name do |n| "First #{n}" end
|
12
|
+
sequence :last_name do |n| "Last #{n}" end
|
13
|
+
end
|
8
14
|
end
|
data/spec/support/model/user.rb
CHANGED
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.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- radiospiel
|
@@ -198,6 +198,7 @@ files:
|
|
198
198
|
- spec/simple/sql_config_spec.rb
|
199
199
|
- spec/simple/sql_conversion_spec.rb
|
200
200
|
- spec/simple/sql_duplicate_spec.rb
|
201
|
+
- spec/simple/sql_duplicate_unique_spec.rb
|
201
202
|
- spec/simple/sql_insert_spec.rb
|
202
203
|
- spec/simple/sql_record_spec.rb
|
203
204
|
- spec/simple/sql_reflection_spec.rb
|
@@ -238,6 +239,7 @@ test_files:
|
|
238
239
|
- spec/simple/sql_config_spec.rb
|
239
240
|
- spec/simple/sql_conversion_spec.rb
|
240
241
|
- spec/simple/sql_duplicate_spec.rb
|
242
|
+
- spec/simple/sql_duplicate_unique_spec.rb
|
241
243
|
- spec/simple/sql_insert_spec.rb
|
242
244
|
- spec/simple/sql_record_spec.rb
|
243
245
|
- spec/simple/sql_reflection_spec.rb
|