activerecord-jdbc-alt-adapter 61.1.0-java → 70.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +166 -0
- data/.github/workflows/ruby.yml +273 -0
- data/.gitignore +1 -0
- data/.travis.yml +3 -4
- data/Gemfile +9 -7
- data/README.md +5 -1
- data/Rakefile +1 -1
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/activerecord-jdbc-alt-adapter.gemspec +2 -2
- data/lib/arel/visitors/compat.rb +5 -33
- data/lib/arel/visitors/h2.rb +1 -13
- data/lib/arel/visitors/hsqldb.rb +1 -21
- data/lib/arel/visitors/sql_server.rb +2 -103
- data/lib/arjdbc/abstract/core.rb +8 -9
- data/lib/arjdbc/abstract/database_statements.rb +12 -4
- data/lib/arjdbc/discover.rb +0 -67
- data/lib/arjdbc/hsqldb/adapter.rb +2 -2
- data/lib/arjdbc/jdbc/adapter.rb +3 -3
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +3 -1
- data/lib/arjdbc/jdbc/column.rb +1 -26
- data/lib/arjdbc/jdbc/type_cast.rb +2 -2
- data/lib/arjdbc/jdbc.rb +0 -7
- data/lib/arjdbc/mssql/adapter.rb +138 -108
- data/lib/arjdbc/mssql/connection_methods.rb +3 -0
- data/lib/arjdbc/mssql/quoting.rb +26 -27
- data/lib/arjdbc/mssql/schema_creation.rb +1 -1
- data/lib/arjdbc/mssql/schema_definitions.rb +32 -17
- data/lib/arjdbc/mssql/schema_dumper.rb +13 -1
- data/lib/arjdbc/mssql/schema_statements.rb +61 -36
- data/lib/arjdbc/mssql/transaction.rb +2 -2
- data/lib/arjdbc/mssql/types/date_and_time_types.rb +6 -6
- data/lib/arjdbc/mssql/types/numeric_types.rb +2 -2
- data/lib/arjdbc/mssql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +2 -1
- data/lib/arjdbc/oracle/adapter.rb +4 -23
- data/lib/arjdbc/postgresql/adapter.rb +153 -4
- data/lib/arjdbc/postgresql/oid_types.rb +155 -108
- data/lib/arjdbc/sqlite3/adapter.rb +152 -99
- data/lib/arjdbc/tasks/database_tasks.rb +0 -12
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +1 -1
- data/lib/arjdbc/util/serialized_attributes.rb +0 -22
- data/lib/arjdbc/util/table_copier.rb +2 -1
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +3 -18
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +17 -2
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +5 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +33 -0
- metadata +7 -38
- data/lib/active_record/connection_adapters/as400_adapter.rb +0 -2
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -1
- data/lib/active_record/connection_adapters/derby_adapter.rb +0 -1
- data/lib/active_record/connection_adapters/informix_adapter.rb +0 -1
- data/lib/arel/visitors/db2.rb +0 -137
- data/lib/arel/visitors/derby.rb +0 -112
- data/lib/arel/visitors/firebird.rb +0 -79
- data/lib/arjdbc/db2/adapter.rb +0 -808
- data/lib/arjdbc/db2/as400.rb +0 -142
- data/lib/arjdbc/db2/column.rb +0 -131
- data/lib/arjdbc/db2/connection_methods.rb +0 -48
- data/lib/arjdbc/db2.rb +0 -4
- data/lib/arjdbc/derby/active_record_patch.rb +0 -13
- data/lib/arjdbc/derby/adapter.rb +0 -521
- data/lib/arjdbc/derby/connection_methods.rb +0 -20
- data/lib/arjdbc/derby/schema_creation.rb +0 -15
- data/lib/arjdbc/derby.rb +0 -3
- data/lib/arjdbc/firebird/adapter.rb +0 -413
- data/lib/arjdbc/firebird/connection_methods.rb +0 -23
- data/lib/arjdbc/firebird.rb +0 -4
- data/lib/arjdbc/informix/adapter.rb +0 -139
- data/lib/arjdbc/informix/connection_methods.rb +0 -9
- data/lib/arjdbc/sybase/adapter.rb +0 -47
- data/lib/arjdbc/sybase.rb +0 -2
- data/lib/arjdbc/tasks/db2_database_tasks.rb +0 -104
- data/lib/arjdbc/tasks/derby_database_tasks.rb +0 -95
- data/src/java/arjdbc/derby/DerbyModule.java +0 -178
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +0 -152
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +0 -174
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +0 -75
@@ -7,36 +7,36 @@ module ActiveRecord
|
|
7
7
|
|
8
8
|
NATIVE_DATABASE_TYPES = {
|
9
9
|
# Logical Rails types to SQL Server types
|
10
|
-
primary_key:
|
11
|
-
integer:
|
12
|
-
boolean:
|
13
|
-
decimal:
|
14
|
-
float:
|
15
|
-
date:
|
16
|
-
time:
|
17
|
-
datetime:
|
18
|
-
string:
|
19
|
-
text:
|
20
|
-
binary:
|
10
|
+
primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
|
11
|
+
integer: { name: 'int', limit: 4 },
|
12
|
+
boolean: { name: 'bit' },
|
13
|
+
decimal: { name: 'decimal' },
|
14
|
+
float: { name: 'float' },
|
15
|
+
date: { name: 'date' },
|
16
|
+
time: { name: 'time' },
|
17
|
+
datetime: { name: 'datetime2' },
|
18
|
+
string: { name: 'nvarchar', limit: 4000 },
|
19
|
+
text: { name: 'nvarchar(max)' },
|
20
|
+
binary: { name: 'varbinary(max)' },
|
21
21
|
# Other types or SQL Server specific
|
22
|
-
bigint:
|
23
|
-
smalldatetime:
|
22
|
+
bigint: { name: 'bigint' },
|
23
|
+
smalldatetime: { name: 'smalldatetime' },
|
24
24
|
datetime_basic: { name: 'datetime' },
|
25
|
-
timestamp:
|
26
|
-
real:
|
27
|
-
money:
|
28
|
-
smallmoney:
|
29
|
-
char:
|
30
|
-
nchar:
|
31
|
-
varchar:
|
32
|
-
varchar_max:
|
33
|
-
uuid:
|
34
|
-
binary_basic:
|
35
|
-
varbinary:
|
25
|
+
timestamp: { name: 'datetime' },
|
26
|
+
real: { name: 'real' },
|
27
|
+
money: { name: 'money' },
|
28
|
+
smallmoney: { name: 'smallmoney' },
|
29
|
+
char: { name: 'char' },
|
30
|
+
nchar: { name: 'nchar' },
|
31
|
+
varchar: { name: 'varchar', limit: 8000 },
|
32
|
+
varchar_max: { name: 'varchar(max)' },
|
33
|
+
uuid: { name: 'uniqueidentifier' },
|
34
|
+
binary_basic: { name: 'binary' },
|
35
|
+
varbinary: { name: 'varbinary', limit: 8000 },
|
36
36
|
# Deprecated SQL Server types
|
37
|
-
image:
|
38
|
-
ntext:
|
39
|
-
text_basic:
|
37
|
+
image: { name: 'image' },
|
38
|
+
ntext: { name: 'ntext' },
|
39
|
+
text_basic: { name: 'text' }
|
40
40
|
}.freeze
|
41
41
|
|
42
42
|
def native_database_types
|
@@ -127,9 +127,17 @@ module ActiveRecord
|
|
127
127
|
create_database(name, options)
|
128
128
|
end
|
129
129
|
|
130
|
-
def
|
131
|
-
|
130
|
+
def remove_columns(table_name, *column_names, type: nil, **options)
|
131
|
+
if column_names.empty?
|
132
|
+
raise ArgumentError.new('You must specify at least one column name. Example: remove_columns(:people, :first_name)')
|
133
|
+
end
|
134
|
+
|
135
|
+
column_names.each do |column_name|
|
136
|
+
remove_column(table_name, column_name, type, **options)
|
137
|
+
end
|
138
|
+
end
|
132
139
|
|
140
|
+
def remove_column(table_name, column_name, _type = nil, **options)
|
133
141
|
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
134
142
|
|
135
143
|
remove_check_constraints(table_name, column_name)
|
@@ -138,7 +146,7 @@ module ActiveRecord
|
|
138
146
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
139
147
|
end
|
140
148
|
|
141
|
-
def drop_table(table_name, options
|
149
|
+
def drop_table(table_name, **options)
|
142
150
|
# mssql cannot recreate referenced table with force: :cascade
|
143
151
|
# https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-table-transact-sql?view=sql-server-2017
|
144
152
|
if options[:force] == :cascade
|
@@ -248,7 +256,7 @@ module ActiveRecord
|
|
248
256
|
(order_columns << super).join(', ')
|
249
257
|
end
|
250
258
|
|
251
|
-
def add_timestamps(table_name, options
|
259
|
+
def add_timestamps(table_name, **options)
|
252
260
|
if !options.key?(:precision) && supports_datetime_with_precision?
|
253
261
|
options[:precision] = 7
|
254
262
|
end
|
@@ -256,6 +264,16 @@ module ActiveRecord
|
|
256
264
|
super
|
257
265
|
end
|
258
266
|
|
267
|
+
def add_column(table_name, column_name, type, **options)
|
268
|
+
if supports_datetime_with_precision?
|
269
|
+
if type == :datetime && !options.key?(:precision)
|
270
|
+
options[:precision] = 7
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
super
|
275
|
+
end
|
276
|
+
|
259
277
|
def create_schema_dumper(options)
|
260
278
|
MSSQL::SchemaDumper.create(self, options)
|
261
279
|
end
|
@@ -323,16 +341,20 @@ module ActiveRecord
|
|
323
341
|
quoted_table = quote_table_name(table_name)
|
324
342
|
quoted_column = quote_column_name(column_name)
|
325
343
|
quoted_default = quote(default)
|
344
|
+
|
326
345
|
unless null || default.nil?
|
327
346
|
execute("UPDATE #{quoted_table} SET #{quoted_column}=#{quoted_default} WHERE #{quoted_column} IS NULL")
|
328
347
|
end
|
348
|
+
|
349
|
+
options = { limit: column.limit, precision: column.precision, scale: column.scale }
|
350
|
+
|
329
351
|
sql_alter = [
|
330
352
|
"ALTER TABLE #{quoted_table}",
|
331
|
-
"ALTER COLUMN #{quoted_column} #{type_to_sql(column.type,
|
332
|
-
('
|
353
|
+
"ALTER COLUMN #{quoted_column} #{type_to_sql(column.type, **options)}",
|
354
|
+
('NOT NULL' unless null)
|
333
355
|
]
|
334
356
|
|
335
|
-
execute(sql_alter.join(' '))
|
357
|
+
execute(sql_alter.compact.join(' '))
|
336
358
|
end
|
337
359
|
|
338
360
|
def update_table_definition(table_name, base) #:nodoc:
|
@@ -345,11 +367,14 @@ module ActiveRecord
|
|
345
367
|
MSSQL::SchemaCreation.new(self)
|
346
368
|
end
|
347
369
|
|
348
|
-
def create_table_definition(
|
349
|
-
MSSQL::TableDefinition.new(self,
|
370
|
+
def create_table_definition(name, **options)
|
371
|
+
MSSQL::TableDefinition.new(self, name, **options)
|
350
372
|
end
|
351
373
|
|
352
374
|
def new_column_from_field(table_name, field)
|
375
|
+
# NOTE: this method is used by the columns method in the abstract Class
|
376
|
+
# to map column_definitions. It would be good if column_definitions is
|
377
|
+
# implemented in ruby
|
353
378
|
field
|
354
379
|
end
|
355
380
|
|
@@ -27,10 +27,10 @@ module ActiveRecord
|
|
27
27
|
module RealTransactionExt
|
28
28
|
attr_reader :initial_transaction_isolation
|
29
29
|
|
30
|
-
def initialize(connection,
|
30
|
+
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
31
31
|
@connection = connection
|
32
32
|
|
33
|
-
if
|
33
|
+
if isolation
|
34
34
|
@initial_transaction_isolation = current_transaction_isolation
|
35
35
|
end
|
36
36
|
|
@@ -13,9 +13,9 @@ module ActiveRecord
|
|
13
13
|
return %("#{value}") if value.acts_like?(:string)
|
14
14
|
|
15
15
|
if value.usec > 0
|
16
|
-
%("#{value.
|
16
|
+
%("#{value.to_fs(:db)}.#{value.usec.to_s.remove(/0+$/)}")
|
17
17
|
else
|
18
|
-
%("#{value.
|
18
|
+
%("#{value.to_fs(:db)}")
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -54,9 +54,9 @@ module ActiveRecord
|
|
54
54
|
return %("#{value}") if value.acts_like?(:string)
|
55
55
|
|
56
56
|
if value.usec > 0
|
57
|
-
%("#{value.
|
57
|
+
%("#{value.to_fs(:db)}.#{value.usec.to_s.remove(/0+$/)}")
|
58
58
|
else
|
59
|
-
%("#{value.
|
59
|
+
%("#{value.to_fs(:db)}")
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
@@ -100,9 +100,9 @@ module ActiveRecord
|
|
100
100
|
return %("#{value}") if value.acts_like?(:string)
|
101
101
|
|
102
102
|
if value.usec > 0
|
103
|
-
%("#{value.
|
103
|
+
%("#{value.to_fs(:db)}.#{value.usec.to_s.remove(/0+$/)}")
|
104
104
|
else
|
105
|
-
%("#{value.
|
105
|
+
%("#{value.to_fs(:db)}")
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
35
35
|
end
|
36
36
|
|
37
37
|
class Money < Decimal
|
38
|
-
def initialize(
|
38
|
+
def initialize(precision: nil, limit: nil, scale: nil)
|
39
39
|
super
|
40
40
|
@precision = 19
|
41
41
|
@scale = 4
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
end
|
47
47
|
|
48
48
|
class SmallMoney < Decimal
|
49
|
-
def initialize(
|
49
|
+
def initialize(precision: nil, limit: nil, scale: nil)
|
50
50
|
super
|
51
51
|
@precision = 10
|
52
52
|
@scale = 4
|
data/lib/arjdbc/mssql.rb
CHANGED
data/lib/arjdbc/mysql/adapter.rb
CHANGED
@@ -40,7 +40,7 @@ module ArJdbc
|
|
40
40
|
return if @@_initialized; @@_initialized = true
|
41
41
|
|
42
42
|
require 'arjdbc/util/serialized_attributes'
|
43
|
-
Util::SerializedAttributes.setup
|
43
|
+
Util::SerializedAttributes.setup %r{LOB\(|LOB$}i, 'after_save_with_oracle_lob'
|
44
44
|
|
45
45
|
unless ActiveRecord::ConnectionAdapters::AbstractAdapter.
|
46
46
|
instance_methods(false).detect { |m| m.to_s == "prefetch_primary_key?" }
|
@@ -285,7 +285,7 @@ module ArJdbc
|
|
285
285
|
execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
|
286
286
|
end
|
287
287
|
end
|
288
|
-
end
|
288
|
+
end
|
289
289
|
|
290
290
|
# @private
|
291
291
|
def add_index_options(table_name, column_name, options = {})
|
@@ -309,7 +309,7 @@ module ArJdbc
|
|
309
309
|
|
310
310
|
quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
|
311
311
|
[ index_name, index_type, quoted_column_names, tablespace, index_options ]
|
312
|
-
end
|
312
|
+
end
|
313
313
|
|
314
314
|
# @override
|
315
315
|
def remove_index(table_name, options = {})
|
@@ -327,12 +327,7 @@ module ArJdbc
|
|
327
327
|
end
|
328
328
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
|
329
329
|
execute "DROP INDEX #{quote_column_name(index_name)}"
|
330
|
-
end
|
331
|
-
|
332
|
-
# @private
|
333
|
-
def remove_index(table_name, options = {})
|
334
|
-
execute "DROP INDEX #{index_name(table_name, options)}"
|
335
|
-
end unless AR42
|
330
|
+
end
|
336
331
|
|
337
332
|
def change_column_default(table_name, column_name, default)
|
338
333
|
execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
@@ -361,25 +356,11 @@ module ArJdbc
|
|
361
356
|
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
362
357
|
end
|
363
358
|
|
364
|
-
if ActiveRecord::VERSION::MAJOR >= 4
|
365
|
-
|
366
359
|
# @override
|
367
360
|
def remove_column(table_name, column_name, type = nil, options = {})
|
368
361
|
do_remove_column(table_name, column_name)
|
369
362
|
end
|
370
363
|
|
371
|
-
else
|
372
|
-
|
373
|
-
# @override
|
374
|
-
def remove_column(table_name, *column_names)
|
375
|
-
for column_name in column_names.flatten
|
376
|
-
do_remove_column(table_name, column_name)
|
377
|
-
end
|
378
|
-
end
|
379
|
-
alias remove_columns remove_column
|
380
|
-
|
381
|
-
end
|
382
|
-
|
383
364
|
def do_remove_column(table_name, column_name)
|
384
365
|
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
385
366
|
end
|
@@ -21,6 +21,7 @@ require 'arjdbc/abstract/transaction_support'
|
|
21
21
|
require 'arjdbc/postgresql/base/array_decoder'
|
22
22
|
require 'arjdbc/postgresql/base/array_encoder'
|
23
23
|
require 'arjdbc/postgresql/name'
|
24
|
+
require 'active_model'
|
24
25
|
|
25
26
|
module ArJdbc
|
26
27
|
# Strives to provide Rails built-in PostgreSQL adapter (API) compatibility.
|
@@ -81,7 +82,7 @@ module ArJdbc
|
|
81
82
|
# If using Active Record's time zone support configure the connection to return
|
82
83
|
# TIMESTAMP WITH ZONE types in UTC.
|
83
84
|
# (SET TIME ZONE does not use an equals sign like other SET variables)
|
84
|
-
if ActiveRecord
|
85
|
+
if ActiveRecord.default_timezone == :utc
|
85
86
|
execute("SET time zone 'UTC'", 'SCHEMA')
|
86
87
|
elsif tz = local_tz
|
87
88
|
execute("SET time zone '#{tz}'", 'SCHEMA')
|
@@ -320,6 +321,38 @@ module ArJdbc
|
|
320
321
|
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
321
322
|
end
|
322
323
|
|
324
|
+
# Returns a list of defined enum types, and their values.
|
325
|
+
def enum_types
|
326
|
+
query = <<~SQL
|
327
|
+
SELECT
|
328
|
+
type.typname AS name,
|
329
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
330
|
+
FROM pg_enum AS enum
|
331
|
+
JOIN pg_type AS type
|
332
|
+
ON (type.oid = enum.enumtypid)
|
333
|
+
GROUP BY type.typname;
|
334
|
+
SQL
|
335
|
+
exec_query(query, "SCHEMA").cast_values
|
336
|
+
end
|
337
|
+
|
338
|
+
# Given a name and an array of values, creates an enum type.
|
339
|
+
def create_enum(name, values)
|
340
|
+
sql_values = values.map { |s| "'#{s}'" }.join(", ")
|
341
|
+
query = <<~SQL
|
342
|
+
DO $$
|
343
|
+
BEGIN
|
344
|
+
IF NOT EXISTS (
|
345
|
+
SELECT 1 FROM pg_type t
|
346
|
+
WHERE t.typname = '#{name}'
|
347
|
+
) THEN
|
348
|
+
CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
|
349
|
+
END IF;
|
350
|
+
END
|
351
|
+
$$;
|
352
|
+
SQL
|
353
|
+
exec_query(query)
|
354
|
+
end
|
355
|
+
|
323
356
|
# Returns the configured supported identifier length supported by PostgreSQL
|
324
357
|
def max_identifier_length
|
325
358
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
@@ -672,6 +705,37 @@ module ActiveRecord::ConnectionAdapters
|
|
672
705
|
class PostgreSQLAdapter < AbstractAdapter
|
673
706
|
class_attribute :create_unlogged_tables, default: false
|
674
707
|
|
708
|
+
##
|
709
|
+
# :singleton-method:
|
710
|
+
# PostgreSQL allows the creation of "unlogged" tables, which do not record
|
711
|
+
# data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
|
712
|
+
# but significantly increases the risk of data loss if the database
|
713
|
+
# crashes. As a result, this should not be used in production
|
714
|
+
# environments. If you would like all created tables to be unlogged in
|
715
|
+
# the test environment you can add the following line to your test.rb
|
716
|
+
# file:
|
717
|
+
#
|
718
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
|
719
|
+
class_attribute :create_unlogged_tables, default: false
|
720
|
+
|
721
|
+
##
|
722
|
+
# :singleton-method:
|
723
|
+
# PostgreSQL supports multiple types for DateTimes. By default, if you use +datetime+
|
724
|
+
# in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
|
725
|
+
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
726
|
+
# store DateTimes as "timestamp with time zone":
|
727
|
+
#
|
728
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
729
|
+
#
|
730
|
+
# Or if you are adding a custom type:
|
731
|
+
#
|
732
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
733
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
734
|
+
#
|
735
|
+
# If you're using +:ruby+ as your +config.active_record.schema_format+ and you change this
|
736
|
+
# setting, you should immediately run <tt>bin/rails db:migrate</tt> to update the types in your schema.rb.
|
737
|
+
class_attribute :datetime_type, default: :timestamp
|
738
|
+
|
675
739
|
# Try to use as much of the built in postgres logic as possible
|
676
740
|
# maybe someday we can extend the actual adapter
|
677
741
|
include ActiveRecord::ConnectionAdapters::PostgreSQL::ReferentialIntegrity
|
@@ -735,11 +799,96 @@ module ActiveRecord::ConnectionAdapters
|
|
735
799
|
|
736
800
|
private
|
737
801
|
|
738
|
-
|
739
|
-
|
802
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
803
|
+
|
804
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false)
|
805
|
+
sql = transform_query(sql)
|
806
|
+
check_if_write_query(sql)
|
807
|
+
|
808
|
+
if !prepare || without_prepared_statement?(binds)
|
809
|
+
result = exec_no_cache(sql, name, binds, async: async)
|
810
|
+
else
|
811
|
+
result = exec_cache(sql, name, binds, async: async)
|
812
|
+
end
|
813
|
+
begin
|
814
|
+
ret = yield result
|
815
|
+
ensure
|
816
|
+
# Is this really result in AR PG?
|
817
|
+
# result.clear
|
818
|
+
end
|
819
|
+
ret
|
820
|
+
end
|
821
|
+
|
822
|
+
def exec_no_cache(sql, name, binds, async: false)
|
823
|
+
materialize_transactions
|
824
|
+
mark_transaction_written_if_write(sql)
|
825
|
+
|
826
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
827
|
+
# made since we established the connection
|
828
|
+
update_typemap_for_default_timezone
|
829
|
+
|
830
|
+
type_casted_binds = type_casted_binds(binds)
|
831
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
832
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
833
|
+
@connection.exec_params(sql, type_casted_binds)
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
def exec_cache(sql, name, binds, async: false)
|
839
|
+
materialize_transactions
|
840
|
+
mark_transaction_written_if_write(sql)
|
841
|
+
update_typemap_for_default_timezone
|
842
|
+
|
843
|
+
stmt_key = prepare_statement(sql, binds)
|
844
|
+
type_casted_binds = type_casted_binds(binds)
|
845
|
+
|
846
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
847
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
848
|
+
@connection.exec_prepared(stmt_key, type_casted_binds)
|
849
|
+
end
|
850
|
+
end
|
851
|
+
rescue ActiveRecord::StatementInvalid => e
|
852
|
+
raise unless is_cached_plan_failure?(e)
|
853
|
+
|
854
|
+
# Nothing we can do if we are in a transaction because all commands
|
855
|
+
# will raise InFailedSQLTransaction
|
856
|
+
if in_transaction?
|
857
|
+
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
|
858
|
+
else
|
859
|
+
@lock.synchronize do
|
860
|
+
# outside of transactions we can simply flush this query and retry
|
861
|
+
@statements.delete sql_key(sql)
|
862
|
+
end
|
863
|
+
retry
|
864
|
+
end
|
865
|
+
end
|
866
|
+
|
867
|
+
# Annoyingly, the code for prepared statements whose return value may
|
868
|
+
# have changed is FEATURE_NOT_SUPPORTED.
|
869
|
+
#
|
870
|
+
# This covers various different error types so we need to do additional
|
871
|
+
# work to classify the exception definitively as a
|
872
|
+
# ActiveRecord::PreparedStatementCacheExpired
|
873
|
+
#
|
874
|
+
# Check here for more details:
|
875
|
+
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
876
|
+
def is_cached_plan_failure?(e)
|
877
|
+
pgerror = e.cause
|
878
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
879
|
+
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
880
|
+
rescue
|
881
|
+
false
|
882
|
+
end
|
883
|
+
|
884
|
+
def in_transaction?
|
885
|
+
open_transactions > 0
|
886
|
+
end
|
887
|
+
|
888
|
+
# Returns the statement identifier for the client side cache
|
889
|
+
# of statements
|
740
890
|
def sql_key(sql)
|
741
891
|
"#{schema_search_path}-#{sql}"
|
742
892
|
end
|
743
|
-
|
744
893
|
end
|
745
894
|
end
|