upsert 2.2.0 → 2.9.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -0
  3. data/.standard.yml +1 -0
  4. data/.travis.yml +54 -31
  5. data/CHANGELOG +16 -0
  6. data/Gemfile +12 -1
  7. data/LICENSE +3 -1
  8. data/README.md +43 -8
  9. data/Rakefile +7 -1
  10. data/lib/upsert.rb +49 -7
  11. data/lib/upsert/column_definition/mysql.rb +2 -2
  12. data/lib/upsert/column_definition/postgresql.rb +9 -8
  13. data/lib/upsert/column_definition/sqlite3.rb +3 -3
  14. data/lib/upsert/connection/Java_ComMysqlJdbc_JDBC4Connection.rb +5 -3
  15. data/lib/upsert/connection/Java_OrgPostgresqlJdbc_PgConnection.rb +33 -0
  16. data/lib/upsert/connection/PG_Connection.rb +5 -0
  17. data/lib/upsert/connection/jdbc.rb +7 -1
  18. data/lib/upsert/connection/postgresql.rb +2 -3
  19. data/lib/upsert/merge_function.rb +3 -2
  20. data/lib/upsert/merge_function/{Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb → Java_OrgPostgresqlJdbc_PgConnection.rb} +2 -2
  21. data/lib/upsert/merge_function/PG_Connection.rb +2 -2
  22. data/lib/upsert/merge_function/postgresql.rb +81 -19
  23. data/lib/upsert/merge_function/sqlite3.rb +10 -0
  24. data/lib/upsert/version.rb +1 -1
  25. data/spec/correctness_spec.rb +20 -5
  26. data/spec/database_functions_spec.rb +6 -2
  27. data/spec/hstore_spec.rb +53 -38
  28. data/spec/logger_spec.rb +1 -1
  29. data/spec/postgresql_spec.rb +81 -3
  30. data/spec/reserved_words_spec.rb +18 -14
  31. data/spec/sequel_spec.rb +16 -7
  32. data/spec/spec_helper.rb +238 -111
  33. data/spec/speed_spec.rb +3 -33
  34. data/spec/threaded_spec.rb +35 -12
  35. data/spec/type_safety_spec.rb +2 -1
  36. data/travis/run_docker_db.sh +20 -0
  37. data/upsert-java.gemspec +13 -0
  38. data/upsert.gemspec +9 -58
  39. data/upsert.gemspec.common +107 -0
  40. metadata +39 -46
  41. data/lib/upsert/connection/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: dfb6bea0fbe024f56a107ddb4db99ef247b5ebdb
4
- data.tar.gz: 6a3f979f4b136debf07b327073b2e774fdd60322
2
+ SHA256:
3
+ metadata.gz: 5d3ee917c4d930fdfb8be20d6838b49f88142ffb35122ed9c84724ceb498df16
4
+ data.tar.gz: f8186de9b5a77c3b5dd58ac23b14a7280a27f8a37d2dd3e0305f9dd725846ae4
5
5
  SHA512:
6
- metadata.gz: 850b04b05fab5c9ec30b9c27253fe8b751beef9dbbc7718abdd6cfbe93d6054796361cde49543018dd3afaf1c59ffe0c8baca1f2874b58053a94726cbf444112
7
- data.tar.gz: 49db75e385e5d9cb3960e9f8d8927e8fa0657932e8422fe5ee7f77bcaf77a8dfc5b844b28c8a061953a285590b0c6ed9a1e8f87afb6c7c3ab1ba0d8dbdb8e312
6
+ metadata.gz: 9e874aed50b2fd6d15e9591764f2d16656d0e1a22d44474fb2c279cd2bb508098b81aa2482a1605c0dfcbb7ec356eae65d8a70b1d1763f709f8c9694b78c8862
7
+ data.tar.gz: a7f35d0e6b5c423b5676c740d9285e7a32134486edc811d184bd35c4f6c97f20024a4db8847fbfd9adecd6a9e8731993e5047528d12fb19387809e306b8067a5
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.5
data/.standard.yml ADDED
@@ -0,0 +1 @@
1
+ ruby_version: 2.2.4
data/.travis.yml CHANGED
@@ -1,40 +1,63 @@
1
- sudo: required
2
- dist: trusty
1
+ dist: xenial
3
2
  language: ruby
