activerecord-jdbc-adapter 70.1-java → 71.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.
@@ -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,13 @@ 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
+
24
28
  require 'active_model'
25
29
 
30
+ require "arjdbc/abstract/relation_query_attribute_monkey_patch"
31
+
26
32
  module ArJdbc
27
33
  # Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
28
34
  module PostgreSQL
@@ -35,11 +41,6 @@ module ArJdbc
35
41
  # @private
36
42
  Type = ::ActiveRecord::Type
37
43
 
38
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
39
- def self.jdbc_connection_class
40
- ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
41
- end
42
-
43
44
  # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
44
45
  def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn end
45
46
 
@@ -52,8 +53,8 @@ module ArJdbc
52
53
  def redshift?
53
54
  # SELECT version() :
54
55
  # 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')
56
+ if (redshift = @config[:redshift]).nil?
57
+ redshift = !! (valid_raw_connection.database_product || '').index('Redshift')
57
58
  end
58
59
  redshift
59
60
  end
@@ -73,8 +74,8 @@ module ArJdbc
73
74
  # see http://jdbc.postgresql.org/documentation/91/connect.html
74
75
  # self.set_client_encoding(encoding)
75
76
  #end
76
- self.client_min_messages = config[:min_messages] || 'warning'
77
- self.schema_search_path = config[:schema_search_path] || config[:schema_order]
77
+ self.client_min_messages = @config[:min_messages] || 'warning'
78
+ self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
78
79
 
79
80
  # Use standard-conforming strings if available so we don't have to do the E'...' dance.
80
81
  set_standard_conforming_strings
@@ -93,7 +94,7 @@ module ArJdbc
93
94
 
94
95
  # SET statements from :variables config hash
95
96
  # http://www.postgresql.org/docs/8.3/static/sql-set.html
96
- (config[:variables] || {}).map do |k, v|
97
+ (@config[:variables] || {}).map do |k, v|
97
98
  if v == ':default' || v == :default
98
99
  # Sets the value to the global or compile default
99
100
  execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
@@ -101,6 +102,8 @@ module ArJdbc
101
102
  execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
102
103
  end
103
104
  end
105
+
106
+ reload_type_map
104
107
  end
105
108
 
106
109
  # @private
@@ -120,14 +123,15 @@ module ArJdbc
120
123
  citext: { name: 'citext' },
121
124
  date: { name: 'date' },
122
125
  daterange: { name: 'daterange' },
123
- datetime: { name: 'timestamp' },
126
+ datetime: {}, # set dynamically based on datetime_type
127
+ timestamptz: { name: 'timestamptz' },
124
128
  decimal: { name: 'decimal' }, # :limit => 1000
125
129
  float: { name: 'float' },
126
130
  hstore: { name: 'hstore' },
127
131
  inet: { name: 'inet' },
128
132
  int4range: { name: 'int4range' },
129
133
  int8range: { name: 'int8range' },
130
- integer: { name: 'integer' },
134
+ integer: { name: 'integer', limit: 4 },
131
135
  interval: { name: 'interval' },
132
136
  json: { name: 'json' },
133
137
  jsonb: { name: 'jsonb' },
@@ -150,17 +154,10 @@ module ArJdbc
150
154
  tstzrange: { name: 'tstzrange' },
151
155
  tsvector: { name: 'tsvector' },
152
156
  uuid: { name: 'uuid' },
153
- xml: { name: 'xml' }
157
+ xml: { name: 'xml' },
158
+ enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
154
159
  }
155
160
 
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
161
  def set_standard_conforming_strings
165
162
  execute("SET standard_conforming_strings = on", "SCHEMA")
166
163
  end
@@ -232,6 +229,18 @@ module ArJdbc
232
229
  alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
233
230
  alias supports_insert_conflict_target? supports_insert_on_conflict?
234
231
 
232
+ def supports_virtual_columns?
233
+ database_version >= 12_00_00 # >= 12.0
234
+ end
235
+
236
+ def supports_identity_columns? # :nodoc:
237
+ database_version >= 10_00_00 # >= 10.0
238
+ end
239
+
240
+ def supports_nulls_not_distinct?
241
+ database_version >= 15_00_00 # >= 15.0
242
+ end
243
+
235
244
  def index_algorithms
236
245
  { concurrently: 'CONCURRENTLY' }
237
246
  end
@@ -297,14 +306,21 @@ module ArJdbc
297
306
  query_value("SELECT pg_advisory_unlock(#{lock_id})")
298
307
  end
299
308
 
300
- def enable_extension(name)
301
- exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
302
- reload_type_map
303
- }
309
+ def enable_extension(name, **)
310
+ schema, name = name.to_s.split(".").values_at(-2, -1)
311
+ sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
312
+ sql << " SCHEMA #{schema}" if schema
313
+
314
+ internal_exec_query(sql).tap { reload_type_map }
304
315
  end
305
316
 
306
- def disable_extension(name)
307
- exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
317
+ # Removes an extension from the database.
318
+ #
319
+ # [<tt>:force</tt>]
320
+ # Set to +:cascade+ to drop dependent objects as well.
321
+ # Defaults to false.
322
+ def disable_extension(name, force: false)
323
+ internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
308
324
  reload_type_map
309
325
  }
310
326
  end
@@ -318,39 +334,106 @@ module ArJdbc
318
334
  end
319
335
 
320
336
  def extensions
321
- exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
337
+ internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
322
338
  end
323
339
 
324
340
  # Returns a list of defined enum types, and their values.
325
341
  def enum_types
326
342
  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;
343
+ SELECT
344
+ type.typname AS name,
345
+ type.OID AS oid,
346
+ n.nspname AS schema,
347
+ string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
348
+ FROM pg_enum AS enum
349
+ JOIN pg_type AS type ON (type.oid = enum.enumtypid)
350
+ JOIN pg_namespace n ON type.typnamespace = n.oid
351
+ WHERE n.nspname = ANY (current_schemas(false))
352
+ GROUP BY type.OID, n.nspname, type.typname;
334
353
  SQL
335
- exec_query(query, "SCHEMA").cast_values
354
+
355
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
356
+ name, schema = row[0], row[2]
357
+ schema = nil if schema == current_schema
358
+ full_name = [schema, name].compact.join(".")
359
+ memo[full_name] = row.last
360
+ end.to_a
336
361
  end
337
362
 
338
363
  # 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(", ")
364
+ def create_enum(name, values, **options)
365
+ sql_values = values.map { |s| quote(s) }.join(", ")
366
+ scope = quoted_scope(name)
367
+ query = <<~SQL
368
+ DO $$
369
+ BEGIN
370
+ IF NOT EXISTS (
371
+ SELECT 1
372
+ FROM pg_type t
373
+ JOIN pg_namespace n ON t.typnamespace = n.oid
374
+ WHERE t.typname = #{scope[:name]}
375
+ AND n.nspname = #{scope[:schema]}
376
+ ) THEN
377
+ CREATE TYPE #{quote_table_name(name)} AS ENUM (#{sql_values});
378
+ END IF;
379
+ END
380
+ $$;
381
+ SQL
382
+
383
+ internal_exec_query(query).tap { reload_type_map }
384
+ end
385
+
386
+ # Drops an enum type.
387
+ #
388
+ # If the <tt>if_exists: true</tt> option is provided, the enum is dropped
389
+ # only if it exists. Otherwise, if the enum doesn't exist, an error is
390
+ # raised.
391
+ #
392
+ # The +values+ parameter will be ignored if present. It can be helpful
393
+ # to provide this in a migration's +change+ method so it can be reverted.
394
+ # In that case, +values+ will be used by #create_enum.
395
+ def drop_enum(name, values = nil, **options)
341
396
  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
- $$;
397
+ DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
352
398
  SQL
353
- exec_query(query)
399
+ internal_exec_query(query).tap { reload_type_map }
400
+ end
401
+
402
+ # Rename an existing enum type to something else.
403
+ def rename_enum(name, options = {})
404
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
405
+
406
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
407
+ end
408
+
409
+ # Add enum value to an existing enum type.
410
+ def add_enum_value(type_name, value, options = {})
411
+ before, after = options.values_at(:before, :after)
412
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
413
+
414
+ if before && after
415
+ raise ArgumentError, "Cannot have both :before and :after at the same time"
416
+ elsif before
417
+ sql << " BEFORE '#{before}'"
418
+ elsif after
419
+ sql << " AFTER '#{after}'"
420
+ end
421
+
422
+ execute(sql).tap { reload_type_map }
423
+ end
424
+
425
+ # Rename enum value on an existing enum type.
426
+ def rename_enum_value(type_name, options = {})
427
+ unless database_version >= 10_00_00 # >= 10.0
428
+ raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
429
+ end
430
+
431
+ from = options.fetch(:from) { raise ArgumentError, ":from is required" }
432
+ to = options.fetch(:to) { raise ArgumentError, ":to is required" }
433
+
434
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
435
+ reload_type_map
436
+ }
354
437
  end
355
438
 
356
439
  # Returns the configured supported identifier length supported by PostgreSQL
@@ -370,7 +453,7 @@ module ArJdbc
370
453
 
371
454
  def get_database_version # :nodoc:
372
455
  begin
373
- version = @connection.database_product
456
+ version = valid_raw_connection.database_product
374
457
  if match = version.match(/([\d\.]*\d).*?/)
375
458
  version = match[1].split('.').map(&:to_i)
376
459
  # PostgreSQL version representation does not have more than 4 digits
@@ -426,8 +509,7 @@ module ArJdbc
426
509
  end
427
510
  end
428
511
 
429
-
430
- def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
512
+ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
431
513
  val = super
432
514
  if !use_insert_returning? && pk
433
515
  unless sequence_name
@@ -445,11 +527,6 @@ module ArJdbc
445
527
  execute(combine_multi_statements(statements), name)
446
528
  end
447
529
 
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
530
  # from ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
454
531
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
455
532
  :close, :declare, :fetch, :move, :set, :show
@@ -464,18 +541,33 @@ module ArJdbc
464
541
  # since apparently calling close on the statement object
465
542
  # doesn't always free the server resources and calling
466
543
  # '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
544
+ # def clear_cache!
545
+ # super
546
+ # # Make sure all query plans are *really* gone
547
+ # @connection.execute 'DEALLOCATE ALL' if active?
548
+ # end
472
549
 
473
550
  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
551
+ @lock.synchronize do
552
+ return connect! unless @raw_connection
553
+
554
+ # Have to deal with rollbacks differently than the AR adapter
555
+ @raw_connection.rollback
556
+
557
+ @raw_connection.execute("DISCARD ALL")
558
+
559
+ super
560
+ end
561
+ end
562
+
563
+ # Disconnects from the database if already connected. Otherwise, this
564
+ # method does nothing.
565
+ def disconnect!
566
+ @lock.synchronize do
567
+ super
568
+ @raw_connection&.close
569
+ @raw_connection = nil
570
+ end
479
571
  end
480
572
 
481
573
  def default_sequence_name(table_name, pk = "id") #:nodoc:
@@ -593,17 +685,19 @@ module ArJdbc
593
685
  # - format_type includes the column size constraint, e.g. varchar(50)
594
686
  # - ::regclass is a function that gives the id for a table name
595
687
  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
688
+ query(<<~SQL, "SCHEMA")
689
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
690
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
691
+ c.collname, col_description(a.attrelid, a.attnum) AS comment,
692
+ #{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
693
+ #{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
694
+ FROM pg_attribute a
695
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
696
+ LEFT JOIN pg_type t ON a.atttypid = t.oid
697
+ LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
698
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
699
+ AND a.attnum > 0 AND NOT a.attisdropped
700
+ ORDER BY a.attnum
607
701
  SQL
608
702
  end
609
703
 
@@ -618,22 +712,27 @@ module ArJdbc
618
712
 
619
713
  # Pulled from ActiveRecord's Postgres adapter and modified to use execute
620
714
  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')
