upsert 2.1.0 → 2.9.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -0
  3. data/.standard.yml +1 -0
  4. data/.travis.yml +60 -12
  5. data/CHANGELOG +39 -0
  6. data/Gemfile +12 -1
  7. data/LICENSE +3 -1
  8. data/README.md +47 -6
  9. data/Rakefile +7 -1
  10. data/lib/upsert.rb +54 -11
  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 +11 -5
  15. data/lib/upsert/connection/Java_OrgPostgresqlJdbc_PgConnection.rb +33 -0
  16. data/lib/upsert/connection/PG_Connection.rb +10 -1
  17. data/lib/upsert/connection/jdbc.rb +20 -1
  18. data/lib/upsert/connection/postgresql.rb +2 -3
  19. data/lib/upsert/merge_function.rb +5 -4
  20. data/lib/upsert/merge_function/Java_OrgPostgresqlJdbc_PgConnection.rb +27 -0
  21. data/lib/upsert/merge_function/PG_Connection.rb +11 -42
  22. data/lib/upsert/merge_function/postgresql.rb +215 -1
  23. data/lib/upsert/merge_function/sqlite3.rb +10 -0
  24. data/lib/upsert/version.rb +1 -1
  25. data/spec/active_record_upsert_spec.rb +10 -0
  26. data/spec/correctness_spec.rb +34 -5
  27. data/spec/database_functions_spec.rb +16 -9
  28. data/spec/database_spec.rb +7 -0
  29. data/spec/hstore_spec.rb +56 -55
  30. data/spec/jruby_spec.rb +9 -0
  31. data/spec/logger_spec.rb +8 -6
  32. data/spec/postgresql_spec.rb +94 -0
  33. data/spec/reserved_words_spec.rb +21 -17
  34. data/spec/sequel_spec.rb +26 -7
  35. data/spec/spec_helper.rb +251 -92
  36. data/spec/speed_spec.rb +3 -32
  37. data/spec/threaded_spec.rb +35 -12
  38. data/spec/type_safety_spec.rb +2 -1
  39. data/travis/install_postgres.sh +18 -0
  40. data/travis/run_docker_db.sh +20 -0
  41. data/travis/tune_mysql.sh +7 -0
  42. data/upsert-java.gemspec +12 -0
  43. data/upsert.gemspec +9 -57
  44. data/upsert.gemspec.common +106 -0
  45. metadata +53 -40
  46. data/lib/upsert/connection/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb +0 -15
  47. data/lib/upsert/merge_function/Java_OrgPostgresqlJdbc4_Jdbc4Connection.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8a8d49c15022e8faa065c2f060349210b28998b9
4
- data.tar.gz: 3814d296ef548257347af887ca39a04b801ceb91
2
+ SHA256:
3
+ metadata.gz: 0d2ed0c63bb4539d42dcf15f20aaabdbca692c9fd5187c06aa76e1b1fc5d9655
4
+ data.tar.gz: 03bbc3ca8ebf200b3552bebe05a1fc13dbea187a023ad996691be02568ce02cb
5
5
  SHA512:
6
- metadata.gz: e3a2553e8a0d143bb90592d1dc7f5a4ccd9224386fdce394dceef2ad2f46e186a853e61560ef1d6ea93f52b5bb7e948659c62398c60cc6c5d8c5887bdd9c6d0f
7
- data.tar.gz: 864fcc5434d104ae1c4d5e8d68a8e8546217612be2d710b01a8ecfbd5e25435f2181a8c59afd7f6224b388762276f93256be6c7cec4ea5e18e994129ba1b396d
6
+ metadata.gz: 6c65b45f0d6305bea8c70a2046ccf045bebc29827d6a4d5bd859b3a034d2209e5230abf10f213aa9aebfbee76021c54f2e6bc138917ed280de7273662d4077a4
7
+ data.tar.gz: 486ea504857992b1711b8966d42f9996a14f27078fbe54809ebb4d7a53d18bed80d0f29f68a9992e3a5d6ab8fa3d61a1279375b1bb8efe6bca6453b49a3965a1
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,15 +1,63 @@
1
+ dist: xenial
1
2
  language: ruby
2
- global:
3
- - USERNAME=travis
4
- PASSWORD=
3
+ cache: bundler
4
+ services:
5
+ - docker
5
6
  rvm:
