activerecord-jdbc-alt-adapter 71.0.0.alpha2-java → 71.0.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.
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: