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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +18 -18
- data/.gitignore +8 -0
- data/Gemfile +17 -4
- data/README.md +8 -3
- 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 +5 -12
- data/lib/arjdbc/abstract/database_statements.rb +35 -35
- 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 +37 -22
- 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 +106 -27
- data/lib/arjdbc/mysql/connection_methods.rb +43 -42
- data/lib/arjdbc/postgresql/adapter.rb +252 -105
- data/lib/arjdbc/postgresql/database_statements.rb +20 -0
- data/lib/arjdbc/postgresql/oid_types.rb +8 -27
- data/lib/arjdbc/postgresql/schema_statements.rb +57 -0
- data/lib/arjdbc/sqlite3/adapter.rb +213 -145
- data/lib/arjdbc/sqlite3/column.rb +103 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +7 -2
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +1 -1
- data/rakelib/rails.rake +2 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4 -2
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +11 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +2 -1
- metadata +12 -11
@@ -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 (
|
56
|
-
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: {
|
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
|
-
|
302
|
-
|
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
|
-
|
307
|
-
|
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
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
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
|
-
|
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|
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
469
|
-
|
470
|
-
|
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
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
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
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
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
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
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
|
-
|
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
|
797
|
-
|
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
|
-
|
833
|
-
|
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
|
-
|
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)
|