6
- - 2.2.0
7
- - 2.1.0
8
- - 2.0.0
9
- - 1.9.3
10
- - 1.9.2
11
- - 1.8.7
12
- - rbx-2
7
+ - 2.6
8
+ - 2.5
9
+ - 2.4
10
+ - 2.3
11
+ - 2.2
12
+ - jruby-9.1.14.0
13
+ - jruby-9.1.17.0
14
+ - jruby-9.2.7.0
13
15
  env:
14
- - DB=postgresql
15
- - DB=mysql
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
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
56
+ allow_failures:
57
+ - env: DB=postgresql DB_VERSION=postgres:12
58
+ - env: DB=postgresql DB_VERSION=postgres:12 UNIQUE_CONSTRAINT=true
59
+ before_install:
60
+ - ./travis/run_docker_db.sh
61
+ - bundle --version
62
+ - gem --version
63
+ script: ./travis/run_specs.sh
data/CHANGELOG CHANGED
@@ -1,3 +1,42 @@
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
+
17
+ 2.2.0 / 2017-04-14
18
+
19
+ * Enhancements
20
+
21
+ * Use native "upsert" on Postgres 9.5+! (thanks to @pnomolos https://github.com/seamusabshere/upsert/pull/79)
22
+ * More modern CI tests
23
+
24
+ 2.1.2 / 2016-02-25
25
+
26
+ * Enhancements
27
+
28
+ * Test on Ruby 2.3 - thanks @Ch4s3 https://github.com/seamusabshere/upsert/pull/70
29
+
30
+ * Bug fixes
31
+
32
+ * Stop using Thread.exclusive - thanks @hpetru https://github.com/seamusabshere/upsert/pull/67
33
+
34
+ 2.1.1 / 2016-02-12
35
+
36
+ * Enhancements
37
+
38
+ * Assume function exists to avoid huge amounts of recreation
39
+
1
40
  2.1.0 / 2015-03-13
2
41
 
3
42
  * Bug fixes
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
@@ -57,6 +57,14 @@ end
57
57
 
58
58
  Batch mode is tested to be about 80% faster on PostgreSQL, MySQL, and SQLite3 than other ways to emulate upsert (see the tests, which fail if they are not faster).
59
59
 
60
+ ### Native Postgres upsert
61
+
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.
65
+
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.
67
+
60
68
  ### ActiveRecord helper method
61
69
 
62
70
  ```ruby
@@ -115,7 +123,10 @@ See below for details about what SQL MERGE trick (emulation of upsert) is used,
115
123
 
116
124
  ### Rails / ActiveRecord
117
125
 
118
- (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
119
130
 
120
131
  ```ruby
121
132
  Upsert.new Pet.connection, Pet.table_name
@@ -167,7 +178,7 @@ BEGIN
167
178
  DECLARE done BOOLEAN;
168
179
  REPEAT
169
180
  BEGIN
170
- -- If there is a unique key constraint error then
181
+ -- If there is a unique key constraint error then
171
182
  -- someone made a concurrent insert. Reset the sentinel
172
183
  -- and try again.
173
184
  DECLARE ER_DUP_UNIQUE CONDITION FOR 23000;
@@ -175,7 +186,7 @@ BEGIN
175
186
  DECLARE CONTINUE HANDLER FOR ER_DUP_UNIQUE BEGIN
176
187
  SET done = FALSE;
177
188
  END;
178
-
189
+
179
190
  DECLARE CONTINUE HANDLER FOR ER_INTEG BEGIN
180
191
  SET done = TRUE;
181
192
  END;
@@ -185,7 +196,7 @@ BEGIN
185
196
  -- Race condition here. If a concurrent INSERT is made after
186
197
  -- the SELECT but before the INSERT below we'll get a duplicate
187
198
  -- key error. But the handler above will take care of that.
188
- IF @count > 0 THEN
199
+ IF @count > 0 THEN
189
200
  -- UPDATE table_name SET b = b_SET WHERE a = a_SEL;
190
201
  UPDATE `pets` SET `name` = `name_set`, `tag_number` = `tag_number_set` WHERE `name` = `name_sel` AND `tag_number` = `tag_number_sel`;
191
202
  ELSE
@@ -224,6 +235,15 @@ require 'pg_hstore'
224
235
  upsert.row({:name => 'Bill'}, :mydata => {:a => 1, :b => 2})
225
236
  ```
226
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
+
227
247
  #### Speed
228
248
 
229
249
  From the tests (updated 9/21/12):
@@ -361,10 +381,31 @@ If you're using MySQL, make sure server/connection timezone is UTC. If you're us
361
381
 
362
382
  In general, run some upserts and make sure datetimes get persisted like you expect.
363
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
+
364
400
  ### Doesn't work with transactional fixtures
365
401
 
366
402
  Per https://github.com/seamusabshere/upsert/issues/23 you might have issues if you try to use transactional fixtures and this library.
367
403
 
404
+ ##
405
+ Testmetrics - https://www.testmetrics.app/seamusabshere/upsert
406
+
368
407
  ## Copyright
369
408
 
370
- 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
@@ -14,11 +14,12 @@ class Upsert
14
14
  # What logger to use.
15
15
  # @return [#info,#warn,#debug]
16
16
  attr_writer :logger
17
-
17
+ MUTEX_FOR_PERFORM = Mutex.new
18
+
18
19
  # The current logger
19
20
  # @return [#info,#warn,#debug]
20
21
  def logger
21
- @logger || Thread.exclusive do
22
+ @logger || MUTEX_FOR_PERFORM.synchronize do
22
23
  @logger ||= if defined?(::Rails) and (rails_logger = ::Rails.logger)
23
24
  rails_logger
24
25
  elsif defined?(::ActiveRecord) and ::ActiveRecord.const_defined?(:Base) and (ar_logger = ::ActiveRecord::Base.logger)
@@ -36,6 +37,31 @@ class Upsert
36
37
  end
37
38
  end
38
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
+
39
65
  # @param [Mysql2::Client,Sqlite3::Database,PG::Connection,#metal] connection A supported database connection.
40
66
  #
41
67
  # Clear any database functions that may have been created.
@@ -182,9 +208,9 @@ class Upsert
182
208
  # @param [Mysql2::Client,Sqlite3::Database,PG::Connection,#metal] connection A supported database connection.
183
209
  # @param [String,Symbol] table_name The name of the table into which you will be upserting.
184
210
  # @param [Hash] options
185
- # @option options [TrueClass,FalseClass] :assume_function_exists (false) Assume the function has already been defined correctly by another process.
211
+ # @option options [TrueClass,FalseClass] :assume_function_exists (true) Assume the function has already been defined correctly by another process.
186
212
  def initialize(connection, table_name, options = {})
187
- @table_name = table_name.to_s
213
+ @table_name = self.class.normalize_table_name(table_name)
188
214
  metal = Upsert.metal connection
189
215
  @flavor = Upsert.flavor metal
190
216
  @adapter = Upsert.adapter metal
@@ -195,7 +221,10 @@ class Upsert
195
221
  @connection = Connection.const_get(adapter).new self, metal
196
222
  @merge_function_class = MergeFunction.const_get adapter
197
223
  @merge_function_cache = {}
198
- @assume_function_exists = options.fetch :assume_function_exists, false
224
+ @assume_function_exists = options.fetch :assume_function_exists, @flavor != "Postgresql"
225
+
226
+ @merge_function_mutex = Mutex.new
227
+ @row_mutex = Mutex.new
199
228
  end
200
229
 
201
230
  # Upsert a row given a selector and a setter.
@@ -215,27 +244,41 @@ class Upsert
215
244
  # upsert.row({:name => 'Pierre'}, :breed => 'tabby')
216
245
  def row(selector, setter = {}, options = nil)
217
246
  row_object = Row.new(selector, setter, options)
218
- merge_function(row_object).execute(row_object)
219
- nil
247
+ self.class.mutex_for_row(self, row_object).synchronize do
248
+ merge_function(row_object).execute(row_object)
249
+ nil
250
+ end
220
251
  end
221
252
 
222
253
  # @private
223
254
  def clear_database_functions
224
255
  merge_function_class.clear connection
225
256
  end
226
-
257
+
227
258
  def merge_function(row)
228
259
  cache_key = [row.selector.keys, row.setter.keys]
229
- @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
230
264
  end
231
265
 
232
266
  # @private
233
267
  def quoted_table_name
234
- @quoted_table_name ||= connection.quote_ident table_name
268
+ @quoted_table_name ||= table_name.map { |t| connection.quote_ident(t) }.join(".")
235
269
  end
236
270
 
237
271
  # @private
238
272
  def column_definitions
239
- @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
240
283
  end
241
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