upsert 2.2.0 → 2.9.10

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.
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