activerecord 5.2.4.4 → 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 +611 -590
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations.rb +19 -14
- 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.rb +24 -28
- 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/preloader.rb +40 -32
- 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/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- 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/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.rb +233 -0
- 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/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 +4 -4
- 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.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- 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.rb +310 -80
- 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.rb +4 -6
- 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/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/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.rb +8 -8
- data/lib/active_record/scoping/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +19 -15
- 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.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- 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.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +51 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -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.rb +68 -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/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.rb +20 -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/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- 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/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
@@ -22,8 +22,8 @@ module ActiveRecord
|
|
22
22
|
def create_database(name, options = {})
|
23
23
|
options = { encoding: "utf8" }.merge!(options.symbolize_keys)
|
24
24
|
|
25
|
-
option_string = options.
|
26
|
-
memo
|
25
|
+
option_string = options.each_with_object(+"") do |(key, value), memo|
|
26
|
+
memo << case key
|
27
27
|
when :owner
|
28
28
|
" OWNER = \"#{value}\""
|
29
29
|
when :template
|
@@ -68,7 +68,7 @@ module ActiveRecord
|
|
68
68
|
table = quoted_scope(table_name)
|
69
69
|
index = quoted_scope(index_name)
|
70
70
|
|
71
|
-
query_value(
|
71
|
+
query_value(<<~SQL, "SCHEMA").to_i > 0
|
72
72
|
SELECT COUNT(*)
|
73
73
|
FROM pg_class t
|
74
74
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
@@ -85,7 +85,7 @@ module ActiveRecord
|
|
85
85
|
def indexes(table_name) # :nodoc:
|
86
86
|
scope = quoted_scope(table_name)
|
87
87
|
|
88
|
-
result = query(
|
88
|
+
result = query(<<~SQL, "SCHEMA")
|
89
89
|
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
90
90
|
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
91
91
|
FROM pg_class t
|
@@ -115,7 +115,7 @@ module ActiveRecord
|
|
115
115
|
if indkey.include?(0)
|
116
116
|
columns = expressions
|
117
117
|
else
|
118
|
-
columns = Hash[query(
|
118
|
+
columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
|
119
119
|
SELECT a.attnum, a.attname
|
120
120
|
FROM pg_attribute a
|
121
121
|
WHERE a.attrelid = #{oid}
|
@@ -158,7 +158,7 @@ module ActiveRecord
|
|
158
158
|
def table_comment(table_name) # :nodoc:
|
159
159
|
scope = quoted_scope(table_name, type: "BASE TABLE")
|
160
160
|
if scope[:name]
|
161
|
-
query_value(
|
161
|
+
query_value(<<~SQL, "SCHEMA")
|
162
162
|
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
|
163
163
|
FROM pg_catalog.pg_class c
|
164
164
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
@@ -196,7 +196,7 @@ module ActiveRecord
|
|
196
196
|
|
197
197
|
# Returns an array of schema names.
|
198
198
|
def schema_names
|
199
|
-
query_values(
|
199
|
+
query_values(<<~SQL, "SCHEMA")
|
200
200
|
SELECT nspname
|
201
201
|
FROM pg_namespace
|
202
202
|
WHERE nspname !~ '^pg_.*'
|
@@ -287,7 +287,7 @@ module ActiveRecord
|
|
287
287
|
quoted_sequence = quote_table_name(sequence)
|
288
288
|
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
289
289
|
if max_pk.nil?
|
290
|
-
if
|
290
|
+
if database_version >= 100000
|
291
291
|
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
292
292
|
else
|
293
293
|
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
|
@@ -302,7 +302,7 @@ module ActiveRecord
|
|
302
302
|
def pk_and_sequence_for(table) #:nodoc:
|
303
303
|
# First try looking for a sequence with a dependency on the
|
304
304
|
# given table's primary key.
|
305
|
-
result = query(
|
305
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
306
306
|
SELECT attr.attname, nsp.nspname, seq.relname
|
307
307
|
FROM pg_class seq,
|
308
308
|
pg_attribute attr,
|
@@ -319,10 +319,10 @@ module ActiveRecord
|
|
319
319
|
AND cons.contype = 'p'
|
320
320
|
AND dep.classid = 'pg_class'::regclass
|
321
321
|
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
322
|
-
|
322
|
+
SQL
|
323
323
|
|
324
324
|
if result.nil? || result.empty?
|
325
|
-
result = query(
|
325
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
326
326
|
SELECT attr.attname, nsp.nspname,
|
327
327
|
CASE
|
328
328
|
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
@@ -339,7 +339,7 @@ module ActiveRecord
|
|
339
339
|
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
340
340
|
AND cons.contype = 'p'
|
341
341
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
342
|
-
|
342
|
+
SQL
|
343
343
|
end
|
344
344
|
|
345
345
|
pk = result.shift
|
@@ -353,7 +353,7 @@ module ActiveRecord
|
|
353
353
|
end
|
354
354
|
|
355
355
|
def primary_keys(table_name) # :nodoc:
|
356
|
-
query_values(
|
356
|
+
query_values(<<~SQL, "SCHEMA")
|
357
357
|
SELECT a.attname
|
358
358
|
FROM (
|
359
359
|
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
|
@@ -368,31 +368,6 @@ module ActiveRecord
|
|
368
368
|
SQL
|
369
369
|
end
|
370
370
|
|
371
|
-
def bulk_change_table(table_name, operations)
|
372
|
-
sql_fragments = []
|
373
|
-
non_combinable_operations = []
|
374
|
-
|
375
|
-
operations.each do |command, args|
|
376
|
-
table, arguments = args.shift, args
|
377
|
-
method = :"#{command}_for_alter"
|
378
|
-
|
379
|
-
if respond_to?(method, true)
|
380
|
-
sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
|
381
|
-
sql_fragments << sqls
|
382
|
-
non_combinable_operations.concat(procs)
|
383
|
-
else
|
384
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
385
|
-
non_combinable_operations.each(&:call)
|
386
|
-
sql_fragments = []
|
387
|
-
non_combinable_operations = []
|
388
|
-
send(command, table, *arguments)
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
|
393
|
-
non_combinable_operations.each(&:call)
|
394
|
-
end
|
395
|
-
|
396
371
|
# Renames a table.
|
397
372
|
# Also renames a table's primary key sequence if the sequence name exists and
|
398
373
|
# matches the Active Record default.
|
@@ -443,14 +418,16 @@ module ActiveRecord
|
|
443
418
|
end
|
444
419
|
|
445
420
|
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
446
|
-
def change_column_comment(table_name, column_name,
|
421
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
447
422
|
clear_cache!
|
423
|
+
comment = extract_new_comment_value(comment_or_changes)
|
448
424
|
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
|
449
425
|
end
|
450
426
|
|
451
427
|
# Adds comment for given table or drops it if +comment+ is a +nil+
|
452
|
-
def change_table_comment(table_name,
|
428
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
453
429
|
clear_cache!
|
430
|
+
comment = extract_new_comment_value(comment_or_changes)
|
454
431
|
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
|
455
432
|
end
|
456
433
|
|
@@ -502,7 +479,7 @@ module ActiveRecord
|
|
502
479
|
|
503
480
|
def foreign_keys(table_name)
|
504
481
|
scope = quoted_scope(table_name)
|
505
|
-
fk_info = exec_query(
|
482
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
506
483
|
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
|
507
484
|
FROM pg_constraint c
|
508
485
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
@@ -548,21 +525,21 @@ module ActiveRecord
|
|
548
525
|
# The hard limit is 1GB, because of a 32-bit size field, and TOAST.
|
549
526
|
case limit
|
550
527
|
when nil, 0..0x3fffffff; super(type)
|
551
|
-
else raise
|
528
|
+
else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
|
552
529
|
end
|
553
530
|
when "text"
|
554
531
|
# PostgreSQL doesn't support limits on text columns.
|
555
532
|
# The hard limit is 1GB, according to section 8.3 in the manual.
|
556
533
|
case limit
|
557
534
|
when nil, 0..0x3fffffff; super(type)
|
558
|
-
else raise
|
535
|
+
else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
|
559
536
|
end
|
560
537
|
when "integer"
|
561
538
|
case limit
|
562
539
|
when 1, 2; "smallint"
|
563
540
|
when nil, 3, 4; "integer"
|
564
541
|
when 5..8; "bigint"
|
565
|
-
else raise
|
542
|
+
else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
|
566
543
|
end
|
567
544
|
else
|
568
545
|
super
|
@@ -623,10 +600,10 @@ module ActiveRecord
|
|
623
600
|
# validate_foreign_key :accounts, name: :special_fk_name
|
624
601
|
#
|
625
602
|
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
626
|
-
def validate_foreign_key(from_table,
|
603
|
+
def validate_foreign_key(from_table, to_table = nil, **options)
|
627
604
|
return unless supports_validate_constraints?
|
628
605
|
|
629
|
-
fk_name_to_validate = foreign_key_for!(from_table,
|
606
|
+
fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
|
630
607
|
|
631
608
|
validate_constraint from_table, fk_name_to_validate
|
632
609
|
end
|
@@ -637,7 +614,7 @@ module ActiveRecord
|
|
637
614
|
end
|
638
615
|
|
639
616
|
def create_table_definition(*args)
|
640
|
-
PostgreSQL::TableDefinition.new(*args)
|
617
|
+
PostgreSQL::TableDefinition.new(self, *args)
|
641
618
|
end
|
642
619
|
|
643
620
|
def create_alter_table(name)
|
@@ -650,16 +627,19 @@ module ActiveRecord
|
|
650
627
|
default_value = extract_value_from_default(default)
|
651
628
|
default_function = extract_default_function(default_value, default)
|
652
629
|
|
653
|
-
|
630
|
+
if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
|
631
|
+
serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
|
632
|
+
end
|
633
|
+
|
634
|
+
PostgreSQL::Column.new(
|
654
635
|
column_name,
|
655
636
|
default_value,
|
656
637
|
type_metadata,
|
657
638
|
!notnull,
|
658
|
-
table_name,
|
659
639
|
default_function,
|
660
|
-
collation,
|
640
|
+
collation: collation,
|
661
641
|
comment: comment.presence,
|
662
|
-
|
642
|
+
serial: serial
|
663
643
|
)
|
664
644
|
end
|
665
645
|
|
@@ -672,7 +652,23 @@ module ActiveRecord
|
|
672
652
|
precision: cast_type.precision,
|
673
653
|
scale: cast_type.scale,
|
674
654
|
)
|
675
|
-
|
655
|
+
PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
|
656
|
+
end
|
657
|
+
|
658
|
+
def sequence_name_from_parts(table_name, column_name, suffix)
|
659
|
+
over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
|
660
|
+
|
661
|
+
if over_length > 0
|
662
|
+
column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
|
663
|
+
over_length -= column_name.length - column_name_length
|
664
|
+
column_name = column_name[0, column_name_length - [over_length, 0].min]
|
665
|
+
end
|
666
|
+
|
667
|
+
if over_length > 0
|
668
|
+
table_name = table_name[0, table_name.length - over_length]
|
669
|
+
end
|
670
|
+
|
671
|
+
"#{table_name}_#{column_name}_#{suffix}"
|
676
672
|
end
|
677
673
|
|
678
674
|
def extract_foreign_key_action(specifier)
|
@@ -716,6 +712,12 @@ module ActiveRecord
|
|
716
712
|
end
|
717
713
|
|
718
714
|
def add_timestamps_for_alter(table_name, options = {})
|
715
|
+
options[:null] = false if options[:null].nil?
|
716
|
+
|
717
|
+
if !options.key?(:precision) && supports_datetime_with_precision?
|
718
|
+
options[:precision] = 6
|
719
|
+
end
|
720
|
+
|
719
721
|
[add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
|
720
722
|
end
|
721
723
|
|
@@ -739,7 +741,7 @@ module ActiveRecord
|
|
739
741
|
scope = quoted_scope(name, type: type)
|
740
742
|
scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
|
741
743
|
|
742
|
-
sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
|
744
|
+
sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
|
743
745
|
sql << " WHERE n.nspname = #{scope[:schema]}"
|
744
746
|
sql << " AND c.relname = #{scope[:name]}" if scope[:name]
|
745
747
|
sql << " AND c.relkind IN (#{scope[:type]})"
|
@@ -1,39 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
+
# :stopdoc:
|
4
5
|
module ConnectionAdapters
|
5
|
-
|
6
|
-
|
6
|
+
module PostgreSQL
|
7
|
+
class TypeMetadata < DelegateClass(SqlTypeMetadata)
|
8
|
+
undef to_yaml if method_defined?(:to_yaml)
|
7
9
|
|
8
|
-
|
10
|
+
attr_reader :oid, :fmod
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@array = /\[\]$/.match?(type_metadata.sql_type)
|
16
|
-
end
|
17
|
-
|
18
|
-
def sql_type
|
19
|
-
super.gsub(/\[\]$/, "".freeze)
|
20
|
-
end
|
21
|
-
|
22
|
-
def ==(other)
|
23
|
-
other.is_a?(PostgreSQLTypeMetadata) &&
|
24
|
-
attributes_for_hash == other.attributes_for_hash
|
25
|
-
end
|
26
|
-
alias eql? ==
|
27
|
-
|
28
|
-
def hash
|
29
|
-
attributes_for_hash.hash
|
30
|
-
end
|
12
|
+
def initialize(type_metadata, oid: nil, fmod: nil)
|
13
|
+
super(type_metadata)
|
14
|
+
@oid = oid
|
15
|
+
@fmod = fmod
|
16
|
+
end
|
31
17
|
|
32
|
-
|
18
|
+
def ==(other)
|
19
|
+
other.is_a?(TypeMetadata) &&
|
20
|
+
__getobj__ == other.__getobj__ &&
|
21
|
+
oid == other.oid &&
|
22
|
+
fmod == other.fmod
|
23
|
+
end
|
24
|
+
alias eql? ==
|
33
25
|
|
34
|
-
def
|
35
|
-
|
26
|
+
def hash
|
27
|
+
TypeMetadata.hash ^
|
28
|
+
__getobj__.hash ^
|
29
|
+
oid.hash ^
|
30
|
+
fmod.hash
|
36
31
|
end
|
32
|
+
end
|
37
33
|
end
|
34
|
+
PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
|
38
35
|
end
|
39
36
|
end
|
@@ -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)
|