activerecord-jdbc-alt-adapter 71.0.0.alpha2-java → 71.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6590c4d705c35e5a4849e4ef0bb076efba93509b8bcd8b4c2db49a4bc84b0f31
4
- data.tar.gz: cb87d9f9f2763b178f990359efecb6b1199cbe15866460bb837232fcdd8b75e1
3
+ metadata.gz: 973f3cfa750c3424b5de9105f177584edea8ebe705d50b20e4b9f6d989a4919f
4
+ data.tar.gz: e7f86bfeb49f90f2767e3bdcc3e0101a58149e97421e8d7b96da8f59fc469959
5
5
  SHA512:
6
- metadata.gz: 511e55410d149724a0765b5494a15ca4fe493bbd36b969a587d7bab23458cb32a45d34c01f79ca8b170b7f1e86fdf58e81bc4ba3ea15d464fe954a8266c41743
7
- data.tar.gz: 9fdfe69ab63373f8e31cd2d60f5ae88fd3f4499250010aab4ffbeabb9c9345f017bb012932598a84297bd63d8b28f5bc38d18213f95b19b3307a93f38ecfc775
6
+ metadata.gz: f276911281e38f162526b603959f5798fa5d09c73284a79078376126b5fade87add2e4a4345242029a4b4e39097b5a6964651483576f51ed8bee3ac7b6e586a3
7
+ data.tar.gz: 8e5d458add630631bfaa82605f20d28c80310e25787c9db2c94c1b8cc677ac8ff6e0730c97b6271a333cbfe919da8a35ad8deb7911aeedc89c3021629e0c5370
@@ -27,7 +27,7 @@ jobs:
27
27
  ruby-version: ['jruby-head']
28
28
  db: ['mysql2']
29
29
  test_targets: ["rails:test_mysql2"]
30
- ar_version: ["7-1-stable"]
30
+ ar_version: ["7-2-stable"]
31
31
  prepared_statements: ['false', 'true']
32
32
  driver: ['MySQL']
33
33
 
@@ -42,7 +42,7 @@ jobs:
42
42
  AR_VERSION: ${{ matrix.ar_version }}
43
43
  PREPARED_STATEMENTS: ${{ matrix.prepared_statements }}
44
44
  DRIVER: ${{ matrix.driver }}
45
- JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M"
45
+ JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M --dev"
46
46
 
47
47
  steps:
48
48
  - uses: actions/checkout@v4
@@ -79,7 +79,7 @@ jobs:
79
79
  ruby-version: [ 'jruby-head' ]
80
80
  db: [ 'postgresql' ]
81
81
  test_targets: [ "rails:test_postgresql" ]
82
- ar_version: ["7-1-stable"]
82
+ ar_version: ["7-2-stable"]
83
83
  prepared_statements: [ 'false', 'true' ]
84
84
 
85
85
  services:
@@ -95,7 +95,7 @@ jobs:
95
95
  env:
96
96
  DB: ${{ matrix.db }}
97
97
  AR_VERSION: ${{ matrix.ar_version }}
98
- JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M"
98
+ JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M --dev"
99
99
  PREPARED_STATEMENTS: ${{ matrix.prepared_statements }}
100
100
  PGHOST: localhost
101
101
  PGPORT: 5432
@@ -129,12 +129,12 @@ jobs:
129
129
  ruby-version: ['jruby-head']
130
130
  db: ['sqlite3']
131
131
  test_targets: ["rails:test_sqlite3"]
132
- ar_version: ["7-1-stable", "main"]
132
+ ar_version: ["7-2-stable"]
133
133
 
134
134
  env:
135
135
  DB: ${{ matrix.db }}
136
136
  AR_VERSION: ${{ matrix.ar_version }}
137
- JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M"
137
+ JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M --dev"
138
138
 
139
139
  steps:
140
140
  - uses: actions/checkout@v4
@@ -149,7 +149,7 @@ jobs:
149
149
  rake jar # compiles ext generates: lib/arjdbc/jdbc/adapter_java.jar
150
150
  - name: Run tests
151
151
  run: |
152
- bundle exec rake ${{ matrix.test_targets }}
152
+ bundle exec rake ${{ matrix.test_targets }} --trace
153
153
 
