activerecord-jdbc-adapter 52.7-java → 60.0-java

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 (63) 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_require.rb +3 -1
  22. data/lib/arjdbc/jdbc/base_ext.rb +3 -1
  23. data/lib/arjdbc/jdbc/callbacks.rb +2 -0
  24. data/lib/arjdbc/jdbc/column.rb +2 -0
  25. data/lib/arjdbc/jdbc/connection.rb +2 -0
  26. data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
  27. data/lib/arjdbc/jdbc/error.rb +2 -0
  28. data/lib/arjdbc/jdbc/extension.rb +2 -0
  29. data/lib/arjdbc/jdbc/java.rb +3 -1
  30. data/lib/arjdbc/jdbc/railtie.rb +3 -1
  31. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
  32. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
  33. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  34. data/lib/arjdbc/jdbc/type_converter.rb +2 -0
  35. data/lib/arjdbc/mysql/adapter.rb +47 -18
  36. data/lib/arjdbc/mysql/connection_methods.rb +0 -1
  37. data/lib/arjdbc/postgresql/adapter.rb +220 -213
  38. data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
  39. data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
  40. data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
  41. data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
  42. data/lib/arjdbc/postgresql/column.rb +6 -4
  43. data/lib/arjdbc/postgresql/connection_methods.rb +0 -1
  44. data/lib/arjdbc/postgresql/name.rb +2 -0
  45. data/lib/arjdbc/postgresql/oid_types.rb +2 -0
  46. data/lib/arjdbc/sqlite3/adapter.rb +175 -180
  47. data/lib/arjdbc/sqlite3/connection_methods.rb +15 -5
  48. data/lib/arjdbc/tasks/databases.rake +13 -10
  49. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  50. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  51. data/lib/arjdbc/util/table_copier.rb +3 -1
  52. data/lib/arjdbc/version.rb +1 -1
  53. data/pom.xml +4 -4
  54. data/rakelib/01-tomcat.rake +2 -2
  55. data/rakelib/rails.rake +1 -1
  56. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  57. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  58. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +434 -701
  59. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +0 -51
  60. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  61. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +31 -24
  62. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
  63. metadata +8 -10
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  ArJdbc::ConnectionMethods.module_eval do
3
3
  def mysql_connection(config)
4
- config = config.deep_dup
5
4
  # NOTE: this isn't "really" necessary but Rails (in tests) assumes being able to :
6
5
  # ActiveRecord::Base.mysql2_connection ActiveRecord::Base.configurations['arunit'].merge(database: ...)
7
6
  config = symbolize_keys_if_necessary(config)
@@ -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