activerecord-jdbc-adapter 70.2-java → 72.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +26 -26
  3. data/.gitignore +8 -0
  4. data/Gemfile +17 -4
  5. data/README.md +10 -5
  6. data/RUNNING_TESTS.md +36 -0
  7. data/activerecord-jdbc-adapter.gemspec +2 -2
  8. data/lib/arjdbc/abstract/connection_management.rb +25 -10
  9. data/lib/arjdbc/abstract/core.rb +15 -13
  10. data/lib/arjdbc/abstract/database_statements.rb +36 -36
  11. data/lib/arjdbc/abstract/relation_query_attribute_monkey_patch.rb +24 -0
  12. data/lib/arjdbc/abstract/statement_cache.rb +2 -7
  13. data/lib/arjdbc/abstract/transaction_support.rb +39 -22
  14. data/lib/arjdbc/jdbc/adapter.rb +0 -1
  15. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  16. data/lib/arjdbc/jdbc/column.rb +0 -34
  17. data/lib/arjdbc/jdbc/connection_methods.rb +1 -1
  18. data/lib/arjdbc/mysql/adapter.rb +108 -32
  19. data/lib/arjdbc/mysql/adapter_hash_config.rb +159 -0
  20. data/lib/arjdbc/mysql.rb +1 -1
  21. data/lib/arjdbc/postgresql/adapter.rb +267 -114
  22. data/lib/arjdbc/postgresql/adapter_hash_config.rb +98 -0
  23. data/lib/arjdbc/postgresql/base/array_encoder.rb +3 -1
  24. data/lib/arjdbc/postgresql/database_statements.rb +20 -0
  25. data/lib/arjdbc/postgresql/oid_types.rb +10 -29
  26. data/lib/arjdbc/postgresql/schema_statements.rb +57 -0
  27. data/lib/arjdbc/postgresql.rb +1 -1
  28. data/lib/arjdbc/sqlite3/adapter.rb +343 -172
  29. data/lib/arjdbc/sqlite3/adapter_hash_config.rb +91 -0
  30. data/lib/arjdbc/sqlite3/column.rb +117 -0
  31. data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
  32. data/lib/arjdbc/sqlite3/pragmas.rb +105 -0
  33. data/lib/arjdbc/sqlite3.rb +1 -1
  34. data/lib/arjdbc/version.rb +1 -1
  35. data/lib/arjdbc.rb +13 -1
  36. data/rakelib/02-test.rake +2 -2
  37. data/rakelib/rails.rake +2 -0
  38. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +9 -2
  39. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +12 -5
  40. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +14 -3
  41. metadata +15 -11
  42. data/lib/arjdbc/jdbc/base_ext.rb +0 -17
@@ -13,6 +13,7 @@ require 'active_record/connection_adapters/postgresql/schema_dumper'
13
13
  require 'active_record/connection_adapters/postgresql/schema_statements'
14
14
  require 'active_record/connection_adapters/postgresql/type_metadata'
15
15
  require 'active_record/connection_adapters/postgresql/utils'
16
+
16
17
  require 'arjdbc/abstract/core'
17
18
  require 'arjdbc/abstract/connection_management'
18
19
  require 'arjdbc/abstract/database_statements'
@@ -21,8 +22,14 @@ require 'arjdbc/abstract/transaction_support'
21
22
  require 'arjdbc/postgresql/base/array_decoder'
22
23
  require 'arjdbc/postgresql/base/array_encoder'
23
24
  require 'arjdbc/postgresql/name'
25
+ require 'arjdbc/postgresql/database_statements'
26
+ require 'arjdbc/postgresql/schema_statements'
27
+ require "arjdbc/postgresql/adapter_hash_config"
28
+
24
29
  require 'active_model'
25
30
 
31
+ require "arjdbc/abstract/relation_query_attribute_monkey_patch"
32
+
26
33
  module ArJdbc
27
34
  # Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
28
35
  module PostgreSQL
@@ -35,11 +42,6 @@ module ArJdbc
35
42
  # @private
36
43
  Type = ::ActiveRecord::Type
37
44
 
38
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
39
- def self.jdbc_connection_class
40
- ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
41
- end
42
-
43
45
  # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
44
46
  def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn end
45
47
 
@@ -52,8 +54,8 @@ module ArJdbc
52
54
  def redshift?
