activerecord-jdbc-adapter 52.8-java → 60.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -2
  3. data/.travis.yml +58 -37
  4. data/Gemfile +9 -2
  5. data/README.md +25 -9
  6. data/Rakefile +1 -1
  7. data/Rakefile.jdbc +8 -1
  8. data/activerecord-jdbc-adapter.gemspec +5 -8
  9. data/lib/arjdbc/abstract/connection_management.rb +7 -0
  10. data/lib/arjdbc/abstract/core.rb +16 -23
  11. data/lib/arjdbc/abstract/database_statements.rb +26 -2
  12. data/lib/arjdbc/abstract/statement_cache.rb +2 -5
  13. data/lib/arjdbc/abstract/transaction_support.rb +5 -3
  14. data/lib/arjdbc/db2/column.rb +0 -39
  15. data/lib/arjdbc/derby/adapter.rb +1 -20
  16. data/lib/arjdbc/firebird/adapter.rb +0 -21
  17. data/lib/arjdbc/h2/adapter.rb +0 -15
  18. data/lib/arjdbc/hsqldb/adapter.rb +0 -14
  19. data/lib/arjdbc/informix/adapter.rb +0 -23
  20. data/lib/arjdbc/jdbc/adapter.rb +3 -1
  21. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  22. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  23. data/lib/arjdbc/jdbc/base_ext.rb +3 -1
  24. data/lib/arjdbc/jdbc/callbacks.rb +2 -0
  25. data/lib/arjdbc/jdbc/column.rb +2 -0
  26. data/lib/arjdbc/jdbc/connection.rb +2 -0
  27. data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
  28. data/lib/arjdbc/jdbc/error.rb +2 -0
  29. data/lib/arjdbc/jdbc/extension.rb +2 -0
  30. data/lib/arjdbc/jdbc/java.rb +3 -1
  31. data/lib/arjdbc/jdbc/railtie.rb +3 -1
  32. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
  33. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
  34. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  35. data/lib/arjdbc/jdbc/type_converter.rb +2 -0
  36. data/lib/arjdbc/mysql/adapter.rb +47 -18
  37. data/lib/arjdbc/mysql/connection_methods.rb +0 -1
  38. data/lib/arjdbc/postgresql/adapter.rb +220 -213
  39. data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
  40. data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
  41. data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
  42. data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
  43. data/lib/arjdbc/postgresql/column.rb +6 -4
  44. data/lib/arjdbc/postgresql/connection_methods.rb +0 -1
  45. data/lib/arjdbc/postgresql/name.rb +2 -0
  46. data/lib/arjdbc/postgresql/oid_types.rb +2 -0
  47. data/lib/arjdbc/sqlite3/adapter.rb +175 -180
  48. data/lib/arjdbc/sqlite3/connection_methods.rb +15 -5
  49. data/lib/arjdbc/tasks/databases.rake +13 -10
  50. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  51. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  52. data/lib/arjdbc/util/table_copier.rb +3 -1
  53. data/lib/arjdbc/version.rb +1 -1
  54. data/pom.xml +4 -4
  55. data/rakelib/01-tomcat.rake +2 -2
  56. data/rakelib/rails.rake +1 -1
  57. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  58. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  59. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +434 -701
  60. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +0 -51
  61. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  62. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +31 -24
  63. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
  64. metadata +7 -9
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  ArJdbc.load_java_part :PostgreSQL
3
3
 
4
4
  require 'ipaddr'
@@ -42,46 +42,52 @@ module ArJdbc
42
42
  # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
43
43
  def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn end
44
44
 
45
- ADAPTER_NAME = 'PostgreSQL'.freeze
45
+ ADAPTER_NAME = 'PostgreSQL'
46
46
 
47
47
  def adapter_name
48
48
  ADAPTER_NAME
49
49
  end
50
50
 
