activerecord-jdbc-alt-adapter 52.5.1-java → 60.2.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.nvimlog +0 -0
  4. data/.travis.yml +61 -37
  5. data/Gemfile +10 -3
  6. data/README.md +44 -28
  7. data/Rakefile +1 -1
  8. data/Rakefile.jdbc +8 -1
  9. data/activerecord-jdbc-adapter.gemspec +5 -8
  10. data/activerecord-jdbc-alt-adapter.gemspec +5 -8
  11. data/lib/arel/visitors/sqlserver.rb +33 -23
  12. data/lib/arjdbc/abstract/connection_management.rb +7 -0
  13. data/lib/arjdbc/abstract/core.rb +16 -23
  14. data/lib/arjdbc/abstract/database_statements.rb +24 -0
  15. data/lib/arjdbc/abstract/statement_cache.rb +2 -5
  16. data/lib/arjdbc/abstract/transaction_support.rb +5 -3
  17. data/lib/arjdbc/db2/column.rb +0 -39
  18. data/lib/arjdbc/derby/adapter.rb +1 -20
  19. data/lib/arjdbc/firebird/adapter.rb +0 -21
  20. data/lib/arjdbc/h2/adapter.rb +0 -15
  21. data/lib/arjdbc/hsqldb/adapter.rb +0 -14
  22. data/lib/arjdbc/informix/adapter.rb +0 -23
  23. data/lib/arjdbc/jdbc/adapter.rb +3 -1
  24. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  25. data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
  26. data/lib/arjdbc/jdbc/base_ext.rb +3 -1
  27. data/lib/arjdbc/jdbc/callbacks.rb +2 -0
  28. data/lib/arjdbc/jdbc/column.rb +2 -0
  29. data/lib/arjdbc/jdbc/connection.rb +2 -0
  30. data/lib/arjdbc/jdbc/connection_methods.rb +2 -0
  31. data/lib/arjdbc/jdbc/error.rb +2 -0
  32. data/lib/arjdbc/jdbc/extension.rb +2 -0
  33. data/lib/arjdbc/jdbc/java.rb +3 -1
  34. data/lib/arjdbc/jdbc/railtie.rb +3 -1
  35. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -1
  36. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -1
  37. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  38. data/lib/arjdbc/jdbc/type_converter.rb +2 -0
  39. data/lib/arjdbc/mssql/adapter.rb +105 -36
  40. data/lib/arjdbc/mssql/column.rb +5 -1
  41. data/lib/arjdbc/mssql/connection_methods.rb +8 -2
  42. data/lib/arjdbc/mssql/database_limits.rb +2 -0
  43. data/lib/arjdbc/mssql/database_statements.rb +43 -5
  44. data/lib/arjdbc/mssql/errors.rb +2 -0
  45. data/lib/arjdbc/mssql/explain_support.rb +3 -1
  46. data/lib/arjdbc/mssql/extensions/attribute_methods.rb +5 -1
  47. data/lib/arjdbc/mssql/extensions/calculations.rb +2 -0
  48. data/lib/arjdbc/mssql/quoting.rb +38 -0
  49. data/lib/arjdbc/mssql/schema_creation.rb +24 -2
  50. data/lib/arjdbc/mssql/schema_definitions.rb +10 -0
  51. data/lib/arjdbc/mssql/schema_dumper.rb +2 -0
  52. data/lib/arjdbc/mssql/schema_statements.rb +63 -21
  53. data/lib/arjdbc/mssql/transaction.rb +2 -0
  54. data/lib/arjdbc/mssql/types/binary_types.rb +2 -0
  55. data/lib/arjdbc/mssql/types/date_and_time_types.rb +2 -0
  56. data/lib/arjdbc/mssql/types/deprecated_types.rb +2 -0
  57. data/lib/arjdbc/mssql/types/numeric_types.rb +2 -0
  58. data/lib/arjdbc/mssql/types/string_types.rb +2 -0
  59. data/lib/arjdbc/mssql/types.rb +2 -0
  60. data/lib/arjdbc/mssql/utils.rb +2 -0
  61. data/lib/arjdbc/mssql.rb +3 -1
  62. data/lib/arjdbc/mysql/adapter.rb +47 -18
  63. data/lib/arjdbc/postgresql/adapter.rb +240 -214
  64. data/lib/arjdbc/postgresql/base/array_decoder.rb +2 -0
  65. data/lib/arjdbc/postgresql/base/array_encoder.rb +4 -2
  66. data/lib/arjdbc/postgresql/base/array_parser.rb +4 -2
  67. data/lib/arjdbc/postgresql/base/pgconn.rb +2 -0
  68. data/lib/arjdbc/postgresql/column.rb +6 -4
  69. data/lib/arjdbc/postgresql/name.rb +2 -0
  70. data/lib/arjdbc/postgresql/oid_types.rb +3 -1
  71. data/lib/arjdbc/sqlite3/adapter.rb +188 -180
  72. data/lib/arjdbc/sqlite3/connection_methods.rb +15 -4
  73. data/lib/arjdbc/tasks/databases.rake +13 -10
  74. data/lib/arjdbc/tasks/mssql_database_tasks.rb +49 -5
  75. data/lib/arjdbc/util/quoted_cache.rb +3 -1
  76. data/lib/arjdbc/util/serialized_attributes.rb +3 -1
  77. data/lib/arjdbc/util/table_copier.rb +3 -1
  78. data/lib/arjdbc/version.rb +1 -1
  79. data/pom.xml +4 -4
  80. data/rakelib/01-tomcat.rake +2 -2
  81. data/src/java/arjdbc/ArJdbcModule.java +5 -5
  82. data/src/java/arjdbc/jdbc/DriverWrapper.java +1 -9
  83. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +406 -629
  84. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +88 -0
  85. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +13 -23
  86. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +56 -30
  87. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +94 -99
  88. data/src/java/arjdbc/util/DateTimeUtils.java +12 -4
  89. metadata +7 -16
@@ -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,118 @@ 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_common_table_expressions?
307
+ true
308
+ end
309
+
310
+ def supports_lazy_transactions?
311
+ true
302
312
  end
303
313
 
304
314
  # From AR 5.1 postgres_adapter.rb
@@ -306,65 +316,60 @@ module ArJdbc
306
316
  index.using == :btree || super
307
317
  end
308
318
 
319
+ def get_advisory_lock(lock_id) # :nodoc:
320
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
321
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
322
+ end
323
+ query_value("SELECT pg_try_advisory_lock(#{lock_id})")
324
+ end
325
+
326
+ def release_advisory_lock(lock_id) # :nodoc:
327
+ unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
328
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
329
+ end
330
+ query_value("SELECT pg_advisory_unlock(#{lock_id})")
331
+ end
332
+
309
333
  def enable_extension(name)
310
- execute("CREATE EXTENSION IF NOT EXISTS \"#{name}\"")
334
+ exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
335
+ reload_type_map
336
+ }
311
337
  end
312
338
 
313
339
  def disable_extension(name)
314
- execute("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE")
340
+ exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
341
+ reload_type_map
342
+ }
343
+ end
344
+
345
+ def extension_available?(name)
346
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
315
347
  end
316
348
 
317
349
  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
350
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
323
351
  end
324
352
 
325
353
  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
354
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
332
355
  end
333
356
 
334
- def index_algorithms
335
- { :concurrently => 'CONCURRENTLY' }
357
+ # Returns the configured supported identifier length supported by PostgreSQL
358
+ def max_identifier_length
359
+ @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
336
360
  end
337
361
 
338
- # Set the authorized user for this session.
362
+ # Set the authorized user for this session
339
363
  def session_auth=(user)
340
364
  clear_cache!
341
- execute "SET SESSION AUTHORIZATION #{user}"
342
- end
343
-
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});")
365
+ execute("SET SESSION AUTHORIZATION #{user}")
350
366
  end
351
367
 
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
368
+ def use_insert_returning?
369
+ @use_insert_returning
363
370
  end
364
- alias table_alias_length max_identifier_length
365
- alias index_name_length max_identifier_length
366
371
 
367
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
372
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
368
373
  val = super
369
374
  if !use_insert_returning? && pk
370
375
  unless sequence_name
@@ -378,25 +383,27 @@ module ArJdbc
378
383
  end
379
384
  end
380
385
 
386
+ def execute_batch(statements, name = nil)
387
+ if statements.is_a? Array
388
+ execute(combine_multi_statements(statements), name)
389
+ else
390
+ execute(statements, name)
391
+ end
392
+ end
393
+
381
394
  def explain(arel, binds = [])
382
395
  sql, binds = to_sql_and_binds(arel, binds)
383
396
  ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query("EXPLAIN #{sql}", 'EXPLAIN', binds))
384
397
  end
385
398
 
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
399
+ # from ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
400
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
401
+ :begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :with
402
+ ) # :nodoc:
403
+ private_constant :READ_QUERY
404
+
405
+ def write_query?(sql) # :nodoc:
406
+ !READ_QUERY.match?(sql)
400
407
  end
401
408
 
402
409
  # We need to make sure to deallocate all the prepared statements
@@ -427,6 +434,29 @@ module ArJdbc
427
434
  exec_query("SELECT currval('#{sequence_name}')", 'SQL')
428
435
  end
429
436
 
437
+ def build_insert_sql(insert) # :nodoc:
438
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
439
+
440
+ if insert.skip_duplicates?
441
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
442
+ elsif insert.update_duplicates?
443
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
444
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
445
+ end
446
+
447
+ sql << " RETURNING #{insert.returning}" if insert.returning
448
+ sql
449
+ end
450
+
451
+ def build_truncate_statements(*table_names)
452
+ ["TRUNCATE TABLE #{table_names.flatten.map(&method(:quote_table_name)).join(", ")}"]
453
+ end
454
+
455
+ def truncate(table_name, name = nil)
456
+ ActiveRecord::Base.clear_query_caches_for_current_thread if @query_cache_enabled
457
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)}", name)
458
+ end
459
+
430
460
  def all_schemas
431
461
  select('SELECT nspname FROM pg_namespace').map { |row| row["nspname"] }
432
462
  end
@@ -463,13 +493,7 @@ module ArJdbc
463
493
 
464
494
  def escape_bytea(string)
465
495
  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
496
+ "\\x#{string.unpack("H*")[0]}"
473
497
  end
474
498
 
475
499
  # @override
@@ -488,7 +512,7 @@ module ArJdbc
488
512
  alias_method :quote_schema_name, :quote_column_name
489
513
 
490
514
  # Need to clear the cache even though the AR adapter doesn't for some reason
491
- def remove_column(table_name, column_name, type = nil, options = {})
515
+ def remove_column(table_name, column_name, type = nil, **options)
492
516
  super
493
517
  clear_cache!
494
518
  end
@@ -502,6 +526,19 @@ module ArJdbc
502
526
  nil
503
527
  end
504
528
 
529
+
530
+ # @private
531
+ def column_name_for_operation(operation, node)
532
+ case operation
533
+ when 'maximum' then 'max'
534
+ when 'minimum' then 'min'
535
+ when 'average' then 'avg'
536
+ else operation.downcase
537
+ end
538
+ end
539
+
540
+ private
541
+
505
542
  # Returns the list of a table's column names, data types, and default values.
506
543
  #
507
544
  # If the table name is not prefixed with a schema, the database will
@@ -511,82 +548,73 @@ module ArJdbc
511
548
  # - format_type includes the column size constraint, e.g. varchar(50)
512
549
  # - ::regclass is a function that gives the id for a table name
513
550
  def column_definitions(table_name)
514
- select_rows(<<-end_sql, 'SCHEMA')
551
+ select_rows(<<~SQL, 'SCHEMA')
515
552
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
516
553
  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
554
+ c.collname, col_description(a.attrelid, a.attnum) AS comment
521
555
  FROM pg_attribute a
522
556
  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
557
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
558
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
523
559
  WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
524
560
  AND a.attnum > 0 AND NOT a.attisdropped
525
561
  ORDER BY a.attnum
526
- end_sql
562
+ SQL
527
563
  end
528
- private :column_definitions
529
564
 
530
- def truncate(table_name, name = nil)
531
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
565
+ def extract_table_ref_from_insert_sql(sql)
566
+ sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
567
+ $1.strip if $1
532
568
  end
533
569
 
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
570
+ def arel_visitor
571
+ Arel::Visitors::PostgreSQL.new(self)
542
572
  end
543
573
 
544
- private
545
-
546
574
  # Pulled from ActiveRecord's Postgres adapter and modified to use execute
547
575
  def can_perform_case_insensitive_comparison_for?(column)
548
576
  @case_insensitive_cache ||= {}
549
577
  @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
578
+ sql = <<~SQL
579
+ SELECT exists(
580
+ SELECT * FROM pg_proc
581
+ WHERE proname = 'lower'
582
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
583
+ ) OR exists(
584
+ SELECT * FROM pg_proc
585
+ INNER JOIN pg_cast
586
+ ON ARRAY[casttarget]::oidvector = proargtypes
587
+ WHERE proname = 'lower'
588
+ AND castsource = #{quote column.sql_type}::regtype
589
+ )
590
+ SQL
563
591
  select_value(sql, 'SCHEMA')
564
592
  end
565
593
  end
566
594
 
567
- def translate_exception(exception, message)
595
+ def translate_exception(exception, message:, sql:, binds:)
568
596
  return super unless exception.is_a?(ActiveRecord::JDBCError)
569
597
 
570
598
  # TODO: Can we base these on an error code of some kind?
571
599
  case exception.message
572
600
  when /duplicate key value violates unique constraint/
573
- ::ActiveRecord::RecordNotUnique.new(message)
601
+ ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
574
602
  when /violates not-null constraint/
575
- ::ActiveRecord::NotNullViolation.new(message)
603
+ ::ActiveRecord::NotNullViolation.new(message, sql: sql, binds: binds)
576
604
  when /violates foreign key constraint/
577
- ::ActiveRecord::InvalidForeignKey.new(message)
605
+ ::ActiveRecord::InvalidForeignKey.new(message, sql: sql, binds: binds)
578
606
  when /value too long/
579
- ::ActiveRecord::ValueTooLong.new(message)
607
+ ::ActiveRecord::ValueTooLong.new(message, sql: sql, binds: binds)
580
608
  when /out of range/
581
- ::ActiveRecord::RangeError.new(message)
609
+ ::ActiveRecord::RangeError.new(message, sql: sql, binds: binds)
582
610
  when /could not serialize/
583
- ::ActiveRecord::SerializationFailure.new(message)
611
+ ::ActiveRecord::SerializationFailure.new(message, sql: sql, binds: binds)
584
612
  when /deadlock detected/
585
- ::ActiveRecord::Deadlocked.new(message)
613
+ ::ActiveRecord::Deadlocked.new(message, sql: sql, binds: binds)
586
614
  when /lock timeout/
587
- ::ActiveRecord::LockWaitTimeout.new(message)
615
+ ::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
588
616
  when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
589
- ::ActiveRecord::QueryCanceled.new(message)
617
+ ::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
590
618
  else
591
619
  super
592
620
  end
@@ -610,11 +638,6 @@ module ArJdbc
610
638
  end
611
639
  end
612
640
 
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
641
  def local_tz
619
642
  @local_tz ||= execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
620
643
  end
@@ -635,6 +658,7 @@ module ActiveRecord::ConnectionAdapters
635
658
  remove_const(:PostgreSQLAdapter) if const_defined?(:PostgreSQLAdapter)
636
659
 
637
660
  class PostgreSQLAdapter < AbstractAdapter
661
+ class_attribute :create_unlogged_tables, default: false
638
662
 
639
663
  # Try to use as much of the built in postgres logic as possible
640
664
  # maybe someday we can extend the actual adapter
@@ -672,11 +696,16 @@ module ActiveRecord::ConnectionAdapters
672
696
  initialize_type_map
673
697
 
674
698
  @use_insert_returning = @config.key?(:insert_returning) ?
675
- self.class.type_cast_config_to_boolean(@config[:insert_returning]) : nil
699
+ self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
676
700
  end
677
701
 
678
- def arel_visitor # :nodoc:
679
- Arel::Visitors::PostgreSQL.new(self)
702
+ def self.database_exists?(config)
703
+ conn = ActiveRecord::Base.postgresql_connection(config)
704
+ conn && conn.really_valid?
705
+ rescue ActiveRecord::NoDatabaseError
706
+ false
707
+ ensure
708
+ conn.disconnect! if conn
680
709
  end
681
710
 
682
711
  require 'active_record/connection_adapters/postgresql/schema_definitions'
@@ -685,11 +714,8 @@ module ActiveRecord::ConnectionAdapters
685
714
  TableDefinition = ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition
686
715
  Table = ActiveRecord::ConnectionAdapters::PostgreSQL::Table
687
716
 
688
- def create_table_definition(*args) # :nodoc:
689
- TableDefinition.new(*args)
690
- end
691
-
692
717
  public :sql_for_insert
718
+ alias :postgresql_version :database_version
693
719
 
694
720
  def jdbc_connection_class(spec)
695
721
  ::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