activerecord 6.0.0.beta3 → 6.0.0.rc1
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 +286 -6
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/associations.rb +3 -2
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- 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_proxy.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/preloader.rb +11 -6
- data/lib/active_record/associations/preloader/association.rb +32 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +4 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +42 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +13 -3
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
- data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -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/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
- data/lib/active_record/connection_handling.rb +17 -10
- data/lib/active_record/core.rb +15 -20
- data/lib/active_record/database_configurations.rb +14 -14
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +6 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +180 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railties/databases.rake +68 -6
- data/lib/active_record/reflection.rb +2 -2
- data/lib/active_record/relation.rb +98 -20
- data/lib/active_record/relation/calculations.rb +39 -39
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +3 -9
- data/lib/active_record/relation/merger.rb +7 -16
- data/lib/active_record/relation/query_methods.rb +153 -38
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +3 -2
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +3 -3
- data/lib/active_record/tasks/database_tasks.rb +36 -1
- data/lib/active_record/touch_later.rb +2 -2
- data/lib/active_record/transactions.rb +52 -41
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +6 -1
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +87 -108
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- 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 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +12 -11
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
module ConnectionAdapters
|
6
6
|
# An abstract definition of a column in a table.
|
7
7
|
class Column
|
8
|
-
attr_reader :name, :default, :sql_type_metadata, :null, :
|
8
|
+
attr_reader :name, :default, :sql_type_metadata, :null, :default_function, :collation, :comment
|
9
9
|
|
10
10
|
delegate :precision, :scale, :limit, :type, :sql_type, to: :sql_type_metadata, allow_nil: true
|
11
11
|
|
@@ -15,9 +15,8 @@ module ActiveRecord
|
|
15
15
|
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
|
16
16
|
# +sql_type_metadata+ is various information about the type of the column
|
17
17
|
# +null+ determines if this column allows +NULL+ values.
|
18
|
-
def initialize(name, default, sql_type_metadata = nil, null = true,
|
18
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **)
|
19
19
|
@name = name.freeze
|
20
|
-
@table_name = table_name
|
21
20
|
@sql_type_metadata = sql_type_metadata
|
22
21
|
@null = null
|
23
22
|
@default = default
|
@@ -44,7 +43,6 @@ module ActiveRecord
|
|
44
43
|
|
45
44
|
def init_with(coder)
|
46
45
|
@name = coder["name"]
|
47
|
-
@table_name = coder["table_name"]
|
48
46
|
@sql_type_metadata = coder["sql_type_metadata"]
|
49
47
|
@null = coder["null"]
|
50
48
|
@default = coder["default"]
|
@@ -55,7 +53,6 @@ module ActiveRecord
|
|
55
53
|
|
56
54
|
def encode_with(coder)
|
57
55
|
coder["name"] = @name
|
58
|
-
coder["table_name"] = @table_name
|
59
56
|
coder["sql_type_metadata"] = @sql_type_metadata
|
60
57
|
coder["null"] = @null
|
61
58
|
coder["default"] = @default
|
@@ -66,19 +63,26 @@ module ActiveRecord
|
|
66
63
|
|
67
64
|
def ==(other)
|
68
65
|
other.is_a?(Column) &&
|
69
|
-
|
66
|
+
name == other.name &&
|
67
|
+
default == other.default &&
|
68
|
+
sql_type_metadata == other.sql_type_metadata &&
|
69
|
+
null == other.null &&
|
70
|
+
default_function == other.default_function &&
|
71
|
+
collation == other.collation &&
|
72
|
+
comment == other.comment
|
70
73
|
end
|
71
74
|
alias :eql? :==
|
72
75
|
|
73
76
|
def hash
|
74
|
-
|
77
|
+
Column.hash ^
|
78
|
+
name.hash ^
|
79
|
+
default.hash ^
|
80
|
+
sql_type_metadata.hash ^
|
81
|
+
null.hash ^
|
82
|
+
default_function.hash ^
|
83
|
+
collation.hash ^
|
84
|
+
comment.hash
|
75
85
|
end
|
76
|
-
|
77
|
-
protected
|
78
|
-
|
79
|
-
def attributes_for_hash
|
80
|
-
[self.class, name, default, sql_type_metadata, null, table_name, default_function, collation]
|
81
|
-
end
|
82
86
|
end
|
83
87
|
|
84
88
|
class NullColumn < Column
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
else
|
12
12
|
super
|
13
13
|
end
|
14
|
-
|
14
|
+
@connection.abandon_results!
|
15
15
|
result
|
16
16
|
end
|
17
17
|
|
@@ -61,7 +61,9 @@ module ActiveRecord
|
|
61
61
|
|
62
62
|
def exec_delete(sql, name = nil, binds = [])
|
63
63
|
if without_prepared_statement?(binds)
|
64
|
-
|
64
|
+
@lock.synchronize do
|
65
|
+
execute_and_free(sql, name) { @connection.affected_rows }
|
66
|
+
end
|
65
67
|
else
|
66
68
|
exec_stmt_and_free(sql, name, binds) { |stmt| stmt.affected_rows }
|
67
69
|
end
|
@@ -69,22 +71,31 @@ module ActiveRecord
|
|
69
71
|
alias :exec_update :exec_delete
|
70
72
|
|
71
73
|
private
|
74
|
+
def execute_batch(sql, name = nil)
|
75
|
+
super
|
76
|
+
@connection.abandon_results!
|
77
|
+
end
|
78
|
+
|
72
79
|
def default_insert_value(column)
|
73
|
-
|
80
|
+
super unless column.auto_increment?
|
74
81
|
end
|
75
82
|
|
76
83
|
def last_inserted_id(result)
|
77
84
|
@connection.last_id
|
78
85
|
end
|
79
86
|
|
80
|
-
def discard_remaining_results
|
81
|
-
@connection.abandon_results!
|
82
|
-
end
|
83
|
-
|
84
87
|
def supports_set_server_option?
|
85
88
|
@connection.respond_to?(:set_server_option)
|
86
89
|
end
|
87
90
|
|
91
|
+
def build_truncate_statements(*table_names)
|
92
|
+
if table_names.size == 1
|
93
|
+
super.first
|
94
|
+
else
|
95
|
+
super
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
88
99
|
def multi_statements_enabled?(flags)
|
89
100
|
if flags.is_a?(Array)
|
90
101
|
flags.include?("MULTI_STATEMENTS")
|
@@ -117,6 +128,33 @@ module ActiveRecord
|
|
117
128
|
end
|
118
129
|
end
|
119
130
|
|
131
|
+
def combine_multi_statements(total_sql)
|
132
|
+
total_sql.each_with_object([]) do |sql, total_sql_chunks|
|
133
|
+
previous_packet = total_sql_chunks.last
|
134
|
+
if max_allowed_packet_reached?(sql, previous_packet)
|
135
|
+
total_sql_chunks << +sql
|
136
|
+
else
|
137
|
+
previous_packet << ";\n"
|
138
|
+
previous_packet << sql
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def max_allowed_packet_reached?(current_packet, previous_packet)
|
144
|
+
if current_packet.bytesize > max_allowed_packet
|
145
|
+
raise ActiveRecordError,
|
146
|
+
"Fixtures set is too large #{current_packet.bytesize}. Consider increasing the max_allowed_packet variable."
|
147
|
+
elsif previous_packet.nil?
|
148
|
+
true
|
149
|
+
else
|
150
|
+
(current_packet.bytesize + previous_packet.bytesize + 2) > max_allowed_packet
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def max_allowed_packet
|
155
|
+
@max_allowed_packet ||= show_variable("max_allowed_packet")
|
156
|
+
end
|
157
|
+
|
120
158
|
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
121
159
|
if preventing_writes? && write_query?(sql)
|
122
160
|
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
@@ -55,7 +55,7 @@ module ActiveRecord
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def schema_collation(column)
|
58
|
-
if column.collation
|
58
|
+
if column.collation
|
59
59
|
@table_collation_cache ||= {}
|
60
60
|
@table_collation_cache[table_name] ||=
|
61
61
|
@connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
|
@@ -64,14 +64,14 @@ module ActiveRecord
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def extract_expression_for_virtual_column(column)
|
67
|
-
if @connection.mariadb? && @connection.
|
68
|
-
create_table_info = @connection.send(:create_table_info,
|
67
|
+
if @connection.mariadb? && @connection.database_version < "10.2.5"
|
68
|
+
create_table_info = @connection.send(:create_table_info, table_name)
|
69
69
|
column_name = @connection.quote_column_name(column.name)
|
70
70
|
if %r/#{column_name} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
|
71
71
|
$~[:expression].inspect
|
72
72
|
end
|
73
73
|
else
|
74
|
-
scope = @connection.send(:quoted_scope,
|
74
|
+
scope = @connection.send(:quoted_scope, table_name)
|
75
75
|
column_name = @connection.quote(column.name)
|
76
76
|
sql = "SELECT generation_expression FROM information_schema.columns" \
|
77
77
|
" WHERE table_schema = #{scope[:schema]}" \
|
@@ -121,14 +121,18 @@ module ActiveRecord
|
|
121
121
|
sql
|
122
122
|
end
|
123
123
|
|
124
|
+
def table_alias_length
|
125
|
+
256 # https://dev.mysql.com/doc/refman/8.0/en/identifiers.html
|
126
|
+
end
|
127
|
+
|
124
128
|
private
|
125
129
|
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
|
126
130
|
|
127
131
|
def row_format_dynamic_by_default?
|
128
132
|
if mariadb?
|
129
|
-
|
133
|
+
database_version >= "10.2.2"
|
130
134
|
else
|
131
|
-
|
135
|
+
database_version >= "5.7.9"
|
132
136
|
end
|
133
137
|
end
|
134
138
|
|
@@ -170,9 +174,8 @@ module ActiveRecord
|
|
170
174
|
default,
|
171
175
|
type_metadata,
|
172
176
|
field[:Null] == "YES",
|
173
|
-
table_name,
|
174
177
|
default_function,
|
175
|
-
field[:Collation],
|
178
|
+
collation: field[:Collation],
|
176
179
|
comment: field[:Comment].presence
|
177
180
|
)
|
178
181
|
end
|
@@ -240,7 +243,7 @@ module ActiveRecord
|
|
240
243
|
when nil, 0x100..0xffff; nil
|
241
244
|
when 0x10000..0xffffff; "medium"
|
242
245
|
when 0x1000000..0xffffffff; "long"
|
243
|
-
else raise
|
246
|
+
else raise ArgumentError, "No #{type} type has byte size #{limit}"
|
244
247
|
end
|
245
248
|
end
|
246
249
|
end
|
@@ -252,7 +255,7 @@ module ActiveRecord
|
|
252
255
|
when 3; "mediumint"
|
253
256
|
when nil, 4; "int"
|
254
257
|
when 5..8; "bigint"
|
255
|
-
else raise
|
258
|
+
else raise ArgumentError, "No integer type has byte size #{limit}. Use a decimal with scale 0 instead."
|
256
259
|
end
|
257
260
|
end
|
258
261
|
end
|
@@ -10,25 +10,21 @@ module ActiveRecord
|
|
10
10
|
|
11
11
|
def initialize(type_metadata, extra: "")
|
12
12
|
super(type_metadata)
|
13
|
-
@type_metadata = type_metadata
|
14
13
|
@extra = extra
|
15
14
|
end
|
16
15
|
|
17
16
|
def ==(other)
|
18
|
-
other.is_a?(
|
19
|
-
|
17
|
+
other.is_a?(TypeMetadata) &&
|
18
|
+
__getobj__ == other.__getobj__ &&
|
19
|
+
extra == other.extra
|
20
20
|
end
|
21
21
|
alias eql? ==
|
22
22
|
|
23
23
|
def hash
|
24
|
-
|
24
|
+
TypeMetadata.hash ^
|
25
|
+
__getobj__.hash ^
|
26
|
+
extra.hash
|
25
27
|
end
|
26
|
-
|
27
|
-
protected
|
28
|
-
|
29
|
-
def attributes_for_hash
|
30
|
-
[self.class, @type_metadata, extra]
|
31
|
-
end
|
32
28
|
end
|
33
29
|
end
|
34
30
|
end
|
@@ -43,7 +43,7 @@ module ActiveRecord
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def supports_json?
|
46
|
-
!mariadb? &&
|
46
|
+
!mariadb? && database_version >= "5.7.8"
|
47
47
|
end
|
48
48
|
|
49
49
|
def supports_comments?
|
@@ -126,7 +126,11 @@ module ActiveRecord
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def full_version
|
129
|
-
|
129
|
+
schema_cache.database_version.full_version_string
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_full_version
|
133
|
+
@connection.server_info[:version]
|
130
134
|
end
|
131
135
|
end
|
132
136
|
end
|
@@ -2,42 +2,29 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
alias :array? :array
|
5
|
+
module PostgreSQL
|
6
|
+
class Column < ConnectionAdapters::Column # :nodoc:
|
7
|
+
delegate :oid, :fmod, to: :sql_type_metadata
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def serial?
|
16
|
-
return unless default_function
|
17
|
-
|
18
|
-
if %r{\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z} =~ default_function
|
19
|
-
sequence_name_from_parts(table_name, name, suffix) == sequence_name
|
9
|
+
def initialize(*, serial: nil, **)
|
10
|
+
super
|
11
|
+
@serial = serial
|
20
12
|
end
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
attr_reader :max_identifier_length
|
25
13
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
if over_length > 0
|
30
|
-
column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
|
31
|
-
over_length -= column_name.length - column_name_length
|
32
|
-
column_name = column_name[0, column_name_length - [over_length, 0].min]
|
33
|
-
end
|
14
|
+
def serial?
|
15
|
+
@serial
|
16
|
+
end
|
34
17
|
|
35
|
-
|
36
|
-
|
37
|
-
|
18
|
+
def array
|
19
|
+
sql_type_metadata.sql_type.end_with?("[]")
|
20
|
+
end
|
21
|
+
alias :array? :array
|
38
22
|
|
39
|
-
|
23
|
+
def sql_type
|
24
|
+
super.sub(/\[\]\z/, "")
|
40
25
|
end
|
26
|
+
end
|
41
27
|
end
|
28
|
+
PostgreSQLColumn = PostgreSQL::Column # :nodoc:
|
42
29
|
end
|
43
30
|
end
|
@@ -110,7 +110,7 @@ module ActiveRecord
|
|
110
110
|
end
|
111
111
|
alias :exec_update :exec_delete
|
112
112
|
|
113
|
-
def sql_for_insert(sql, pk,
|
113
|
+
def sql_for_insert(sql, pk, binds) # :nodoc:
|
114
114
|
if pk.nil?
|
115
115
|
# Extract the table from the insert sql. Yuck.
|
116
116
|
table_ref = extract_table_ref_from_insert_sql(sql)
|
@@ -164,6 +164,10 @@ module ActiveRecord
|
|
164
164
|
end
|
165
165
|
|
166
166
|
private
|
167
|
+
def build_truncate_statements(*table_names)
|
168
|
+
"TRUNCATE TABLE #{table_names.map(&method(:quote_table_name)).join(", ")}"
|
169
|
+
end
|
170
|
+
|
167
171
|
# Returns the current ID of a table's sequence.
|
168
172
|
def last_insert_id_result(sequence_name)
|
169
173
|
exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
|
@@ -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")
|
@@ -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
|
|
@@ -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
|
@@ -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)
|