715
+ # NOTE: citext is an exception. It is possible to perform a
716
+ # case-insensitive comparison using `LOWER()`, but it is
717
+ # unnecessary, as `citext` is case-insensitive by definition.
718
+ @case_insensitive_cache ||= { "citext" => false }
719
+ @case_insensitive_cache.fetch(column.sql_type) do
720
+ @case_insensitive_cache[column.sql_type] = begin
721
+ sql = <<~SQL
722
+ SELECT exists(
723
+ SELECT * FROM pg_proc
724
+ WHERE proname = 'lower'
725
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
726
+ ) OR exists(
727
+ SELECT * FROM pg_proc
728
+ INNER JOIN pg_cast
729
+ ON ARRAY[casttarget]::oidvector = proargtypes
730
+ WHERE proname = 'lower'
731
+ AND castsource = #{quote column.sql_type}::regtype
732
+ )
733
+ SQL
734
+ select_value(sql, 'SCHEMA')
735
+ end
637
736
  end
638
737
  end
639
738
 
@@ -642,6 +741,8 @@ module ArJdbc
642
741
 
643
742
  # TODO: Can we base these on an error code of some kind?
644
743
  case exception.message
744
+ when /could not create unique index/
745
+ ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
645
746
  when /duplicate key value violates unique constraint/
646
747
  ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
647
748
  when /violates not-null constraint/
@@ -660,6 +761,10 @@ module ArJdbc
660
761
  ::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
661
762
  when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
662
763
  ::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
764
+ when /relation .* does not exist/i
765
+ ::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
766
+ when /syntax error at or near/i
767
+ ::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
663
768
  else
664
769
  super
665
770
  end
@@ -742,7 +847,7 @@ module ActiveRecord::ConnectionAdapters
742
847
  include ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements
743
848
  include ActiveRecord::ConnectionAdapters::PostgreSQL::Quoting
744
849
 
745
- include Jdbc::ConnectionPoolCallbacks
850
+ # include Jdbc::ConnectionPoolCallbacks
746
851
 
747
852
  include ArJdbc::Abstract::Core
748
853
  include ArJdbc::Abstract::ConnectionManagement
@@ -753,6 +858,8 @@ module ActiveRecord::ConnectionAdapters
753
858
 
754
859
  require 'arjdbc/postgresql/oid_types'
755
860
  include ::ArJdbc::PostgreSQL::OIDTypes
861
+ include ::ArJdbc::PostgreSQL::DatabaseStatements
862
+ include ::ArJdbc::PostgreSQL::SchemaStatements
756
863
 
757
864
  include ::ArJdbc::PostgreSQL::ColumnHelpers
758
865
 
@@ -761,16 +868,46 @@ module ActiveRecord::ConnectionAdapters
761
868
  # AR expects OID to be available on the adapter
762
869
  OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
763
870
 
764
- def initialize(connection, logger = nil, connection_parameters = nil, config = {})
871
+ class << self
872
+ def jdbc_connection_class
873
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
874
+ end
875
+
876
+ def new_client(conn_params, adapter_instance)
877
+ jdbc_connection_class.new(conn_params, adapter_instance)
878
+ end
879
+
880
+ def dbconsole(config, options = {})
881
+ pg_config = config.configuration_hash
882
+
883
+ ENV["PGUSER"] = pg_config[:username] if pg_config[:username]
884
+ ENV["PGHOST"] = pg_config[:host] if pg_config[:host]
885
+ ENV["PGPORT"] = pg_config[:port].to_s if pg_config[:port]
886
+ ENV["PGPASSWORD"] = pg_config[:password].to_s if pg_config[:password] && options[:include_password]
887
+ ENV["PGSSLMODE"] = pg_config[:sslmode].to_s if pg_config[:sslmode]
888
+ ENV["PGSSLCERT"] = pg_config[:sslcert].to_s if pg_config[:sslcert]
889
+ ENV["PGSSLKEY"] = pg_config[:sslkey].to_s if pg_config[:sslkey]
890
+ ENV["PGSSLROOTCERT"] = pg_config[:sslrootcert].to_s if pg_config[:sslrootcert]
891
+ if pg_config[:variables]
892
+ ENV["PGOPTIONS"] = pg_config[:variables].filter_map do |name, value|
893
+ "-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
894
+ end.join(" ")
895
+ end
896
+ find_cmd_and_exec("psql", config.database)
897
+ end
898
+ end
899
+
900
+ def initialize(...)
901
+ super
902
+
903
+ conn_params = @config.compact
904
+
905
+ @connection_parameters = conn_params
906
+
765
907
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
766
908
  @local_tz = nil