154
154
  test-arjdbc-mysql:
155
155
 
@@ -173,7 +173,7 @@ jobs:
173
173
  env:
174
174
  DB: ${{ matrix.db }}
175
175
  DRIVER: ${{ matrix.driver }}
176
- JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M"
176
+ JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M --dev"
177
177
  MY_USER: root
178
178
  MY_PASSWORD: root
179
179
  PREPARED_STATEMENTS: ${{ matrix.prepared_statements }}
@@ -222,7 +222,7 @@ jobs:
222
222
  env:
223
223
  DB: ${{ matrix.db }}
224
224
  DRIVER: ${{ matrix.driver }}
225
- JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M"
225
+ JRUBY_OPTS: "-J-Xms64M -J-Xmx1024M --dev"
226
226
  PREPARED_STATEMENTS: ${{ matrix.prepared_statements }}
227
227
  INSERT_RETURNING: ${{ matrix.insert_returning }}
228
228
  PGHOST: localhost
data/Gemfile CHANGED
@@ -62,13 +62,13 @@ group :test do
62
62
  gem 'mocha', '~> 1.2', require: false # Rails has '~> 0.14'
63
63
 
64
64
  gem 'bcrypt', '~> 3.1.11', require: false
65
- gem 'jdbc-mssql', '~> 12.2', require: nil
65
+ gem 'jdbc-mssql', '~> 12.6', require: nil
66
66
  # gem 'pry-debugger-jruby', platform: :jruby
67
67
  end
68
68
 
69
69
  group :rails do
70
70
  group :test do
71
- gem 'minitest', require: nil
71
+ gem 'minitest', '~> 5.24.0', require: nil
72
72
  gem 'minitest-excludes', require: nil
73
73
  gem 'minitest-rg', require: nil
74
74
 
data/README.md CHANGED
@@ -145,9 +145,10 @@ Versions are targeted at certain versions of Rails and live on their own branche
145
145
  | 60.x | 6.0.x | 60-stable | 9.2.7 | 8 |
146
146
  | 61.x | 6.1.x | 61-stable | 9.2.7 | 8 |
147
147
  | 70.x | 7.0.x | 70-stable | 9.3.0 | 8 |
148
- | 71.x | 7.1.x | master | 9.4.3 | 8 |
148
+ | 71.x | 7.1.x | 71-stable | 9.4.3 | 8 |
149
+ | 72.x | 7.2.x | master | 9.4.3 | 8 |
149
150
 
150
- Note: 71.x is still under development and not supported yet.
151
+ Note: 72.x is still under development and not supported yet.
151
152
 
152
153
  Note that JRuby 9.1.x and JRuby 9.2.x are at end-of-life. We recommend Java 8
153
154
  at a minimum for all versions.
@@ -241,8 +241,21 @@ module Arel
241
241
 
242
242
  # column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.try(:second).try(:name)
243
243
  # NOTE: for table name aliases columns_hash('table_alias') requires to return an empty hash.
244
- column_name = @connection.schema_cache.primary_keys(t.name) ||
245
- @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
244
+ primary_keys = @connection.schema_cache.primary_keys(t.name)
245
+ column_name = nil
246
+
247
+ case primary_keys
248
+ when NilClass
249
+ column_name = @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
250
+ when String
251
+ column_name = primary_keys
252
+ when Array
253
+ candidate_columns = @connection.schema_cache.columns_hash(t.name).slice(*primary_keys).values
254
+ candidate_column = candidate_columns.find(&:identity?)
255
+ candidate_column ||= candidate_columns.first
256
+ column_name = candidate_column.try(:name)
257
+ end
258
+
246
259
  column_name ? t[column_name] : nil
247
260
  end
248
261
 
@@ -46,13 +46,12 @@ module ArJdbc
46
46
  end
47
47
 
48
48
  def reconnect
49
- if active?
50
- @raw_connection.rollback rescue nil
51
- else
52
- connect
53
- end
54
- end
49
+ @raw_connection&.close
50
+
51
+ @raw_connection = nil
55
52
 
53
+ connect
54
+ end
56
55
  end
57
56
  end
58
57
  end
@@ -78,18 +78,6 @@ module ArJdbc
78
78
  end
