activerecord 6.1.7.10 → 7.0.0.alpha1
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +726 -1404
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +31 -9
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +14 -23
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +161 -47
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +41 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +2 -14
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -14
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -32
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +159 -102
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +20 -38
- data/lib/active_record/core.rb +111 -125
- data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +17 -9
- data/lib/active_record/delegated_type.rb +33 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +80 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +41 -41
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +34 -5
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +89 -10
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +45 -31
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +72 -48
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +39 -26
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -22
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +230 -49
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +8 -4
- data/lib/active_record/relation.rb +166 -77
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/store.rb +1 -6
- data/lib/active_record/tasks/database_tasks.rb +106 -22
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -13
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +3 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +43 -2
- data/lib/arel.rb +1 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- metadata +52 -14
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
module SchemaStatements
|
7
7
|
# Drops the database specified on the +name+ attribute
|
8
8
|
# and creates it again using the provided +options+.
|
9
|
-
def recreate_database(name, options = {})
|
9
|
+
def recreate_database(name, options = {}) # :nodoc:
|
10
10
|
drop_database(name)
|
11
11
|
create_database(name, options)
|
12
12
|
end
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
50
50
|
#
|
51
51
|
# Example:
|
52
52
|
# drop_database 'matt_development'
|
53
|
-
def drop_database(name)
|
53
|
+
def drop_database(name) # :nodoc:
|
54
54
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
55
55
|
end
|
56
56
|
|
@@ -244,7 +244,7 @@ module ActiveRecord
|
|
244
244
|
end
|
245
245
|
|
246
246
|
# Returns the sequence name for a table's primary key or some other specified key.
|
247
|
-
def default_sequence_name(table_name, pk = "id")
|
247
|
+
def default_sequence_name(table_name, pk = "id") # :nodoc:
|
248
248
|
result = serial_sequence(table_name, pk)
|
249
249
|
return nil unless result
|
250
250
|
Utils.extract_schema_qualified_name(result).to_s
|
@@ -257,7 +257,7 @@ module ActiveRecord
|
|
257
257
|
end
|
258
258
|
|
259
259
|
# Sets the sequence of a table's primary key to the specified value.
|
260
|
-
def set_pk_sequence!(table, value)
|
260
|
+
def set_pk_sequence!(table, value) # :nodoc:
|
261
261
|
pk, sequence = pk_and_sequence_for(table)
|
262
262
|
|
263
263
|
if pk
|
@@ -272,7 +272,7 @@ module ActiveRecord
|
|
272
272
|
end
|
273
273
|
|
274
274
|
# Resets the sequence of a table's primary key to the maximum value.
|
275
|
-
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
275
|
+
def reset_pk_sequence!(table, pk = nil, sequence = nil) # :nodoc:
|
276
276
|
unless pk && sequence
|
277
277
|
default_pk, default_sequence = pk_and_sequence_for(table)
|
278
278
|
|
@@ -300,7 +300,7 @@ module ActiveRecord
|
|
300
300
|
end
|
301
301
|
|
302
302
|
# Returns a table's primary key and belonging sequence.
|
303
|
-
def pk_and_sequence_for(table)
|
303
|
+
def pk_and_sequence_for(table) # :nodoc:
|
304
304
|
# First try looking for a sequence with a dependency on the
|
305
305
|
# given table's primary key.
|
306
306
|
result = query(<<~SQL, "SCHEMA")[0]
|
@@ -393,13 +393,13 @@ module ActiveRecord
|
|
393
393
|
rename_table_indexes(table_name, new_name)
|
394
394
|
end
|
395
395
|
|
396
|
-
def add_column(table_name, column_name, type, **options)
|
396
|
+
def add_column(table_name, column_name, type, **options) # :nodoc:
|
397
397
|
clear_cache!
|
398
398
|
super
|
399
399
|
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
400
400
|
end
|
401
401
|
|
402
|
-
def change_column(table_name, column_name, type, **options)
|
402
|
+
def change_column(table_name, column_name, type, **options) # :nodoc:
|
403
403
|
clear_cache!
|
404
404
|
sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
|
405
405
|
execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
|
@@ -411,7 +411,7 @@ module ActiveRecord
|
|
411
411
|
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
412
412
|
end
|
413
413
|
|
414
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
414
|
+
def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
|
415
415
|
clear_cache!
|
416
416
|
unless null || default.nil?
|
417
417
|
column = column_for(table_name, column_name)
|
@@ -435,13 +435,13 @@ module ActiveRecord
|
|
435
435
|
end
|
436
436
|
|
437
437
|
# Renames a column in a table.
|
438
|
-
def rename_column(table_name, column_name, new_column_name)
|
438
|
+
def rename_column(table_name, column_name, new_column_name) # :nodoc:
|
439
439
|
clear_cache!
|
440
440
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
441
441
|
rename_column_indexes(table_name, column_name, new_column_name)
|
442
442
|
end
|
443
443
|
|
444
|
-
def add_index(table_name, column_name, **options)
|
444
|
+
def add_index(table_name, column_name, **options) # :nodoc:
|
445
445
|
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
446
446
|
|
447
447
|
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
@@ -576,7 +576,7 @@ module ActiveRecord
|
|
576
576
|
|
577
577
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
578
578
|
# requires that the ORDER BY include the distinct column.
|
579
|
-
def columns_for_distinct(columns, orders)
|
579
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
580
580
|
order_columns = orders.compact_blank.map { |s|
|
581
581
|
# Convert Arel node to string
|
582
582
|
s = visitor.compile(s) unless s.is_a?(String)
|
@@ -52,7 +52,7 @@ module ActiveRecord
|
|
52
52
|
# * <tt>:port</tt> - Defaults to 5432.
|
53
53
|
# * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
|
54
54
|
# * <tt>:password</tt> - Password to be used if the server demands password authentication.
|
55
|
-
# * <tt>:database</tt> - Defaults to be the same as the
|
55
|
+
# * <tt>:database</tt> - Defaults to be the same as the username.
|
56
56
|
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
|
57
57
|
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
|
58
58
|
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
|
@@ -75,10 +75,14 @@ module ActiveRecord
|
|
75
75
|
|
76
76
|
class << self
|
77
77
|
def new_client(conn_params)
|
78
|
-
PG.connect(
|
78
|
+
PG.connect(conn_params)
|
79
79
|
rescue ::PG::Error => error
|
80
80
|
if conn_params && conn_params[:dbname] && error.message.include?(conn_params[:dbname])
|
81
|
-
raise ActiveRecord::NoDatabaseError
|
81
|
+
raise ActiveRecord::NoDatabaseError.db_error(conn_params[:dbname])
|
82
|
+
elsif conn_params && conn_params[:user] && error.message.include?(conn_params[:user])
|
83
|
+
raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:user])
|
84
|
+
elsif conn_params && conn_params[:hostname] && error.message.include?(conn_params[:hostname])
|
85
|
+
raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:hostname])
|
82
86
|
else
|
83
87
|
raise ActiveRecord::ConnectionNotEstablished, error.message
|
84
88
|
end
|
@@ -98,6 +102,24 @@ module ActiveRecord
|
|
98
102
|
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
|
99
103
|
class_attribute :create_unlogged_tables, default: false
|
100
104
|
|
105
|
+
##
|
106
|
+
# :singleton-method:
|
107
|
+
# PostgreSQL supports multiple types for DateTimes. By default if you use `datetime`
|
108
|
+
# in migrations, Rails will translate this to a PostgreSQL "timestamp without time zone".
|
109
|
+
# Change this in an initializer to use another NATIVE_DATABASE_TYPES. For example, to
|
110
|
+
# store DateTimes as "timestamp with time zone":
|
111
|
+
#
|
112
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
|
113
|
+
#
|
114
|
+
# Or if you are adding a custom type:
|
115
|
+
#
|
116
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:my_custom_type] = { name: "my_custom_type_name" }
|
117
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :my_custom_type
|
118
|
+
#
|
119
|
+
# If you're using :ruby as your config.active_record.schema_format and you change this
|
120
|
+
# setting, you should immediately run bin/rails db:migrate to update the types in your schema.rb.
|
121
|
+
class_attribute :datetime_type, default: :timestamp
|
122
|
+
|
101
123
|
NATIVE_DATABASE_TYPES = {
|
102
124
|
primary_key: "bigserial primary key",
|
103
125
|
string: { name: "character varying" },
|
@@ -105,7 +127,9 @@ module ActiveRecord
|
|
105
127
|
integer: { name: "integer", limit: 4 },
|
106
128
|
float: { name: "float" },
|
107
129
|
decimal: { name: "decimal" },
|
108
|
-
datetime: {
|
130
|
+
datetime: {}, # set dynamically based on datetime_type
|
131
|
+
timestamp: { name: "timestamp" },
|
132
|
+
timestamptz: { name: "timestamptz" },
|
109
133
|
time: { name: "time" },
|
110
134
|
date: { name: "date" },
|
111
135
|
daterange: { name: "daterange" },
|
@@ -141,7 +165,7 @@ module ActiveRecord
|
|
141
165
|
oid: { name: "oid" },
|
142
166
|
}
|
143
167
|
|
144
|
-
OID = PostgreSQL::OID
|
168
|
+
OID = PostgreSQL::OID # :nodoc:
|
145
169
|
|
146
170
|
include PostgreSQL::Quoting
|
147
171
|
include PostgreSQL::ReferentialIntegrity
|
@@ -247,7 +271,7 @@ module ActiveRecord
|
|
247
271
|
def initialize(connection, logger, connection_parameters, config)
|
248
272
|
super(connection, logger, config)
|
249
273
|
|
250
|
-
@connection_parameters = connection_parameters
|
274
|
+
@connection_parameters = connection_parameters
|
251
275
|
|
252
276
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
253
277
|
@local_tz = nil
|
@@ -272,19 +296,25 @@ module ActiveRecord
|
|
272
296
|
# Is this connection alive and ready for queries?
|
273
297
|
def active?
|
274
298
|
@lock.synchronize do
|
275
|
-
@connection.query "
|
299
|
+
@connection.query ";"
|
276
300
|
end
|
277
301
|
true
|
278
302
|
rescue PG::Error
|
279
303
|
false
|
280
304
|
end
|
281
305
|
|
306
|
+
def reload_type_map # :nodoc:
|
307
|
+
type_map.clear
|
308
|
+
initialize_type_map
|
309
|
+
end
|
310
|
+
|
282
311
|
# Close then reopen the connection.
|
283
312
|
def reconnect!
|
284
313
|
@lock.synchronize do
|
285
314
|
super
|
286
315
|
@connection.reset
|
287
316
|
configure_connection
|
317
|
+
reload_type_map
|
288
318
|
rescue PG::ConnectionBad
|
289
319
|
connect
|
290
320
|
end
|
@@ -317,8 +347,16 @@ module ActiveRecord
|
|
317
347
|
@connection = nil
|
318
348
|
end
|
319
349
|
|
320
|
-
def native_database_types
|
321
|
-
|
350
|
+
def native_database_types # :nodoc:
|
351
|
+
self.class.native_database_types
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.native_database_types # :nodoc:
|
355
|
+
@native_database_types ||= begin
|
356
|
+
types = NATIVE_DATABASE_TYPES.dup
|
357
|
+
types[:datetime] = types[datetime_type]
|
358
|
+
types
|
359
|
+
end
|
322
360
|
end
|
323
361
|
|
324
362
|
def set_standard_conforming_strings
|
@@ -438,8 +476,12 @@ module ActiveRecord
|
|
438
476
|
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
439
477
|
elsif insert.update_duplicates?
|
440
478
|
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
441
|
-
|
442
|
-
|
479
|
+
if insert.raw_update_sql?
|
480
|
+
sql << insert.raw_update_sql
|
481
|
+
else
|
482
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column} IS NOT DISTINCT FROM excluded.#{column}" }
|
483
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
484
|
+
end
|
443
485
|
end
|
444
486
|
|
445
487
|
sql << " RETURNING #{insert.returning}" if insert.returning
|
@@ -452,68 +494,8 @@ module ActiveRecord
|
|
452
494
|
end
|
453
495
|
end
|
454
496
|
|
455
|
-
|
456
|
-
#
|
457
|
-
VALUE_LIMIT_VIOLATION = "22001"
|
458
|
-
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
459
|
-
NOT_NULL_VIOLATION = "23502"
|
460
|
-
FOREIGN_KEY_VIOLATION = "23503"
|
461
|
-
UNIQUE_VIOLATION = "23505"
|
462
|
-
SERIALIZATION_FAILURE = "40001"
|
463
|
-
DEADLOCK_DETECTED = "40P01"
|
464
|
-
DUPLICATE_DATABASE = "42P04"
|
465
|
-
LOCK_NOT_AVAILABLE = "55P03"
|
466
|
-
QUERY_CANCELED = "57014"
|
467
|
-
|
468
|
-
def translate_exception(exception, message:, sql:, binds:)
|
469
|
-
return exception unless exception.respond_to?(:result)
|
470
|
-
|
471
|
-
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
472
|
-
when nil
|
473
|
-
if exception.message.match?(/connection is closed/i)
|
474
|
-
ConnectionNotEstablished.new(exception)
|
475
|
-
else
|
476
|
-
super
|
477
|
-
end
|
478
|
-
when UNIQUE_VIOLATION
|
479
|
-
RecordNotUnique.new(message, sql: sql, binds: binds)
|
480
|
-
when FOREIGN_KEY_VIOLATION
|
481
|
-
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
482
|
-
when VALUE_LIMIT_VIOLATION
|
483
|
-
ValueTooLong.new(message, sql: sql, binds: binds)
|
484
|
-
when NUMERIC_VALUE_OUT_OF_RANGE
|
485
|
-
RangeError.new(message, sql: sql, binds: binds)
|
486
|
-
when NOT_NULL_VIOLATION
|
487
|
-
NotNullViolation.new(message, sql: sql, binds: binds)
|
488
|
-
when SERIALIZATION_FAILURE
|
489
|
-
SerializationFailure.new(message, sql: sql, binds: binds)
|
490
|
-
when DEADLOCK_DETECTED
|
491
|
-
Deadlocked.new(message, sql: sql, binds: binds)
|
492
|
-
when DUPLICATE_DATABASE
|
493
|
-
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
494
|
-
when LOCK_NOT_AVAILABLE
|
495
|
-
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
496
|
-
when QUERY_CANCELED
|
497
|
-
QueryCanceled.new(message, sql: sql, binds: binds)
|
498
|
-
else
|
499
|
-
super
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
|
-
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
504
|
-
if !type_map.key?(oid)
|
505
|
-
load_additional_types([oid])
|
506
|
-
end
|
507
|
-
|
508
|
-
type_map.fetch(oid, fmod, sql_type) {
|
509
|
-
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
510
|
-
Type.default_value.tap do |cast_type|
|
511
|
-
type_map.register_type(oid, cast_type)
|
512
|
-
end
|
513
|
-
}
|
514
|
-
end
|
515
|
-
|
516
|
-
def initialize_type_map(m = type_map)
|
497
|
+
class << self
|
498
|
+
def initialize_type_map(m) # :nodoc:
|
517
499
|
m.register_type "int2", Type::Integer.new(limit: 2)
|
518
500
|
m.register_type "int4", Type::Integer.new(limit: 4)
|
519
501
|
m.register_type "int8", Type::Integer.new(limit: 8)
|
@@ -528,7 +510,6 @@ module ActiveRecord
|
|
528
510
|
m.register_type "bool", Type::Boolean.new
|
529
511
|
register_class_with_limit m, "bit", OID::Bit
|
530
512
|
register_class_with_limit m, "varbit", OID::BitVarying
|
531
|
-
m.alias_type "timestamptz", "timestamp"
|
532
513
|
m.register_type "date", OID::Date.new
|
533
514
|
|
534
515
|
m.register_type "money", OID::Money.new
|
@@ -553,7 +534,8 @@ module ActiveRecord
|
|
553
534
|
m.register_type "circle", OID::SpecializedString.new(:circle)
|
554
535
|
|
555
536
|
register_class_with_precision m, "time", Type::Time
|
556
|
-
register_class_with_precision m, "timestamp", OID::
|
537
|
+
register_class_with_precision m, "timestamp", OID::Timestamp
|
538
|
+
register_class_with_precision m, "timestamptz", OID::TimestampWithTimeZone
|
557
539
|
|
558
540
|
m.register_type "numeric" do |_, fmod, sql_type|
|
559
541
|
precision = extract_precision(sql_type)
|
@@ -579,7 +561,16 @@ module ActiveRecord
|
|
579
561
|
precision = extract_precision(sql_type)
|
580
562
|
OID::Interval.new(precision: precision)
|
581
563
|
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
private
|
568
|
+
def type_map
|
569
|
+
@type_map ||= Type::HashLookupTypeMap.new
|
570
|
+
end
|
582
571
|
|
572
|
+
def initialize_type_map(m = type_map)
|
573
|
+
self.class.initialize_type_map(m)
|
583
574
|
load_additional_types
|
584
575
|
end
|
585
576
|
|
@@ -587,7 +578,7 @@ module ActiveRecord
|
|
587
578
|
def extract_value_from_default(default)
|
588
579
|
case default
|
589
580
|
# Quoted types
|
590
|
-
when /\A[
|
581
|
+
when /\A[(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
591
582
|
# The default 'now'::date is CURRENT_DATE
|
592
583
|
if $1 == "now" && $2 == "date"
|
593
584
|
nil
|
@@ -618,37 +609,100 @@ module ActiveRecord
|
|
618
609
|
!default_value && %r{\w+\(.*\)|\(.*\)::\w+|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
619
610
|
end
|
620
611
|
|
612
|
+
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
613
|
+
VALUE_LIMIT_VIOLATION = "22001"
|
614
|
+
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
615
|
+
NOT_NULL_VIOLATION = "23502"
|
616
|
+
FOREIGN_KEY_VIOLATION = "23503"
|
617
|
+
UNIQUE_VIOLATION = "23505"
|
618
|
+
SERIALIZATION_FAILURE = "40001"
|
619
|
+
DEADLOCK_DETECTED = "40P01"
|
620
|
+
DUPLICATE_DATABASE = "42P04"
|
621
|
+
LOCK_NOT_AVAILABLE = "55P03"
|
622
|
+
QUERY_CANCELED = "57014"
|
623
|
+
|
624
|
+
def translate_exception(exception, message:, sql:, binds:)
|
625
|
+
return exception unless exception.respond_to?(:result)
|
626
|
+
|
627
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
628
|
+
when nil
|
629
|
+
if exception.message.match?(/connection is closed/i)
|
630
|
+
ConnectionNotEstablished.new(exception)
|
631
|
+
else
|
632
|
+
super
|
633
|
+
end
|
634
|
+
when UNIQUE_VIOLATION
|
635
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
636
|
+
when FOREIGN_KEY_VIOLATION
|
637
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
638
|
+
when VALUE_LIMIT_VIOLATION
|
639
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
640
|
+
when NUMERIC_VALUE_OUT_OF_RANGE
|
641
|
+
RangeError.new(message, sql: sql, binds: binds)
|
642
|
+
when NOT_NULL_VIOLATION
|
643
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
644
|
+
when SERIALIZATION_FAILURE
|
645
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
646
|
+
when DEADLOCK_DETECTED
|
647
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
648
|
+
when DUPLICATE_DATABASE
|
649
|
+
DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
|
650
|
+
when LOCK_NOT_AVAILABLE
|
651
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
652
|
+
when QUERY_CANCELED
|
653
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
654
|
+
else
|
655
|
+
super
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
660
|
+
if !type_map.key?(oid)
|
661
|
+
load_additional_types([oid])
|
662
|
+
end
|
663
|
+
|
664
|
+
type_map.fetch(oid, fmod, sql_type) {
|
665
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
666
|
+
Type.default_value.tap do |cast_type|
|
667
|
+
type_map.register_type(oid, cast_type)
|
668
|
+
end
|
669
|
+
}
|
670
|
+
end
|
671
|
+
|
621
672
|
def load_additional_types(oids = nil)
|
622
673
|
initializer = OID::TypeMapInitializer.new(type_map)
|
674
|
+
load_types_queries(initializer, oids) do |query|
|
675
|
+
execute_and_clear(query, "SCHEMA", []) do |records|
|
676
|
+
initializer.run(records)
|
677
|
+
end
|
678
|
+
end
|
679
|
+
end
|
623
680
|
|
681
|
+
def load_types_queries(initializer, oids)
|
624
682
|
query = <<~SQL
|
625
683
|
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
626
684
|
FROM pg_type as t
|
627
685
|
LEFT JOIN pg_range as r ON oid = rngtypid
|
628
686
|
SQL
|
629
|
-
|
630
687
|
if oids
|
631
|
-
query
|
688
|
+
yield query + "WHERE t.oid IN (%s)" % oids.join(", ")
|
632
689
|
else
|
633
|
-
query
|
634
|
-
|
635
|
-
|
636
|
-
execute_and_clear(query, "SCHEMA", []) do |records|
|
637
|
-
initializer.run(records)
|
690
|
+
yield query + initializer.query_conditions_for_known_type_names
|
691
|
+
yield query + initializer.query_conditions_for_known_type_types
|
692
|
+
yield query + initializer.query_conditions_for_array_types
|
638
693
|
end
|
639
694
|
end
|
640
695
|
|
641
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
696
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
642
697
|
|
643
|
-
def execute_and_clear(sql, name, binds, prepare: false)
|
644
|
-
|
645
|
-
|
646
|
-
end
|
698
|
+
def execute_and_clear(sql, name, binds, prepare: false, async: false)
|
699
|
+
sql = transform_query(sql)
|
700
|
+
check_if_write_query(sql)
|
647
701
|
|
648
702
|
if !prepare || without_prepared_statement?(binds)
|
649
|
-
result = exec_no_cache(sql, name, binds)
|
703
|
+
result = exec_no_cache(sql, name, binds, async: async)
|
650
704
|
else
|
651
|
-
result = exec_cache(sql, name, binds)
|
705
|
+
result = exec_cache(sql, name, binds, async: async)
|
652
706
|
end
|
653
707
|
begin
|
654
708
|
ret = yield result
|
@@ -658,23 +712,23 @@ module ActiveRecord
|
|
658
712
|
ret
|
659
713
|
end
|
660
714
|
|
661
|
-
def exec_no_cache(sql, name, binds)
|
715
|
+
def exec_no_cache(sql, name, binds, async: false)
|
662
716
|
materialize_transactions
|
663
717
|
mark_transaction_written_if_write(sql)
|
664
718
|
|
665
|
-
# make sure we carry over any changes to ActiveRecord
|
719
|
+
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
666
720
|
# made since we established the connection
|
667
721
|
update_typemap_for_default_timezone
|
668
722
|
|
669
723
|
type_casted_binds = type_casted_binds(binds)
|
670
|
-
log(sql, name, binds, type_casted_binds) do
|
724
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
671
725
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
672
726
|
@connection.exec_params(sql, type_casted_binds)
|
673
727
|
end
|
674
728
|
end
|
675
729
|
end
|
676
730
|
|
677
|
-
def exec_cache(sql, name, binds)
|
731
|
+
def exec_cache(sql, name, binds, async: false)
|
678
732
|
materialize_transactions
|
679
733
|
mark_transaction_written_if_write(sql)
|
680
734
|
update_typemap_for_default_timezone
|
@@ -682,7 +736,7 @@ module ActiveRecord
|
|
682
736
|
stmt_key = prepare_statement(sql, binds)
|
683
737
|
type_casted_binds = type_casted_binds(binds)
|
684
738
|
|
685
|
-
log(sql, name, binds, type_casted_binds, stmt_key) do
|
739
|
+
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
686
740
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
687
741
|
@connection.exec_prepared(stmt_key, type_casted_binds)
|
688
742
|
end
|
@@ -776,7 +830,7 @@ module ActiveRecord
|
|
776
830
|
# If using Active Record's time zone support configure the connection to return
|
777
831
|
# TIMESTAMP WITH ZONE types in UTC.
|
778
832
|
unless variables["timezone"]
|
779
|
-
if ActiveRecord
|
833
|
+
if ActiveRecord.default_timezone == :utc
|
780
834
|
variables["timezone"] = "UTC"
|
781
835
|
elsif @local_tz
|
782
836
|
variables["timezone"] = @local_tz
|
@@ -875,14 +929,19 @@ module ActiveRecord
|
|
875
929
|
end
|
876
930
|
|
877
931
|
def update_typemap_for_default_timezone
|
878
|
-
if @default_timezone != ActiveRecord
|
879
|
-
decoder_class = ActiveRecord
|
932
|
+
if @default_timezone != ActiveRecord.default_timezone && @timestamp_decoder
|
933
|
+
decoder_class = ActiveRecord.default_timezone == :utc ?
|
880
934
|
PG::TextDecoder::TimestampUtc :
|
881
935
|
PG::TextDecoder::TimestampWithoutTimeZone
|
882
936
|
|
883
937
|
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
884
938
|
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
885
|
-
|
939
|
+
|
940
|
+
@default_timezone = ActiveRecord.default_timezone
|
941
|
+
|
942
|
+
# if default timezone has changed, we need to reconfigure the connection
|
943
|
+
# (specifically, the session time zone)
|
944
|
+
configure_connection
|
886
945
|
end
|
887
946
|
end
|
888
947
|
|
@@ -910,9 +969,7 @@ module ActiveRecord
|
|
910
969
|
WHERE t.typname IN (%s)
|
911
970
|
SQL
|
912
971
|
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
913
|
-
result
|
914
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
915
|
-
.compact
|
972
|
+
result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
916
973
|
end
|
917
974
|
|
918
975
|
map = PG::TypeMapByOid.new
|
@@ -86,6 +86,7 @@ module ActiveRecord
|
|
86
86
|
|
87
87
|
# A cached lookup for table existence.
|
88
88
|
def data_source_exists?(name)
|
89
|
+
return if ignored_table?(name)
|
89
90
|
prepare_data_sources if @data_sources.empty?
|
90
91
|
return @data_sources[name] if @data_sources.key? name
|
91
92
|
|
@@ -108,6 +109,10 @@ module ActiveRecord
|
|
108
109
|
|
109
110
|
# Get the columns for a table
|
110
111
|
def columns(table_name)
|
112
|
+
if ignored_table?(table_name)
|
113
|
+
raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist"
|
114
|
+
end
|
115
|
+
|
111
116
|
@columns.fetch(table_name) do
|
112
117
|
@columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
|
113
118
|
end
|
@@ -128,7 +133,11 @@ module ActiveRecord
|
|
128
133
|
|
129
134
|
def indexes(table_name)
|
130
135
|
@indexes.fetch(table_name) do
|
131
|
-
|
136
|
+
if data_source_exists?(table_name)
|
137
|
+
@indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
|
138
|
+
else
|
139
|
+
[]
|
140
|
+
end
|
132
141
|
end
|
133
142
|
end
|
134
143
|
|
@@ -162,7 +171,7 @@ module ActiveRecord
|
|
162
171
|
|
163
172
|
def dump_to(filename)
|
164
173
|
clear!
|
165
|
-
|
174
|
+
tables_to_cache.each { |table| add(table) }
|
166
175
|
open(filename) { |f|
|
167
176
|
if filename.include?(".dump")
|
168
177
|
f.write(Marshal.dump(self))
|
@@ -186,6 +195,18 @@ module ActiveRecord
|
|
186
195
|
end
|
187
196
|
|
188
197
|
private
|
198
|
+
def tables_to_cache
|
199
|
+
connection.data_sources.reject do |table|
|
200
|
+
ignored_table?(table)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def ignored_table?(table_name)
|
205
|
+
ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
|
206
|
+
ignored === table_name
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
189
210
|
def reset_version!
|
190
211
|
@version = connection.migration_context.current_version
|
191
212
|
end
|
@@ -198,45 +219,23 @@ module ActiveRecord
|
|
198
219
|
@indexes = deep_deduplicate(@indexes)
|
199
220
|
end
|
200
221
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
# In Ruby 2.6, the receiver of the String#-@ method is modified under certain
|
212
|
-
# circumstances, and this was later identified as a bug
|
213
|
-
# (https://bugs.ruby-lang.org/issues/15926) and only fixed in Ruby 2.7.
|
214
|
-
value = value.dup
|
215
|
-
end
|
216
|
-
-value
|
217
|
-
when Deduplicable
|
218
|
-
-value
|
219
|
-
else
|
220
|
-
value
|
221
|
-
end
|
222
|
-
end
|
223
|
-
else
|
224
|
-
def deep_deduplicate(value)
|
225
|
-
case value
|
226
|
-
when Hash
|
227
|
-
value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
|
228
|
-
when Array
|
229
|
-
value.map { |i| deep_deduplicate(i) }
|
230
|
-
when String, Deduplicable
|
231
|
-
-value
|
232
|
-
else
|
233
|
-
value
|
234
|
-
end
|
222
|
+
def deep_deduplicate(value)
|
223
|
+
case value
|
224
|
+
when Hash
|
225
|
+
value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
|
226
|
+
when Array
|
227
|
+
value.map { |i| deep_deduplicate(i) }
|
228
|
+
when String, Deduplicable
|
229
|
+
-value
|
230
|
+
else
|
231
|
+
value
|
235
232
|
end
|
236
233
|
end
|
237
234
|
|
238
235
|
def prepare_data_sources
|
239
|
-
|
236
|
+
tables_to_cache.each do |source|
|
237
|
+
@data_sources[source] = true
|
238
|
+
end
|
240
239
|
end
|
241
240
|
|
242
241
|
def open(filename)
|