767
909
  @max_identifier_length = nil
768
910
 
769
- super(connection, logger, config) # configure_connection happens in super
770
-
771
- @type_map = Type::HashLookupTypeMap.new
772
- initialize_type_map
773
-
774
911
  @use_insert_returning = @config.key?(:insert_returning) ?
775
912
  self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
776
913
  end
@@ -793,8 +930,16 @@ module ActiveRecord::ConnectionAdapters
793
930
  public :sql_for_insert
794
931
  alias :postgresql_version :database_version
795
932
 
796
- def jdbc_connection_class(spec)
797
- ::ArJdbc::PostgreSQL.jdbc_connection_class
933
+ def native_database_types # :nodoc:
934
+ self.class.native_database_types
935
+ end
936
+
937
+ def self.native_database_types # :nodoc:
938
+ @native_database_types ||= begin
939
+ types = NATIVE_DATABASE_TYPES.dup
940
+ types[:datetime] = types[datetime_type]
941
+ types
942
+ end
798
943
  end
799
944
 
800
945
  private
@@ -829,8 +974,10 @@ module ActiveRecord::ConnectionAdapters
829
974
 
830
975
  type_casted_binds = type_casted_binds(binds)
831
976
  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)
977
+ with_raw_connection do |conn|
978
+ result = conn.exec_params(sql, type_casted_binds)
979
+ verified!
980
+ result
834
981
  end
835
982
  end
836
983
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArJdbc
4
+ module PostgreSQL
5
+ module DatabaseStatements
6
+ def explain(arel, binds = [], options = [])
7
+ sql = build_explain_clause(options) + " " + to_sql(arel, binds)
8
+
9
+ result = internal_exec_query(sql, "EXPLAIN", binds)
10
+ ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(result)
11
+ end
12
+
13
+ def build_explain_clause(options = [])
14
+ return "EXPLAIN" if options.empty?
15
+
16
+ "EXPLAIN (#{options.join(", ").upcase})"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -67,28 +67,6 @@ module ArJdbc
67
67
 
68
68
  # @private
69
69
  module OIDTypes
70
-
71
- # @override
72
- def enable_extension(name)
73
- result = super(name)
74
- @extensions = nil
75
- reload_type_map
76
- result
77
- end
78
-
79
- # @override
80
- def disable_extension(name)
81
- result = super(name)
82
- @extensions = nil
83
- reload_type_map
84
- result
85
- end
86
-
87
- # @override
88
- def extensions
89
- @extensions ||= super
90
- end
91
-
92
70
  def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
93
71
  # Note: type_map is storing a bunch of oid type prefixed with a namespace even
94
72
  # if they are not namespaced (e.g. ""."oidvector"). builtin types which are
@@ -118,8 +96,15 @@ module ArJdbc
118
96
  end
119
97
 
120
98
  def reload_type_map
121
- type_map.clear
99
+ @lock.synchronize do
100
+ if @type_map
101
+ type_map.clear
102
+ else
103
+ @type_map = Type::HashLookupTypeMap.new
104
+ end
105
+
122
106
  initialize_type_map
107
+ end
123
108
  end
124
109
 
125
110
  def initialize_type_map_inner(m)
@@ -274,10 +259,6 @@ module ArJdbc
274
259
  $1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
275
260
  end
276
261
 
277
- def extract_limit(sql_type)
278
- $1.to_i if sql_type =~ /\((.*)\)/
279
- end
280
-
281
262
  # Support arrays/ranges for defining attributes that don't exist in the db
282
263
  ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
283
264
  ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)