79
79
  alias :exec_delete :exec_update
80
80
 
81
- def execute(sql, name = nil, async: false, allow_retry: false, materialize_transactions: true)
82
- sql = transform_query(sql)
83
-
84
- if preventing_writes? && write_query?(sql)
85
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
86
- end
87
-
88
- mark_transaction_written_if_write(sql)
89
-
90
- raw_execute(sql, name, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
91
- end
92
-
93
81
  # overridden to support legacy binds
94
82
  def select_all(arel, name = nil, binds = NO_BINDS, preparable: nil, async: false)
95
83
  binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
@@ -107,7 +95,9 @@ module ArJdbc
107
95
  def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
108
96
  log(sql, name, async: async) do
109
97
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
110
- conn.execute(sql)
98
+ result = conn.execute(sql)
99
+ verified!
100
+ result
111
101
  end
112
102
  end
113
103
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/attribute"
4
+
5
+ module ActiveRecord
6
+ # NOTE: improved implementation for hash methods that is used to
7
+ # compare objects. AR and arel commonly use `[a, b] - [b]` operations and
8
+ # JRuby internally uses the hash method to implement that operation,
9
+ # on the other hand, CRuby does not use the hash method
10
+ # for small arrays (length <= 16).
11
+ class Relation
12
+ # monkey patch
13
+ module RelationQueryAttributeMonkeyPatch
14
+ def hash
15
+ # [self.class, name, value_for_database, type].hash
16
+ [self.class, name, value_before_type_cast, type].hash
17
+ end
18
+ end
19
+
20
+ class QueryAttribute
21
+ prepend RelationQueryAttributeMonkeyPatch
22
+ end
23
+ end
24
+ end
Binary file
@@ -30,6 +30,8 @@ require 'arjdbc/mssql/errors'
30
30
  require 'arjdbc/mssql/schema_creation'
31
31
  require 'arjdbc/mssql/database_limits'
32
32
 
33
+ require "arjdbc/abstract/relation_query_attribute_monkey_patch"
34
+
33
35
  module ActiveRecord
34
36
  module ConnectionAdapters
35
37
  # MSSQL (SQLServer) adapter class definition
data/lib/arjdbc/mssql.rb CHANGED
@@ -6,4 +6,4 @@ module ArJdbc
6
6
  MsSQL = MSSQL # compatibility with 1.2
7
7
  end
8
8
 
9
- ArJdbc.warn_unsupported_adapter 'mssql', [7, 0] # warns on AR >= 4.2
9
+ ArJdbc.warn_unsupported_adapter 'mssql', [7, 1] # warns on AR >= 4.2
@@ -11,6 +11,8 @@ require 'arjdbc/abstract/database_statements'
11
11
  require 'arjdbc/abstract/statement_cache'
12
12
  require 'arjdbc/abstract/transaction_support'
13
13
 
14
+ require "arjdbc/abstract/relation_query_attribute_monkey_patch"
15
+
14
16
  module ActiveRecord
15
17
  module ConnectionAdapters
16
18
  AbstractMysqlAdapter.class_eval do
@@ -22,10 +22,13 @@ require 'arjdbc/abstract/transaction_support'
22
22
  require 'arjdbc/postgresql/base/array_decoder'
23
23
  require 'arjdbc/postgresql/base/array_encoder'
24
24
  require 'arjdbc/postgresql/name'
25
+ require 'arjdbc/postgresql/database_statements'
25
26
  require 'arjdbc/postgresql/schema_statements'
26
27
 
27
28
  require 'active_model'
28
29
 
30
+ require "arjdbc/abstract/relation_query_attribute_monkey_patch"
31
+
29
32
  module ArJdbc
30
33
  # Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
31
34
  module PostgreSQL
@@ -120,7 +123,8 @@ 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' },
@@ -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,10 +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
+
235
236
  def supports_identity_columns? # :nodoc:
236
237
  database_version >= 10_00_00 # >= 10.0
237
238
  end
238
239
 
240
+ def supports_nulls_not_distinct?
241
+ database_version >= 15_00_00 # >= 15.0
242
+ end
243
+
239
244
  def index_algorithms
240
245
  { concurrently: 'CONCURRENTLY' }
241
246
  end
@@ -335,33 +340,100 @@ module ArJdbc
335
340
  # Returns a list of defined enum types, and their values.
336
341
  def enum_types
337
342
  query = <<~SQL
338
- SELECT
339
- type.typname AS name,
340
- string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
341
- FROM pg_enum AS enum
342
- JOIN pg_type AS type
343
- ON (type.oid = enum.enumtypid)
344
- 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;
345
353
  SQL
346
- 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
347
361
  end
348
362
 
349
363
  # Given a name and an array of values, creates an enum type.
350
- def create_enum(name, values)
351
- 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)
352
367
  query = <<~SQL
353
- DO $$
354
- BEGIN
355
- IF NOT EXISTS (
356
- SELECT 1 FROM pg_type t
357
- WHERE t.typname = '#{name}'
358
- ) THEN
359
- CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
360
- END IF;
361
- END
362
- $$;
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
+ $$;
363
381
  SQL
364
- exec_query(query)
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)
396
+ query = <<~SQL
397
+ DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
398
+ SQL
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
+ }
365
437
  end
366
438
 
367
439
  # Returns the configured supported identifier length supported by PostgreSQL
@@ -455,11 +527,6 @@ module ArJdbc
455
527
  execute(combine_multi_statements(statements), name)
456
528
  end
457
529
 
458
- def explain(arel, binds = [])
459
- sql, binds = to_sql_and_binds(arel, binds)
460
- ActiveRecord::ConnectionAdapters::PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query("EXPLAIN #{sql}", 'EXPLAIN', binds))
461
- end
462
-
463
530
  # from ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements
464
531
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
465
532
  :close, :declare, :fetch, :move, :set, :show
@@ -493,6 +560,16 @@ module ArJdbc
493
560
  end
494
561
  end
495
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
571
+ end
572
+
496
573
  def default_sequence_name(table_name, pk = "id") #:nodoc:
497
574
  serial_sequence(table_name, pk)
498
575
  rescue ActiveRecord::StatementInvalid
@@ -608,17 +685,19 @@ module ArJdbc
608
685
  # - format_type includes the column size constraint, e.g. varchar(50)
609
686
  # - ::regclass is a function that gives the id for a table name
610
687
  def column_definitions(table_name)
611
- select_rows(<<~SQL, 'SCHEMA')
612
- SELECT a.attname, format_type(a.atttypid, a.atttypmod),
613
- pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
614
- c.collname, col_description(a.attrelid, a.attnum) AS comment
615
- FROM pg_attribute a
616
- LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
617
- LEFT JOIN pg_type t ON a.atttypid = t.oid
618
- LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
619
- WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
620
- AND a.attnum > 0 AND NOT a.attisdropped
621
- 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
622
701
  SQL
623
702
  end
624
703
 
@@ -633,22 +712,27 @@ module ArJdbc
633
712
 
634
713
  # Pulled from ActiveRecord's Postgres adapter and modified to use execute
635
714
  def can_perform_case_insensitive_comparison_for?(column)
636
- @case_insensitive_cache ||= {}
637
- @case_insensitive_cache[column.sql_type] ||= begin
638
- sql = <<~SQL
639
- SELECT exists(
640
- SELECT * FROM pg_proc
641
- WHERE proname = 'lower'
642
- AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
643
- ) OR exists(
644
- SELECT * FROM pg_proc
645
- INNER JOIN pg_cast
646
- ON ARRAY[casttarget]::oidvector = proargtypes
647
- WHERE proname = 'lower'
648
- AND castsource = #{quote column.sql_type}::regtype
649
- )
650
- SQL
651
- 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
652
736
  end
653
737
  end
654
738
 
@@ -657,6 +741,8 @@ module ArJdbc
657
741
 
658
742
  # TODO: Can we base these on an error code of some kind?
659
743
  case exception.message
744
+ when /could not create unique index/
745
+ ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
660
746
  when /duplicate key value violates unique constraint/
661
747
  ::ActiveRecord::RecordNotUnique.new(message, sql: sql, binds: binds)
662
748
  when /violates not-null constraint/
@@ -675,7 +761,9 @@ module ArJdbc
675
761
  ::ActiveRecord::LockWaitTimeout.new(message, sql: sql, binds: binds)