51
- def postgresql_version
52
- @postgresql_version ||=
53
- begin
54
- version = @connection.database_product
55
- if match = version.match(/([\d\.]*\d).*?/)
56
- version = match[1].split('.').map(&:to_i)
57
- # PostgreSQL version representation does not have more than 4 digits
58
- # From version 10 onwards, PG has changed its versioning policy to
59
- # limit it to only 2 digits. i.e. in 10.x, 10 being the major
60
- # version and x representing the patch release
61
- # Refer to:
62
- # https://www.postgresql.org/support/versioning/
63
- # https://www.postgresql.org/docs/10/static/libpq-status.html -> PQserverVersion()
64
- # for more info
65
-
66
- if version.size >= 3
67
- (version[0] * 100 + version[1]) * 100 + version[2]
68
- elsif version.size == 2
69
- if version[0] >= 10
70
- version[0] * 100 * 100 + version[1]
71
- else
72
- (version[0] * 100 + version[1]) * 100
73
- end
74
- elsif version.size == 1
75
- version[0] * 100 * 100
51
+ def get_database_version # :nodoc:
52
+ begin
53
+ version = @connection.database_product
54
+ if match = version.match(/([\d\.]*\d).*?/)
55
+ version = match[1].split('.').map(&:to_i)
56
+ # PostgreSQL version representation does not have more than 4 digits
57
+ # From version 10 onwards, PG has changed its versioning policy to
58
+ # limit it to only 2 digits. i.e. in 10.x, 10 being the major
59
+ # version and x representing the patch release
60
+ # Refer to:
61
+ # https://www.postgresql.org/support/versioning/
62
+ # https://www.postgresql.org/docs/10/static/libpq-status.html -> PQserverVersion()
63
+ # for more info
64
+
65
+ if version.size >= 3
66
+ (version[0] * 100 + version[1]) * 100 + version[2]
67
+ elsif version.size == 2
68
+ if version[0] >= 10
69
+ version[0] * 100 * 100 + version[1]
76
70
  else
77
- 0
71
+ (version[0] * 100 + version[1]) * 100
78
72
  end
73
+ elsif version.size == 1
74
+ version[0] * 100 * 100
79
75
  else
80
76
  0
81
77
  end
78
+ else
79
+ 0
82
80
  end
81
+ end
82
+ end
83
+
84
+ def check_version # :nodoc:
85
+ if database_version < 90300
86
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
87
+ end
83
88
  end
84
89
 
90
+
85
91
  def redshift?
86
92
  # SELECT version() :
87
93
  # PostgreSQL 8.0.2 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.647
@@ -92,13 +98,6 @@ module ArJdbc
92
98
  end
93
99
  private :redshift?
94
100
 
95
- def use_insert_returning?
96
- if @use_insert_returning.nil?
97
- @use_insert_returning = supports_insert_with_returning?
98
- end
99
- @use_insert_returning
100
- end
101
-
102
101
  def set_client_encoding(encoding)
103
102
  ActiveRecord::Base.logger.warn "client_encoding is set by the driver and should not be altered, ('#{encoding}' ignored)"
104
103
  ActiveRecord::Base.logger.debug "Set the 'allowEncodingChanges' driver property (e.g. using config[:properties]) if you need to override the client encoding when doing a copy."
@@ -165,7 +164,7 @@ module ArJdbc
165
164
  int4range: { name: 'int4range' },
166
165
  int8range: { name: 'int8range' },
167
166
  integer: { name: 'integer' },
168
- interval: { name: 'interval' }, # This doesn't get added to AR's postgres adapter until 5.1 but it fixes broken tests in 5.0 ...
167
+ interval: { name: 'interval' },
169
168
  json: { name: 'json' },
170
169
  jsonb: { name: 'jsonb' },
171
170
  line: { name: 'line' },
@@ -198,107 +197,114 @@ module ArJdbc
198
197
  !native_database_types[type].nil?
199
198
  end
200
199
 
201
- # Enable standard-conforming strings if available.
202
200
  def set_standard_conforming_strings
203
- self.standard_conforming_strings=(true)
201
+ execute("SET standard_conforming_strings = on", "SCHEMA")
204
202
  end
205
203
 
206
- # Enable standard-conforming strings if available.
207
- def standard_conforming_strings=(enable)
208
- client_min_messages = self.client_min_messages
209
- begin
210
- self.client_min_messages = 'panic'
211
- value = enable ? "on" : "off"
212
- execute("SET standard_conforming_strings = #{value}", 'SCHEMA')
213
- @standard_conforming_strings = ( value == "on" )
214
- rescue
215
- @standard_conforming_strings = :unsupported
216
- ensure
217
- self.client_min_messages = client_min_messages
218
- end
204
+ def supports_bulk_alter?
205
+ true
219
206
  end
220
207
 
