postgres_upsert 2.0.0 → 3.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 +4 -4
- data/Gemfile.lock +30 -35
- data/lib/postgres_upsert/active_record.rb +1 -1
- data/lib/postgres_upsert/writer.rb +47 -26
- data/postgres_upsert.gemspec +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b0a956bd60011cf247a58c3031d5157be3c6f7f
|
|
4
|
+
data.tar.gz: 918a0d163efa3915f8b3d76421ab69e67e2469d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1dda03e61a25fb875bbaf8eab001d0f8b780dbcb329f8be1c0aabbb03c4be4bb66c6adf2dbfbf21d3edb55135be2fc01826dcc79259b928b64c7bb1748c0369
|
|
7
|
+
data.tar.gz: ca59859938a0671ffed159495d10a08328a498500f8c18610c4d50f9d452901b55c5e81e63e62eb35dc8c74b532a713e4f2285db690721be5f4de9b3aed3f8ad
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
postgres_upsert (
|
|
4
|
+
postgres_upsert (3.0.0)
|
|
5
5
|
activerecord (>= 3.0.0)
|
|
6
6
|
pg (~> 0.17.0)
|
|
7
7
|
rails (>= 3.0.0)
|
|
@@ -9,26 +9,26 @@ PATH
|
|
|
9
9
|
GEM
|
|
10
10
|
remote: https://rubygems.org/
|
|
11
11
|
specs:
|
|
12
|
-
actionmailer (4.0.
|
|
13
|
-
actionpack (= 4.0.
|
|
14
|
-
mail (~> 2.5.4)
|
|
15
|
-
actionpack (4.0.
|
|
16
|
-
activesupport (= 4.0.
|
|
12
|
+
actionmailer (4.0.11)
|
|
13
|
+
actionpack (= 4.0.11)
|
|
14
|
+
mail (~> 2.5, >= 2.5.4)
|
|
15
|
+
actionpack (4.0.11)
|
|
16
|
+
activesupport (= 4.0.11)
|
|
17
17
|
builder (~> 3.1.0)
|
|
18
18
|
erubis (~> 2.7.0)
|
|
19
19
|
rack (~> 1.5.2)
|
|
20
20
|
rack-test (~> 0.6.2)
|
|
21
|
-
activemodel (4.0.
|
|
22
|
-
activesupport (= 4.0.
|
|
21
|
+
activemodel (4.0.11)
|
|
22
|
+
activesupport (= 4.0.11)
|
|
23
23
|
builder (~> 3.1.0)
|
|
24
|
-
activerecord (4.0.
|
|
25
|
-
activemodel (= 4.0.
|
|
24
|
+
activerecord (4.0.11)
|
|
25
|
+
activemodel (= 4.0.11)
|
|
26
26
|
activerecord-deprecated_finders (~> 1.0.2)
|
|
27
|
-
activesupport (= 4.0.
|
|
27
|
+
activesupport (= 4.0.11)
|
|
28
28
|
arel (~> 4.0.0)
|
|
29
29
|
activerecord-deprecated_finders (1.0.3)
|
|
30
|
-
activesupport (4.0.
|
|
31
|
-
i18n (~> 0.6, >= 0.6.
|
|
30
|
+
activesupport (4.0.11)
|
|
31
|
+
i18n (~> 0.6, >= 0.6.9)
|
|
32
32
|
minitest (~> 4.2)
|
|
33
33
|
multi_json (~> 1.3)
|
|
34
34
|
thread_safe (~> 0.1)
|
|
@@ -41,15 +41,13 @@ GEM
|
|
|
41
41
|
hike (1.2.3)
|
|
42
42
|
i18n (0.6.11)
|
|
43
43
|
json (1.7.6)
|
|
44
|
-
mail (2.
|
|
45
|
-
mime-types (
|
|
46
|
-
treetop (~> 1.4.8)
|
|
44
|
+
mail (2.6.3)
|
|
45
|
+
mime-types (>= 1.16, < 3)
|
|
47
46
|
method_source (0.8.2)
|
|
48
|
-
mime-types (
|
|
47
|
+
mime-types (2.4.3)
|
|
49
48
|
minitest (4.7.5)
|
|
50
49
|
multi_json (1.10.1)
|
|
51
50
|
pg (0.17.1)
|
|
52
|
-
polyglot (0.3.5)
|
|
53
51
|
pry (0.10.1)
|
|
54
52
|
coderay (~> 1.1.0)
|
|
55
53
|
method_source (~> 0.8.1)
|
|
@@ -59,17 +57,17 @@ GEM
|
|
|
59
57
|
rack (1.5.2)
|
|
60
58
|
rack-test (0.6.2)
|
|
61
59
|
rack (>= 1.0)
|
|
62
|
-
rails (4.0.
|
|
63
|
-
actionmailer (= 4.0.
|
|
64
|
-
actionpack (= 4.0.
|
|
65
|
-
activerecord (= 4.0.
|
|
66
|
-
activesupport (= 4.0.
|
|
60
|
+
rails (4.0.11)
|
|
61
|
+
actionmailer (= 4.0.11)
|
|
62
|
+
actionpack (= 4.0.11)
|
|
63
|
+
activerecord (= 4.0.11)
|
|
64
|
+
activesupport (= 4.0.11)
|
|
67
65
|
bundler (>= 1.3.0, < 2.0)
|
|
68
|
-
railties (= 4.0.
|
|
69
|
-
sprockets-rails (~> 2.0
|
|
70
|
-
railties (4.0.
|
|
71
|
-
actionpack (= 4.0.
|
|
72
|
-
activesupport (= 4.0.
|
|
66
|
+
railties (= 4.0.11)
|
|
67
|
+
sprockets-rails (~> 2.0)
|
|
68
|
+
railties (4.0.11)
|
|
69
|
+
actionpack (= 4.0.11)
|
|
70
|
+
activesupport (= 4.0.11)
|
|
73
71
|
rake (>= 0.8.7)
|
|
74
72
|
thor (>= 0.18.1, < 2.0)
|
|
75
73
|
rake (10.3.2)
|
|
@@ -84,22 +82,19 @@ GEM
|
|
|
84
82
|
diff-lcs (~> 1.1.3)
|
|
85
83
|
rspec-mocks (2.12.2)
|
|
86
84
|
slop (3.6.0)
|
|
87
|
-
sprockets (2.
|
|
85
|
+
sprockets (2.12.3)
|
|
88
86
|
hike (~> 1.2)
|
|
89
87
|
multi_json (~> 1.0)
|
|
90
88
|
rack (~> 1.0)
|
|
91
89
|
tilt (~> 1.1, != 1.3.0)
|
|
92
|
-
sprockets-rails (2.0
|
|
90
|
+
sprockets-rails (2.2.0)
|
|
93
91
|
actionpack (>= 3.0)
|
|
94
92
|
activesupport (>= 3.0)
|
|
95
|
-
sprockets (
|
|
93
|
+
sprockets (>= 2.8, < 4.0)
|
|
96
94
|
thor (0.19.1)
|
|
97
95
|
thread_safe (0.3.4)
|
|
98
96
|
tilt (1.4.1)
|
|
99
|
-
|
|
100
|
-
polyglot
|
|
101
|
-
polyglot (>= 0.3.1)
|
|
102
|
-
tzinfo (0.3.40)
|
|
97
|
+
tzinfo (0.3.42)
|
|
103
98
|
|
|
104
99
|
PLATFORMS
|
|
105
100
|
ruby
|
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
# * You can map fields from the file to different fields in the table using a map in the options hash
|
|
8
8
|
# * For further details on usage take a look at the README.md
|
|
9
9
|
def self.pg_upsert path_or_io, options = {}
|
|
10
|
-
PostgresUpsert::Writer.new(
|
|
10
|
+
PostgresUpsert::Writer.new(table_name, path_or_io, options).write
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -2,13 +2,13 @@ module PostgresUpsert
|
|
|
2
2
|
|
|
3
3
|
class Writer
|
|
4
4
|
|
|
5
|
-
def initialize(
|
|
6
|
-
@
|
|
5
|
+
def initialize(table_name, source, options = {})
|
|
6
|
+
@table_name = table_name
|
|
7
7
|
@options = options.reverse_merge({
|
|
8
8
|
:delimiter => ",",
|
|
9
9
|
:format => :csv,
|
|
10
10
|
:header => true,
|
|
11
|
-
:key_column =>
|
|
11
|
+
:key_column => primary_key,
|
|
12
12
|
:update_only => false})
|
|
13
13
|
@source = source.instance_of?(String) ? File.open(source, 'r') : source
|
|
14
14
|
@columns_list = get_columns
|
|
@@ -23,7 +23,6 @@ module PostgresUpsert
|
|
|
23
23
|
csv_options = @options[:format] == :binary ? "BINARY" : "DELIMITER '#{@options[:delimiter]}' CSV"
|
|
24
24
|
|
|
25
25
|
copy_table = @temp_table_name
|
|
26
|
-
destination_table = get_table_name
|
|
27
26
|
|
|
28
27
|
columns_string = columns_string_for_copy
|
|
29
28
|
create_temp_table
|
|
@@ -36,14 +35,40 @@ module PostgresUpsert
|
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
drop_temp_table
|
|
42
|
-
end
|
|
38
|
+
upsert_from_temp_table
|
|
39
|
+
drop_temp_table
|
|
43
40
|
end
|
|
44
41
|
|
|
45
42
|
private
|
|
46
43
|
|
|
44
|
+
def primary_key
|
|
45
|
+
@primary_key ||= begin
|
|
46
|
+
query = <<-sql
|
|
47
|
+
SELECT
|
|
48
|
+
pg_attribute.attname,
|
|
49
|
+
format_type(pg_attribute.atttypid, pg_attribute.atttypmod)
|
|
50
|
+
FROM pg_index, pg_class, pg_attribute
|
|
51
|
+
WHERE
|
|
52
|
+
pg_class.oid = '#{@table_name}'::regclass AND
|
|
53
|
+
indrelid = pg_class.oid AND
|
|
54
|
+
pg_attribute.attrelid = pg_class.oid AND
|
|
55
|
+
pg_attribute.attnum = any(pg_index.indkey)
|
|
56
|
+
AND indisprimary
|
|
57
|
+
sql
|
|
58
|
+
|
|
59
|
+
pg_result = ActiveRecord::Base.connection.execute query
|
|
60
|
+
pg_result.each{ |row| return row['attname'] }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def column_names
|
|
65
|
+
@column_names ||= begin
|
|
66
|
+
query = "SELECT * FROM information_schema.columns WHERE TABLE_NAME = '#{@table_name}'"
|
|
67
|
+
pg_result = ActiveRecord::Base.connection.execute query
|
|
68
|
+
pg_result.map{ |row| row['column_name'] }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
47
72
|
def get_columns
|
|
48
73
|
columns_list = @options[:columns] || []
|
|
49
74
|
if @options[:format] != :binary && @options[:header]
|
|
@@ -64,23 +89,23 @@ module PostgresUpsert
|
|
|
64
89
|
|
|
65
90
|
def columns_string_for_select
|
|
66
91
|
columns = @columns_list.clone
|
|
67
|
-
columns << "created_at" if
|
|
68
|
-
columns << "updated_at" if
|
|
92
|
+
columns << "created_at" if column_names.include?("created_at")
|
|
93
|
+
columns << "updated_at" if column_names.include?("updated_at")
|
|
69
94
|
str = get_columns_string(columns)
|
|
70
95
|
end
|
|
71
96
|
|
|
72
97
|
def columns_string_for_insert
|
|
73
98
|
columns = @columns_list.clone
|
|
74
|
-
columns << "created_at" if
|
|
75
|
-
columns << "updated_at" if
|
|
99
|
+
columns << "created_at" if column_names.include?("created_at")
|
|
100
|
+
columns << "updated_at" if column_names.include?("updated_at")
|
|
76
101
|
str = get_columns_string(columns)
|
|
77
102
|
end
|
|
78
103
|
|
|
79
104
|
def select_string_for_insert
|
|
80
105
|
columns = @columns_list.clone
|
|
81
106
|
str = get_columns_string(columns)
|
|
82
|
-
str << ",'#{DateTime.now.utc}'" if
|
|
83
|
-
str << ",'#{DateTime.now.utc}'" if
|
|
107
|
+
str << ",'#{DateTime.now.utc}'" if column_names.include?("created_at")
|
|
108
|
+
str << ",'#{DateTime.now.utc}'" if column_names.include?("updated_at")
|
|
84
109
|
str
|
|
85
110
|
end
|
|
86
111
|
|
|
@@ -95,16 +120,12 @@ module PostgresUpsert
|
|
|
95
120
|
columns.size > 0 ? "\"#{columns.join('","')}\"" : ""
|
|
96
121
|
end
|
|
97
122
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
connection.quote_table_name(@options[:table])
|
|
101
|
-
else
|
|
102
|
-
@klass.quoted_table_name
|
|
103
|
-
end
|
|
123
|
+
def quoted_table_name
|
|
124
|
+
@quoted_table_name ||= ActiveRecord::Base.connection.quote_table_name(@table_name)
|
|
104
125
|
end
|
|
105
126
|
|
|
106
127
|
def generate_temp_table_name
|
|
107
|
-
@temp_table_name = "#{@
|
|
128
|
+
@temp_table_name = "#{@table_name}_temp_#{rand(1000)}"
|
|
108
129
|
end
|
|
109
130
|
|
|
110
131
|
def read_input_line
|
|
@@ -126,7 +147,7 @@ module PostgresUpsert
|
|
|
126
147
|
|
|
127
148
|
def update_from_temp_table
|
|
128
149
|
ActiveRecord::Base.connection.execute <<-SQL
|
|
129
|
-
UPDATE #{
|
|
150
|
+
UPDATE #{quoted_table_name} AS d
|
|
130
151
|
#{update_set_clause}
|
|
131
152
|
FROM #{@temp_table_name} as t
|
|
132
153
|
WHERE t.#{@options[:key_column]} = d.#{@options[:key_column]}
|
|
@@ -138,7 +159,7 @@ module PostgresUpsert
|
|
|
138
159
|
command = @columns_list.map do |col|
|
|
139
160
|
"\"#{col}\" = t.\"#{col}\""
|
|
140
161
|
end
|
|
141
|
-
command << "\"updated_at\" = '#{DateTime.now.utc}'" if
|
|
162
|
+
command << "\"updated_at\" = '#{DateTime.now.utc}'" if column_names.include?("updated_at")
|
|
142
163
|
"SET #{command.join(',')}"
|
|
143
164
|
end
|
|
144
165
|
|
|
@@ -146,12 +167,12 @@ module PostgresUpsert
|
|
|
146
167
|
columns_string = columns_string_for_insert
|
|
147
168
|
select_string = select_string_for_insert
|
|
148
169
|
ActiveRecord::Base.connection.execute <<-SQL
|
|
149
|
-
INSERT INTO #{
|
|
170
|
+
INSERT INTO #{quoted_table_name} (#{columns_string})
|
|
150
171
|
SELECT #{select_string}
|
|
151
172
|
FROM #{@temp_table_name} as t
|
|
152
173
|
WHERE NOT EXISTS
|
|
153
174
|
(SELECT 1
|
|
154
|
-
FROM #{
|
|
175
|
+
FROM #{quoted_table_name} as d
|
|
155
176
|
WHERE d.#{@options[:key_column]} = t.#{@options[:key_column]})
|
|
156
177
|
AND t.#{@options[:key_column]} IS NOT NULL;
|
|
157
178
|
SQL
|
|
@@ -164,7 +185,7 @@ module PostgresUpsert
|
|
|
164
185
|
DROP TABLE IF EXISTS #{@temp_table_name};
|
|
165
186
|
|
|
166
187
|
CREATE TEMP TABLE #{@temp_table_name}
|
|
167
|
-
AS SELECT #{columns_string} FROM #{
|
|
188
|
+
AS SELECT #{columns_string} FROM #{quoted_table_name} WHERE 0 = 1;
|
|
168
189
|
SQL
|
|
169
190
|
end
|
|
170
191
|
|
data/postgres_upsert.gemspec
CHANGED