676
762
  when /canceling statement/ # This needs to come after lock timeout because the lock timeout message also contains "canceling statement"
677
763
  ::ActiveRecord::QueryCanceled.new(message, sql: sql, binds: binds)
678
- when /relation "animals" does not exist/i
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
679
767
  ::ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
680
768
  else
681
769
  super
@@ -770,6 +858,7 @@ module ActiveRecord::ConnectionAdapters
770
858
 
771
859
  require 'arjdbc/postgresql/oid_types'
772
860
  include ::ArJdbc::PostgreSQL::OIDTypes
861
+ include ::ArJdbc::PostgreSQL::DatabaseStatements
773
862
  include ::ArJdbc::PostgreSQL::SchemaStatements
774
863
 
775
864
  include ::ArJdbc::PostgreSQL::ColumnHelpers
@@ -787,6 +876,25 @@ module ActiveRecord::ConnectionAdapters
787
876
  def new_client(conn_params, adapter_instance)
788
877
  jdbc_connection_class.new(conn_params, adapter_instance)
789
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
790
898
  end
791
899
 
792
900
  def initialize(...)
@@ -822,6 +930,18 @@ module ActiveRecord::ConnectionAdapters
822
930
  public :sql_for_insert
823
931
  alias :postgresql_version :database_version
824
932
 
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
943
+ end
944
+
825
945
  private
826
946
 
827
947
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
@@ -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
@@ -18,6 +18,8 @@ require "active_record/connection_adapters/sqlite3/schema_statements"
18
18
  require "active_support/core_ext/class/attribute"
19
19
  require "arjdbc/sqlite3/column"
20
20
 
21
+ require "arjdbc/abstract/relation_query_attribute_monkey_patch"
22
+
21
23
  module SQLite3
22
24
  module Constants
23
25
  module Open
@@ -669,6 +671,14 @@ module ArJdbc
669
671
  StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
670
672
  end
671
673
 
674
+ def reconnect
675
+ if active?
676
+ @raw_connection.rollback rescue nil
677
+ else
678
+ connect
679
+ end
680
+ end
681
+
672
682
  def configure_connection
673
683
  if @config[:timeout] && @config[:retries]
674
684
  raise ArgumentError, "Cannot specify both timeout and retries arguments"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ArJdbc
4
- VERSION = '71.0.0.alpha2'
4
+ VERSION = "71.0.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-jdbc-alt-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 71.0.0.alpha2
4
+ version: 71.0.0
5
5
  platform: java
6
6
  authors:
7
7
  - Nick Sieger, Ola Bini, Karol Bucek, Jesse Chavez, and JRuby contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-06 00:00:00.000000000 Z
11
+ date: 2025-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -78,6 +78,7 @@ files:
78
78
  - lib/arjdbc/abstract/connection_management.rb
79
79
  - lib/arjdbc/abstract/core.rb
80
80
  - lib/arjdbc/abstract/database_statements.rb
81
+ - lib/arjdbc/abstract/relation_query_attribute_monkey_patch.rb
81
82
  - lib/arjdbc/abstract/statement_cache.rb
82
83
  - lib/arjdbc/abstract/transaction_support.rb
83
84
  - lib/arjdbc/discover.rb
@@ -143,6 +144,7 @@ files:
143
144
  - lib/arjdbc/postgresql/base/pgconn.rb
144
145
  - lib/arjdbc/postgresql/column.rb
145
146
  - lib/arjdbc/postgresql/connection_methods.rb
147
+ - lib/arjdbc/postgresql/database_statements.rb
146
148
  - lib/arjdbc/postgresql/name.rb
147
149
  - lib/arjdbc/postgresql/oid_types.rb
148
150
  - lib/arjdbc/postgresql/schema_statements.rb
@@ -229,9 +231,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
229
231
  version: '0'
230
232
  required_rubygems_version: !ruby/object:Gem::Requirement
231
233
  requirements:
232
- - - ">"
234
+ - - ">="
233
235
  - !ruby/object:Gem::Version
234
- version: 1.3.1
236
+ version: '0'
235
237
  requirements: []
236
238
  rubygems_version: 3.3.26
237
239
  signing_key: