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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +26 -26
- data/.gitignore +8 -0
- data/Gemfile +17 -4
- data/README.md +10 -5
- data/RUNNING_TESTS.md +36 -0
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/lib/arjdbc/abstract/connection_management.rb +25 -10
- data/lib/arjdbc/abstract/core.rb +15 -13
- data/lib/arjdbc/abstract/database_statements.rb +36 -36
- data/lib/arjdbc/abstract/relation_query_attribute_monkey_patch.rb +24 -0
- data/lib/arjdbc/abstract/statement_cache.rb +2 -7
- data/lib/arjdbc/abstract/transaction_support.rb +39 -22
- data/lib/arjdbc/jdbc/adapter.rb +0 -1
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/column.rb +0 -34
- data/lib/arjdbc/jdbc/connection_methods.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +108 -32
- data/lib/arjdbc/mysql/adapter_hash_config.rb +159 -0
- data/lib/arjdbc/mysql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +267 -114
- data/lib/arjdbc/postgresql/adapter_hash_config.rb +98 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +3 -1
- data/lib/arjdbc/postgresql/database_statements.rb +20 -0
- data/lib/arjdbc/postgresql/oid_types.rb +10 -29
- data/lib/arjdbc/postgresql/schema_statements.rb +57 -0
- data/lib/arjdbc/postgresql.rb +1 -1
- data/lib/arjdbc/sqlite3/adapter.rb +343 -172
- data/lib/arjdbc/sqlite3/adapter_hash_config.rb +91 -0
- data/lib/arjdbc/sqlite3/column.rb +117 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
- data/lib/arjdbc/sqlite3/pragmas.rb +105 -0
- data/lib/arjdbc/sqlite3.rb +1 -1
- data/lib/arjdbc/version.rb +1 -1
- data/lib/arjdbc.rb +13 -1
- data/rakelib/02-test.rake +2 -2
- data/rakelib/rails.rake +2 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +9 -2
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +12 -5
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +14 -3
- metadata +15 -11
- 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 (
|
56
|
-
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: {
|
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
|
-
|
302
|
-
|
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
|
-
|
307
|
-
|
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
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
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
|
-
|
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|
|
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
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
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
|
-
|
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 =
|
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
|
-
|
469
|
-
|
470
|
-
|
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
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
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
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
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
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
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
|
-
|
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
|
797
|
-
|
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
|
-
|
833
|
-
|
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
|