53
55
  # SELECT version() :
54
56
  # 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
55
- if ( redshift = config[:redshift] ).nil?
56
- redshift = !! (@connection.database_product || '').index('Redshift')
57
+ if (redshift = @config[:redshift]).nil?
58
+ redshift = !! (valid_raw_connection.database_product || '').index('Redshift')
57
59
  end
58
60
  redshift
59
61
  end
@@ -73,8 +75,8 @@ module ArJdbc
73
75
  # see http://jdbc.postgresql.org/documentation/91/connect.html
74
76
  # self.set_client_encoding(encoding)
75
77
  #end
76
- self.client_min_messages = config[:min_messages] || 'warning'
77
- self.schema_search_path = config[:schema_search_path] || config[:schema_order]
78
+ self.client_min_messages = @config[:min_messages] || 'warning'
79
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
78
80
 
79
81
  # Use standard-conforming strings if available so we don't have to do the E'...' dance.
80
82
  set_standard_conforming_strings
@@ -93,7 +95,7 @@ module ArJdbc
93
95
 
94
96
  # SET statements from :variables config hash
95
97
  # http://www.postgresql.org/docs/8.3/static/sql-set.html
96
- (config[:variables] || {}).map do |k, v|
98
+ (@config[:variables] || {}).map do |k, v|
97
99
  if v == ':default' || v == :default
98
100
  # Sets the value to the global or compile default
99
101
  execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
@@ -101,6 +103,8 @@ module ArJdbc
101
103
  execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
102
104
  end
103
105
  end
106
+
107
+ reload_type_map
104
108
  end
105
109
 
106
110
  # @private
@@ -120,14 +124,15 @@ module ArJdbc
120
124
  citext: { name: 'citext' },
121
125
  date: { name: 'date' },
122
126
  daterange: { name: 'daterange' },
123
- datetime: { name: 'timestamp' },
127
+ datetime: {}, # set dynamically based on datetime_type
128
+ timestamptz: { name: 'timestamptz' },
124
129
  decimal: { name: 'decimal' }, # :limit => 1000
125
130
  float: { name: 'float' },
126
131
  hstore: { name: 'hstore' },
127
132
  inet: { name: 'inet' },
128
133
  int4range: { name: 'int4range' },
129
134
  int8range: { name: 'int8range' },
130
- integer: { name: 'integer' },
135
+ integer: { name: 'integer', limit: 4 },
131
136
  interval: { name: 'interval' },
132
137
  json: { name: 'json' },
133
138
  jsonb: { name: 'jsonb' },
@@ -150,17 +155,10 @@ module ArJdbc
150
155
  tstzrange: { name: 'tstzrange' },
151
156
  tsvector: { name: 'tsvector' },
152
157
  uuid: { name: 'uuid' },
153
- xml: { name: 'xml' }
158
+ xml: { name: 'xml' },
159
+ enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
154
160
  }
155
161
 
156
- def native_database_types
157
- NATIVE_DATABASE_TYPES
158
- end
159
-
160
- def valid_type?(type)
161
- !native_database_types[type].nil?
162
- end
163
-
164
162
  def set_standard_conforming_strings
165
163
  execute("SET standard_conforming_strings = on", "SCHEMA")
166
164
  end
@@ -232,6 +230,18 @@ module ArJdbc
232
230
  alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
233
231
  alias supports_insert_conflict_target? supports_insert_on_conflict?
234
232
 
233
+ def supports_virtual_columns?
234
+ database_version >= 12_00_00 # >= 12.0
235
+ end
236
+
237
+ def supports_identity_columns? # :nodoc:
238
+ database_version >= 10_00_00 # >= 10.0
239
+ end
240
+
241
+ def supports_nulls_not_distinct?
242
+ database_version >= 15_00_00 # >= 15.0
243
+ end
244
+
235
245
  def index_algorithms
236
246
  { concurrently: 'CONCURRENTLY' }
237
247
  end
@@ -297,14 +307,21 @@ module ArJdbc
297
307
  query_value("SELECT pg_advisory_unlock(#{lock_id})")
298
308
  end
299
309
 
300
- def enable_extension(name)
301
- exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
302
- reload_type_map
303
- }
310
+ def enable_extension(name, **)
311
+ schema, name = name.to_s.split(".").values_at(-2, -1)
312
+ sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
313
+ sql << " SCHEMA #{schema}" if schema
314
+
315
+ internal_exec_query(sql).tap { reload_type_map }
304
316
  end
305
317
 
306
- def disable_extension(name)
307
- exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
318
+ # Removes an extension from the database.
319
+ #
320
+ # [<tt>:force</tt>]
321
+ # Set to +:cascade+ to drop dependent objects as well.
322
+ # Defaults to false.
323
+ def disable_extension(name, force: false)
324
+ internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
308
325
  reload_type_map
309
326
  }
310
327
  end
@@ -318,39 +335,106 @@ module ArJdbc
318
335
  end
319
336
 
320
337
  def extensions
321
- exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
338
+ internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
322
339
  end
323
340
 
324
341
  # Returns a list of defined enum types, and their values.
325
342
  def enum_types
326
343
  query = <<~SQL
327
- SELECT
328
- type.typname AS name,
329
- string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
330
- FROM pg_enum AS enum
331
- JOIN pg_type AS type
332
- ON (type.oid = enum.enumtypid)
333
- GROUP BY type.typname;
344
+ SELECT
345
+ type.typname AS name,
346
+ type.OID AS oid,
347
+ n.nspname AS schema,
348
+ array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
349
+ FROM pg_enum AS enum
350
+ JOIN pg_type AS type ON (type.oid = enum.enumtypid)
351
+ JOIN pg_namespace n ON type.typnamespace = n.oid
352
+ WHERE n.nspname = ANY (current_schemas(false))
353
+ GROUP BY type.OID, n.nspname, type.typname;
334
354
  SQL
335
- exec_query(query, "SCHEMA").cast_values
355
+
356
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
357
+ name, schema = row[0], row[2]
358
+ schema = nil if schema == current_schema
359
+ full_name = [schema, name].compact.join(".")
360
+ memo[full_name] = row.last
361
+ end.to_a
336
362
  end
337
363
 
338
364
  # Given a name and an array of values, creates an enum type.
339
- def create_enum(name, values)
340
- sql_values = values.map { |s| "'#{s}'" }.join(", ")
365
+ def create_enum(name, values, **options)
366
+ sql_values = values.map { |s| quote(s) }.join(", ")
367
+ scope = quoted_scope(name)
341
368
  query = <<~SQL
342
- DO $$
343
- BEGIN
344
- IF NOT EXISTS (
345
- SELECT 1 FROM pg_type t
346
- WHERE t.typname = '#{name}'
347
- ) THEN
348
- CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
349
- END IF;
350
- END
351
- $$;
369
+ DO $$
370
+ BEGIN
371
+ IF NOT EXISTS (
372
+ SELECT 1
373
+ FROM pg_type t
374
+ JOIN pg_namespace n ON t.typnamespace = n.oid
375
+ WHERE t.typname = #{scope[:name]}
376
+ AND n.nspname = #{scope[:schema]}
377
+ ) THEN
378
+ CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
379
+ END IF;
380
+ END
381
+ $$;
352
382
  SQL
353
- exec_query(query)
383
+
384
+ internal_exec_query(query).tap { reload_type_map }
385
+ end
386
+
387
+ # Drops an enum type.
388
+ #
389
+ # If the <tt>if_exists: true</tt> option is provided, the enum is dropped
390
+ # only if it exists. Otherwise, if the enum doesn't exist, an error is
391
+ # raised.
392
+ #
393
+ # The +values+ parameter will be ignored if present. It can be helpful
394
+ # to provide this in a migration's +change+ method so it can be reverted.
395
+ # In that case, +values+ will be used by #create_enum.
396
+ def drop_enum(name, values = nil, **options)
397
+ query = <<~SQL
398
+ DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
399
+ SQL
400
+ internal_exec_query(query).tap { reload_type_map }
401
+ end
402
+
403
+ # Rename an existing enum type to something else.
404
+ def rename_enum(name, options = {})
405
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
406
+
407
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
408
+ end
409
+
410
+ # Add enum value to an existing enum type.
411
+ def add_enum_value(type_name, value, options = {})
412
+ before, after = options.values_at(:before, :after)
413
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
414
+
415
+ if before && after
416
+ raise ArgumentError, "Cannot have both :before and :after at the same time"
417
+ elsif before
418
+ sql << " BEFORE '#{before}'"
419
+ elsif after
420
+ sql << " AFTER '#{after}'"
421
+ end
422
+
423
+ execute(sql).tap { reload_type_map }
424
+ end
425
+
426
+ # Rename enum value on an existing enum type.
427
+ def rename_enum_value(type_name, options = {})
428
+ unless database_version >= 10_00_00 # >= 10.0
429
+ raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
430
+ end
431
+
432
+ from = options.fetch(:from) { raise ArgumentError, ":from is required" }
433
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
434
+
435
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
436
+ reload_type_map
437
+ }
354
438
  end
355
439
 
356
440
  # Returns the configured supported identifier length supported by PostgreSQL
@@ -370,7 +454,7 @@ module ArJdbc
370
454
 
371
455
  def get_database_version # :nodoc:
372
456
  begin
373
- version = @connection.database_product
457
+ version = valid_raw_connection.database_product
374
458
  if match = version.match(/([\d\.]*\d).*?/)
375
459
  version = match[1].split('.').map(&:to_i)
376
460
  # PostgreSQL version representation does not have more than 4 digits
@@ -426,8 +510,7 @@ module ArJdbc
426
510
  end
427
511
  end
428
512
 
429
-
430
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
513
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
431
514
  val = super
432
515
  if !use_insert_returning? && pk
433
516
  unless sequence_name
@@ -445,11 +528,6 @@ module ArJdbc
445
528
  execute(combine_multi_statements(statements), name)
446
529
  end
447
530
 
448
- def explain(arel, binds = [])
449
- sql, binds = to_sql_and_binds(arel, binds)
450
- ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query("EXPLAIN #{sql}", 'EXPLAIN', binds))
451
- end
452
-
453
531
  # from ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
454
532
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
455
533
  :close, :declare, :fetch, :move, :set, :show
@@ -464,18 +542,33 @@ module ArJdbc
464
542
  # since apparently calling close on the statement object
465
543
  # doesn't always free the server resources and calling
466
544
  # 'DISCARD ALL' fails if we are inside a transaction
467
- def clear_cache!
468
- super
469
- # Make sure all query plans are *really* gone
470
- @connection.execute 'DEALLOCATE ALL' if active?
471
- end
545
+ # def clear_cache!
546
+ # super
547
+ # # Make sure all query plans are *really* gone
548
+ # @connection.execute 'DEALLOCATE ALL' if active?
549
+ # end
472
550
 
473
551
  def reset!
474
- clear_cache!
475
- reset_transaction
476
- @connection.rollback # Have to deal with rollbacks differently than the AR adapter
477
- @connection.execute 'DISCARD ALL'
478
- @connection.configure_connection
552
+ @lock.synchronize do
553
+ return connect! unless @raw_connection
554
+
555
+ # Have to deal with rollbacks differently than the AR adapter
556
+ @raw_connection.rollback
557
+
558
+ @raw_connection.execute("DISCARD ALL")
559
+
560
+ super
561
+ end
562
+ end
563
+
564
+ # Disconnects from the database if already connected. Otherwise, this
565
+ # method does nothing.
566
+ def disconnect!
567
+ @lock.synchronize do
568
+ super
569
+ @raw_connection&.close
570
+ @raw_connection = nil
571
+ end
479
572
  end
480
573
 
481
574
  def default_sequence_name(table_name, pk = "id") #:nodoc:
@@ -593,17 +686,19 @@ module ArJdbc
593
686
  # - format_type includes the column size constraint, e.g. varchar(50)
594
687
  # - ::regclass is a function that gives the id for a table name
595
688
  def column_definitions(table_name)
596
- select_rows(<<~SQL, 'SCHEMA')
597
- SELECT a.attname, format_type(a.atttypid, a.atttypmod),
598
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
599
- c.collname, col_description(a.attrelid, a.attnum) AS comment
600
- FROM pg_attribute a
601
- LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
602
- LEFT JOIN pg_type t ON a.atttypid = t.oid
603
- LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
604
- WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
605
- AND a.attnum > 0 AND NOT a.attisdropped
606
- ORDER BY a.attnum
689
+ query(<<~SQL, "SCHEMA")
690
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
691
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
692
+ c.collname, col_description(a.attrelid, a.attnum) AS comment,
693
+ #{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
694
+ #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
695
+ FROM pg_attribute a
696
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
697
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
698
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
699
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
700
+ AND a.attnum > 0 AND NOT a.attisdropped
701
+ ORDER BY a.attnum
607
702
  SQL