221
- def standard_conforming_strings?
222
- if @standard_conforming_strings.nil?
223
- client_min_messages = self.client_min_messages
224
- begin
225
- self.client_min_messages = 'panic'
226
- value = select_one('SHOW standard_conforming_strings', 'SCHEMA')['standard_conforming_strings']
227
- @standard_conforming_strings = ( value == "on" )
228
- rescue
229
- @standard_conforming_strings = :unsupported
230
- ensure
231
- self.client_min_messages = client_min_messages
232
- end
233
- end
234
- @standard_conforming_strings == true # return false if :unsupported
208
+ def supports_index_sort_order?
209
+ true
235
210
  end
236
211
 
237
- def supports_ddl_transactions?; true end
212
+ def supports_partial_index?
213
+ true
214
+ end
238
215
 
239
- def supports_advisory_locks?; true end
216
+ def supports_expression_index?
217
+ true
218
+ end
240
219
 
241
- def supports_explain?; true end
220
+ def supports_transaction_isolation?
221
+ true
222
+ end
242
223
 
243
- def supports_expression_index?; true end
224
+ def supports_foreign_keys?
225
+ true
226
+ end
244
227
 
245
- def supports_foreign_keys?; true end
228
+ def supports_validate_constraints?
229
+ true
230
+ end
246
231
 
247
- def supports_validate_constraints?; true end
232
+ def supports_views?
233
+ true
234
+ end
248
235
 
249
- def supports_index_sort_order?; true end
236
+ def supports_datetime_with_precision?
237
+ true
238
+ end
250
239
 
251
- def supports_partial_index?; true end
240
+ def supports_json?
241
+ database_version >= 90200
242
+ end
252
243
 
253
- def supports_savepoints?; true end
244
+ def supports_comments?
245
+ true
246
+ end
254
247
 
255
- def supports_transaction_isolation?; true end
248
+ def supports_savepoints?
249
+ true
250
+ end
256
251
 
257
- def supports_views?; true end
252
+ def supports_insert_returning?
253
+ true
254
+ end
258
255
 
259
- def supports_bulk_alter?; true end
256
+ def supports_insert_on_conflict?
257
+ database_version >= 90500
258
+ end
259
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
260
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
261
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
260
262
 
261
- def supports_datetime_with_precision?; true end
263
+ def index_algorithms
264
+ { concurrently: 'CONCURRENTLY' }
265
+ end
262
266
 
263
- def supports_comments?; true end
267
+ def supports_ddl_transactions?
268
+ true
269
+ end
264
270
 
265
- # Does PostgreSQL support standard conforming strings?
266
- def supports_standard_conforming_strings?
267
- standard_conforming_strings?
268
- @standard_conforming_strings != :unsupported
271
+ def supports_advisory_locks?
272
+ true
269
273
  end
270
274
 
271
- def supports_foreign_tables? # we don't really support this yet, its a reminder :)
272
- postgresql_version >= 90300
275
+ def supports_explain?
276
+ true
273
277
  end
274
278
 
275
- def supports_hex_escaped_bytea?
276
- postgresql_version >= 90000
279
+ def supports_extensions?
280
+ database_version >= 90200
277
281
  end
278
282
 
279
- def supports_materialized_views?
280
- postgresql_version >= 90300
283
+ def supports_ranges?
284
+ database_version >= 90200
281
285
  end
282
286
 
283
- def supports_json?
284
- postgresql_version >= 90200
287
+ def supports_materialized_views?
288
+ database_version >= 90300
285
289
  end
286
290
 
287
- def supports_insert_with_returning?
288
- postgresql_version >= 80200
291
+ def supports_foreign_tables? # we don't really support this yet, its a reminder :)
292
+ database_version >= 90300
289
293
  end
290
294
 
291
295
  def supports_pgcrypto_uuid?
292
- postgresql_version >= 90400
296
+ database_version >= 90400
293
297
  end
294
298
 
295
- # Range data-types weren't introduced until PostgreSQL 9.2.
296
- def supports_ranges?
297
- postgresql_version >= 90200
299
+ def supports_optimizer_hints?
300
+ unless defined?(@has_pg_hint_plan)
301
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
302
+ end
303
+ @has_pg_hint_plan
298
304
  end
299
305
 
300
- def supports_extensions?
301
- postgresql_version >= 90200
306
+ def supports_lazy_transactions?
307
+ true
302
308
  end
303
309
 
304
310
  # From AR 5.1 postgres_adapter.rb
@@ -306,65 +312,60 @@ module ArJdbc
306
312
  index.using == :btree || super
307
313
  end
308
314
 