4
- global:
5
- - USERNAME=travis
6
- - PASSWORD=
7
- addons:
8
- apt:
9
- packages:
10
- # https://github.com/travis-ci/docs-travis-ci-com/pull/743
11
- - haveged
12
- - mysql-server-5.6
13
- - mysql-client-core-5.6
14
- - mysql-client-5.6
3
+ cache: bundler
4
+ services:
5
+ - docker
15
6
  rvm:
7
+ - 2.6
8
+ - 2.5
9
+ - 2.4
16
10
  - 2.3
17
11
  - 2.2
18
- - 2.1
19
- - 1.9.3
20
- - rbx
21
- - jruby-1.7
22
- - jruby-9
12
+ - jruby-9.1.14.0
13
+ - jruby-9.1.17.0
14
+ - jruby-9.2.7.0
15
+ env:
16
+ global:
17
+ - USERNAME=travis
18
+ - PASSWORD=
19
+ - DB_USER=upsert_test
20
+ - DB_PASSWORD=upsert_test
21
+ - DB_NAME=upsert_test
22
+ matrix:
23
+ - DB=postgresql DB_VERSION=postgres:9.4
24
+ - DB=postgresql DB_VERSION=postgres:9.5
25
+ - DB=postgresql DB_VERSION=postgres:9.6
26
+ - DB=postgresql DB_VERSION=postgres:10
27
+ - DB=postgresql DB_VERSION=postgres:11
28
+ - DB=postgresql DB_VERSION=postgres:12
29
+ - DB=postgresql DB_VERSION=postgres:9.4 UNIQUE_CONSTRAINT=true
30
+ - DB=postgresql DB_VERSION=postgres:9.5 UNIQUE_CONSTRAINT=true
31
+ - DB=postgresql DB_VERSION=postgres:9.6 UNIQUE_CONSTRAINT=true
32
+ - DB=postgresql DB_VERSION=postgres:10 UNIQUE_CONSTRAINT=true
33
+ - DB=postgresql DB_VERSION=postgres:11 UNIQUE_CONSTRAINT=true
34
+ - DB=postgresql DB_VERSION=postgres:12 UNIQUE_CONSTRAINT=true
35
+ - DB=mysql DB_VERSION=mysql:5.6
36
+ - DB=mysql DB_VERSION=mysql:5.7
37
+ - DB=mysql DB_VERSION=mysql:8
23
38
  matrix:
39
+ exclude:
40
+ - rvm: 2.6
41
+ env: DB=postgresql DB_VERSION=postgres:9.4
42
+ - rvm: 2.6
43
+ env: DB=postgresql DB_VERSION=postgres:9.5
44
+ - rvm: 2.6
45
+ env: DB=postgresql DB_VERSION=postgres:9.4 UNIQUE_CONSTRAINT=true
46
+ - rvm: 2.6
47
+ env: DB=postgresql DB_VERSION=postgres:9.5 UNIQUE_CONSTRAINT=true
48
+ - rvm: jruby-9.2.7
49
+ env: DB=postgresql DB_VERSION=postgres:9.4
50
+ - rvm: jruby-9.2.7
51
+ env: DB=postgresql DB_VERSION=postgres:9.5
52
+ - rvm: jruby-9.2.7
53
+ env: DB=postgresql DB_VERSION=postgres:9.4 UNIQUE_CONSTRAINT=true
54
+ - rvm: jruby-9.2.7
55
+ env: DB=postgresql DB_VERSION=postgres:9.5 UNIQUE_CONSTRAINT=true
24
56
  allow_failures:
25
- - rvm: rbx
26
- env:
27
- - DB=postgresql PGVERSION=9.4
28
- - DB=postgresql PGVERSION=9.5
29
- - DB=postgresql PGVERSION=9.4 UNIQUE_CONSTRAINT=true
30
- - DB=postgresql PGVERSION=9.5 UNIQUE_CONSTRAINT=true
31
- - DB=mysql DB_USER=root
57
+ - env: DB=postgresql DB_VERSION=postgres:12
58
+ - env: DB=postgresql DB_VERSION=postgres:12 UNIQUE_CONSTRAINT=true
32
59
  before_install:
33
- - if [ "$DB" = 'mysql' ]; then sudo ./travis/tune_mysql.sh; fi
34
- # Right now the build-script is properly installing Postgres version. We will need this to test PG 9.6 and up, though
35
- # - if [ "$DB" = 'postgresql' ]; then sudo ./travis/install_postgres.sh; fi
36
- - gem update --system
37
- - gem update bundler
60
+ - ./travis/run_docker_db.sh
38
61
  - bundle --version
39
62
  - gem --version
40
- script: bundle exec rake spec
63
+ script: ./travis/run_specs.sh
data/CHANGELOG CHANGED
@@ -1,3 +1,19 @@
1
+ -- no version -- / 2019-06-05
2
+
3
+ * Enhancements
4
+
5
+ * Bump development Ruby version to 2.5 since Ruby 2.2 is no longer supported.
6
+ This should not affect usage of the gem, only local development for people
7
+ working *on* the gem. Ruby 2.2 is also not dropped from Upsert compatibility
8
+ at this time but you should consider upgrading to newer Ruby versions anyway.
9
+
10
+ 2.2.1 / 2017-04-20
11
+
12
+ * Bug fixes
13
+
14
+ * Fix unique constraint detection on pg >9.5.5 (@pnomolos https://github.com/seamusabshere/upsert/pull/99)
15
+ * Fix Ruby 1.9 tests
16
+
1
17
  2.2.0 / 2017-04-14
2
18
 
3
19
  * Enhancements
data/Gemfile CHANGED
@@ -2,4 +2,15 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in upsert.gemspec
4
4
 
5
- gemspec
5
+ gemspec name: RUBY_PLATFORM == "java" ? "upsert-java" : "upsert"
6
+
7
+ case RUBY_PLATFORM
8
+ when "java"
9
+ gem "ffi", platforms: :jruby
10
+ else
11
+ gem "ffi"
12
+ end
13
+
14
+ group "test" do
15
+ gem "testmetrics_rspec"
16
+ end
data/LICENSE CHANGED
@@ -1,4 +1,6 @@
1
- Copyright (c) 2013 Seamus Abshere
1
+ Copyright (c) 2013-2019 Seamus Abshere
2
+ Copyright (c) 2017-2019 Philip Schalm
3
+ Portions Copyright (c) 2019 The JRuby Team
2
4
 
3
5
  MIT License
4
6
 
data/README.md CHANGED
@@ -23,7 +23,7 @@ You pass a __selector__ that uniquely identifies a row, whether it exists or not
23
23
  Syntax inspired by [mongo-ruby-driver's update method](http://api.mongodb.org/ruby/1.6.4/Mongo/Collection.html#update-instance_method).
24
24
 
25
25
  ### Basic
26
-
26
+
27
27
  ```ruby
28
28
  connection = Mysql2::Client.new([...])
29
29
  table_name = :pets
@@ -59,9 +59,11 @@ Batch mode is tested to be about 80% faster on PostgreSQL, MySQL, and SQLite3 th
59
59
 
60
60
  ### Native Postgres upsert
61
61
 
62
- `INSERT ... ON CONFLICT DO UPDATE` is used when Postgres 9.5+ is detected and *unique indexes are in place.*
62
+ `INSERT ... ON CONFLICT DO UPDATE` is used when Postgres 9.5+ is detected and *unique constraint are in place.*
63
+
64
+ **Note: ** You must have a **unique constraint** on the column(s) you're using as a selector. A unique index won't work. See https://github.com/seamusabshere/upsert/issues/98#issuecomment-295341405 for more information and some ways to check.
63
65
 
64
- If you don't have unique indexes, it will fall back to the classic Upsert gem user-defined function, which does not require indexes.
66
+ If you don't have unique constraints, it will fall back to the classic Upsert gem user-defined function, which does not require a constraint.
65
67
 
66
68
  ### ActiveRecord helper method
67
69
 
@@ -121,7 +123,10 @@ See below for details about what SQL MERGE trick (emulation of upsert) is used,
121
123
 
122
124
  ### Rails / ActiveRecord
123
125
 
124
- (assuming that one of the other three supported drivers is being used under the covers)
126
+ (Assuming that one of the other three supported drivers is being used under the covers).
127
+
128
+ * add "upsert" to your Gemfile and
129
+ * run bundle install
125
130
 
126
131
  ```ruby
127
132
  Upsert.new Pet.connection, Pet.table_name
@@ -173,7 +178,7 @@ BEGIN
173
178
  DECLARE done BOOLEAN;
174
179
  REPEAT
175
180
  BEGIN
176
- -- If there is a unique key constraint error then
181
+ -- If there is a unique key constraint error then
177
182
  -- someone made a concurrent insert. Reset the sentinel
178
183
  -- and try again.
179
184
  DECLARE ER_DUP_UNIQUE CONDITION FOR 23000;
@@ -181,7 +186,7 @@ BEGIN
181
186
  DECLARE CONTINUE HANDLER FOR ER_DUP_UNIQUE BEGIN
182
187
  SET done = FALSE;
183
188
  END;
184
-
189
+
185
190
  DECLARE CONTINUE HANDLER FOR ER_INTEG BEGIN
186
191
  SET done = TRUE;
187
192
  END;
@@ -191,7 +196,7 @@ BEGIN
191
196
  -- Race condition here. If a concurrent INSERT is made after
192
197
  -- the SELECT but before the INSERT below we'll get a duplicate
193
198
  -- key error. But the handler above will take care of that.
194
- IF @count > 0 THEN
199
+ IF @count > 0 THEN
195
200
  -- UPDATE table_name SET b = b_SET WHERE a = a_SEL;
196
201
  UPDATE `pets` SET `name` = `name_set`, `tag_number` = `tag_number_set` WHERE `name` = `name_sel` AND `tag_number` = `tag_number_sel`;
197
202
  ELSE
@@ -230,6 +235,15 @@ require 'pg_hstore'
230
235
  upsert.row({:name => 'Bill'}, :mydata => {:a => 1, :b => 2})
231
236
  ```
232
237
 
238
+ #### PostgreSQL notes
239
+
240
+ - Upsert doesn't do any type casting, so if you attempt to do something like the following:
241
+ `upsert.row({ :name => 'A Name' }, :tag_number => 'bob')`
242
+ you'll get an error which reads something like:
243
+ `invalid input syntax for integer: "bob"`
244
+
245
+
246
+
233
247
  #### Speed
234
248
 
235
249
  From the tests (updated 9/21/12):
@@ -367,10 +381,31 @@ If you're using MySQL, make sure server/connection timezone is UTC. If you're us
367
381
 
368
382
  In general, run some upserts and make sure datetimes get persisted like you expect.
369
383
 
384
+ ### Clearning all library-generated functions
385
+
386
+ Place the following in to a rake task (so you don't globally redefine the `NAME_PREFIX` constant)
387
+
388
+ ```ruby
389
+ Upsert::MergeFunction::NAME_PREFIX = "upsert"
390
+
391
+ # ActiveRecord
392
+ Upsert.clear_database_functions(ActiveRecord::Base.connection)
393
+
394
+ # Sequel
395
+ DB.synchronize do |conn|
396
+ Upsert.clear_database_functions(conn)
397
+ end
398
+ ```
399
+
370
400
  ### Doesn't work with transactional fixtures
371
401
 
372
402
  Per https://github.com/seamusabshere/upsert/issues/23 you might have issues if you try to use transactional fixtures and this library.
373
403
 
404
+ ##
405
+ Testmetrics - https://www.testmetrics.app/seamusabshere/upsert
406
+
374
407
  ## Copyright
375
408
 
376
- Copyright 2014 Seamus Abshere
409
+ Copyright 2013-2019 Seamus Abshere
410
+ Copyright 2017-2019 Philip Schalm
411
+ Portions Copyright (c) 2019 The JRuby Team
data/Rakefile CHANGED
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
2
+ require "bundler/gem_helper"
3
+ case RUBY_PLATFORM
4
+ when "java"
5
+ Bundler::GemHelper.install_tasks name: "upsert-java"
6
+ else
7
+ Bundler::GemHelper.install_tasks name: "upsert"
8
+ end
3
9
  require "rspec/core/rake_task"
4
10
 
5
11
  RSpec::Core::RakeTask.new(:spec) do |t|
data/lib/upsert.rb CHANGED
@@ -37,6 +37,31 @@ class Upsert
37
37
  end
38
38
  end
39
39
 
40
+ def mutex_for_row(upsert, row)
41
+ retrieve_mutex(upsert.table_name, row.selector.keys)
42
+ end
43
+
44
+ def mutex_for_function(upsert, row)
45
+ retrieve_mutex(upsert.table_name, row.selector.keys, row.setter.keys)
46
+ end
47
+
48
+ # TODO: Rewrite this to use the thread_safe gem, perhaps?
49
+ def retrieve_mutex(*args)
50
+ # ||= isn't an atomic operation
51
+ MUTEX_FOR_PERFORM.synchronize do
52
+ @mutex_cache ||= {}
53
+ end
54
+
55
+ @mutex_cache.fetch(args.flatten.join('::')) do |k|
56
+ MUTEX_FOR_PERFORM.synchronize do
57
+ # We still need the ||= because this block could have
58
+ # theoretically been entered simultaneously by two threads
59
+ # but the actual assignment is protected by the mutex
60
+ @mutex_cache[k] ||= Mutex.new
61
+ end
62
+ end
63
+ end
64
+
40
65
  # @param [Mysql2::Client,Sqlite3::Database,PG::Connection,#metal] connection A supported database connection.
41
66
  #
42
67
  # Clear any database functions that may have been created.
@@ -185,7 +210,7 @@ class Upsert
185
210
  # @param [Hash] options
186
211
  # @option options [TrueClass,FalseClass] :assume_function_exists (true) Assume the function has already been defined correctly by another process.
187
212
  def initialize(connection, table_name, options = {})
188
- @table_name = table_name.to_s
213
+ @table_name = self.class.normalize_table_name(table_name)
189
214
  metal = Upsert.metal connection
190
215
  @flavor = Upsert.flavor metal
191
216
  @adapter = Upsert.adapter metal
@@ -196,7 +221,10 @@ class Upsert
196
221
  @connection = Connection.const_get(adapter).new self, metal
197
222
  @merge_function_class = MergeFunction.const_get adapter
198
223
  @merge_function_cache = {}
199
- @assume_function_exists = options.fetch :assume_function_exists, true
224
+ @assume_function_exists = options.fetch :assume_function_exists, @flavor != "Postgresql"
225
+
226
+ @merge_function_mutex = Mutex.new
227
+ @row_mutex = Mutex.new
200
228
  end
201
229
 
202
230
  # Upsert a row given a selector and a setter.
@@ -216,8 +244,10 @@ class Upsert
216
244
  # upsert.row({:name => 'Pierre'}, :breed => 'tabby')
217
245
  def row(selector, setter = {}, options = nil)
218
246
  row_object = Row.new(selector, setter, options)
219
- merge_function(row_object).execute(row_object)
220
- nil
247
+ self.class.mutex_for_row(self, row_object).synchronize do
248
+ merge_function(row_object).execute(row_object)
249
+ nil
250
+ end
221
251
  end
222
252
 
223
253
  # @private
@@ -227,16 +257,28 @@ class Upsert
227
257
 
228
258
  def merge_function(row)
229
259
  cache_key = [row.selector.keys, row.setter.keys]
230
- @merge_function_cache[cache_key] ||= merge_function_class.new(self, row.selector.keys, row.setter.keys, assume_function_exists?)
260
+ self.class.mutex_for_function(self, row).synchronize do
261
+ @merge_function_cache[cache_key] ||=
262
+ merge_function_class.new(self, row.selector.keys, row.setter.keys, assume_function_exists?)
263
+ end
231
264
  end
232
265
 
233
266
  # @private
234
267
  def quoted_table_name
235
- @quoted_table_name ||= connection.quote_ident table_name
268
+ @quoted_table_name ||= table_name.map { |t| connection.quote_ident(t) }.join(".")
236
269
  end
237
270
 
238
271
  # @private
239
272
  def column_definitions
240
- @column_definitions ||= ColumnDefinition.const_get(flavor).all connection, table_name
273
+ @column_definitions ||= ColumnDefinition.const_get(flavor).all connection, quoted_table_name
274
+ end
275
+
276
+ # @private
277
+ def self.normalize_table_name(table_name)
278
+ if defined?(Sequel) && table_name.is_a?(::Sequel::SQL::QualifiedIdentifier)
279
+ [table_name.table, table_name.column]
280
+ else
281
+ [*table_name].map(&:to_s)
282
+ end
241
283
  end
242
284
  end
@@ -3,8 +3,8 @@ class Upsert
3
3
  # @private
4
4
  class Mysql < ColumnDefinition
5
5
  class << self
6
- def all(connection, table_name)
7
- connection.execute("SHOW COLUMNS FROM #{connection.quote_ident(table_name)}").map do |row|
6
+ def all(connection, quoted_table_name)
7
+ connection.execute("SHOW COLUMNS FROM #{quoted_table_name}").map do |row|
8
8
  # {"Field"=>"name", "Type"=>"varchar(255)", "Null"=>"NO", "Key"=>"PRI", "Default"=>nil, "Extra"=>""}
9
9
  name = row['Field'] || row['COLUMN_NAME'] || row[:Field] || row[:COLUMN_NAME]
10
10
  type = row['Type'] || row['COLUMN_TYPE'] || row[:Type] || row[:COLUMN_TYPE]
@@ -4,14 +4,14 @@ class Upsert
4
4
  class Postgresql < ColumnDefinition
5
5
  class << self
6
6
  # activerecord-3.2.5/lib/active_record/connection_adapters/postgresql_adapter.rb#column_definitions
7
- def all(connection, table_name)
7
+ def all(connection, quoted_table_name)
8
8
  res = connection.execute <<-EOS
9
- SELECT a.attname AS name, format_type(a.atttypid, a.atttypmod) AS sql_type, d.adsrc AS default
10
- FROM pg_attribute a LEFT JOIN pg_attrdef d
9
+ SELECT a.attname AS name, format_type(a.atttypid, a.atttypmod) AS sql_type, d.adsrc AS default
10
+ FROM pg_attribute a LEFT JOIN pg_attrdef d
11
11
  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
12
- WHERE a.attrelid = '#{connection.quote_ident(table_name)}'::regclass
13
- AND a.attnum > 0 AND NOT a.attisdropped
14
- EOS
12
+ WHERE a.attrelid = '#{quoted_table_name}'::regclass
13
+ AND a.attnum > 0 AND NOT a.attisdropped
14
+ EOS
15
15
  res.map do |row|
16
16
  new connection, row['name'], row['sql_type'], row['default']
17
17
  end.sort_by do |cd|
@@ -19,7 +19,7 @@ EOS
19
19
  end
20
20
  end
21
21
  end
22
-
22
+
23
23
  # NOTE not using this because it can't be indexed
24
24
  # def equality(left, right)
25
25
  # "#{left} IS NOT DISTINCT FROM #{right}"
@@ -40,7 +40,8 @@ EOS
40
40
  if hstore?
41
41
  'text'
42
42
  else
43
- super
43
+ # JDBC uses prepared statements and properly sends date objects (which are otherwise escaped)
44
+ RUBY_PLATFORM == "java" ? sql_type : super
44
45
  end
45
46
  end
46
47
 
@@ -3,9 +3,9 @@ class Upsert
3
3
  # @private
4
4
  class Sqlite3 < ColumnDefinition
5
5
  class << self
6
- def all(connection, table_name)
6
+ def all(connection, quoted_table_name)
7
7
  # activerecord-3.2.13/lib/active_record/connection_adapters/sqlite_adapter.rb
8
- connection.execute("PRAGMA table_info(#{connection.quote_ident(table_name)})").map do |row|#, 'SCHEMA').to_hash
8
+ connection.execute("PRAGMA table_info(#{quoted_table_name})").map do |row|#, 'SCHEMA').to_hash
9
9
  if connection.metal.respond_to?(:results_as_hash) and not connection.metal.results_as_hash
10
10
  row = {'name' => row[1], 'type' => row[2], 'dflt_value' => row[4]}
11
11
  end
@@ -25,7 +25,7 @@ class Upsert
25
25
  end
26
26
  end
27
27
  end
28
-
28
+
29
29
  def equality(left, right)
30
30
  "(#{left} IS #{right} OR (#{left} IS NULL AND #{right} IS NULL))"
31
31
  end