activerecord 5.2.4.2 → 6.0.2.2
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 +715 -566
- 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/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +61 -20
- 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 +12 -23
- 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 +28 -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 +39 -31
- 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 +2 -2
- 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 +104 -16
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -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 +187 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -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 +22 -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 +164 -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 +120 -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 +129 -141
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +103 -59
- 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 +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.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -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 +33 -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 +199 -46
- data/lib/active_record/reflection.rb +32 -30
- data/lib/active_record/relation.rb +311 -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 +23 -27
- 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 +213 -64
- 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 +20 -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 +225 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -65
- 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 +58 -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 +109 -24
- 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,35 @@ 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_common_table_expressions?
|
365
|
+
true
|
366
|
+
end
|
367
|
+
|
368
|
+
def supports_lazy_transactions?
|
369
|
+
true
|
335
370
|
end
|
336
371
|
|
337
372
|
def get_advisory_lock(lock_id) # :nodoc:
|
@@ -360,9 +395,12 @@ module ActiveRecord
|
|
360
395
|
}
|
361
396
|
end
|
362
397
|
|
398
|
+
def extension_available?(name)
|
399
|
+
query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
400
|
+
end
|
401
|
+
|
363
402
|
def extension_enabled?(name)
|
364
|
-
|
365
|
-
res.cast_values.first
|
403
|
+
query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
|
366
404
|
end
|
367
405
|
|
368
406
|
def extensions
|
@@ -373,8 +411,6 @@ module ActiveRecord
|
|
373
411
|
def max_identifier_length
|
374
412
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
375
413
|
end
|
376
|
-
alias table_alias_length max_identifier_length
|
377
|
-
alias index_name_length max_identifier_length
|
378
414
|
|
379
415
|
# Set the authorized user for this session
|
380
416
|
def session_auth=(user)
|
@@ -397,15 +433,37 @@ module ActiveRecord
|
|
397
433
|
}
|
398
434
|
|
399
435
|
# Returns the version of the connected PostgreSQL server.
|
400
|
-
def
|
436
|
+
def get_database_version # :nodoc:
|
401
437
|
@connection.server_version
|
402
438
|
end
|
439
|
+
alias :postgresql_version :database_version
|
403
440
|
|
404
441
|
def default_index_type?(index) # :nodoc:
|
405
442
|
index.using == :btree || super
|
406
443
|
end
|
407
444
|
|
445
|
+
def build_insert_sql(insert) # :nodoc:
|
446
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
447
|
+
|
448
|
+
if insert.skip_duplicates?
|
449
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
|
450
|
+
elsif insert.update_duplicates?
|
451
|
+
sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
|
452
|
+
sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
|
453
|
+
end
|
454
|
+
|
455
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
456
|
+
sql
|
457
|
+
end
|
458
|
+
|
459
|
+
def check_version # :nodoc:
|
460
|
+
if database_version < 90300
|
461
|
+
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
408
465
|
private
|
466
|
+
|
409
467
|
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
410
468
|
VALUE_LIMIT_VIOLATION = "22001"
|
411
469
|
NUMERIC_VALUE_OUT_OF_RANGE = "22003"
|
@@ -417,34 +475,34 @@ module ActiveRecord
|
|
417
475
|
LOCK_NOT_AVAILABLE = "55P03"
|
418
476
|
QUERY_CANCELED = "57014"
|
419
477
|
|
420
|
-
def translate_exception(exception, message)
|
478
|
+
def translate_exception(exception, message:, sql:, binds:)
|
421
479
|
return exception unless exception.respond_to?(:result)
|
422
480
|
|
423
481
|
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
424
482
|
when UNIQUE_VIOLATION
|
425
|
-
RecordNotUnique.new(message)
|
483
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
426
484
|
when FOREIGN_KEY_VIOLATION
|
427
|
-
InvalidForeignKey.new(message)
|
485
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
428
486
|
when VALUE_LIMIT_VIOLATION
|
429
|
-
ValueTooLong.new(message)
|
487
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
430
488
|
when NUMERIC_VALUE_OUT_OF_RANGE
|
431
|
-
RangeError.new(message)
|
489
|
+
RangeError.new(message, sql: sql, binds: binds)
|
432
490
|
when NOT_NULL_VIOLATION
|
433
|
-
NotNullViolation.new(message)
|
491
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
434
492
|
when SERIALIZATION_FAILURE
|
435
|
-
SerializationFailure.new(message)
|
493
|
+
SerializationFailure.new(message, sql: sql, binds: binds)
|
436
494
|
when DEADLOCK_DETECTED
|
437
|
-
Deadlocked.new(message)
|
495
|
+
Deadlocked.new(message, sql: sql, binds: binds)
|
438
496
|
when LOCK_NOT_AVAILABLE
|
439
|
-
LockWaitTimeout.new(message)
|
497
|
+
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
440
498
|
when QUERY_CANCELED
|
441
|
-
QueryCanceled.new(message)
|
499
|
+
QueryCanceled.new(message, sql: sql, binds: binds)
|
442
500
|
else
|
443
501
|
super
|
444
502
|
end
|
445
503
|
end
|
446
504
|
|
447
|
-
def get_oid_type(oid, fmod, column_name, sql_type = ""
|
505
|
+
def get_oid_type(oid, fmod, column_name, sql_type = "")
|
448
506
|
if !type_map.key?(oid)
|
449
507
|
load_additional_types([oid])
|
450
508
|
end
|
@@ -533,13 +591,13 @@ module ActiveRecord
|
|
533
591
|
# Quoted types
|
534
592
|
when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
|
535
593
|
# The default 'now'::date is CURRENT_DATE
|
536
|
-
if $1 == "now"
|
594
|
+
if $1 == "now" && $2 == "date"
|
537
595
|
nil
|
538
596
|
else
|
539
|
-
$1.gsub("''"
|
597
|
+
$1.gsub("''", "'")
|
540
598
|
end
|
541
599
|
# Boolean types
|
542
|
-
when "true"
|
600
|
+
when "true", "false"
|
543
601
|
default
|
544
602
|
# Numeric types
|
545
603
|
when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
|
@@ -565,18 +623,11 @@ module ActiveRecord
|
|
565
623
|
def load_additional_types(oids = nil)
|
566
624
|
initializer = OID::TypeMapInitializer.new(type_map)
|
567
625
|
|
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
|
626
|
+
query = <<~SQL
|
627
|
+
SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
|
628
|
+
FROM pg_type as t
|
629
|
+
LEFT JOIN pg_range as r ON oid = rngtypid
|
630
|
+
SQL
|
580
631
|
|
581
632
|
if oids
|
582
633
|
query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
|
@@ -592,6 +643,10 @@ module ActiveRecord
|
|
592
643
|
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
|
593
644
|
|
594
645
|
def execute_and_clear(sql, name, binds, prepare: false)
|
646
|
+
if preventing_writes? && write_query?(sql)
|
647
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
648
|
+
end
|
649
|
+
|
595
650
|
if without_prepared_statement?(binds)
|
596
651
|
result = exec_no_cache(sql, name, [])
|
597
652
|
elsif !prepare
|
@@ -605,6 +660,12 @@ module ActiveRecord
|
|
605
660
|
end
|
606
661
|
|
607
662
|
def exec_no_cache(sql, name, binds)
|
663
|
+
materialize_transactions
|
664
|
+
|
665
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
666
|
+
# made since we established the connection
|
667
|
+
update_typemap_for_default_timezone
|
668
|
+
|
608
669
|
type_casted_binds = type_casted_binds(binds)
|
609
670
|
log(sql, name, binds, type_casted_binds) do
|
610
671
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -614,7 +675,10 @@ module ActiveRecord
|
|
614
675
|
end
|
615
676
|
|
616
677
|
def exec_cache(sql, name, binds)
|
617
|
-
|
678
|
+
materialize_transactions
|
679
|
+
update_typemap_for_default_timezone
|
680
|
+
|
681
|
+
stmt_key = prepare_statement(sql, binds)
|
618
682
|
type_casted_binds = type_casted_binds(binds)
|
619
683
|
|
620
684
|
log(sql, name, binds, type_casted_binds, stmt_key) do
|
@@ -647,7 +711,7 @@ module ActiveRecord
|
|
647
711
|
#
|
648
712
|
# Check here for more details:
|
649
713
|
# 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"
|
714
|
+
CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
|
651
715
|
def is_cached_plan_failure?(e)
|
652
716
|
pgerror = e.cause
|
653
717
|
code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
|
@@ -668,7 +732,7 @@ module ActiveRecord
|
|
668
732
|
|
669
733
|
# Prepare the statement if it hasn't been prepared, return
|
670
734
|
# the statement key.
|
671
|
-
def prepare_statement(sql)
|
735
|
+
def prepare_statement(sql, binds)
|
672
736
|
@lock.synchronize do
|
673
737
|
sql_key = sql_key(sql)
|
674
738
|
unless @statements.key? sql_key
|
@@ -676,7 +740,7 @@ module ActiveRecord
|
|
676
740
|
begin
|
677
741
|
@connection.prepare nextkey, sql
|
678
742
|
rescue => e
|
679
|
-
raise translate_exception_class(e, sql)
|
743
|
+
raise translate_exception_class(e, sql, binds)
|
680
744
|
end
|
681
745
|
# Clear the queue
|
682
746
|
@connection.get_last_result
|
@@ -691,12 +755,8 @@ module ActiveRecord
|
|
691
755
|
def connect
|
692
756
|
@connection = PG.connect(@connection_parameters)
|
693
757
|
configure_connection
|
694
|
-
|
695
|
-
|
696
|
-
raise ActiveRecord::NoDatabaseError
|
697
|
-
else
|
698
|
-
raise
|
699
|
-
end
|
758
|
+
add_pg_encoders
|
759
|
+
add_pg_decoders
|
700
760
|
end
|
701
761
|
|
702
762
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -754,7 +814,7 @@ module ActiveRecord
|
|
754
814
|
# - format_type includes the column size constraint, e.g. varchar(50)
|
755
815
|
# - ::regclass is a function that gives the id for a table name
|
756
816
|
def column_definitions(table_name)
|
757
|
-
query(
|
817
|
+
query(<<~SQL, "SCHEMA")
|
758
818
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
759
819
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
760
820
|
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
@@ -765,7 +825,7 @@ module ActiveRecord
|
|
765
825
|
WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
|
766
826
|
AND a.attnum > 0 AND NOT a.attisdropped
|
767
827
|
ORDER BY a.attnum
|
768
|
-
|
828
|
+
SQL
|
769
829
|
end
|
770
830
|
|
771
831
|
def extract_table_ref_from_insert_sql(sql)
|
@@ -777,10 +837,14 @@ module ActiveRecord
|
|
777
837
|
Arel::Visitors::PostgreSQL.new(self)
|
778
838
|
end
|
779
839
|
|
840
|
+
def build_statement_pool
|
841
|
+
StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
|
842
|
+
end
|
843
|
+
|
780
844
|
def can_perform_case_insensitive_comparison_for?(column)
|
781
845
|
@case_insensitive_cache ||= {}
|
782
846
|
@case_insensitive_cache[column.sql_type] ||= begin
|
783
|
-
sql =
|
847
|
+
sql = <<~SQL
|
784
848
|
SELECT exists(
|
785
849
|
SELECT * FROM pg_proc
|
786
850
|
WHERE proname = 'lower'
|
@@ -792,7 +856,7 @@ module ActiveRecord
|
|
792
856
|
WHERE proname = 'lower'
|
793
857
|
AND castsource = #{quote column.sql_type}::regtype
|
794
858
|
)
|
795
|
-
|
859
|
+
SQL
|
796
860
|
execute_and_clear(sql, "SCHEMA", []) do |result|
|
797
861
|
result.getvalue(0, 0)
|
798
862
|
end
|
@@ -807,7 +871,22 @@ module ActiveRecord
|
|
807
871
|
@connection.type_map_for_queries = map
|
808
872
|
end
|
809
873
|
|
874
|
+
def update_typemap_for_default_timezone
|
875
|
+
if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
|
876
|
+
decoder_class = ActiveRecord::Base.default_timezone == :utc ?
|
877
|
+
PG::TextDecoder::TimestampUtc :
|
878
|
+
PG::TextDecoder::TimestampWithoutTimeZone
|
879
|
+
|
880
|
+
@timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
|
881
|
+
@connection.type_map_for_results.add_coder(@timestamp_decoder)
|
882
|
+
@default_timezone = ActiveRecord::Base.default_timezone
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
810
886
|
def add_pg_decoders
|
887
|
+
@default_timezone = nil
|
888
|
+
@timestamp_decoder = nil
|
889
|
+
|
811
890
|
coders_by_name = {
|
812
891
|
"int2" => PG::TextDecoder::Integer,
|
813
892
|
"int4" => PG::TextDecoder::Integer,
|
@@ -817,8 +896,15 @@ module ActiveRecord
|
|
817
896
|
"float8" => PG::TextDecoder::Float,
|
818
897
|
"bool" => PG::TextDecoder::Boolean,
|
819
898
|
}
|
899
|
+
|
900
|
+
if defined?(PG::TextDecoder::TimestampUtc)
|
901
|
+
# Use native PG encoders available since pg-1.1
|
902
|
+
coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
|
903
|
+
coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
|
904
|
+
end
|
905
|
+
|
820
906
|
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
821
|
-
query =
|
907
|
+
query = <<~SQL % known_coder_types.join(", ")
|
822
908
|
SELECT t.oid, t.typname
|
823
909
|
FROM pg_type as t
|
824
910
|
WHERE t.typname IN (%s)
|
@@ -832,6 +918,10 @@ module ActiveRecord
|
|
832
918
|
map = PG::TypeMapByOid.new
|
833
919
|
coders.each { |coder| map.add_coder(coder) }
|
834
920
|
@connection.type_map_for_results = map
|
921
|
+
|
922
|
+
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
923
|
+
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
924
|
+
update_typemap_for_default_timezone
|
835
925
|
end
|
836
926
|
|
837
927
|
def construct_coder(row, coder_class)
|