608
703
  end
609
704
 
@@ -618,22 +713,27 @@ module ArJdbc
618
713
 
619
714
  # Pulled from ActiveRecord's Postgres adapter and modified to use execute
620
715
  def can_perform_case_insensitive_comparison_for?(column)
621
- @case_insensitive_cache ||= {}
622
- @case_insensitive_cache[column.sql_type] ||= begin
623
- sql = <<~SQL
624
- SELECT exists(
625
- SELECT * FROM pg_proc
626
- WHERE proname = 'lower'
627
- AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
628
- ) OR exists(
629
- SELECT * FROM pg_proc
630
- INNER JOIN pg_cast
631
- ON ARRAY[casttarget]::oidvector = proargtypes
632
- WHERE proname = 'lower'
633
- AND castsource = #{quote column.sql_type}::regtype
634
- )
635
- SQL
636
- select_value(sql, 'SCHEMA')
716
+ # NOTE: citext is an exception. It is possible to perform a
717
+ # case-insensitive comparison using `LOWER()`, but it is
718
+ # unnecessary, as `citext` is case-insensitive by definition.
719
+ @case_insensitive_cache ||= { "citext" => false }
720
+ @case_insensitive_cache.fetch(column.sql_type) do
721
+ @case_insensitive_cache[column.sql_type] = begin
722
+ sql = <<~SQL
723
+ SELECT exists(
724
+ SELECT * FROM pg_proc
725
+ WHERE proname = 'lower'
726
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
727
+ ) OR exists(
728
+ SELECT * FROM pg_proc
729
+ INNER JOIN pg_cast
730
+ ON ARRAY[casttarget]::oidvector = proargtypes
731
+ WHERE proname = 'lower'
732
+ AND castsource = #{quote column.sql_type}::regtype
733
+ )
734
+ SQL
735
+ select_value(sql, 'SCHEMA')
736
+ end
637
737
  end
638
738
  end
639
739
 
@@ -642,6 +742,8 @@ module ArJdbc
642
742
 
643
743
  # TODO: Can we base these on an error code of some kind?
644
744
  case exception.message
745
+ when /could not create unique index/
746
+ ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
645
747
  when /duplicate key value violates unique constraint/
646
748
  ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
647
749
  when /violates not-null constraint/
@@ -660,6 +762,10 @@ module ArJdbc
660
762
  ::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
661
763
  when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
662
764
  ::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
765
+ when /relation .* does not exist/i
766
+ ::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
767
+ when /syntax error at or near/i
768
+ ::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
663
769
  else
664
770
  super
665
771
  end
@@ -736,23 +842,38 @@ module ActiveRecord::ConnectionAdapters
736
842
  # setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
737
843
  class_attribute :datetime_type, default: :timestamp
738
844
 
845
+ ##
846
+ # :singleton-method:
847
+ # Toggles automatic decoding of date columns.
848
+ #
849
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> String
850
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.decode_dates = true
851
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date").class #=> Date
852
+ class_attribute :decode_dates, default: false
853
+
739
854
  # Try to use as much of the built in postgres logic as possible
740
855
  # maybe someday we can extend the actual adapter
741
856
  include ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity
742
857
  include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
743
858
  include ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting
744
859
 
745
- include Jdbc::ConnectionPoolCallbacks
860
+ # include Jdbc::ConnectionPoolCallbacks
746
861
 
747
862
  include ArJdbc::Abstract::Core
748
863
  include ArJdbc::Abstract::ConnectionManagement
749
864
  include ArJdbc::Abstract::DatabaseStatements
750
865
  include ArJdbc::Abstract::StatementCache
751
866
  include ArJdbc::Abstract::TransactionSupport
867
+ include ArJdbc::PostgreSQLConfig
868
+
869
+ # NOTE: after AR refactor quote_column_name became class and instance method
752
870
  include ArJdbc::PostgreSQL
871
+ extend ArJdbc::PostgreSQL
753
872
 
754
873
  require 'arjdbc/postgresql/oid_types'
755
874
  include ::ArJdbc::PostgreSQL::OIDTypes
875
+ include ::ArJdbc::PostgreSQL::DatabaseStatements
876
+ include ::ArJdbc::PostgreSQL::SchemaStatements
756
877
 
757
878
  include ::ArJdbc::PostgreSQL::ColumnHelpers
758
879
 
@@ -761,29 +882,51 @@ module ActiveRecord::ConnectionAdapters
761
882
  # AR expects OID to be available on the adapter
762
883
  OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
763
884
 
764
- def initialize(connection, logger = nil, connection_parameters = nil, config = {})
885
+ class << self
886
+ def jdbc_connection_class
887
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
888
+ end
889
+
890
+ def new_client(conn_params, adapter_instance)
891
+ jdbc_connection_class.new(conn_params, adapter_instance)
892
+ end
893
+
894
+ def dbconsole(config, options = {})
895
+ pg_config = config.configuration_hash
896
+
897
+ ENV["PGUSER"] = pg_config[:username] if pg_config[:username]
898
+ ENV["PGHOST"] = pg_config[:host] if pg_config[:host]
899
+ ENV["PGPORT"] = pg_config[:port].to_s if pg_config[:port]
900
+ ENV["PGPASSWORD"] = pg_config[:password].to_s if pg_config[:password] && options[:include_password]
901
+ ENV["PGSSLMODE"] = pg_config[:sslmode].to_s if pg_config[:sslmode]
902
+ ENV["PGSSLCERT"] = pg_config[:sslcert].to_s if pg_config[:sslcert]
903
+ ENV["PGSSLKEY"] = pg_config[:sslkey].to_s if pg_config[:sslkey]
904
+ ENV["PGSSLROOTCERT"] = pg_config[:sslrootcert].to_s if pg_config[:sslrootcert]
905
+ if pg_config[:variables]
906
+ ENV["PGOPTIONS"] = pg_config[:variables].filter_map do |name, value|
907
+ "-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
908
+ end.join(" ")
909
+ end
910
+ find_cmd_and_exec("psql", config.database)
911
+ end
912
+ end
913
+
914
+ def initialize(...)
915
+ super
916
+
917
+ # assign arjdbc extra connection params
918
+ conn_params = build_connection_config(@config.compact)
919
+
920
+ @connection_parameters = conn_params
921
+
765
922
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
766
923
  @local_tz = nil
767
924
  @max_identifier_length = nil
768
925
 
769
- super(connection, logger, config) # configure_connection happens in super
770
-
771
- @type_map = Type::HashLookupTypeMap.new
772
- initialize_type_map
773
-
774
926
  @use_insert_returning = @config.key?(:insert_returning) ?
775
927
  self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
776
928
  end
777
929
 
778
- def self.database_exists?(config)
779
- conn = ActiveRecord::Base.postgresql_connection(config)
780
- conn && conn.really_valid?
781
- rescue ActiveRecord::NoDatabaseError
782
- false
783
- ensure
784
- conn.disconnect! if conn
785
- end
786
-
787
930
  require 'active_record/connection_adapters/postgresql/schema_definitions'
788
931
 
789
932
  ColumnMethods = ActiveRecord::ConnectionAdapters::PostgreSQL::ColumnMethods
@@ -793,8 +936,16 @@ module ActiveRecord::ConnectionAdapters
793
936
  public :sql_for_insert
794
937
  alias :postgresql_version :database_version
795
938
 
796
- def jdbc_connection_class(spec)
797
- ::ArJdbc::PostgreSQL.jdbc_connection_class
939
+ def native_database_types # :nodoc:
940
+ self.class.native_database_types
941
+ end
942
+
943
+ def self.native_database_types # :nodoc:
944
+ @native_database_types ||= begin
945
+ types = NATIVE_DATABASE_TYPES.dup
946
+ types[:datetime] = types[datetime_type]
947
+ types
948
+ end
798
949
  end
799
950
 
800
951
  private
@@ -829,8 +980,10 @@ module ActiveRecord::ConnectionAdapters
829
980
 
830
981
  type_casted_binds = type_casted_binds(binds)
831
982
  log(sql, name, binds, type_casted_binds, async: async) do
832
- ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
833
- @connection.exec_params(sql, type_casted_binds)
983
+ with_raw_connection do |conn|
984
+ result = conn.exec_params(sql, type_casted_binds)
985
+ verified!
986
+ result
834
987
  end
835
988
  end
836
989
  end