activerecord 5.2.6 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +609 -622
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +52 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +24 -28
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/preloader.rb +40 -32
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +19 -14
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +5 -9
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
- data/lib/active_record/connection_handling.rb +149 -27
- data/lib/active_record/core.rb +100 -60
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +32 -30
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +53 -47
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +13 -26
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +189 -63
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/relation.rb +310 -80
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +9 -2
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +108 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -5,7 +5,7 @@ gem "pg", ">= 0.18", "< 2.0"
|
|
5
5
|
require "pg"
|
6
6
|
|
7
7
|
# Use async_exec instead of exec_params on pg versions before 1.1
|
8
|
-
class ::PG::Connection
|
8
|
+
class ::PG::Connection # :nodoc:
|
9
9
|
unless self.public_method_defined?(:async_exec_params)
|
10
10
|
remove_method :exec_params
|
11
11
|
alias exec_params async_exec
|
@@ -43,9 +43,14 @@ module ActiveRecord
|
|
43
43
|
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
44
44
|
conn_params.slice!(*valid_conn_param_keys)
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
conn = PG.connect(conn_params)
|
47
|
+
ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
|
48
|
+
rescue ::PG::Error => error
|
49
|
+
if error.message.include?(conn_params[:dbname])
|
50
|
+
raise ActiveRecord::NoDatabaseError
|
51
|
+
else
|
52
|
+
raise
|
53
|
+
end
|
49
54
|
end
|
50
55
|
end
|
51
56
|
|
@@ -78,7 +83,20 @@ module ActiveRecord
|
|
78
83
|
# In addition, default connection parameters of libpq can be set per environment variables.
|
79
84
|
# See https://www.postgresql.org/docs/current/static/libpq-envars.html .
|
80
85
|
class PostgreSQLAdapter < AbstractAdapter
|
81
|
-
ADAPTER_NAME = "PostgreSQL"
|
86
|
+
ADAPTER_NAME = "PostgreSQL"
|
87
|
+
|
88
|
+
##
|
89
|
+
# :singleton-method:
|
90
|
+
# PostgreSQL allows the creation of "unlogged" tables, which do not record
|
91
|
+
# data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
|
92
|
+
# but significantly increases the risk of data loss if the database
|
93
|
+
# crashes. As a result, this should not be used in production
|
94
|
+
# environments. If you would like all created tables to be unlogged in
|
95
|
+
# the test environment you can add the following line to your test.rb
|
96
|
+
# file:
|
97
|
+
#
|
98
|
+
# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
|
99
|
+
class_attribute :create_unlogged_tables, default: false
|
82
100
|
|
83
101
|
NATIVE_DATABASE_TYPES = {
|
84
102
|
primary_key: "bigserial primary key",
|
@@ -167,7 +185,7 @@ module ActiveRecord
|
|
167
185
|
end
|
168
186
|
|
169
187
|
def supports_json?
|
170
|
-
|
188
|
+
true
|
171
189
|
end
|
172
190
|
|
173
191
|
def supports_comments?
|
@@ -178,6 +196,17 @@ module ActiveRecord
|
|
178
196
|
true
|
179
197
|
end
|
180
198
|
|
199
|
+
def supports_insert_returning?
|
200
|
+
true
|
201
|
+
end
|
202
|
+
|
203
|
+
def supports_insert_on_conflict?
|
204
|
+
database_version >= 90500
|
205
|
+
end
|
206
|
+
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
207
|
+
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
208
|
+
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
209
|
+
|
181
210
|
def index_algorithms
|
182
211
|
{ concurrently: "CONCURRENTLY" }
|
183
212
|
end
|
@@ -220,15 +249,8 @@ module ActiveRecord
|
|
220
249
|
@local_tz = nil
|
221
250
|
@max_identifier_length = nil
|
222
251
|
|
223
|
-
|
252
|
+
configure_connection
|
224
253
|
add_pg_encoders
|
225
|
-
@statements = StatementPool.new @connection,
|
226
|
-
self.class.type_cast_config_to_integer(config[:statement_limit])
|
227
|
-
|
228
|
-
if postgresql_version < 90100
|
229
|
-
raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
|
230
|
-
end
|
231
|
-
|
232
254
|
add_pg_decoders
|
233
255
|
|
234
256
|
@type_map = Type::HashLookupTypeMap.new
|
@@ -237,15 +259,10 @@ module ActiveRecord
|
|
237
259
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
238
260
|
end
|
239
261
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
def truncate(table_name, name = nil)
|
248
|
-
exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
|
262
|
+
def self.database_exists?(config)
|
263
|
+
!!ActiveRecord::Base.postgresql_connection(config)
|
264
|
+
rescue ActiveRecord::NoDatabaseError
|
265
|
+
false
|
249
266
|
end
|
250
267
|
|
251
268
|
# Is this connection alive and ready for queries?
|
@@ -264,6 +281,8 @@ module ActiveRecord
|
|
264
281
|
super
|
265
282
|
@connection.reset
|
266
283
|
configure_connection
|
284
|
+
rescue PG::ConnectionBad
|
285
|
+
connect
|
267
286
|
end
|
268
287
|
end
|
269
288
|
|
@@ -289,6 +308,7 @@ module ActiveRecord
|
|
289
308
|
end
|
290
309
|
|
291
310
|
def discard! # :nodoc:
|
311
|
+
super
|
292
312
|
@connection.socket_io.reopen(IO::NULL) rescue nil
|
293
313
|
@connection = nil
|
294
314
|
end
|
@@ -318,20 +338,31 @@ module ActiveRecord
|
|
318
338
|
end
|
319
339
|
|
320
340
|
def supports_ranges?
|
321
|
-
|
322
|
-
postgresql_version >= 90200
|
341
|
+
true
|
323
342
|
end
|
343
|
+
deprecate :supports_ranges?
|
324
344
|
|
325
345
|
def supports_materialized_views?
|
326
|
-
|
346
|
+
true
|
327
347
|
end
|
328
348
|
|
329
349
|
def supports_foreign_tables?
|
330
|
-
|
350
|
+
true
|
331
351
|
end
|
332
352
|
|
333
353
|
def supports_pgcrypto_uuid?
|
334
|
-
|
354
|
+
database_version >= 90400
|
355
|
+
end
|
356
|
+
|
357
|
+
def supports_optimizer_hints?
|
358
|
+
unless defined?(@has_pg_hint_plan)
|
359
|
+
@has_pg_hint_plan = extension_available?("pg_hint_plan")
|
360
|
+
end
|
361
|
+
@has_pg_hint_plan
|
362
|
+
end
|
363
|
+
|
364
|
+
def supports_lazy_transactions?
|
365
|
+
true
|
335
366
|
end
|
336
367
|
|
337
368
|
def get_advisory_lock(lock_id) # :nodoc:
|
@@ -360,9 +391,12 @@ module ActiveRecord
|
|
360
391
|
}
|
361
392
|
end
|
362
393
|
|
394
|
+
def extension_available?(name)
|
395
|
+
query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
396
|
+
end
|
397
|
+
|
363
398
|
def extension_enabled?(name)
|
364
|
-
|
365
|
-
res.cast_values.first
|
399
|
+
query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
366
400
|
end
|
367
401
|
|
368
402
|
def extensions
|
@@ -373,8 +407,6 @@ module ActiveRecord
|
|
373
407
|
def max_identifier_length
|
374
408
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
375
409
|
end
|
376
|
-
alias table_alias_length max_identifier_length
|
377
|
-
alias index_name_length max_identifier_length
|
378
410
|
|
379
411
|
# Set the authorized user for this session
|
380
412
|
def session_auth=(user)
|
@@ -397,15 +429,37 @@ module ActiveRecord
|
|
397
429
|
}
|
398
430
|
|
399
431
|
# Returns the version of the connected PostgreSQL server.
|
400
|
-
def
|
432
|
+
def get_database_version # :nodoc:
|
401
433
|
@connection.server_version
|
402
434
|
end
|
435
|
+
alias :postgresql_version :database_version
|
403
436
|
|
404
437
|
def default_index_type?(index) # :nodoc:
|
405
438
|
index.using == :btree || super
|
406
439
|
end
|
407
440
|
|
441
|
+
def build_insert_sql(insert) # :nodoc:
|
442
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
443
|
+
|
444
|
+
if insert.skip_duplicates?
|
445
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
446
|
+
elsif insert.update_duplicates?
|
447
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
448
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
449
|
+
end
|
450
|
+
|
451
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
452
|
+
sql
|
453
|
+
end
|
454
|
+
|
455
|
+
def check_version # :nodoc:
|
456
|
+
if database_version < 90300
|
457
|
+
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
408
461
|
private
|
462
|
+
|
409
463
|
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
410
464
|
VALUE_LIMIT_VIOLATION = "22001"
|
411
465
|
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
@@ -417,34 +471,34 @@ module ActiveRecord
|
|
417
471
|
LOCK_NOT_AVAILABLE = "55P03"
|
418
472
|
QUERY_CANCELED = "57014"
|
419
473
|
|
420
|
-
def translate_exception(exception, message)
|
474
|
+
def translate_exception(exception, message:, sql:, binds:)
|
421
475
|
return exception unless exception.respond_to?(:result)
|
422
476
|
|
423
477
|
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
424
478
|
when UNIQUE_VIOLATION
|
425
|
-
RecordNotUnique.new(message)
|
479
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
426
480
|
when FOREIGN_KEY_VIOLATION
|
427
|
-
InvalidForeignKey.new(message)
|
481
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
428
482
|
when VALUE_LIMIT_VIOLATION
|
429
|
-
ValueTooLong.new(message)
|
483
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
430
484
|
when NUMERIC_VALUE_OUT_OF_RANGE
|
431
|
-
RangeError.new(message)
|
485
|
+
RangeError.new(message, sql: sql, binds: binds)
|
432
486
|
when NOT_NULL_VIOLATION
|
433
|
-
NotNullViolation.new(message)
|
487
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
434
488
|
when SERIALIZATION_FAILURE
|
435
|
-
SerializationFailure.new(message)
|
489
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
436
490
|
when DEADLOCK_DETECTED
|
437
|
-
Deadlocked.new(message)
|
491
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
438
492
|
when LOCK_NOT_AVAILABLE
|
439
|
-
LockWaitTimeout.new(message)
|
493
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
440
494
|
when QUERY_CANCELED
|
441
|
-
QueryCanceled.new(message)
|
495
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
442
496
|
else
|
443
497
|
super
|
444
498
|
end
|
445
499
|
end
|
446
500
|
|
447
|
-
def get_oid_type(oid, fmod, column_name, sql_type = ""
|
501
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
448
502
|
if !type_map.key?(oid)
|
449
503
|
load_additional_types([oid])
|
450
504
|
end
|
@@ -533,13 +587,13 @@ module ActiveRecord
|
|
533
587
|
# Quoted types
|
534
588
|
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
535
589
|
# The default 'now'::date is CURRENT_DATE
|
536
|
-
if $1 == "now"
|
590
|
+
if $1 == "now" && $2 == "date"
|
537
591
|
nil
|
538
592
|
else
|
539
|
-
$1.gsub("''"
|
593
|
+
$1.gsub("''", "'")
|
540
594
|
end
|
541
595
|
# Boolean types
|
542
|
-
when "true"
|
596
|
+
when "true", "false"
|
543
597
|
default
|
544
598
|
# Numeric types
|
545
599
|
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
|
@@ -565,18 +619,11 @@ module ActiveRecord
|
|
565
619
|
def load_additional_types(oids = nil)
|
566
620
|
initializer = OID::TypeMapInitializer.new(type_map)
|
567
621
|
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
SQL
|
574
|
-
else
|
575
|
-
query = <<-SQL
|
576
|
-
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
|
577
|
-
FROM pg_type as t
|
578
|
-
SQL
|
579
|
-
end
|
622
|
+
query = <<~SQL
|
623
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
624
|
+
FROM pg_type as t
|
625
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
626
|
+
SQL
|
580
627
|
|
581
628
|
if oids
|
582
629
|
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
@@ -592,6 +639,10 @@ module ActiveRecord
|
|
592
639
|
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
|
593
640
|
|
594
641
|
def execute_and_clear(sql, name, binds, prepare: false)
|
642
|
+
if preventing_writes? && write_query?(sql)
|
643
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
644
|
+
end
|
645
|
+
|
595
646
|
if without_prepared_statement?(binds)
|
596
647
|
result = exec_no_cache(sql, name, [])
|
597
648
|
elsif !prepare
|
@@ -605,6 +656,12 @@ module ActiveRecord
|
|
605
656
|
end
|
606
657
|
|
607
658
|
def exec_no_cache(sql, name, binds)
|
659
|
+
materialize_transactions
|
660
|
+
|
661
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
662
|
+
# made since we established the connection
|
663
|
+
update_typemap_for_default_timezone
|
664
|
+
|
608
665
|
type_casted_binds = type_casted_binds(binds)
|
609
666
|
log(sql, name, binds, type_casted_binds) do
|
610
667
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -614,7 +671,10 @@ module ActiveRecord
|
|
614
671
|
end
|
615
672
|
|
616
673
|
def exec_cache(sql, name, binds)
|
617
|
-
|
674
|
+
materialize_transactions
|
675
|
+
update_typemap_for_default_timezone
|
676
|
+
|
677
|
+
stmt_key = prepare_statement(sql, binds)
|
618
678
|
type_casted_binds = type_casted_binds(binds)
|
619
679
|
|
620
680
|
log(sql, name, binds, type_casted_binds, stmt_key) do
|
@@ -647,7 +707,7 @@ module ActiveRecord
|
|
647
707
|
#
|
648
708
|
# Check here for more details:
|
649
709
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
650
|
-
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
710
|
+
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
651
711
|
def is_cached_plan_failure?(e)
|
652
712
|
pgerror = e.cause
|
653
713
|
code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
|
@@ -668,7 +728,7 @@ module ActiveRecord
|
|
668
728
|
|
669
729
|
# Prepare the statement if it hasn't been prepared, return
|
670
730
|
# the statement key.
|
671
|
-
def prepare_statement(sql)
|
731
|
+
def prepare_statement(sql, binds)
|
672
732
|
@lock.synchronize do
|
673
733
|
sql_key = sql_key(sql)
|
674
734
|
unless @statements.key? sql_key
|
@@ -676,7 +736,7 @@ module ActiveRecord
|
|
676
736
|
begin
|
677
737
|
@connection.prepare nextkey, sql
|
678
738
|
rescue => e
|
679
|
-
raise translate_exception_class(e, sql)
|
739
|
+
raise translate_exception_class(e, sql, binds)
|
680
740
|
end
|
681
741
|
# Clear the queue
|
682
742
|
@connection.get_last_result
|
@@ -691,12 +751,8 @@ module ActiveRecord
|
|
691
751
|
def connect
|
692
752
|
@connection = PG.connect(@connection_parameters)
|
693
753
|
configure_connection
|
694
|
-
|
695
|
-
|
696
|
-
raise ActiveRecord::NoDatabaseError
|
697
|
-
else
|
698
|
-
raise
|
699
|
-
end
|
754
|
+
add_pg_encoders
|
755
|
+
add_pg_decoders
|
700
756
|
end
|
701
757
|
|
702
758
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -754,7 +810,7 @@ module ActiveRecord
|
|
754
810
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
755
811
|
# - ::regclass is a function that gives the id for a table name
|
756
812
|
def column_definitions(table_name)
|
757
|
-
query(
|
813
|
+
query(<<~SQL, "SCHEMA")
|
758
814
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
759
815
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
760
816
|
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
@@ -765,7 +821,7 @@ module ActiveRecord
|
|
765
821
|
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
766
822
|
AND a.attnum > 0 AND NOT a.attisdropped
|
767
823
|
ORDER BY a.attnum
|
768
|
-
|
824
|
+
SQL
|
769
825
|
end
|
770
826
|
|
771
827
|
def extract_table_ref_from_insert_sql(sql)
|
@@ -777,10 +833,14 @@ module ActiveRecord
|
|
777
833
|
Arel::Visitors::PostgreSQL.new(self)
|
778
834
|
end
|
779
835
|
|
836
|
+
def build_statement_pool
|
837
|
+
StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
838
|
+
end
|
839
|
+
|
780
840
|
def can_perform_case_insensitive_comparison_for?(column)
|
781
841
|
@case_insensitive_cache ||= {}
|
782
842
|
@case_insensitive_cache[column.sql_type] ||= begin
|
783
|
-
sql =
|
843
|
+
sql = <<~SQL
|
784
844
|
SELECT exists(
|
785
845
|
SELECT * FROM pg_proc
|
786
846
|
WHERE proname = 'lower'
|
@@ -792,7 +852,7 @@ module ActiveRecord
|
|
792
852
|
WHERE proname = 'lower'
|
793
853
|
AND castsource = #{quote column.sql_type}::regtype
|
794
854
|
)
|
795
|
-
|
855
|
+
SQL
|
796
856
|
execute_and_clear(sql, "SCHEMA", []) do |result|
|
797
857
|
result.getvalue(0, 0)
|
798
858
|
end
|
@@ -807,7 +867,22 @@ module ActiveRecord
|
|
807
867
|
@connection.type_map_for_queries = map
|
808
868
|
end
|
809
869
|
|
870
|
+
def update_typemap_for_default_timezone
|
871
|
+
if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
|
872
|
+
decoder_class = ActiveRecord::Base.default_timezone == :utc ?
|
873
|
+
PG::TextDecoder::TimestampUtc :
|
874
|
+
PG::TextDecoder::TimestampWithoutTimeZone
|
875
|
+
|
876
|
+
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
877
|
+
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
878
|
+
@default_timezone = ActiveRecord::Base.default_timezone
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
810
882
|
def add_pg_decoders
|
883
|
+
@default_timezone = nil
|
884
|
+
@timestamp_decoder = nil
|
885
|
+
|
811
886
|
coders_by_name = {
|
812
887
|
"int2" => PG::TextDecoder::Integer,
|
813
888
|
"int4" => PG::TextDecoder::Integer,
|
@@ -817,8 +892,15 @@ module ActiveRecord
|
|
817
892
|
"float8" => PG::TextDecoder::Float,
|
818
893
|
"bool" => PG::TextDecoder::Boolean,
|
819
894
|
}
|
895
|
+
|
896
|
+
if defined?(PG::TextDecoder::TimestampUtc)
|
897
|
+
# Use native PG encoders available since pg-1.1
|
898
|
+
coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
|
899
|
+
coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
|
900
|
+
end
|
901
|
+
|
820
902
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
821
|
-
query =
|
903
|
+
query = <<~SQL % known_coder_types.join(", ")
|
822
904
|
SELECT t.oid, t.typname
|
823
905
|
FROM pg_type as t
|
824
906
|
WHERE t.typname IN (%s)
|
@@ -832,6 +914,10 @@ module ActiveRecord
|
|
832
914
|
map = PG::TypeMapByOid.new
|
833
915
|
coders.each { |coder| map.add_coder(coder) }
|
834
916
|
@connection.type_map_for_results = map
|
917
|
+
|
918
|
+
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
919
|
+
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
920
|
+
update_typemap_for_default_timezone
|
835
921
|
end
|
836
922
|
|
837
923
|
def construct_coder(row, coder_class)
|
@@ -13,6 +13,7 @@ module ActiveRecord
|
|
13
13
|
@columns_hash = {}
|
14
14
|
@primary_keys = {}
|
15
15
|
@data_sources = {}
|
16
|
+
@indexes = {}
|
16
17
|
end
|
17
18
|
|
18
19
|
def initialize_dup(other)
|
@@ -21,22 +22,27 @@ module ActiveRecord
|
|
21
22
|
@columns_hash = @columns_hash.dup
|
22
23
|
@primary_keys = @primary_keys.dup
|
23
24
|
@data_sources = @data_sources.dup
|
25
|
+
@indexes = @indexes.dup
|
24
26
|
end
|
25
27
|
|
26
28
|
def encode_with(coder)
|
27
|
-
coder["columns"]
|
28
|
-
coder["columns_hash"]
|
29
|
-
coder["primary_keys"]
|
30
|
-
coder["data_sources"]
|
31
|
-
coder["
|
29
|
+
coder["columns"] = @columns
|
30
|
+
coder["columns_hash"] = @columns_hash
|
31
|
+
coder["primary_keys"] = @primary_keys
|
32
|
+
coder["data_sources"] = @data_sources
|
33
|
+
coder["indexes"] = @indexes
|
34
|
+
coder["version"] = connection.migration_context.current_version
|
35
|
+
coder["database_version"] = database_version
|
32
36
|
end
|
33
37
|
|
34
38
|
def init_with(coder)
|
35
|
-
@columns
|
36
|
-
@columns_hash
|
37
|
-
@primary_keys
|
38
|
-
@data_sources
|
39
|
-
@
|
39
|
+
@columns = coder["columns"]
|
40
|
+
@columns_hash = coder["columns_hash"]
|
41
|
+
@primary_keys = coder["primary_keys"]
|
42
|
+
@data_sources = coder["data_sources"]
|
43
|
+
@indexes = coder["indexes"] || {}
|
44
|
+
@version = coder["version"]
|
45
|
+
@database_version = coder["database_version"]
|
40
46
|
end
|
41
47
|
|
42
48
|
def primary_keys(table_name)
|
@@ -57,6 +63,7 @@ module ActiveRecord
|
|
57
63
|
primary_keys(table_name)
|
58
64
|
columns(table_name)
|
59
65
|
columns_hash(table_name)
|
66
|
+
indexes(table_name)
|
60
67
|
end
|
61
68
|
end
|
62
69
|
|
@@ -77,17 +84,32 @@ module ActiveRecord
|
|
77
84
|
}]
|
78
85
|
end
|
79
86
|
|
87
|
+
# Checks whether the columns hash is already cached for a table.
|
88
|
+
def columns_hash?(table_name)
|
89
|
+
@columns_hash.key?(table_name)
|
90
|
+
end
|
91
|
+
|
92
|
+
def indexes(table_name)
|
93
|
+
@indexes[table_name] ||= connection.indexes(table_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
def database_version # :nodoc:
|
97
|
+
@database_version ||= connection.get_database_version
|
98
|
+
end
|
99
|
+
|
80
100
|
# Clears out internal caches
|
81
101
|
def clear!
|
82
102
|
@columns.clear
|
83
103
|
@columns_hash.clear
|
84
104
|
@primary_keys.clear
|
85
105
|
@data_sources.clear
|
106
|
+
@indexes.clear
|
86
107
|
@version = nil
|
108
|
+
@database_version = nil
|
87
109
|
end
|
88
110
|
|
89
111
|
def size
|
90
|
-
[@columns, @columns_hash, @primary_keys, @data_sources].
|
112
|
+
[@columns, @columns_hash, @primary_keys, @data_sources].sum(&:size)
|
91
113
|
end
|
92
114
|
|
93
115
|
# Clear out internal caches for the data source +name+.
|
@@ -96,20 +118,21 @@ module ActiveRecord
|
|
96
118
|
@columns_hash.delete name
|
97
119
|
@primary_keys.delete name
|
98
120
|
@data_sources.delete name
|
121
|
+
@indexes.delete name
|
99
122
|
end
|
100
123
|
|
101
124
|
def marshal_dump
|
102
125
|
# if we get current version during initialization, it happens stack over flow.
|
103
126
|
@version = connection.migration_context.current_version
|
104
|
-
[@version, @columns, @columns_hash, @primary_keys, @data_sources]
|
127
|
+
[@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
|
105
128
|
end
|
106
129
|
|
107
130
|
def marshal_load(array)
|
108
|
-
@version, @columns, @columns_hash, @primary_keys, @data_sources = array
|
131
|
+
@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
|
132
|
+
@indexes = @indexes || {}
|
109
133
|
end
|
110
134
|
|
111
135
|
private
|
112
|
-
|
113
136
|
def prepare_data_sources
|
114
137
|
connection.data_sources.each { |source| @data_sources[source] = true }
|
115
138
|
end
|
@@ -16,19 +16,22 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
def ==(other)
|
18
18
|
other.is_a?(SqlTypeMetadata) &&
|
19
|
-
|
19
|
+
sql_type == other.sql_type &&
|
20
|
+
type == other.type &&
|
21
|
+
limit == other.limit &&
|
22
|
+
precision == other.precision &&
|
23
|
+
scale == other.scale
|
20
24
|
end
|
21
25
|
alias eql? ==
|
22
26
|
|
23
27
|
def hash
|
24
|
-
|
28
|
+
SqlTypeMetadata.hash ^
|
29
|
+
sql_type.hash ^
|
30
|
+
type.hash ^
|
31
|
+
limit.hash ^
|
32
|
+
precision.hash >> 1 ^
|
33
|
+
scale.hash >> 2
|
25
34
|
end
|
26
|
-
|
27
|
-
protected
|
28
|
-
|
29
|
-
def attributes_for_hash
|
30
|
-
[self.class, sql_type, type, limit, precision, scale]
|
31
|
-
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|