315
+ def get_advisory_lock(lock_id) # :nodoc:
316
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
317
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
318
+ end
319
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
320
+ end
321
+
322
+ def release_advisory_lock(lock_id) # :nodoc:
323
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
324
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
325
+ end
326
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
327
+ end
328
+
309
329
  def enable_extension(name)
310
- execute("CREATE EXTENSION IF NOT EXISTS \"#{name}\"")
330
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
331
+ reload_type_map
332
+ }
311
333
  end
312
334
 
313
335
  def disable_extension(name)
314
- execute("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE")
336
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
337
+ reload_type_map
338
+ }
339
+ end
340
+
341
+ def extension_available?(name)
342
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
315
343
  end
316
344
 
317
345
  def extension_enabled?(name)
318
- if supports_extensions?
319
- rows = select_rows("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)", 'SCHEMA')
320
- available = rows.first.first # true/false or 't'/'f'
321
- available == true || available == 't'
322
- end
346
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
323
347
  end
324
348
 
325
349
  def extensions
326
- if supports_extensions?
327
- rows = select_rows "SELECT extname from pg_extension", "SCHEMA"
328
- rows.map { |row| row.first }
329
- else
330
- []
331
- end
350
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
332
351
  end
333
352
 
334
- def index_algorithms
335
- { :concurrently => 'CONCURRENTLY' }
353
+ # Returns the configured supported identifier length supported by PostgreSQL
354
+ def max_identifier_length
355
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
336
356
  end
337
357
 
338
- # Set the authorized user for this session.
358
+ # Set the authorized user for this session
339
359
  def session_auth=(user)
340
360
  clear_cache!
341
- execute "SET SESSION AUTHORIZATION #{user}"
361
+ execute("SET SESSION AUTHORIZATION #{user}")
342
362
  end
343
363
 
344
- # Came from postgres_adapter
345
- def get_advisory_lock(lock_id) # :nodoc:
346
- unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
347
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
348
- end
349
- select_value("SELECT pg_try_advisory_lock(#{lock_id});")
350
- end
351
-
352
- # Came from postgres_adapter
353
- def release_advisory_lock(lock_id) # :nodoc:
354
- unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
355
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
356
- end
357
- select_value("SELECT pg_advisory_unlock(#{lock_id})")
358
- end
359
-
360
- # Returns the max identifier length supported by PostgreSQL
361
- def max_identifier_length
362
- @max_identifier_length ||= select_one('SHOW max_identifier_length', 'SCHEMA'.freeze)['max_identifier_length'].to_i
364
+ def use_insert_returning?
365
+ @use_insert_returning
363
366
  end
364
- alias table_alias_length max_identifier_length
365
- alias index_name_length max_identifier_length
366
367
 
367
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
368
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
368
369
  val = super
369
370
  if !use_insert_returning? && pk
370
371
  unless sequence_name
@@ -383,20 +384,12 @@ module ArJdbc
383
384
  ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query("EXPLAIN #{sql}", 'EXPLAIN', binds))
384
385
  end
385
386
 
386
- # @note Only for "better" AR 4.0 compatibility.
387
- # @private
388
- def query(sql, name = nil)
389
- log(sql, name) do
390
- result = []
391
- @connection.execute_query_raw(sql, []) do |*values|
392
- # We need to use #deep_dup here because it appears that
393
- # the java method is reusing an object in some cases
394
- # which makes all of the entries in the "result"
395
- # array end up with the same values as the last row
396
- result << values.deep_dup
397
- end
398
- result
399
- end
387
+ # from ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
388
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback) # :nodoc:
389
+ private_constant :READ_QUERY
390
+
391
+ def write_query?(sql) # :nodoc:
392
+ !READ_QUERY.match?(sql)
400
393
  end
401
394
 
402
395
  # We need to make sure to deallocate all the prepared statements
@@ -427,6 +420,24 @@ module ArJdbc
427
420
  exec_query("SELECT currval('#{sequence_name}')", 'SQL')
428
421
  end
429
422
 
423
+ def build_insert_sql(insert) # :nodoc:
424
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
425
+
426
+ if insert.skip_duplicates?
427
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
428
+ elsif insert.update_duplicates?
429
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
430
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
431
+ end
432
+
433
+ sql << " RETURNING #{insert.returning}" if insert.returning
434
+ sql
435
+ end
436
+
437
+ def build_truncate_statements(*table_names)
438
+ "TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"
439
+ end
440
+
430
441
  def all_schemas
431
442
  select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
432
443
  end
@@ -463,13 +474,7 @@ module ArJdbc
463
474
 
464
475
  def escape_bytea(string)
465
476
  return unless string
466
- if supports_hex_escaped_bytea?
467
- "\\x#{string.unpack("H*")[0]}"
468
- else
469
- result = ''
470
- string.each_byte { |c| result << sprintf('\\\\%03o', c) }
471
- result
472
- end
477
+ "\\x#{string.unpack("H*")[0]}"
473
478
  end
474
479
 
475
480
  # @override
@@ -502,6 +507,19 @@ module ArJdbc
502
507
  nil
503
508
  end
504
509
 
510
+
511
+ # @private
512
+ def column_name_for_operation(operation, node)
513
+ case operation
514
+ when 'maximum' then 'max'
515
+ when 'minimum' then 'min'
516
+ when 'average' then 'avg'
517
+ else operation.downcase
518
+ end
519
+ end
520
+
521
+ private
522
+
505
523
  # Returns the list of a table's column names, data types, and default values.
506
524
  #
507
525
  # If the table name is not prefixed with a schema, the database will
@@ -511,82 +529,73 @@ module ArJdbc
511
529
  # - format_type includes the column size constraint, e.g. varchar(50)
512
530
  # - ::regclass is a function that gives the id for a table name
513
531
  def column_definitions(table_name)
514
- select_rows(<<-end_sql, 'SCHEMA')
532
+ select_rows(<<~SQL, 'SCHEMA')
515
533
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
516
534
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
517
- (SELECT c.collname FROM pg_collation c, pg_type t
518
- WHERE c.oid = a.attcollation AND t.oid = a.atttypid
519
- AND a.attcollation <> t.typcollation),
520
- col_description(a.attrelid, a.attnum) AS comment
535
+ c.collname, col_description(a.attrelid, a.attnum) AS comment
521
536
  FROM pg_attribute a
522
537
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
538
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
539
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
523
540
  WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
524
541
  AND a.attnum > 0 AND NOT a.attisdropped
525
542
  ORDER BY a.attnum
526
- end_sql
543
+ SQL
527
544
  end
528
- private :column_definitions
529
545
 
530
- def truncate(table_name, name = nil)
531
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
546
+ def extract_table_ref_from_insert_sql(sql)
547
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
548
+ $1.strip if $1
532
549
  end
533
550
 
534
- # @private
535
- def column_name_for_operation(operation, node)
536
- case operation
537
- when 'maximum' then 'max'
538
- when 'minimum' then 'min'
539
- when 'average' then 'avg'
540
- else operation.downcase
541
- end
551
+ def arel_visitor
552
+ Arel::Visitors::PostgreSQL.new(self)
542
553
  end
543
554
 
544
- private
545
-
546
555
  # Pulled from ActiveRecord's Postgres adapter and modified to use execute
547
556
  def can_perform_case_insensitive_comparison_for?(column)
548
557
  @case_insensitive_cache ||= {}
549
558
  @case_insensitive_cache[column.sql_type] ||= begin
550
- sql = <<-end_sql
551
- SELECT exists(
552
- SELECT * FROM pg_proc
553
- WHERE proname = 'lower'
554
- AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
555
- ) OR exists(
556
- SELECT * FROM pg_proc
557
- INNER JOIN pg_cast
558
- ON ARRAY[casttarget]::oidvector = proargtypes
559
- WHERE proname = 'lower'
560
- AND castsource = #{quote column.sql_type}::regtype
561
- )
562
- end_sql
559
+ sql = <<~SQL
560
+ SELECT exists(
561
+ SELECT * FROM pg_proc
562
+ WHERE proname = 'lower'
563
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
564
+ ) OR exists(
565
+ SELECT * FROM pg_proc
566
+ INNER JOIN pg_cast
567
+ ON ARRAY[casttarget]::oidvector = proargtypes
568
+ WHERE proname = 'lower'
569
+ AND castsource = #{quote column.sql_type}::regtype
570
+ )
571
+ SQL
563
572
  select_value(sql, 'SCHEMA')
564
573
  end
565
574
  end
566
575
 
567
- def translate_exception(exception, message)
576
+ def translate_exception(exception, message:, sql:, binds:)
568
577
  return super unless exception.is_a?(ActiveRecord::JDBCError)
569
578
 
570
579
  # TODO: Can we base these on an error code of some kind?
571
580
  case exception.message
572
581
  when /duplicate key value violates unique constraint/
573
- ::ActiveRecord::RecordNotUnique.new(message)
582
+ ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
574
583
  when /violates not-null constraint/
575
- ::ActiveRecord::NotNullViolation.new(message)
584
+ ::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
576
585
  when /violates foreign key constraint/
577
- ::ActiveRecord::InvalidForeignKey.new(message)
586
+ ::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
578
587
  when /value too long/
579
- ::ActiveRecord::ValueTooLong.new(message)
588
+ ::ActiveRecord::ValueTooLong.new(message, sql: sql, binds: binds)
580
589
  when /out of range/
581
- ::ActiveRecord::RangeError.new(message)
590
+ ::ActiveRecord::RangeError.new(message, sql: sql, binds: binds)
582
591
  when /could not serialize/
583
- ::ActiveRecord::SerializationFailure.new(message)
592
+ ::ActiveRecord::SerializationFailure.new(message, sql: sql, binds: binds)
584
593
  when /deadlock detected/
585
- ::ActiveRecord::Deadlocked.new(message)
594
+ ::ActiveRecord::Deadlocked.new(message, sql: sql, binds: binds)
586
595
  when /lock timeout/
587
- ::ActiveRecord::LockWaitTimeout.new(message)
596
+ ::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
588
597
  when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
589
- ::ActiveRecord::QueryCanceled.new(message)
598
+ ::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
590
599
  else
591
600
  super
592
601
  end
@@ -610,11 +619,6 @@ module ArJdbc
610
619
  end
611
620
  end
612
621
 
613
- def extract_table_ref_from_insert_sql(sql)
614
- sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
615
- $1.strip if $1
616
- end
617
-
618
622
  def local_tz
619
623
  @local_tz ||= execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
620
624
  end
@@ -635,6 +639,7 @@ module ActiveRecord::ConnectionAdapters
635
639
  remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
636
640
 
637
641
  class PostgreSQLAdapter < AbstractAdapter
642
+ class_attribute :create_unlogged_tables, default: false
638
643
 
639
644
  # Try to use as much of the built in postgres logic as possible
640
645
  # maybe someday we can extend the actual adapter
@@ -672,11 +677,16 @@ module ActiveRecord::ConnectionAdapters
672
677
  initialize_type_map
673
678
 
674
679
  @use_insert_returning = @config.key?(:insert_returning) ?
675
- self.class.type_cast_config_to_boolean(@config[:insert_returning]) : nil
680
+ self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
676
681
  end
677
682
 
678
- def arel_visitor # :nodoc:
679
- Arel::Visitors::PostgreSQL.new(self)
683
+ def self.database_exists?(config)
684
+ conn = ActiveRecord::Base.postgresql_connection(config)
685
+ conn && conn.really_valid?
686
+ rescue ActiveRecord::NoDatabaseError
687
+ false
688
+ ensure
689
+ conn.disconnect! if conn
680
690
  end
681
691
 
682
692
  require 'active_record/connection_adapters/postgresql/schema_definitions'
@@ -685,11 +695,8 @@ module ActiveRecord::ConnectionAdapters
685
695
  TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
686
696
  Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
687
697
 
688
- def create_table_definition(*args) # :nodoc:
689
- TableDefinition.new(*args)
690
- end
691
-
692
698
  public :sql_for_insert
699
+ alias :postgresql_version :database_version
693
700
 
694
701
  def jdbc_connection_class(spec)
695
702
  ::ArJdbc::PostgreSQL.jdbc_connection_class
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This implements a basic decoder to work around ActiveRecord's dependence on the pg gem
2
4
  module ActiveRecord::ConnectionAdapters::PostgreSQL::OID
3
5
  class Array < ActiveModel::Type::Value
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This implements a basic encoder to work around ActiveRecord's dependence on the pg gem
2
4
  module ActiveRecord::ConnectionAdapters::PostgreSQL::OID
3
5
  class Array < ActiveModel::Type::Value
@@ -6,10 +8,10 @@ module ActiveRecord::ConnectionAdapters::PostgreSQL::OID
6
8
  class Array
7
9
 
8
10
  def initialize(name:, delimiter:)
9
- @type = if name == 'string[]'.freeze
11
+ @type = if name == 'string[]'
10
12
  'text'.freeze
11
13
  else
12
- base_type = name.chomp('[]'.freeze).to_sym
14
+ base_type = name.chomp('[]').to_sym
13
15
  ActiveRecord::Base.connection.native_database_types[base_type][:name]
14
16
  end
15
17
  end