upsert 1.2.0 → 2.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.
- data/CHANGELOG +7 -0
- data/README.md +8 -1
- data/lib/upsert.rb +4 -3
- data/lib/upsert/column_definition/sqlite3.rb +3 -0
- data/lib/upsert/connection/{Java_OrgSqliteConn.rb → Java_OrgSqlite_Conn.rb} +1 -1
- data/lib/upsert/connection/SQLite3_Database.rb +1 -1
- data/lib/upsert/connection/jdbc.rb +6 -1
- data/lib/upsert/connection/postgresql.rb +9 -0
- data/lib/upsert/merge_function.rb +3 -1
- data/lib/upsert/merge_function/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb +7 -3
- data/lib/upsert/merge_function/{Java_OrgSqliteConn.rb → Java_OrgSqlite_Conn.rb} +1 -1
- data/lib/upsert/merge_function/PG_Connection.rb +16 -4
- data/lib/upsert/merge_function/mysql.rb +1 -1
- data/lib/upsert/merge_function/postgresql.rb +56 -6
- data/lib/upsert/row.rb +11 -2
- data/lib/upsert/version.rb +1 -1
- data/spec/hstore_spec.rb +177 -0
- data/spec/logger_spec.rb +2 -2
- data/spec/sequel_spec.rb +38 -0
- data/spec/spec_helper.rb +12 -3
- data/upsert.gemspec +2 -1
- metadata +61 -11
- checksums.yaml +0 -15
data/CHANGELOG
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
2.0.0 / 2013-07-24
|
2
|
+
|
3
|
+
* Breaking changes
|
4
|
+
|
5
|
+
* For Postgres Hstore, null keys are deliberately deleted - so there's no way to set "foo" => NULL - "foo" will just get deleted as a key
|
6
|
+
* Name merge functions "upsert1_2_0" instead of "upsert_"
|
7
|
+
|
1
8
|
1.2.0 / 2013-04-04
|
2
9
|
|
3
10
|
* Breaking changes
|
data/README.md
CHANGED
@@ -66,10 +66,17 @@ Pet.upsert({:name => 'Jerry'}, :breed => 'beagle')
|
|
66
66
|
Pull requests for any of these would be greatly appreciated:
|
67
67
|
|
68
68
|
1. Cache JDBC PreparedStatement objects.
|
69
|
-
1. Allow "true" upserting like `upsert.row({name: 'Jerry'}, counter: Upsert.sql('counter+1'))`
|
70
69
|
1. Sanity check my three benchmarks (four if you include activerecord-import on MySQL). Do they accurately represent optimized alternatives?
|
71
70
|
1. Provide `require 'upsert/debug'` that will make sure you are selecting on columns that have unique indexes
|
72
71
|
1. Test that `Upsert` instances accept arbitrary columns, even within a batch, which is what people probably expect.
|
72
|
+
1. [@antage](https://github.com/antage)'s idea for "true" upserting: (from https://github.com/seamusabshere/upsert/issues/17)
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
selector = { id: 15 }
|
76
|
+
update_setter = { count: Upsert.sql('count + 1') }
|
77
|
+
insert_setter = { count: 1 }
|
78
|
+
upsert.row_with_two_setter(update_setter, insert_setter, selector)
|
79
|
+
```
|
73
80
|
|
74
81
|
## Real-world usage
|
75
82
|
|
data/lib/upsert.rb
CHANGED
@@ -153,10 +153,11 @@ class Upsert
|
|
153
153
|
UTC_TZ = '+00:00'
|
154
154
|
NULL_WORD = 'NULL'
|
155
155
|
METAL_CLASS_ALIAS = {
|
156
|
-
'PGConn'
|
157
|
-
'org.sqlite.Conn'
|
156
|
+
'PGConn' => 'PG::Connection',
|
157
|
+
'org.sqlite.Conn' => 'Java::OrgSqlite::Conn', # for some reason, org.sqlite.Conn doesn't have a ruby class name
|
158
|
+
'Sequel::Postgres::Adapter' => 'PG::Connection', # Only the Postgres adapter needs an alias
|
158
159
|
}
|
159
|
-
CREATED_COL_REGEX = /\Acreated_(at|on)\
|
160
|
+
CREATED_COL_REGEX = /\Acreated_(at|on)\z/
|
160
161
|
|
161
162
|
# @return [Upsert::Connection]
|
162
163
|
attr_reader :connection
|
@@ -6,6 +6,9 @@ class Upsert
|
|
6
6
|
def all(connection, table_name)
|
7
7
|
# activerecord-3.2.13/lib/active_record/connection_adapters/sqlite_adapter.rb
|
8
8
|
connection.execute("PRAGMA table_info(#{connection.quote_ident(table_name)})").map do |row|#, 'SCHEMA').to_hash
|
9
|
+
if connection.metal.respond_to?(:results_as_hash) and not connection.metal.results_as_hash
|
10
|
+
row = {'name' => row[1], 'type' => row[2], 'dflt_value' => row[4]}
|
11
|
+
end
|
9
12
|
default = case row["dflt_value"]
|
10
13
|
when /^null$/i
|
11
14
|
nil
|
@@ -8,6 +8,7 @@ class Upsert
|
|
8
8
|
java.sql.Types::OTHER => 'getString', # ?! i guess unicode text?
|
9
9
|
java.sql.Types::BINARY => 'getBlob',
|
10
10
|
java.sql.Types::LONGVARCHAR => 'getString',
|
11
|
+
java.sql.Types::INTEGER => 'getInt',
|
11
12
|
}
|
12
13
|
java.sql.Types.constants.each do |type_name|
|
13
14
|
i = java.sql.Types.const_get type_name
|
@@ -69,7 +70,11 @@ class Upsert
|
|
69
70
|
row = {}
|
70
71
|
column_name_and_getter.each do |i, cg|
|
71
72
|
column_name, getter = cg
|
72
|
-
|
73
|
+
if getter == 'getNull'
|
74
|
+
row[column_name] = nil
|
75
|
+
else
|
76
|
+
row[column_name] = raw_result.send(getter, i)
|
77
|
+
end
|
73
78
|
end
|
74
79
|
result << row
|
75
80
|
end
|
@@ -4,6 +4,15 @@ class Upsert
|
|
4
4
|
module Postgresql
|
5
5
|
def bind_value(v)
|
6
6
|
case v
|
7
|
+
when Array
|
8
|
+
# pg array escaping lifted from https://github.com/tlconnor/activerecord-postgres-array/blob/master/lib/activerecord-postgres-array/array.rb
|
9
|
+
'{' + v.map do |vv|
|
10
|
+
vv = vv.to_s
|
11
|
+
vv.gsub! /\\/, '\&\&'
|
12
|
+
vv.gsub! /'/, "''"
|
13
|
+
vv.gsub! /"/, '\"'
|
14
|
+
%{"#{vv}"}
|
15
|
+
end.join(',') + '}'
|
7
16
|
when Hash
|
8
17
|
# you must require 'pg_hstore' from the 'pg-hstore' gem yourself
|
9
18
|
::PgHstore.dump v, true
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'zlib'
|
2
|
+
require 'upsert/version'
|
2
3
|
|
3
4
|
class Upsert
|
4
5
|
# @private
|
5
6
|
class MergeFunction
|
6
7
|
MAX_NAME_LENGTH = 62
|
8
|
+
NAME_PREFIX = "upsert#{Upsert::VERSION.gsub('.', '_')}"
|
7
9
|
|
8
10
|
class << self
|
9
11
|
def execute(controller, row)
|
@@ -13,7 +15,7 @@ class Upsert
|
|
13
15
|
|
14
16
|
def unique_name(table_name, selector_keys, setter_keys)
|
15
17
|
parts = [
|
16
|
-
|
18
|
+
NAME_PREFIX,
|
17
19
|
table_name,
|
18
20
|
'SEL',
|
19
21
|
selector_keys.join('_A_'),
|
@@ -8,10 +8,14 @@ class Upsert
|
|
8
8
|
|
9
9
|
def execute(row)
|
10
10
|
first_try = true
|
11
|
-
|
12
|
-
|
11
|
+
values = []
|
12
|
+
values += row.selector.values
|
13
|
+
values += row.setter.values
|
14
|
+
hstore_delete_handlers.each do |hstore_delete_handler|
|
15
|
+
values << row.hstore_delete_keys.fetch(hstore_delete_handler.name, [])
|
16
|
+
end
|
13
17
|
begin
|
14
|
-
connection.execute sql,
|
18
|
+
connection.execute sql, values.map { |v| connection.bind_value v }
|
15
19
|
rescue org.postgresql.util.PSQLException => pg_error
|
16
20
|
if pg_error.message =~ /function #{name}.* does not exist/i
|
17
21
|
if first_try
|
@@ -8,10 +8,14 @@ class Upsert
|
|
8
8
|
|
9
9
|
def execute(row)
|
10
10
|
first_try = true
|
11
|
-
|
12
|
-
|
11
|
+
values = []
|
12
|
+
values += row.selector.values
|
13
|
+
values += row.setter.values
|
14
|
+
hstore_delete_handlers.each do |hstore_delete_handler|
|
15
|
+
values << row.hstore_delete_keys.fetch(hstore_delete_handler.name, [])
|
16
|
+
end
|
13
17
|
begin
|
14
|
-
connection.execute sql,
|
18
|
+
connection.execute sql, values.map { |v| connection.bind_value v }
|
15
19
|
rescue PG::Error => pg_error
|
16
20
|
if pg_error.message =~ /function #{name}.* does not exist/i
|
17
21
|
if first_try
|
@@ -33,7 +37,15 @@ class Upsert
|
|
33
37
|
def sql
|
34
38
|
@sql ||= begin
|
35
39
|
bind_params = []
|
36
|
-
|
40
|
+
i = 1
|
41
|
+
(selector_keys.length + setter_keys.length).times do
|
42
|
+
bind_params << "$#{i}"
|
43
|
+
i += 1
|
44
|
+
end
|
45
|
+
hstore_delete_handlers.length.times do
|
46
|
+
bind_params << "$#{i}::text[]"
|
47
|
+
i += 1
|
48
|
+
end
|
37
49
|
%{SELECT #{name}(#{bind_params.join(', ')})}
|
38
50
|
end
|
39
51
|
end
|
@@ -9,7 +9,7 @@ class Upsert
|
|
9
9
|
module ClassMethods
|
10
10
|
# http://stackoverflow.com/questions/733349/list-of-stored-procedures-functions-mysql-command-line
|
11
11
|
def clear(connection)
|
12
|
-
connection.execute("SHOW PROCEDURE STATUS WHERE Db = DATABASE() AND Name LIKE '
|
12
|
+
connection.execute("SHOW PROCEDURE STATUS WHERE Db = DATABASE() AND Name LIKE '#{MergeFunction::NAME_PREFIX}%'").map do |row|
|
13
13
|
row['Name'] || row['ROUTINE_NAME']
|
14
14
|
end.each do |name|
|
15
15
|
connection.execute "DROP PROCEDURE IF EXISTS #{connection.quote_ident(name)}"
|
@@ -31,7 +31,7 @@ class Upsert
|
|
31
31
|
$BODY$
|
32
32
|
LANGUAGE plpgsql;
|
33
33
|
})
|
34
|
-
connection.execute(%{SELECT proname FROM pg_proc WHERE proname LIKE '
|
34
|
+
connection.execute(%{SELECT proname FROM pg_proc WHERE proname LIKE '#{MergeFunction::NAME_PREFIX}%'}).each do |row|
|
35
35
|
k = row['proname']
|
36
36
|
next if k == 'upsert_delfunc'
|
37
37
|
Upsert.logger.info %{[upsert] Dropping function #{k.inspect}}
|
@@ -42,21 +42,69 @@ class Upsert
|
|
42
42
|
|
43
43
|
def sql
|
44
44
|
@sql ||= begin
|
45
|
-
bind_params = Array.new(selector_keys.length + setter_keys.length, '?')
|
45
|
+
bind_params = Array.new(selector_keys.length + setter_keys.length, '?') + Array.new(hstore_delete_handlers.length, '?::text[]')
|
46
46
|
%{SELECT #{name}(#{bind_params.join(', ')})}
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
class HstoreDeleteHandler
|
51
|
+
attr_reader :merge_function
|
52
|
+
attr_reader :column_definition
|
53
|
+
def initialize(merge_function, column_definition)
|
54
|
+
@merge_function = merge_function
|
55
|
+
@column_definition = column_definition
|
56
|
+
end
|
57
|
+
def name
|
58
|
+
column_definition.name
|
59
|
+
end
|
60
|
+
def to_arg
|
61
|
+
"#{quoted_name} text[]"
|
62
|
+
end
|
63
|
+
# use coalesce(foo, '{}':text[])
|
64
|
+
def to_setter
|
65
|
+
"#{column_definition.quoted_name} = DELETE(#{column_definition.quoted_name}, #{quoted_name})"
|
66
|
+
end
|
67
|
+
def to_pgsql
|
68
|
+
%{
|
69
|
+
IF array_length(#{quoted_name}, 1) > 0 THEN
|
70
|
+
UPDATE #{merge_function.quoted_table_name} SET #{to_setter}
|
71
|
+
WHERE #{merge_function.selector_column_definitions.map(&:to_selector).join(' AND ') };
|
72
|
+
END IF;
|
73
|
+
}.gsub(/\s+/, ' ')
|
74
|
+
end
|
75
|
+
private
|
76
|
+
def quoted_name
|
77
|
+
@quoted_name ||= merge_function.connection.quote_ident "_delete_#{column_definition.name}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def hstore_delete_handlers
|
82
|
+
@hstore_delete_handlers ||= setter_column_definitions.select do |column_definition|
|
83
|
+
column_definition.hstore?
|
84
|
+
end.map do |column_definition|
|
85
|
+
HstoreDeleteHandler.new self, column_definition
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def selector_column_definitions
|
90
|
+
column_definitions.select { |cd| selector_keys.include?(cd.name) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def setter_column_definitions
|
94
|
+
column_definitions.select { |cd| setter_keys.include?(cd.name) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_column_definitions
|
98
|
+
setter_column_definitions.select { |cd| cd.name !~ CREATED_COL_REGEX }
|
99
|
+
end
|
100
|
+
|
50
101
|
# the "canonical example" from http://www.postgresql.org/docs/9.1/static/plpgsql-control-structures.html#PLPGSQL-UPSERT-EXAMPLE
|
51
102
|
# differentiate between selector and setter
|
52
103
|
def create!
|
53
104
|
Upsert.logger.info "[upsert] Creating or replacing database function #{name.inspect} on table #{table_name.inspect} for selector #{selector_keys.map(&:inspect).join(', ')} and setter #{setter_keys.map(&:inspect).join(', ')}"
|
54
|
-
selector_column_definitions = column_definitions.select { |cd| selector_keys.include?(cd.name) }
|
55
|
-
setter_column_definitions = column_definitions.select { |cd| setter_keys.include?(cd.name) }
|
56
|
-
update_column_definitions = setter_column_definitions.select { |cd| cd.name !~ CREATED_COL_REGEX }
|
57
105
|
first_try = true
|
58
106
|
connection.execute(%{
|
59
|
-
CREATE OR REPLACE FUNCTION #{name}(#{(selector_column_definitions.map(&:to_selector_arg) + setter_column_definitions.map(&:to_setter_arg)).join(', ')}) RETURNS VOID AS
|
107
|
+
CREATE OR REPLACE FUNCTION #{name}(#{(selector_column_definitions.map(&:to_selector_arg) + setter_column_definitions.map(&:to_setter_arg) + hstore_delete_handlers.map(&:to_arg)).join(', ')}) RETURNS VOID AS
|
60
108
|
$$
|
61
109
|
DECLARE
|
62
110
|
first_try INTEGER := 1;
|
@@ -66,6 +114,7 @@ class Upsert
|
|
66
114
|
UPDATE #{quoted_table_name} SET #{update_column_definitions.map(&:to_setter).join(', ')}
|
67
115
|
WHERE #{selector_column_definitions.map(&:to_selector).join(' AND ') };
|
68
116
|
IF found THEN
|
117
|
+
#{hstore_delete_handlers.map(&:to_pgsql).join(' ')}
|
69
118
|
RETURN;
|
70
119
|
END IF;
|
71
120
|
-- not there, so try to insert the key
|
@@ -73,6 +122,7 @@ class Upsert
|
|
73
122
|
-- we could get a unique-key failure
|
74
123
|
BEGIN
|
75
124
|
INSERT INTO #{quoted_table_name}(#{setter_column_definitions.map(&:quoted_name).join(', ')}) VALUES (#{setter_column_definitions.map(&:to_setter_value).join(', ')});
|
125
|
+
#{hstore_delete_handlers.map(&:to_pgsql).join(' ')}
|
76
126
|
RETURN;
|
77
127
|
EXCEPTION WHEN unique_violation THEN
|
78
128
|
-- seamusabshere 9/20/12 only retry once
|
data/lib/upsert/row.rb
CHANGED
@@ -14,7 +14,7 @@ class Upsert
|
|
14
14
|
|
15
15
|
attr_reader :selector
|
16
16
|
attr_reader :setter
|
17
|
-
|
17
|
+
attr_reader :hstore_delete_keys
|
18
18
|
|
19
19
|
def initialize(raw_selector, raw_setter)
|
20
20
|
@selector = raw_selector.inject({}) do |memo, (k, v)|
|
@@ -22,8 +22,17 @@ class Upsert
|
|
22
22
|
memo
|
23
23
|
end
|
24
24
|
|
25
|
+
@hstore_delete_keys = {}
|
25
26
|
@setter = raw_setter.inject({}) do |memo, (k, v)|
|
26
|
-
|
27
|
+
k = k.to_s
|
28
|
+
if v.is_a?(::Hash)
|
29
|
+
v.each do |kk, vv|
|
30
|
+
if vv.nil?
|
31
|
+
(@hstore_delete_keys[k] ||= []) << kk
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
memo[k] = v
|
27
36
|
memo
|
28
37
|
end
|
29
38
|
|
data/lib/upsert/version.rb
CHANGED
data/spec/hstore_spec.rb
CHANGED
@@ -5,6 +5,11 @@ describe Upsert do
|
|
5
5
|
require 'pg_hstore'
|
6
6
|
Pet.connection.execute 'CREATE EXTENSION HSTORE'
|
7
7
|
Pet.connection.execute "ALTER TABLE pets ADD COLUMN crazy HSTORE"
|
8
|
+
Pet.connection.execute "ALTER TABLE pets ADD COLUMN cool HSTORE"
|
9
|
+
|
10
|
+
before do
|
11
|
+
Pet.delete_all
|
12
|
+
end
|
8
13
|
|
9
14
|
it "works for ugly text" do
|
10
15
|
upsert = Upsert.new $conn, :pets
|
@@ -53,5 +58,177 @@ EOS
|
|
53
58
|
crazy = PgHstore.parse row['crazy']
|
54
59
|
crazy.should == { a: '2', whatdat: "D'ONOFRIO" }
|
55
60
|
end
|
61
|
+
|
62
|
+
it "can nullify entire hstore" do
|
63
|
+
upsert = Upsert.new $conn, :pets
|
64
|
+
|
65
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1})
|
66
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
67
|
+
crazy = PgHstore.parse row['crazy']
|
68
|
+
crazy.should == { a: '1' }
|
69
|
+
|
70
|
+
upsert.row({name: 'Bill'}, crazy: nil)
|
71
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
72
|
+
row['crazy'].should == nil
|
73
|
+
end
|
74
|
+
|
75
|
+
it "deletes keys that are nil" do
|
76
|
+
upsert = Upsert.new $conn, :pets
|
77
|
+
|
78
|
+
upsert.row({name: 'Bill'}, crazy: nil)
|
79
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
80
|
+
row['crazy'].should == nil
|
81
|
+
|
82
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1})
|
83
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
84
|
+
crazy = PgHstore.parse row['crazy']
|
85
|
+
crazy.should == { a: '1' }
|
86
|
+
|
87
|
+
upsert.row({name: 'Bill'}, crazy: {})
|
88
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
89
|
+
crazy = PgHstore.parse row['crazy']
|
90
|
+
crazy.should == { a: '1' }
|
91
|
+
|
92
|
+
upsert.row({name: 'Bill'}, crazy: {a: nil})
|
93
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
94
|
+
crazy = PgHstore.parse row['crazy']
|
95
|
+
crazy.should == {}
|
96
|
+
|
97
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1, b: 5})
|
98
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
99
|
+
crazy = PgHstore.parse row['crazy']
|
100
|
+
crazy.should == { a: '1', b: '5' }
|
101
|
+
|
102
|
+
upsert.row({name: 'Bill'}, crazy: {})
|
103
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
104
|
+
crazy = PgHstore.parse row['crazy']
|
105
|
+
crazy.should == { a: '1', b: '5' }
|
106
|
+
|
107
|
+
upsert.row({name: 'Bill'}, crazy: {a: nil})
|
108
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
109
|
+
crazy = PgHstore.parse row['crazy']
|
110
|
+
crazy.should == { b: '5' }
|
111
|
+
|
112
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1, b: 5})
|
113
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
114
|
+
crazy = PgHstore.parse row['crazy']
|
115
|
+
crazy.should == { a: '1', b: '5' }
|
116
|
+
|
117
|
+
upsert.row({name: 'Bill'}, crazy: {a: nil, b: nil, c: 12})
|
118
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
119
|
+
crazy = PgHstore.parse row['crazy']
|
120
|
+
crazy.should == { c: '12' }
|
121
|
+
end
|
122
|
+
|
123
|
+
it "takes dangerous keys" do
|
124
|
+
upsert = Upsert.new $conn, :pets
|
125
|
+
|
126
|
+
upsert.row({name: 'Bill'}, crazy: nil)
|
127
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
128
|
+
row['crazy'].should == nil
|
129
|
+
|
130
|
+
upsert.row({name: 'Bill'}, crazy: {:'foo"bar' => 1})
|
131
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
132
|
+
crazy = PgHstore.parse row['crazy']
|
133
|
+
crazy.should == { :'foo"bar' => '1' }
|
134
|
+
|
135
|
+
upsert.row({name: 'Bill'}, crazy: {})
|
136
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
137
|
+
crazy = PgHstore.parse row['crazy']
|
138
|
+
crazy.should == { :'foo"bar' => '1' }
|
139
|
+
|
140
|
+
upsert.row({name: 'Bill'}, crazy: {:'foo"bar' => nil})
|
141
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
142
|
+
crazy = PgHstore.parse row['crazy']
|
143
|
+
crazy.should == {}
|
144
|
+
|
145
|
+
upsert.row({name: 'Bill'}, crazy: {:'foo"bar' => 1, b: 5})
|
146
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
147
|
+
crazy = PgHstore.parse row['crazy']
|
148
|
+
crazy.should == { :'foo"bar' => '1', b: '5' }
|
149
|
+
|
150
|
+
upsert.row({name: 'Bill'}, crazy: {})
|
151
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
152
|
+
crazy = PgHstore.parse row['crazy']
|
153
|
+
crazy.should == { :'foo"bar' => '1', b: '5' }
|
154
|
+
|
155
|
+
upsert.row({name: 'Bill'}, crazy: {:'foo"bar' => nil})
|
156
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
157
|
+
crazy = PgHstore.parse row['crazy']
|
158
|
+
crazy.should == { b: '5' }
|
159
|
+
|
160
|
+
upsert.row({name: 'Bill'}, crazy: {:'foo"bar' => 1, b: 5})
|
161
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
162
|
+
crazy = PgHstore.parse row['crazy']
|
163
|
+
crazy.should == { :'foo"bar' => '1', b: '5' }
|
164
|
+
|
165
|
+
upsert.row({name: 'Bill'}, crazy: {:'foo"bar' => nil, b: nil, c: 12})
|
166
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
167
|
+
crazy = PgHstore.parse row['crazy']
|
168
|
+
crazy.should == { c: '12' }
|
169
|
+
end
|
170
|
+
|
171
|
+
it "handles multiple hstores" do
|
172
|
+
upsert = Upsert.new $conn, :pets
|
173
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1, b: 9}, cool: {c: 12, d: 19})
|
174
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
175
|
+
crazy = PgHstore.parse row['crazy']
|
176
|
+
crazy.should == { a: '1', b: '9' }
|
177
|
+
cool = PgHstore.parse row['cool']
|
178
|
+
cool.should == { c: '12', d: '19' }
|
179
|
+
end
|
180
|
+
|
181
|
+
it "can deletes keys from multiple hstores at once" do
|
182
|
+
upsert = Upsert.new $conn, :pets
|
183
|
+
|
184
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1}, cool: {5 => 9})
|
185
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
186
|
+
crazy = PgHstore.parse row['crazy']
|
187
|
+
crazy.should == { a: '1' }
|
188
|
+
cool = PgHstore.parse row['cool'], false
|
189
|
+
cool.should == { '5' => '9' }
|
190
|
+
|
191
|
+
# NOOP
|
192
|
+
upsert.row({name: 'Bill'}, crazy: {}, cool: {})
|
193
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
194
|
+
crazy = PgHstore.parse row['crazy']
|
195
|
+
crazy.should == { a: '1' }
|
196
|
+
cool = PgHstore.parse row['cool'], false
|
197
|
+
cool.should == { '5' => '9' }
|
198
|
+
|
199
|
+
upsert.row({name: 'Bill'}, crazy: {a: nil}, cool: {13 => 17})
|
200
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
201
|
+
crazy = PgHstore.parse row['crazy']
|
202
|
+
crazy.should == {}
|
203
|
+
cool = PgHstore.parse row['cool'], false
|
204
|
+
cool.should == { '5' => '9', '13' => '17' }
|
205
|
+
|
206
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1, b: 5})
|
207
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
208
|
+
crazy = PgHstore.parse row['crazy']
|
209
|
+
crazy.should == { a: '1', b: '5' }
|
210
|
+
|
211
|
+
upsert.row({name: 'Bill'}, crazy: {b: nil}, cool: {5 => nil})
|
212
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
213
|
+
crazy = PgHstore.parse row['crazy']
|
214
|
+
crazy.should == {a: '1'}
|
215
|
+
cool = PgHstore.parse row['cool'], false
|
216
|
+
cool.should == {'13' => '17' }
|
217
|
+
end
|
218
|
+
|
219
|
+
it "deletes keys whether new or existing record" do
|
220
|
+
upsert = Upsert.new $conn, :pets
|
221
|
+
|
222
|
+
upsert.row({name: 'Bill'}, crazy: {z: 1, x: nil})
|
223
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
224
|
+
crazy = PgHstore.parse row['crazy'], false
|
225
|
+
crazy.should == { 'z' => '1' }
|
226
|
+
|
227
|
+
upsert.row({name: 'Bill'}, crazy: {a: 1})
|
228
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
229
|
+
crazy = PgHstore.parse row['crazy'], false
|
230
|
+
crazy.should == { 'a' => '1', 'z' => '1' }
|
231
|
+
end
|
232
|
+
|
56
233
|
end
|
57
234
|
end if ENV['DB'] == 'postgresql'
|
data/spec/logger_spec.rb
CHANGED
@@ -34,9 +34,9 @@ describe Upsert do
|
|
34
34
|
when /sqlite/i
|
35
35
|
log.should =~ /insert or ignore/i
|
36
36
|
when /mysql/i
|
37
|
-
log.should =~ /call
|
37
|
+
log.should =~ /call #{Upsert::MergeFunction::NAME_PREFIX}_pets_SEL_name/i
|
38
38
|
when /p.*g/i
|
39
|
-
log.should =~ /select
|
39
|
+
log.should =~ /select #{Upsert::MergeFunction::NAME_PREFIX}_pets_SEL_name/i
|
40
40
|
else
|
41
41
|
raise "not sure"
|
42
42
|
end
|
data/spec/sequel_spec.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sequel'
|
3
|
+
|
4
|
+
describe Upsert do
|
5
|
+
describe "Plays nice with Sequel" do
|
6
|
+
config = ActiveRecord::Base.connection.instance_variable_get(:@config)
|
7
|
+
case
|
8
|
+
when 'postgresql' == config[:adapter]; config[:adapter] = 'postgres'
|
9
|
+
when 'sqlite3' == config[:adapter]; config[:adapter] = 'sqlite'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Doesn't explode on connection" do
|
13
|
+
expect { DB = Sequel.connect config }.to_not raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it "Doesn't explode when using DB.pool.hold" do
|
17
|
+
DB.pool.hold do |conn|
|
18
|
+
expect {
|
19
|
+
upsert = Upsert.new(conn, :pets)
|
20
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male'}]) do
|
21
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'male'})
|
22
|
+
end
|
23
|
+
}.to_not raise_error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "Doesn't explode when using DB.synchronize" do
|
28
|
+
DB.synchronize do |conn|
|
29
|
+
expect {
|
30
|
+
upsert = Upsert.new(conn, :pets)
|
31
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male'}]) do
|
32
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'male'})
|
33
|
+
end
|
34
|
+
}.to_not raise_error
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -25,7 +25,8 @@ class RawConnectionFactory
|
|
25
25
|
CONFIG = "jdbc:postgresql://localhost/#{DATABASE}?user=#{CURRENT_USER}"
|
26
26
|
require 'jdbc/postgres'
|
27
27
|
# http://thesymanual.wordpress.com/2011/02/21/connecting-jruby-to-postgresql-with-jdbc-postgre-api/
|
28
|
-
|
28
|
+
Jdbc::Postgres.load_driver
|
29
|
+
# java.sql.DriverManager.register_driver org.postgresql.Driver.new
|
29
30
|
def new_connection
|
30
31
|
java.sql.DriverManager.get_connection CONFIG
|
31
32
|
end
|
@@ -44,7 +45,8 @@ class RawConnectionFactory
|
|
44
45
|
if RUBY_PLATFORM == 'java'
|
45
46
|
CONFIG = "jdbc:mysql://127.0.0.1/#{DATABASE}?user=root&password=password"
|
46
47
|
require 'jdbc/mysql'
|
47
|
-
|
48
|
+
Jdbc::MySQL.load_driver
|
49
|
+
# java.sql.DriverManager.register_driver com.mysql.jdbc.Driver.new
|
48
50
|
def new_connection
|
49
51
|
java.sql.DriverManager.get_connection CONFIG
|
50
52
|
end
|
@@ -58,7 +60,11 @@ class RawConnectionFactory
|
|
58
60
|
ActiveRecord::Base.establish_connection "#{RUBY_PLATFORM == 'java' ? 'mysql' : 'mysql2'}://root:password@127.0.0.1/#{DATABASE}"
|
59
61
|
|
60
62
|
when 'sqlite3'
|
63
|
+
CONFIG = { :adapter => 'sqlite3', :database => 'file::memory:?cache=shared' }
|
61
64
|
if RUBY_PLATFORM == 'java'
|
65
|
+
# CONFIG = 'jdbc:sqlite://test.sqlite3'
|
66
|
+
require 'jdbc/sqlite3'
|
67
|
+
Jdbc::SQLite3.load_driver
|
62
68
|
def new_connection
|
63
69
|
ActiveRecord::Base.connection.raw_connection.connection
|
64
70
|
end
|
@@ -68,7 +74,10 @@ class RawConnectionFactory
|
|
68
74
|
ActiveRecord::Base.connection.raw_connection
|
69
75
|
end
|
70
76
|
end
|
71
|
-
ActiveRecord::Base.establish_connection
|
77
|
+
ActiveRecord::Base.establish_connection CONFIG
|
78
|
+
|
79
|
+
when 'postgres'
|
80
|
+
raise "please use DB=postgresql NOT postgres"
|
72
81
|
|
73
82
|
else
|
74
83
|
raise "not supported"
|
data/upsert.gemspec
CHANGED
@@ -22,13 +22,14 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_development_dependency 'rspec-expectations'
|
23
23
|
gem.add_development_dependency 'rspec-mocks'
|
24
24
|
|
25
|
-
gem.add_development_dependency 'activerecord'
|
25
|
+
gem.add_development_dependency 'activerecord', '~>3'
|
26
26
|
gem.add_development_dependency 'active_record_inline_schema'
|
27
27
|
gem.add_development_dependency 'faker'
|
28
28
|
gem.add_development_dependency 'yard'
|
29
29
|
gem.add_development_dependency 'activerecord-import'
|
30
30
|
gem.add_development_dependency 'pry'
|
31
31
|
gem.add_development_dependency 'pg-hstore', ">=1.1.3"
|
32
|
+
gem.add_development_dependency 'sequel'
|
32
33
|
|
33
34
|
unless RUBY_VERSION >= '1.9'
|
34
35
|
gem.add_development_dependency 'orderedhash'
|
metadata
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: upsert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Seamus Abshere
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2013-
|
12
|
+
date: 2013-07-24 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: rspec-core
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
16
18
|
requirements:
|
17
19
|
- - ! '>='
|
18
20
|
- !ruby/object:Gem::Version
|
@@ -20,6 +22,7 @@ dependencies:
|
|
20
22
|
type: :development
|
21
23
|
prerelease: false
|
22
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
23
26
|
requirements:
|
24
27
|
- - ! '>='
|
25
28
|
- !ruby/object:Gem::Version
|
@@ -27,6 +30,7 @@ dependencies:
|
|
27
30
|
- !ruby/object:Gem::Dependency
|
28
31
|
name: rspec-expectations
|
29
32
|
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
30
34
|
requirements:
|
31
35
|
- - ! '>='
|
32
36
|
- !ruby/object:Gem::Version
|
@@ -34,6 +38,7 @@ dependencies:
|
|
34
38
|
type: :development
|
35
39
|
prerelease: false
|
36
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
37
42
|
requirements:
|
38
43
|
- - ! '>='
|
39
44
|
- !ruby/object:Gem::Version
|
@@ -41,6 +46,7 @@ dependencies:
|
|
41
46
|
- !ruby/object:Gem::Dependency
|
42
47
|
name: rspec-mocks
|
43
48
|
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
44
50
|
requirements:
|
45
51
|
- - ! '>='
|
46
52
|
- !ruby/object:Gem::Version
|
@@ -48,6 +54,7 @@ dependencies:
|
|
48
54
|
type: :development
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
51
58
|
requirements:
|
52
59
|
- - ! '>='
|
53
60
|
- !ruby/object:Gem::Version
|
@@ -55,20 +62,23 @@ dependencies:
|
|
55
62
|
- !ruby/object:Gem::Dependency
|
56
63
|
name: activerecord
|
57
64
|
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
58
66
|
requirements:
|
59
|
-
- -
|
67
|
+
- - ~>
|
60
68
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
69
|
+
version: '3'
|
62
70
|
type: :development
|
63
71
|
prerelease: false
|
64
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
65
74
|
requirements:
|
66
|
-
- -
|
75
|
+
- - ~>
|
67
76
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
77
|
+
version: '3'
|
69
78
|
- !ruby/object:Gem::Dependency
|
70
79
|
name: active_record_inline_schema
|
71
80
|
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
72
82
|
requirements:
|
73
83
|
- - ! '>='
|
74
84
|
- !ruby/object:Gem::Version
|
@@ -76,6 +86,7 @@ dependencies:
|
|
76
86
|
type: :development
|
77
87
|
prerelease: false
|
78
88
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
79
90
|
requirements:
|
80
91
|
- - ! '>='
|
81
92
|
- !ruby/object:Gem::Version
|
@@ -83,6 +94,7 @@ dependencies:
|
|
83
94
|
- !ruby/object:Gem::Dependency
|
84
95
|
name: faker
|
85
96
|
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
86
98
|
requirements:
|
87
99
|
- - ! '>='
|
88
100
|
- !ruby/object:Gem::Version
|
@@ -90,6 +102,7 @@ dependencies:
|
|
90
102
|
type: :development
|
91
103
|
prerelease: false
|
92
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
93
106
|
requirements:
|
94
107
|
- - ! '>='
|
95
108
|
- !ruby/object:Gem::Version
|
@@ -97,6 +110,7 @@ dependencies:
|
|
97
110
|
- !ruby/object:Gem::Dependency
|
98
111
|
name: yard
|
99
112
|
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
100
114
|
requirements:
|
101
115
|
- - ! '>='
|
102
116
|
- !ruby/object:Gem::Version
|
@@ -104,6 +118,7 @@ dependencies:
|
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
107
122
|
requirements:
|
108
123
|
- - ! '>='
|
109
124
|
- !ruby/object:Gem::Version
|
@@ -111,6 +126,7 @@ dependencies:
|
|
111
126
|
- !ruby/object:Gem::Dependency
|
112
127
|
name: activerecord-import
|
113
128
|
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
114
130
|
requirements:
|
115
131
|
- - ! '>='
|
116
132
|
- !ruby/object:Gem::Version
|
@@ -118,6 +134,7 @@ dependencies:
|
|
118
134
|
type: :development
|
119
135
|
prerelease: false
|
120
136
|
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
121
138
|
requirements:
|
122
139
|
- - ! '>='
|
123
140
|
- !ruby/object:Gem::Version
|
@@ -125,6 +142,7 @@ dependencies:
|
|
125
142
|
- !ruby/object:Gem::Dependency
|
126
143
|
name: pry
|
127
144
|
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
128
146
|
requirements:
|
129
147
|
- - ! '>='
|
130
148
|
- !ruby/object:Gem::Version
|
@@ -132,6 +150,7 @@ dependencies:
|
|
132
150
|
type: :development
|
133
151
|
prerelease: false
|
134
152
|
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
135
154
|
requirements:
|
136
155
|
- - ! '>='
|
137
156
|
- !ruby/object:Gem::Version
|
@@ -139,6 +158,7 @@ dependencies:
|
|
139
158
|
- !ruby/object:Gem::Dependency
|
140
159
|
name: pg-hstore
|
141
160
|
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
142
162
|
requirements:
|
143
163
|
- - ! '>='
|
144
164
|
- !ruby/object:Gem::Version
|
@@ -146,13 +166,31 @@ dependencies:
|
|
146
166
|
type: :development
|
147
167
|
prerelease: false
|
148
168
|
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
149
170
|
requirements:
|
150
171
|
- - ! '>='
|
151
172
|
- !ruby/object:Gem::Version
|
152
173
|
version: 1.1.3
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: sequel
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
153
190
|
- !ruby/object:Gem::Dependency
|
154
191
|
name: sqlite3
|
155
192
|
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
156
194
|
requirements:
|
157
195
|
- - ! '>='
|
158
196
|
- !ruby/object:Gem::Version
|
@@ -160,6 +198,7 @@ dependencies:
|
|
160
198
|
type: :development
|
161
199
|
prerelease: false
|
162
200
|
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
163
202
|
requirements:
|
164
203
|
- - ! '>='
|
165
204
|
- !ruby/object:Gem::Version
|
@@ -167,6 +206,7 @@ dependencies:
|
|
167
206
|
- !ruby/object:Gem::Dependency
|
168
207
|
name: mysql2
|
169
208
|
requirement: !ruby/object:Gem::Requirement
|
209
|
+
none: false
|
170
210
|
requirements:
|
171
211
|
- - ! '>='
|
172
212
|
- !ruby/object:Gem::Version
|
@@ -174,6 +214,7 @@ dependencies:
|
|
174
214
|
type: :development
|
175
215
|
prerelease: false
|
176
216
|
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
177
218
|
requirements:
|
178
219
|
- - ! '>='
|
179
220
|
- !ruby/object:Gem::Version
|
@@ -181,6 +222,7 @@ dependencies:
|
|
181
222
|
- !ruby/object:Gem::Dependency
|
182
223
|
name: pg
|
183
224
|
requirement: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
184
226
|
requirements:
|
185
227
|
- - ! '>='
|
186
228
|
- !ruby/object:Gem::Version
|
@@ -188,6 +230,7 @@ dependencies:
|
|
188
230
|
type: :development
|
189
231
|
prerelease: false
|
190
232
|
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
191
234
|
requirements:
|
192
235
|
- - ! '>='
|
193
236
|
- !ruby/object:Gem::Version
|
@@ -195,6 +238,7 @@ dependencies:
|
|
195
238
|
- !ruby/object:Gem::Dependency
|
196
239
|
name: redcarpet
|
197
240
|
requirement: !ruby/object:Gem::Requirement
|
241
|
+
none: false
|
198
242
|
requirements:
|
199
243
|
- - ! '>='
|
200
244
|
- !ruby/object:Gem::Version
|
@@ -202,6 +246,7 @@ dependencies:
|
|
202
246
|
type: :development
|
203
247
|
prerelease: false
|
204
248
|
version_requirements: !ruby/object:Gem::Requirement
|
249
|
+
none: false
|
205
250
|
requirements:
|
206
251
|
- - ! '>='
|
207
252
|
- !ruby/object:Gem::Version
|
@@ -209,6 +254,7 @@ dependencies:
|
|
209
254
|
- !ruby/object:Gem::Dependency
|
210
255
|
name: rake
|
211
256
|
requirement: !ruby/object:Gem::Requirement
|
257
|
+
none: false
|
212
258
|
requirements:
|
213
259
|
- - ! '>='
|
214
260
|
- !ruby/object:Gem::Version
|
@@ -216,6 +262,7 @@ dependencies:
|
|
216
262
|
type: :development
|
217
263
|
prerelease: false
|
218
264
|
version_requirements: !ruby/object:Gem::Requirement
|
265
|
+
none: false
|
219
266
|
requirements:
|
220
267
|
- - ! '>='
|
221
268
|
- !ruby/object:Gem::Version
|
@@ -245,7 +292,7 @@ files:
|
|
245
292
|
- lib/upsert/connection.rb
|
246
293
|
- lib/upsert/connection/Java_ComMysqlJdbc_JDBC4Connection.rb
|
247
294
|
- lib/upsert/connection/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb
|
248
|
-
- lib/upsert/connection/
|
295
|
+
- lib/upsert/connection/Java_OrgSqlite_Conn.rb
|
249
296
|
- lib/upsert/connection/Mysql2_Client.rb
|
250
297
|
- lib/upsert/connection/PG_Connection.rb
|
251
298
|
- lib/upsert/connection/SQLite3_Database.rb
|
@@ -255,7 +302,7 @@ files:
|
|
255
302
|
- lib/upsert/merge_function.rb
|
256
303
|
- lib/upsert/merge_function/Java_ComMysqlJdbc_JDBC4Connection.rb
|
257
304
|
- lib/upsert/merge_function/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb
|
258
|
-
- lib/upsert/merge_function/
|
305
|
+
- lib/upsert/merge_function/Java_OrgSqlite_Conn.rb
|
259
306
|
- lib/upsert/merge_function/Mysql2_Client.rb
|
260
307
|
- lib/upsert/merge_function/PG_Connection.rb
|
261
308
|
- lib/upsert/merge_function/SQLite3_Database.rb
|
@@ -277,6 +324,7 @@ files:
|
|
277
324
|
- spec/multibyte_spec.rb
|
278
325
|
- spec/precision_spec.rb
|
279
326
|
- spec/reserved_words_spec.rb
|
327
|
+
- spec/sequel_spec.rb
|
280
328
|
- spec/spec_helper.rb
|
281
329
|
- spec/speed_spec.rb
|
282
330
|
- spec/threaded_spec.rb
|
@@ -285,26 +333,27 @@ files:
|
|
285
333
|
- upsert.gemspec
|
286
334
|
homepage: https://github.com/seamusabshere/upsert
|
287
335
|
licenses: []
|
288
|
-
metadata: {}
|
289
336
|
post_install_message:
|
290
337
|
rdoc_options: []
|
291
338
|
require_paths:
|
292
339
|
- lib
|
293
340
|
required_ruby_version: !ruby/object:Gem::Requirement
|
341
|
+
none: false
|
294
342
|
requirements:
|
295
343
|
- - ! '>='
|
296
344
|
- !ruby/object:Gem::Version
|
297
345
|
version: '0'
|
298
346
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
347
|
+
none: false
|
299
348
|
requirements:
|
300
349
|
- - ! '>='
|
301
350
|
- !ruby/object:Gem::Version
|
302
351
|
version: '0'
|
303
352
|
requirements: []
|
304
353
|
rubyforge_project:
|
305
|
-
rubygems_version:
|
354
|
+
rubygems_version: 1.8.25
|
306
355
|
signing_key:
|
307
|
-
specification_version:
|
356
|
+
specification_version: 3
|
308
357
|
summary: Make it easy to upsert on MySQL, PostgreSQL, and SQLite3. Transparently creates
|
309
358
|
merge functions for MySQL and PostgreSQL; on SQLite3, uses INSERT OR IGNORE.
|
310
359
|
test_files:
|
@@ -321,6 +370,7 @@ test_files:
|
|
321
370
|
- spec/multibyte_spec.rb
|
322
371
|
- spec/precision_spec.rb
|
323
372
|
- spec/reserved_words_spec.rb
|
373
|
+
- spec/sequel_spec.rb
|
324
374
|
- spec/spec_helper.rb
|
325
375
|
- spec/speed_spec.rb
|
326
376
|
- spec/threaded_spec.rb
|
checksums.yaml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
---
|
2
|
-
!binary "U0hBMQ==":
|
3
|
-
metadata.gz: !binary |-
|
4
|
-
YTE3MDYwZTVkNjkyOWUwOTc4MmNjYTFmOWI4OGNhZjFkYjk5ZjE0YQ==
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
MzY5ZDAzZGViY2E5NzQ4NThjOTJiNTg3ZmQ1NTEyMjhmMjRjMjVlNw==
|
7
|
-
!binary "U0hBNTEy":
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
MzZjZjlmYmI4Y2QxZWMxMDAwYTcwMjUwMDc1YTZhMzU3MTA0YTY4MWQzNWE4
|
10
|
-
OWMyZDBlOTVmZGNmNTQzZTQ5MzM4NjgxNTFkNzA0MTNhYzIyYWI0N2VmYTY2
|
11
|
-
MDA1YjQ4NjY3Mzg5NWVjMDQ0YmViMzA2NmM1NDZjNmIxNWM2MGM=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MDM2ZTY0YTRlYTVhMmExZGZmMTAyYTEzMjc5YTFmYWVjNjY2MDIxZTg2ODMw
|
14
|
-
YzQ2OWExNmNhNmRjZmQ2YTAxNjdjOTY3NWRhZDQwMjZjNzA2Mzg0NDY0NjBj
|
15
|
-
ZDUzMzhhZjQzNTY5YjE5YmU3NzI0OTkzMTNlZjdmZDg0YjQ4NDE=
|