upsert 2.9.9-universal-java-11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.ruby-version +1 -0
- data/.standard.yml +1 -0
- data/.travis.yml +63 -0
- data/.yardopts +2 -0
- data/CHANGELOG +265 -0
- data/Gemfile +16 -0
- data/LICENSE +24 -0
- data/README.md +411 -0
- data/Rakefile +54 -0
- data/lib/upsert.rb +284 -0
- data/lib/upsert/active_record_upsert.rb +12 -0
- data/lib/upsert/binary.rb +8 -0
- data/lib/upsert/column_definition.rb +79 -0
- data/lib/upsert/column_definition/mysql.rb +24 -0
- data/lib/upsert/column_definition/postgresql.rb +66 -0
- data/lib/upsert/column_definition/sqlite3.rb +34 -0
- data/lib/upsert/connection.rb +37 -0
- data/lib/upsert/connection/Java_ComMysqlJdbc_JDBC4Connection.rb +31 -0
- data/lib/upsert/connection/Java_OrgPostgresqlJdbc_PgConnection.rb +33 -0
- data/lib/upsert/connection/Java_OrgSqlite_Conn.rb +17 -0
- data/lib/upsert/connection/Mysql2_Client.rb +76 -0
- data/lib/upsert/connection/PG_Connection.rb +35 -0
- data/lib/upsert/connection/SQLite3_Database.rb +28 -0
- data/lib/upsert/connection/jdbc.rb +105 -0
- data/lib/upsert/connection/postgresql.rb +24 -0
- data/lib/upsert/connection/sqlite3.rb +19 -0
- data/lib/upsert/merge_function.rb +73 -0
- data/lib/upsert/merge_function/Java_ComMysqlJdbc_JDBC4Connection.rb +42 -0
- data/lib/upsert/merge_function/Java_OrgPostgresqlJdbc_PgConnection.rb +27 -0
- data/lib/upsert/merge_function/Java_OrgSqlite_Conn.rb +10 -0
- data/lib/upsert/merge_function/Mysql2_Client.rb +36 -0
- data/lib/upsert/merge_function/PG_Connection.rb +26 -0
- data/lib/upsert/merge_function/SQLite3_Database.rb +10 -0
- data/lib/upsert/merge_function/mysql.rb +66 -0
- data/lib/upsert/merge_function/postgresql.rb +365 -0
- data/lib/upsert/merge_function/sqlite3.rb +43 -0
- data/lib/upsert/row.rb +59 -0
- data/lib/upsert/version.rb +3 -0
- data/spec/active_record_upsert_spec.rb +26 -0
- data/spec/binary_spec.rb +21 -0
- data/spec/correctness_spec.rb +190 -0
- data/spec/database_functions_spec.rb +106 -0
- data/spec/database_spec.rb +121 -0
- data/spec/hstore_spec.rb +249 -0
- data/spec/jruby_spec.rb +9 -0
- data/spec/logger_spec.rb +52 -0
- data/spec/misc/get_postgres_reserved_words.rb +12 -0
- data/spec/misc/mysql_reserved.txt +226 -0
- data/spec/misc/pg_reserved.txt +742 -0
- data/spec/multibyte_spec.rb +27 -0
- data/spec/postgresql_spec.rb +94 -0
- data/spec/precision_spec.rb +11 -0
- data/spec/reserved_words_spec.rb +50 -0
- data/spec/sequel_spec.rb +57 -0
- data/spec/spec_helper.rb +417 -0
- data/spec/speed_spec.rb +44 -0
- data/spec/threaded_spec.rb +57 -0
- data/spec/timezones_spec.rb +58 -0
- data/spec/type_safety_spec.rb +12 -0
- data/travis/install_postgres.sh +18 -0
- data/travis/run_docker_db.sh +20 -0
- data/travis/tune_mysql.sh +7 -0
- data/upsert-java.gemspec +13 -0
- data/upsert.gemspec +11 -0
- data/upsert.gemspec.common +107 -0
- metadata +373 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe Upsert do
|
3
|
+
describe "is a database with an upsert trick" do
|
4
|
+
describe :row do
|
5
|
+
it "works for a single row (base case)" do
|
6
|
+
upsert = Upsert.new $conn, :pets
|
7
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male'}]) do
|
8
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'male'})
|
9
|
+
end
|
10
|
+
end
|
11
|
+
it "works for complex selectors" do
|
12
|
+
upsert = Upsert.new $conn, :pets
|
13
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male', :tag_number => 4}]) do
|
14
|
+
upsert.row({:name => 'Jerry', :gender => 'male'}, {:tag_number => 1})
|
15
|
+
upsert.row({:name => 'Jerry', :gender => 'male'}, {:tag_number => 4})
|
16
|
+
end
|
17
|
+
end
|
18
|
+
it "doesn't nullify columns that are not included in the selector or setter" do
|
19
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male', :tag_number => 4}]) do
|
20
|
+
one = Upsert.new $conn, :pets
|
21
|
+
one.row({:name => 'Jerry'}, {:gender => 'male'})
|
22
|
+
two = Upsert.new $conn, :pets
|
23
|
+
two.row({:name => 'Jerry'}, {:tag_number => 4})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
it "works for a single row (not changing anything)" do
|
27
|
+
upsert = Upsert.new $conn, :pets
|
28
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male'}]) do
|
29
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'male'})
|
30
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'male'})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
it "works for a single row (changing something)" do
|
34
|
+
upsert = Upsert.new $conn, :pets
|
35
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'neutered'}]) do
|
36
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'male'})
|
37
|
+
upsert.row({:name => 'Jerry'}, {:gender => 'neutered'})
|
38
|
+
end
|
39
|
+
Pet.where(:gender => 'male').count.should == 0
|
40
|
+
end
|
41
|
+
it "works for a single row with implicit nulls" do
|
42
|
+
upsert = Upsert.new $conn, :pets
|
43
|
+
assert_creates(Pet, [{:name => 'Inky', :gender => nil}]) do
|
44
|
+
upsert.row({:name => 'Inky'}, {})
|
45
|
+
upsert.row({:name => 'Inky'}, {})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
it "works for a single row with empty setter" do
|
49
|
+
upsert = Upsert.new $conn, :pets
|
50
|
+
assert_creates(Pet, [{:name => 'Inky', :gender => nil}]) do
|
51
|
+
upsert.row(:name => 'Inky')
|
52
|
+
upsert.row(:name => 'Inky')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
it "works for a single row with explicit nulls" do
|
56
|
+
upsert = Upsert.new $conn, :pets
|
57
|
+
assert_creates(Pet, [{:name => 'Inky', :gender => nil}]) do
|
58
|
+
upsert.row({:name => 'Inky'}, {:gender => nil})
|
59
|
+
upsert.row({:name => 'Inky'}, {:gender => nil})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
it "works with ids" do
|
63
|
+
jerry = Pet.create :name => 'Jerry', :lovability => 1.0
|
64
|
+
upsert = Upsert.new $conn, :pets
|
65
|
+
assert_creates(Pet, [{:name => 'Jerry', :lovability => 2.0}]) do
|
66
|
+
upsert.row({:id => jerry.id}, :lovability => 2.0)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
it "does not set the created_at and created_on columns on update" do
|
70
|
+
task = Task.create :name => 'Clean bathroom'
|
71
|
+
created = task.created_at
|
72
|
+
upsert = Upsert.new $conn, :tasks
|
73
|
+
upsert.row({:id => task.id}, :name => 'Clean kitchen')
|
74
|
+
task.reload
|
75
|
+
task.created_at.should eql task.created_at
|
76
|
+
task.created_on.should eql task.created_on
|
77
|
+
end
|
78
|
+
|
79
|
+
it "converts symbol values to string" do
|
80
|
+
jerry = Pet.create :name => 'Jerry', :gender => 'female'
|
81
|
+
upsert = Upsert.new $conn, :pets
|
82
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male'}]) do
|
83
|
+
upsert.row({:id => jerry.id}, :gender => :male)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "works for column names with spaces in them" do
|
88
|
+
upsert = Upsert.new $conn, :people
|
89
|
+
assert_creates(Person, [{:"First Name" => 'Major', :"Last Name" => 'Major'}]) do
|
90
|
+
upsert.row({:"First Name" => 'Major'}, :"Last Name" => 'Major')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
describe :batch do
|
95
|
+
it "works for multiple rows (base case)" do
|
96
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male'}]) do
|
97
|
+
Upsert.batch($conn, :pets) do |upsert|
|
98
|
+
upsert.row({:name => 'Jerry'}, :gender => 'male')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
it "works for multiple rows (not changing anything)" do
|
103
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'male'}]) do
|
104
|
+
Upsert.batch($conn, :pets) do |upsert|
|
105
|
+
upsert.row({:name => 'Jerry'}, :gender => 'male')
|
106
|
+
upsert.row({:name => 'Jerry'}, :gender => 'male')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
it "works for multiple rows (changing something)" do
|
111
|
+
assert_creates(Pet, [{:name => 'Jerry', :gender => 'neutered'}]) do
|
112
|
+
Upsert.batch($conn, :pets) do |upsert|
|
113
|
+
upsert.row({:name => 'Jerry'}, :gender => 'male')
|
114
|
+
upsert.row({:name => 'Jerry'}, :gender => 'neutered')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
Pet.where(:gender => 'male').count.should == 0
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/spec/hstore_spec.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
describe Upsert do
|
4
|
+
describe 'hstore on pg' do
|
5
|
+
require 'pg_hstore'
|
6
|
+
|
7
|
+
let(:deserializer) do
|
8
|
+
klass = PgHstore.dup
|
9
|
+
if RUBY_PLATFORM == "java"
|
10
|
+
# activerecord-jdbc-adapter has native support for hstore
|
11
|
+
klass.class_eval do
|
12
|
+
def self.parse(obj)
|
13
|
+
obj
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
klass
|
19
|
+
end
|
20
|
+
|
21
|
+
Pet.connection.execute 'CREATE EXTENSION IF NOT EXISTS HSTORE'
|
22
|
+
Pet.connection.execute "ALTER TABLE pets ADD COLUMN crazy HSTORE"
|
23
|
+
Pet.connection.execute "ALTER TABLE pets ADD COLUMN cool HSTORE"
|
24
|
+
|
25
|
+
before do
|
26
|
+
Pet.delete_all
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:upsert) { Upsert.new $conn, :pets }
|
30
|
+
|
31
|
+
it "works for ugly text" do
|
32
|
+
uggy = <<-EOS
|
33
|
+
{"results":[{"locations":[],"providedLocation":{"location":"3001 STRATTON WAY, MADISON, WI 53719 UNITED STATES"}}],"options":{"ignoreLatLngInput":true,"maxResults":1,"thumbMaps":false},"info":{"copyright":{"text":"© 2012 MapQuest, Inc.","imageUrl":"http://api.mqcdn.com/res/mqlogo.gif","imageAltText":"© 2012 MapQuest, Inc."},"statuscode":0,"messages":[]}}
|
34
|
+
EOS
|
35
|
+
upsert.row({:name => 'Uggy'}, :crazy => {:uggy => uggy})
|
36
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Uggy'})
|
37
|
+
crazy = deserializer.parse row['crazy']
|
38
|
+
crazy.should == { 'uggy' => uggy }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "just works" do
|
42
|
+
upsert.row({:name => 'Bill'}, :crazy => nil)
|
43
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
44
|
+
row['crazy'].should == nil
|
45
|
+
|
46
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
|
47
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
48
|
+
crazy = deserializer.parse row['crazy']
|
49
|
+
crazy.should == { 'a' => '1' }
|
50
|
+
|
51
|
+
upsert.row({:name => 'Bill'}, :crazy => nil)
|
52
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
53
|
+
row['crazy'].should == nil
|
54
|
+
|
55
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
|
56
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
57
|
+
crazy = deserializer.parse row['crazy']
|
58
|
+
crazy.should == { 'a' => '1' }
|
59
|
+
|
60
|
+
upsert.row({:name => 'Bill'}, :crazy => {:whatdat => 'whodat'})
|
61
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
62
|
+
crazy = deserializer.parse row['crazy']
|
63
|
+
crazy.should == { 'a' => '1', 'whatdat' => 'whodat' }
|
64
|
+
|
65
|
+
upsert.row({:name => 'Bill'}, :crazy => {:whatdat => "D'ONOFRIO"})
|
66
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
67
|
+
crazy = deserializer.parse row['crazy']
|
68
|
+
crazy.should == { 'a' => '1', 'whatdat' => "D'ONOFRIO" }
|
69
|
+
|
70
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 2})
|
71
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
72
|
+
crazy = deserializer.parse row['crazy']
|
73
|
+
crazy.should == { 'a' => '2', 'whatdat' => "D'ONOFRIO" }
|
74
|
+
end
|
75
|
+
|
76
|
+
it "can nullify entire hstore" do
|
77
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
|
78
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
79
|
+
crazy = deserializer.parse row['crazy']
|
80
|
+
crazy.should == { 'a' => '1' }
|
81
|
+
|
82
|
+
upsert.row({:name => 'Bill'}, :crazy => nil)
|
83
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
84
|
+
row['crazy'].should == nil
|
85
|
+
end
|
86
|
+
|
87
|
+
it "deletes keys that are nil" do
|
88
|
+
upsert.row({:name => 'Bill'}, :crazy => nil)
|
89
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
90
|
+
row['crazy'].should == nil
|
91
|
+
|
92
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
|
93
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
94
|
+
crazy = deserializer.parse row['crazy']
|
95
|
+
crazy.should == { 'a' => '1' }
|
96
|
+
|
97
|
+
upsert.row({:name => 'Bill'}, :crazy => {})
|
98
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
99
|
+
crazy = deserializer.parse row['crazy']
|
100
|
+
crazy.should == { 'a' => '1' }
|
101
|
+
|
102
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => nil})
|
103
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
104
|
+
crazy = deserializer.parse row['crazy']
|
105
|
+
crazy.should == {}
|
106
|
+
|
107
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5})
|
108
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
109
|
+
crazy = deserializer.parse row['crazy']
|
110
|
+
crazy.should == { 'a' => '1', 'b' => '5' }
|
111
|
+
|
112
|
+
upsert.row({:name => 'Bill'}, :crazy => {})
|
113
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
114
|
+
crazy = deserializer.parse row['crazy']
|
115
|
+
crazy.should == { 'a' => '1', 'b' => '5' }
|
116
|
+
|
117
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => nil})
|
118
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
119
|
+
crazy = deserializer.parse row['crazy']
|
120
|
+
crazy.should == { 'b' => '5' }
|
121
|
+
|
122
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5})
|
123
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
124
|
+
crazy = deserializer.parse row['crazy']
|
125
|
+
crazy.should == { 'a' => '1', 'b' => '5' }
|
126
|
+
|
127
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => nil, :b => nil, :c => 12})
|
128
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
129
|
+
crazy = deserializer.parse row['crazy']
|
130
|
+
crazy.should == { 'c' => '12' }
|
131
|
+
end
|
132
|
+
|
133
|
+
it "takes dangerous keys" do
|
134
|
+
upsert.row({:name => 'Bill'}, :crazy => nil)
|
135
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
136
|
+
row['crazy'].should == nil
|
137
|
+
|
138
|
+
upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1})
|
139
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
140
|
+
crazy = deserializer.parse row['crazy']
|
141
|
+
crazy.should == { 'foo"bar' => '1' }
|
142
|
+
|
143
|
+
upsert.row({:name => 'Bill'}, :crazy => {})
|
144
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
145
|
+
crazy = deserializer.parse row['crazy']
|
146
|
+
crazy.should == { 'foo"bar' => '1' }
|
147
|
+
|
148
|
+
upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil})
|
149
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
150
|
+
crazy = deserializer.parse row['crazy']
|
151
|
+
crazy.should == {}
|
152
|
+
|
153
|
+
upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1, :b => 5})
|
154
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
155
|
+
crazy = deserializer.parse row['crazy']
|
156
|
+
crazy.should == { 'foo"bar' => '1', 'b' => '5' }
|
157
|
+
|
158
|
+
upsert.row({:name => 'Bill'}, :crazy => {})
|
159
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
160
|
+
crazy = deserializer.parse row['crazy']
|
161
|
+
crazy.should == { 'foo"bar' => '1', 'b' => '5' }
|
162
|
+
|
163
|
+
upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil})
|
164
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
165
|
+
crazy = deserializer.parse row['crazy']
|
166
|
+
crazy.should == { 'b' => '5' }
|
167
|
+
|
168
|
+
upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => 1, :b => 5})
|
169
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
170
|
+
crazy = deserializer.parse row['crazy']
|
171
|
+
crazy.should == { 'foo"bar' => '1', 'b' => '5' }
|
172
|
+
|
173
|
+
upsert.row({:name => 'Bill'}, :crazy => {:'foo"bar' => nil, :b => nil, :c => 12})
|
174
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
175
|
+
crazy = deserializer.parse row['crazy']
|
176
|
+
crazy.should == { 'c' => '12' }
|
177
|
+
end
|
178
|
+
|
179
|
+
it "handles multiple hstores" do
|
180
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 9}, :cool => {:c => 12, :d => 19})
|
181
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
182
|
+
crazy = deserializer.parse row['crazy']
|
183
|
+
crazy.should == { 'a' => '1', 'b' => '9' }
|
184
|
+
cool = deserializer.parse row['cool']
|
185
|
+
cool.should == { 'c' => '12', 'd' => '19' }
|
186
|
+
end
|
187
|
+
|
188
|
+
it "can deletes keys from multiple hstores at once" do
|
189
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1}, :cool => {5 => 9})
|
190
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
191
|
+
crazy = deserializer.parse row['crazy']
|
192
|
+
crazy.should == { 'a' => '1' }
|
193
|
+
cool = deserializer.parse row['cool']
|
194
|
+
cool.should == { '5' => '9' }
|
195
|
+
|
196
|
+
# NOOP
|
197
|
+
upsert.row({:name => 'Bill'}, :crazy => {}, :cool => {})
|
198
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
199
|
+
crazy = deserializer.parse row['crazy']
|
200
|
+
crazy.should == { 'a' => '1' }
|
201
|
+
cool = deserializer.parse row['cool']
|
202
|
+
cool.should == { '5' => '9' }
|
203
|
+
|
204
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => nil}, :cool => {13 => 17})
|
205
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
206
|
+
crazy = deserializer.parse row['crazy']
|
207
|
+
crazy.should == {}
|
208
|
+
cool = deserializer.parse row['cool']
|
209
|
+
cool.should == { '5' => '9', '13' => '17' }
|
210
|
+
|
211
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1, :b => 5})
|
212
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
213
|
+
crazy = deserializer.parse row['crazy']
|
214
|
+
crazy.should == { 'a' => '1', 'b' => '5' }
|
215
|
+
|
216
|
+
upsert.row({:name => 'Bill'}, :crazy => {:b => nil}, :cool => {5 => nil})
|
217
|
+
row = Pet.connection.select_one(%{SELECT crazy, cool FROM pets WHERE name = 'Bill'})
|
218
|
+
crazy = deserializer.parse row['crazy']
|
219
|
+
crazy.should == {'a' => '1'}
|
220
|
+
cool = deserializer.parse row['cool']
|
221
|
+
cool.should == {'13' => '17' }
|
222
|
+
end
|
223
|
+
|
224
|
+
it "deletes keys whether new or existing record" do
|
225
|
+
upsert.row({:name => 'Bill'}, :crazy => {:z => 1, :x => nil})
|
226
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
227
|
+
crazy = deserializer.parse row['crazy']
|
228
|
+
crazy.should == { 'z' => '1' }
|
229
|
+
|
230
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
|
231
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
232
|
+
crazy = deserializer.parse row['crazy']
|
233
|
+
crazy.should == { 'a' => '1', 'z' => '1' }
|
234
|
+
end
|
235
|
+
|
236
|
+
it "can turn off eager nullify" do
|
237
|
+
upsert.row({:name => 'Bill'}, {:crazy => {:z => 1, :x => nil}}, :eager_nullify => false)
|
238
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
239
|
+
crazy = deserializer.parse row['crazy']
|
240
|
+
crazy.should == { 'z' => '1', 'x' => nil }
|
241
|
+
|
242
|
+
upsert.row({:name => 'Bill'}, :crazy => {:a => 1})
|
243
|
+
row = Pet.connection.select_one(%{SELECT crazy FROM pets WHERE name = 'Bill'})
|
244
|
+
crazy = deserializer.parse row['crazy']
|
245
|
+
crazy.should == { 'a' => '1', 'z' => '1', 'x' => nil}
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
end if ENV['DB'] == 'postgresql'
|
data/spec/jruby_spec.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe Upsert do
|
3
|
+
it "works correct with large ints" do
|
4
|
+
u = Upsert.new($conn, :pets)
|
5
|
+
Pet.create(:name => "Jerry", :big_tag_number => 2)
|
6
|
+
u.row({ :name => 'Jerry' }, :big_tag_number => 3599657714)
|
7
|
+
Pet.find_by_name('Jerry').big_tag_number.should == 3599657714
|
8
|
+
end
|
9
|
+
end if RUBY_PLATFORM == 'java'
|
data/spec/logger_spec.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe Upsert do
|
3
|
+
MUTEX_FOR_PERFORM = Mutex.new
|
4
|
+
describe "logger" do
|
5
|
+
it "logs where you tell it" do
|
6
|
+
begin
|
7
|
+
old_logger = Upsert.logger
|
8
|
+
io = StringIO.new
|
9
|
+
MUTEX_FOR_PERFORM.synchronize do
|
10
|
+
Upsert.logger = Logger.new(io)
|
11
|
+
|
12
|
+
Upsert.logger.warn "hello"
|
13
|
+
|
14
|
+
io.rewind
|
15
|
+
io.read.chomp.should =~ /hello/
|
16
|
+
end
|
17
|
+
ensure
|
18
|
+
Upsert.logger = old_logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "logs queries" do
|
23
|
+
old_logger = Upsert.logger
|
24
|
+
begin
|
25
|
+
io = StringIO.new
|
26
|
+
MUTEX_FOR_PERFORM.synchronize do
|
27
|
+
Upsert.logger = Logger.new(io)
|
28
|
+
|
29
|
+
u = Upsert.new($conn, :pets)
|
30
|
+
u.row(:name => 'Jerry')
|
31
|
+
|
32
|
+
io.rewind
|
33
|
+
log = io.read.chomp
|
34
|
+
case u.connection.class.name
|
35
|
+
when /sqlite/i
|
36
|
+
log.should =~ /insert or ignore/i
|
37
|
+
when /mysql/i
|
38
|
+
log.should =~ /call #{Upsert::MergeFunction::NAME_PREFIX}_pets_SEL_name/i
|
39
|
+
when /p.*g/i
|
40
|
+
# [54ae2eea857] Possibly much more useful debug output
|
41
|
+
# TODO: Should check for both upsert and non-upsert log output
|
42
|
+
log.should =~ /selector:|SHOW server_version/i
|
43
|
+
else
|
44
|
+
raise "not sure"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
ensure
|
48
|
+
Upsert.logger = old_logger
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'remote_table'
|
2
|
+
|
3
|
+
a = RemoteTable.new(
|
4
|
+
:url => 'http://www.postgresql.org/docs/9.1/static/sql-keywords-appendix.html',
|
5
|
+
:row_css => 'table.CALSTABLE tbody tr',
|
6
|
+
:column_css => 'td',
|
7
|
+
:headers => %w{ key_word }
|
8
|
+
)
|
9
|
+
|
10
|
+
a.each do |row|
|
11
|
+
puts row['key_word']
|
12
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
ACCESSIBLE
|
2
|
+
ADD
|
3
|
+
ALL
|
4
|
+
ALTER
|
5
|
+
ANALYZE
|
6
|
+
AND
|
7
|
+
AS
|
8
|
+
ASC
|
9
|
+
ASENSITIVE
|
10
|
+
BEFORE
|
11
|
+
BETWEEN
|
12
|
+
BIGINT
|
13
|
+
BINARY
|
14
|
+
BLOB
|
15
|
+
BOTH
|
16
|
+
BY
|
17
|
+
CALL
|
18
|
+
CASCADE
|
19
|
+
CASE
|
20
|
+
CHANGE
|
21
|
+
CHAR
|
22
|
+
CHARACTER
|
23
|
+
CHECK
|
24
|
+
COLLATE
|
25
|
+
COLUMN
|
26
|
+
CONDITION
|
27
|
+
CONSTRAINT
|
28
|
+
CONTINUE
|
29
|
+
CONVERT
|
30
|
+
CREATE
|
31
|
+
CROSS
|
32
|
+
CURRENT_DATE
|
33
|
+
CURRENT_TIME
|
34
|
+
CURRENT_TIMESTAMP
|
35
|
+
CURRENT_USER
|
36
|
+
CURSOR
|
37
|
+
DATABASE
|
38
|
+
DATABASES
|
39
|
+
DAY_HOUR
|
40
|
+
DAY_MICROSECOND
|
41
|
+
DAY_MINUTE
|
42
|
+
DAY_SECOND
|
43
|
+
DEC
|
44
|
+
DECIMAL
|
45
|
+
DECLARE
|
46
|
+
DEFAULT
|
47
|
+
DELAYED
|
48
|
+
DELETE
|
49
|
+
DESC
|
50
|
+
DESCRIBE
|
51
|
+
DETERMINISTIC
|
52
|
+
DISTINCT
|
53
|
+
DISTINCTROW
|
54
|
+
DIV
|
55
|
+
DOUBLE
|
56
|
+
DROP
|
57
|
+
DUAL
|
58
|
+
EACH
|
59
|
+
ELSE
|
60
|
+
ELSEIF
|
61
|
+
ENCLOSED
|
62
|
+
ESCAPED
|
63
|
+
EXISTS
|
64
|
+
EXIT
|
65
|
+
EXPLAIN
|
66
|
+
FALSE
|
67
|
+
FETCH
|
68
|
+
FLOAT
|
69
|
+
FLOAT4
|
70
|
+
FLOAT8
|
71
|
+
FOR
|
72
|
+
FORCE
|
73
|
+
FOREIGN
|
74
|
+
FROM
|
75
|
+
FULLTEXT
|
76
|
+
GRANT
|
77
|
+
GROUP
|
78
|
+
HAVING
|
79
|
+
HIGH_PRIORITY
|
80
|
+
HOUR_MICROSECOND
|
81
|
+
HOUR_MINUTE
|
82
|
+
HOUR_SECOND
|
83
|
+
IF
|
84
|
+
IGNORE
|
85
|
+
IN
|
86
|
+
INDEX
|
87
|
+
INFILE
|
88
|
+
INNER
|
89
|
+
INOUT
|
90
|
+
INSENSITIVE
|
91
|
+
INSERT
|
92
|
+
INT
|
93
|
+
INT1
|
94
|
+
INT2
|
95
|
+
INT3
|
96
|
+
INT4
|
97
|
+
INT8
|
98
|
+
INTEGER
|
99
|
+
INTERVAL
|
100
|
+
INTO
|
101
|
+
IS
|
102
|
+
ITERATE
|
103
|
+
JOIN
|
104
|
+
KEY
|
105
|
+
KEYS
|
106
|
+
KILL
|
107
|
+
LEADING
|
108
|
+
LEAVE
|
109
|
+
LEFT
|
110
|
+
LIKE
|
111
|
+
LIMIT
|
112
|
+
LINEAR
|
113
|
+
LINES
|
114
|
+
LOAD
|
115
|
+
LOCALTIME
|
116
|
+
LOCALTIMESTAMP
|
117
|
+
LOCK
|
118
|
+
LONG
|
119
|
+
LONGBLOB
|
120
|
+
LONGTEXT
|
121
|
+
LOOP
|
122
|
+
LOW_PRIORITY
|
123
|
+
MASTER_SSL_VERIFY_SERVER_CERT
|
124
|
+
MATCH
|
125
|
+
MAXVALUE
|
126
|
+
MEDIUMBLOB
|
127
|
+
MEDIUMINT
|
128
|
+
MEDIUMTEXT
|
129
|
+
MIDDLEINT
|
130
|
+
MINUTE_MICROSECOND
|
131
|
+
MINUTE_SECOND
|
132
|
+
MOD
|
133
|
+
MODIFIES
|
134
|
+
NATURAL
|
135
|
+
NOT
|
136
|
+
NO_WRITE_TO_BINLOG
|
137
|
+
NULL
|
138
|
+
NUMERIC
|
139
|
+
ON
|
140
|
+
OPTIMIZE
|
141
|
+
OPTION
|
142
|
+
OPTIONALLY
|
143
|
+
OR
|
144
|
+
ORDER
|
145
|
+
OUT
|
146
|
+
OUTER
|
147
|
+
OUTFILE
|
148
|
+
PRECISION
|
149
|
+
PRIMARY
|
150
|
+
PROCEDURE
|
151
|
+
PURGE
|
152
|
+
RANGE
|
153
|
+
READ
|
154
|
+
READS
|
155
|
+
READ_WRITE
|
156
|
+
REAL
|
157
|
+
REFERENCES
|
158
|
+
REGEXP
|
159
|
+
RELEASE
|
160
|
+
RENAME
|
161
|
+
REPEAT
|
162
|
+
REPLACE
|
163
|
+
REQUIRE
|
164
|
+
RESIGNAL
|
165
|
+
RESTRICT
|
166
|
+
RETURN
|
167
|
+
REVOKE
|
168
|
+
RIGHT
|
169
|
+
RLIKE
|
170
|
+
SCHEMA
|
171
|
+
SCHEMAS
|
172
|
+
SECOND_MICROSECOND
|
173
|
+
SELECT
|
174
|
+
SENSITIVE
|
175
|
+
SEPARATOR
|
176
|
+
SET
|
177
|
+
SHOW
|
178
|
+
SIGNAL
|
179
|
+
SMALLINT
|
180
|
+
SPATIAL
|
181
|
+
SPECIFIC
|
182
|
+
SQL
|
183
|
+
SQLEXCEPTION
|
184
|
+
SQLSTATE
|
185
|
+
SQLWARNING
|
186
|
+
SQL_BIG_RESULT
|
187
|
+
SQL_CALC_FOUND_ROWS
|
188
|
+
SQL_SMALL_RESULT
|
189
|
+
SSL
|
190
|
+
STARTING
|
191
|
+
STRAIGHT_JOIN
|
192
|
+
TABLE
|
193
|
+
TERMINATED
|
194
|
+
THEN
|
195
|
+
TINYBLOB
|
196
|
+
TINYINT
|
197
|
+
TINYTEXT
|
198
|
+
TO
|
199
|
+
TRAILING
|
200
|
+
TRIGGER
|
201
|
+
TRUE
|
202
|
+
UNDO
|
203
|
+
UNION
|
204
|
+
UNIQUE
|
205
|
+
UNLOCK
|
206
|
+
UNSIGNED
|
207
|
+
UPDATE
|
208
|
+
USAGE
|
209
|
+
USE
|
210
|
+
USING
|
211
|
+
UTC_DATE
|
212
|
+
UTC_TIME
|
213
|
+
UTC_TIMESTAMP
|
214
|
+
VALUES
|
215
|
+
VARBINARY
|
216
|
+
VARCHAR
|
217
|
+
VARCHARACTER
|
218
|
+
VARYING
|
219
|
+
WHEN
|
220
|
+
WHERE
|
221
|
+
WHILE
|
222
|
+
WITH
|
223
|
+
WRITE
|
224
|
+
XOR
|
225
|
+
YEAR_MONTH
|
226
|
+
ZEROFILL
|