activerecord 7.0.0.rc1 → 7.0.1
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 +132 -3
- data/MIT-LICENSE +1 -1
- data/lib/active_record/associations/join_dependency.rb +6 -2
- data/lib/active_record/associations.rb +28 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -4
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +28 -0
- data/lib/active_record/encryption/encryptable_record.rb +1 -1
- data/lib/active_record/encryption/extended_deterministic_queries.rb +28 -28
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/migration/compatibility.rb +24 -2
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation/calculations.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +11 -3
- data/lib/active_record/relation.rb +16 -3
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/tasks/database_tasks.rb +6 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record.rb +1 -1
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +14 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0de65b8a004bb74ee8951a2d34e5630f67568091f4c72bd99e28aaf8dd73097
|
4
|
+
data.tar.gz: 56d07fdf1ec276b9b88c7a13e79cfe4cfb3f3768dcbd1b5bf0f828bd2ba657d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bf02198176f838235234fefd0658de10a37c51d704becaeccd1f8040ed4d79360a5128411c01af7226ca0c5806c5ed892c1fc55fc27cca55f48f5dc181d5dd5
|
7
|
+
data.tar.gz: e781dd6be129520aaeab76b1fea5bf98ce25793c623c2cde18cfd293fd2222c28e3d16ad4f8978688a74eaf683c2a6283358d7f815b8a924a0cc66f16c051c1e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,131 @@
|
|
1
|
+
## Rails 7.0.1 (January 06, 2022) ##
|
2
|
+
|
3
|
+
* Change `QueryMethods#in_order_of` to drop records not listed in values.
|
4
|
+
|
5
|
+
`in_order_of` now filters down to the values provided, to match the behavior of the `Enumerable` version.
|
6
|
+
|
7
|
+
*Kevin Newton*
|
8
|
+
|
9
|
+
* Allow named expression indexes to be revertible.
|
10
|
+
|
11
|
+
Previously, the following code would raise an error in a reversible migration executed while rolling back, due to the index name not being used in the index removal.
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
add_index(:settings, "(data->'property')", using: :gin, name: :index_settings_data_property)
|
15
|
+
```
|
16
|
+
|
17
|
+
Fixes #43331.
|
18
|
+
|
19
|
+
*Oliver Günther*
|
20
|
+
|
21
|
+
* Better error messages when association name is invalid in the argument of `ActiveRecord::QueryMethods::WhereChain#missing`.
|
22
|
+
|
23
|
+
*ykpythemind*
|
24
|
+
|
25
|
+
* Fix ordered migrations for single db in multi db environment.
|
26
|
+
|
27
|
+
*Himanshu*
|
28
|
+
|
29
|
+
* Extract `on update CURRENT_TIMESTAMP` for mysql2 adapter.
|
30
|
+
|
31
|
+
*Kazuhiro Masuda*
|
32
|
+
|
33
|
+
* Fix incorrect argument in PostgreSQL structure dump tasks.
|
34
|
+
|
35
|
+
Updating the `--no-comment` argument added in Rails 7 to the correct `--no-comments` argument.
|
36
|
+
|
37
|
+
*Alex Dent*
|
38
|
+
|
39
|
+
* Fix schema dumping column default SQL values for sqlite3.
|
40
|
+
|
41
|
+
*fatkodima*
|
42
|
+
|
43
|
+
* Correctly parse complex check constraint expressions for PostgreSQL.
|
44
|
+
|
45
|
+
*fatkodima*
|
46
|
+
|
47
|
+
* Fix `timestamptz` attributes on PostgreSQL handle blank inputs.
|
48
|
+
|
49
|
+
*Alex Ghiculescu*
|
50
|
+
|
51
|
+
* Fix migration compatibility to create SQLite references/belongs_to column as integer when migration version is 6.0.
|
52
|
+
|
53
|
+
Reference/belongs_to in migrations with version 6.0 were creating columns as
|
54
|
+
bigint instead of integer for the SQLite Adapter.
|
55
|
+
|
56
|
+
*Marcelo Lauxen*
|
57
|
+
|
58
|
+
* Fix joining through a polymorphic association.
|
59
|
+
|
60
|
+
*Alexandre Ruban*
|
61
|
+
|
62
|
+
* Fix `QueryMethods#in_order_of` to handle empty order list.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
Post.in_order_of(:id, []).to_a
|
66
|
+
```
|
67
|
+
|
68
|
+
Also more explicitly set the column as secondary order, so that any other
|
69
|
+
value is still ordered.
|
70
|
+
|
71
|
+
*Jean Boussier*
|
72
|
+
|
73
|
+
* Fix `rails dbconsole` for 3-tier config.
|
74
|
+
|
75
|
+
*Eileen M. Uchitelle*
|
76
|
+
|
77
|
+
* Fix quoting of column aliases generated by calculation methods.
|
78
|
+
|
79
|
+
Since the alias is derived from the table name, we can't assume the result
|
80
|
+
is a valid identifier.
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
class Test < ActiveRecord::Base
|
84
|
+
self.table_name = '1abc'
|
85
|
+
end
|
86
|
+
Test.group(:id).count
|
87
|
+
# syntax error at or near "1" (ActiveRecord::StatementInvalid)
|
88
|
+
# LINE 1: SELECT COUNT(*) AS count_all, "1abc"."id" AS 1abc_id FROM "1...
|
89
|
+
```
|
90
|
+
|
91
|
+
*Jean Boussier*
|
92
|
+
|
93
|
+
|
94
|
+
## Rails 7.0.0 (December 15, 2021) ##
|
95
|
+
|
96
|
+
* Better handle SQL queries with invalid encoding.
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
Post.create(name: "broken \xC8 UTF-8")
|
100
|
+
```
|
101
|
+
|
102
|
+
Would cause all adapters to fail in a non controlled way in the code
|
103
|
+
responsible to detect write queries.
|
104
|
+
|
105
|
+
The query is now properly passed to the database connection, which might or might
|
106
|
+
not be able to handle it, but will either succeed or failed in a more correct way.
|
107
|
+
|
108
|
+
*Jean Boussier*
|
109
|
+
|
110
|
+
* Move database and shard selection config options to a generator.
|
111
|
+
|
112
|
+
Rather than generating the config options in `production.rb` when applications are created, applications can now run a generator to create an initializer and uncomment / update options as needed. All multi-db configuration can be implemented in this initializer.
|
113
|
+
|
114
|
+
*Eileen M. Uchitelle*
|
115
|
+
|
116
|
+
|
117
|
+
## Rails 7.0.0.rc3 (December 14, 2021) ##
|
118
|
+
|
119
|
+
* No changes.
|
120
|
+
|
121
|
+
|
122
|
+
## Rails 7.0.0.rc2 (December 14, 2021) ##
|
123
|
+
|
124
|
+
* No changes.
|
125
|
+
|
126
|
+
|
127
|
+
## Rails 7.0.0.rc1 (December 06, 2021) ##
|
128
|
+
|
1
129
|
* Remove deprecated `ActiveRecord::DatabaseConfigurations::DatabaseConfig#spec_name`.
|
2
130
|
|
3
131
|
*Rafael Mendonça França*
|
@@ -305,7 +433,6 @@
|
|
305
433
|
|
306
434
|
Applications may now set their the filename or path of the schema / structure dump file in their database configuration.
|
307
435
|
|
308
|
-
|
309
436
|
```yaml
|
310
437
|
production:
|
311
438
|
primary:
|
@@ -392,6 +519,8 @@
|
|
392
519
|
`#with_lock` now accepts transaction options like `requires_new:`,
|
393
520
|
`isolation:`, and `joinable:`
|
394
521
|
|
522
|
+
*John Mileham*
|
523
|
+
|
395
524
|
* Adds support for deferrable foreign key constraints in PostgreSQL.
|
396
525
|
|
397
526
|
By default, foreign key constraints in PostgreSQL are checked after each statement. This works for most use cases,
|
@@ -468,7 +597,7 @@
|
|
468
597
|
|
469
598
|
*Alex Ghiculescu*
|
470
599
|
|
471
|
-
*
|
600
|
+
* Avoid COMMENT statements in PostgreSQL structure dumps
|
472
601
|
|
473
602
|
COMMENT statements are now omitted from the output of `db:structure:dump` when using PostgreSQL >= 11.
|
474
603
|
This allows loading the dump without a pgsql superuser account.
|
@@ -521,7 +650,7 @@
|
|
521
650
|
|
522
651
|
*Sam Bostock*
|
523
652
|
|
524
|
-
*
|
653
|
+
* Add ssl support for postgresql database tasks
|
525
654
|
|
526
655
|
Add `PGSSLMODE`, `PGSSLCERT`, `PGSSLKEY` and `PGSSLROOTCERT` to pg_env from database config
|
527
656
|
when running postgresql database tasks.
|
data/MIT-LICENSE
CHANGED
@@ -3,8 +3,12 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
class JoinDependency # :nodoc:
|
6
|
-
|
7
|
-
|
6
|
+
extend ActiveSupport::Autoload
|
7
|
+
|
8
|
+
eager_autoload do
|
9
|
+
autoload :JoinBase
|
10
|
+
autoload :JoinAssociation
|
11
|
+
end
|
8
12
|
|
9
13
|
class Aliases # :nodoc:
|
10
14
|
def initialize(tables)
|
@@ -594,19 +594,27 @@ module ActiveRecord
|
|
594
594
|
# you can also define callbacks that get triggered when you add an object to or remove an
|
595
595
|
# object from an association collection.
|
596
596
|
#
|
597
|
-
# class
|
598
|
-
#
|
597
|
+
# class Firm < ActiveRecord::Base
|
598
|
+
# has_many :clients,
|
599
|
+
# dependent: :destroy,
|
600
|
+
# after_add: :congratulate_client,
|
601
|
+
# after_remove: :log_after_remove
|
599
602
|
#
|
600
|
-
# def
|
601
|
-
# ...
|
603
|
+
# def congratulate_client(record)
|
604
|
+
# # ...
|
605
|
+
# end
|
606
|
+
#
|
607
|
+
# def log_after_remove(record)
|
608
|
+
# # ...
|
602
609
|
# end
|
603
|
-
# end
|
604
610
|
#
|
605
611
|
# It's possible to stack callbacks by passing them as an array. Example:
|
606
612
|
#
|
607
|
-
# class
|
608
|
-
#
|
609
|
-
#
|
613
|
+
# class Firm < ActiveRecord::Base
|
614
|
+
# has_many :clients,
|
615
|
+
# dependent: :destroy,
|
616
|
+
# after_add: [:congratulate_client, -> (firm, record) { firm.log << "after_adding#{record.id}" }],
|
617
|
+
# after_remove: :log_after_remove
|
610
618
|
# end
|
611
619
|
#
|
612
620
|
# Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
|
@@ -617,6 +625,18 @@ module ActiveRecord
|
|
617
625
|
# Similarly, if any of the +before_remove+ callbacks throw an exception, the object
|
618
626
|
# will not be removed from the collection.
|
619
627
|
#
|
628
|
+
# Note: To trigger remove callbacks, you must use +destroy+ / +destroy_all+ methods. For example:
|
629
|
+
#
|
630
|
+
# * <tt>firm.clients.destroy(client)</tt>
|
631
|
+
# * <tt>firm.clients.destroy(*clients)</tt>
|
632
|
+
# * <tt>firm.clients.destroy_all</tt>
|
633
|
+
#
|
634
|
+
# +delete+ / +delete_all+ methods like the following do *not* trigger remove callbacks:
|
635
|
+
#
|
636
|
+
# * <tt>firm.clients.delete(client)</tt>
|
637
|
+
# * <tt>firm.clients.delete(*clients)</tt>
|
638
|
+
# * <tt>firm.clients.delete_all</tt>
|
639
|
+
#
|
620
640
|
# == Association extensions
|
621
641
|
#
|
622
642
|
# The proxy objects that control the access to associations can be extended through anonymous
|
@@ -1438,7 +1438,7 @@ module ActiveRecord
|
|
1438
1438
|
|
1439
1439
|
checks = []
|
1440
1440
|
|
1441
|
-
if !options.key?(:name) &&
|
1441
|
+
if !options.key?(:name) && expression_column_name?(column_name)
|
1442
1442
|
options[:name] = index_name(table_name, column_name)
|
1443
1443
|
column_names = []
|
1444
1444
|
else
|
@@ -1447,7 +1447,7 @@ module ActiveRecord
|
|
1447
1447
|
|
1448
1448
|
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1449
1449
|
|
1450
|
-
if column_names.present?
|
1450
|
+
if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
|
1451
1451
|
checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
|
1452
1452
|
end
|
1453
1453
|
|
@@ -1515,7 +1515,7 @@ module ActiveRecord
|
|
1515
1515
|
end
|
1516
1516
|
|
1517
1517
|
def index_column_names(column_names)
|
1518
|
-
if
|
1518
|
+
if expression_column_name?(column_names)
|
1519
1519
|
column_names
|
1520
1520
|
else
|
1521
1521
|
Array(column_names)
|
@@ -1523,13 +1523,18 @@ module ActiveRecord
|
|
1523
1523
|
end
|
1524
1524
|
|
1525
1525
|
def index_name_options(column_names)
|
1526
|
-
if
|
1526
|
+
if expression_column_name?(column_names)
|
1527
1527
|
column_names = column_names.scan(/\w+/).join("_")
|
1528
1528
|
end
|
1529
1529
|
|
1530
1530
|
{ column: column_names }
|
1531
1531
|
end
|
1532
1532
|
|
1533
|
+
# Try to identify whether the given column name is an expression
|
1534
|
+
def expression_column_name?(column_name)
|
1535
|
+
column_name.is_a?(String) && /\W/.match?(column_name)
|
1536
|
+
end
|
1537
|
+
|
1533
1538
|
def strip_table_name_prefix_and_suffix(table_name)
|
1534
1539
|
prefix = Base.table_name_prefix
|
1535
1540
|
suffix = Base.table_name_suffix
|
@@ -197,7 +197,7 @@ module ActiveRecord
|
|
197
197
|
|
198
198
|
# Executes the SQL statement in the context of this connection.
|
199
199
|
def execute(sql, name = nil, async: false)
|
200
|
-
raw_execute(sql, name, async)
|
200
|
+
raw_execute(sql, name, async: async)
|
201
201
|
end
|
202
202
|
|
203
203
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
@@ -163,6 +163,7 @@ module ActiveRecord
|
|
163
163
|
default, default_function = field[:Default], nil
|
164
164
|
|
165
165
|
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
|
166
|
+
default = "#{default} ON UPDATE #{default}" if /on update CURRENT_TIMESTAMP/i.match?(field[:Extra])
|
166
167
|
default, default_function = nil, default
|
167
168
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
168
169
|
default = +"(#{default})" unless default.start_with?("(")
|
@@ -525,7 +525,7 @@ module ActiveRecord
|
|
525
525
|
scope = quoted_scope(table_name)
|
526
526
|
|
527
527
|
check_info = exec_query(<<-SQL, "SCHEMA")
|
528
|
-
SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.convalidated AS valid
|
528
|
+
SELECT conname, pg_get_constraintdef(c.oid, true) AS constraintdef, c.convalidated AS valid
|
529
529
|
FROM pg_constraint c
|
530
530
|
JOIN pg_class t ON c.conrelid = t.oid
|
531
531
|
WHERE c.contype = 'c'
|
@@ -537,7 +537,7 @@ module ActiveRecord
|
|
537
537
|
name: row["conname"],
|
538
538
|
validate: row["valid"]
|
539
539
|
}
|
540
|
-
expression = row["constraintdef"][/CHECK \(
|
540
|
+
expression = row["constraintdef"][/CHECK \((.+)\)/m, 1]
|
541
541
|
|
542
542
|
CheckConstraintDefinition.new(table_name, expression, options)
|
543
543
|
end
|
@@ -569,7 +569,7 @@ module ActiveRecord
|
|
569
569
|
else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
|
570
570
|
end
|
571
571
|
when "enum"
|
572
|
-
raise ArgumentError "enum_type is required for enums" if enum_type.nil?
|
572
|
+
raise ArgumentError, "enum_type is required for enums" if enum_type.nil?
|
573
573
|
|
574
574
|
enum_type
|
575
575
|
else
|
@@ -45,6 +45,19 @@ module ActiveRecord
|
|
45
45
|
0
|
46
46
|
end
|
47
47
|
|
48
|
+
def quote_default_expression(value, column) # :nodoc:
|
49
|
+
if value.is_a?(Proc)
|
50
|
+
value = value.call
|
51
|
+
if value.match?(/\A\w+\(.*\)\z/)
|
52
|
+
"(#{value})"
|
53
|
+
else
|
54
|
+
value
|
55
|
+
end
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
48
61
|
def type_cast(value) # :nodoc:
|
49
62
|
case value
|
50
63
|
when BigDecimal
|
@@ -127,20 +127,20 @@ module ActiveRecord
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def new_column_from_field(table_name, field)
|
130
|
-
default =
|
131
|
-
case field["dflt_value"]
|
132
|
-
when /^null$/i
|
133
|
-
nil
|
134
|
-
when /^'(.*)'$/m
|
135
|
-
$1.gsub("''", "'")
|
136
|
-
when /^"(.*)"$/m
|
137
|
-
$1.gsub('""', '"')
|
138
|
-
else
|
139
|
-
field["dflt_value"]
|
140
|
-
end
|
130
|
+
default = field["dflt_value"]
|
141
131
|
|
142
132
|
type_metadata = fetch_type_metadata(field["type"])
|
143
|
-
|
133
|
+
default_value = extract_value_from_default(default)
|
134
|
+
default_function = extract_default_function(default_value, default)
|
135
|
+
|
136
|
+
Column.new(
|
137
|
+
field["name"],
|
138
|
+
default_value,
|
139
|
+
type_metadata,
|
140
|
+
field["notnull"].to_i == 0,
|
141
|
+
default_function,
|
142
|
+
collation: field["collation"]
|
143
|
+
)
|
144
144
|
end
|
145
145
|
|
146
146
|
def data_source_sql(name = nil, type: nil)
|
@@ -389,6 +389,34 @@ module ActiveRecord
|
|
389
389
|
end
|
390
390
|
alias column_definitions table_structure
|
391
391
|
|
392
|
+
def extract_value_from_default(default)
|
393
|
+
case default
|
394
|
+
when /^null$/i
|
395
|
+
nil
|
396
|
+
# Quoted types
|
397
|
+
when /^'(.*)'$/m
|
398
|
+
$1.gsub("''", "'")
|
399
|
+
# Quoted types
|
400
|
+
when /^"(.*)"$/m
|
401
|
+
$1.gsub('""', '"')
|
402
|
+
# Numeric types
|
403
|
+
when /\A-?\d+(\.\d*)?\z/
|
404
|
+
$&
|
405
|
+
else
|
406
|
+
# Anything else is blank or some function
|
407
|
+
# and we can't know the value of that, so return nil.
|
408
|
+
nil
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def extract_default_function(default_value, default)
|
413
|
+
default if has_default_function?(default_value, default)
|
414
|
+
end
|
415
|
+
|
416
|
+
def has_default_function?(default_value, default)
|
417
|
+
!default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP}.match?(default)
|
418
|
+
end
|
419
|
+
|
392
420
|
# See: https://www.sqlite.org/lang_altertable.html
|
393
421
|
# SQLite has an additional restriction on the ALTER TABLE statement
|
394
422
|
def invalid_alter_table_type?(type, options)
|
@@ -37,7 +37,7 @@ module ActiveRecord
|
|
37
37
|
# in preserving it.
|
38
38
|
# * <tt>:ignore_case</tt> - When true, it behaves like +:downcase+ but, it also preserves the original case in a specially
|
39
39
|
# designated column +original_<name>+. When reading the encrypted content, the version with the original case is
|
40
|
-
#
|
40
|
+
# served. But you can still execute queries that will ignore the case. This option can only be used when +:deterministic+
|
41
41
|
# is true.
|
42
42
|
# * <tt>:context_properties</tt> - Additional properties that will override +Context+ settings when this attribute is
|
43
43
|
# encrypted and decrypted. E.g: +encryptor:+, +cipher:+, +message_serializer:+, etc.
|
@@ -1,35 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Automatically expand encrypted arguments to support querying both encrypted and unencrypted data
|
4
|
-
#
|
5
|
-
# Active Record Encryption supports querying the db using deterministic attributes. For example:
|
6
|
-
#
|
7
|
-
# Contact.find_by(email_address: "jorge@hey.com")
|
8
|
-
#
|
9
|
-
# The value "jorge@hey.com" will get encrypted automatically to perform the query. But there is
|
10
|
-
# a problem while the data is being encrypted. This won't work. During that time, you need these
|
11
|
-
# queries to be:
|
12
|
-
#
|
13
|
-
# Contact.find_by(email_address: [ "jorge@hey.com", "<encrypted jorge@hey.com>" ])
|
14
|
-
#
|
15
|
-
# This patches ActiveRecord to support this automatically. It addresses both:
|
16
|
-
#
|
17
|
-
# * ActiveRecord::Base: Used in +Contact.find_by_email_address(...)+
|
18
|
-
# * ActiveRecord::Relation: Used in +Contact.internal.find_by_email_address(...)+
|
19
|
-
#
|
20
|
-
# +ActiveRecord::Base+ relies on +ActiveRecord::Relation+ (+ActiveRecord::QueryMethods+) but it does
|
21
|
-
# some prepared statements caching. That's why we need to intercept +ActiveRecord::Base+ as soon
|
22
|
-
# as it's invoked (so that the proper prepared statement is cached).
|
23
|
-
#
|
24
|
-
# When modifying this file run performance tests in +test/performance/extended_deterministic_queries_performance_test.rb+ to
|
25
|
-
# make sure performance overhead is acceptable.
|
26
|
-
#
|
27
|
-
# We will extend this to support previous "encryption context" versions in future iterations
|
28
|
-
#
|
29
|
-
# @TODO Experimental. Support for every kind of query is pending
|
30
|
-
# @TODO It should not patch anything if not needed (no previous schemes or no support for previous encryption schemes)
|
31
3
|
module ActiveRecord
|
32
4
|
module Encryption
|
5
|
+
# Automatically expand encrypted arguments to support querying both encrypted and unencrypted data
|
6
|
+
#
|
7
|
+
# Active Record Encryption supports querying the db using deterministic attributes. For example:
|
8
|
+
#
|
9
|
+
# Contact.find_by(email_address: "jorge@hey.com")
|
10
|
+
#
|
11
|
+
# The value "jorge@hey.com" will get encrypted automatically to perform the query. But there is
|
12
|
+
# a problem while the data is being encrypted. This won't work. During that time, you need these
|
13
|
+
# queries to be:
|
14
|
+
#
|
15
|
+
# Contact.find_by(email_address: [ "jorge@hey.com", "<encrypted jorge@hey.com>" ])
|
16
|
+
#
|
17
|
+
# This patches ActiveRecord to support this automatically. It addresses both:
|
18
|
+
#
|
19
|
+
# * ActiveRecord::Base: Used in +Contact.find_by_email_address(...)+
|
20
|
+
# * ActiveRecord::Relation: Used in +Contact.internal.find_by_email_address(...)+
|
21
|
+
#
|
22
|
+
# +ActiveRecord::Base+ relies on +ActiveRecord::Relation+ (+ActiveRecord::QueryMethods+) but it does
|
23
|
+
# some prepared statements caching. That's why we need to intercept +ActiveRecord::Base+ as soon
|
24
|
+
# as it's invoked (so that the proper prepared statement is cached).
|
25
|
+
#
|
26
|
+
# When modifying this file run performance tests in +test/performance/extended_deterministic_queries_performance_test.rb+ to
|
27
|
+
# make sure performance overhead is acceptable.
|
28
|
+
#
|
29
|
+
# We will extend this to support previous "encryption context" versions in future iterations
|
30
|
+
#
|
31
|
+
# @TODO Experimental. Support for every kind of query is pending
|
32
|
+
# @TODO It should not patch anything if not needed (no previous schemes or no support for previous encryption schemes)
|
33
33
|
module ExtendedDeterministicQueries
|
34
34
|
def self.install_support
|
35
35
|
ActiveRecord::Relation.prepend(RelationQueries)
|
@@ -92,6 +92,22 @@ module ActiveRecord
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
module SQLite3
|
96
|
+
module TableDefinition
|
97
|
+
def references(*args, **options)
|
98
|
+
args.each do |ref_name|
|
99
|
+
ReferenceDefinition.new(ref_name, type: :integer, **options).add_to(self)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
alias :belongs_to :references
|
103
|
+
|
104
|
+
def column(name, type, index: nil, **options)
|
105
|
+
options[:precision] ||= nil
|
106
|
+
super
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
95
111
|
module TableDefinition
|
96
112
|
def references(*args, **options)
|
97
113
|
args.each do |ref_name|
|
@@ -131,8 +147,13 @@ module ActiveRecord
|
|
131
147
|
end
|
132
148
|
|
133
149
|
def add_reference(table_name, ref_name, **options)
|
134
|
-
|
135
|
-
.
|
150
|
+
if connection.adapter_name == "SQLite"
|
151
|
+
reference_definition = ReferenceDefinition.new(ref_name, type: :integer, **options)
|
152
|
+
else
|
153
|
+
reference_definition = ReferenceDefinition.new(ref_name, **options)
|
154
|
+
end
|
155
|
+
|
156
|
+
reference_definition.add_to(connection.update_table_definition(table_name, self))
|
136
157
|
end
|
137
158
|
alias :add_belongs_to :add_reference
|
138
159
|
|
@@ -140,6 +161,7 @@ module ActiveRecord
|
|
140
161
|
def compatible_table_definition(t)
|
141
162
|
class << t
|
142
163
|
prepend TableDefinition
|
164
|
+
prepend SQLite3::TableDefinition
|
143
165
|
end
|
144
166
|
t
|
145
167
|
end
|
@@ -1080,7 +1080,7 @@ module ActiveRecord
|
|
1080
1080
|
# 0 then an empty array will be returned and no migrations
|
1081
1081
|
# will be run.
|
1082
1082
|
#
|
1083
|
-
# If the +current_version+ in the schema is
|
1083
|
+
# If the +current_version+ in the schema is greater than
|
1084
1084
|
# the +target_version+, then +down+ will be run.
|
1085
1085
|
#
|
1086
1086
|
# If none of the conditions are met, +up+ will be run with
|
@@ -1031,7 +1031,7 @@ module ActiveRecord
|
|
1031
1031
|
end
|
1032
1032
|
|
1033
1033
|
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1034
|
-
scopes = @previous_reflection.join_scopes(table, predicate_builder, record) + super
|
1034
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
1035
1035
|
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1036
1036
|
end
|
1037
1037
|
|
@@ -155,7 +155,7 @@ module ActiveRecord
|
|
155
155
|
end
|
156
156
|
|
157
157
|
# Use #pluck as a shortcut to select one or more attributes without
|
158
|
-
# loading
|
158
|
+
# loading an entire record object per row.
|
159
159
|
#
|
160
160
|
# Person.pluck(:name)
|
161
161
|
#
|
@@ -345,12 +345,13 @@ module ActiveRecord
|
|
345
345
|
column = aggregate_column(column_name)
|
346
346
|
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
347
347
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
348
|
-
select_value.as(column_alias)
|
348
|
+
select_value.as(connection.quote_column_name(column_alias))
|
349
349
|
|
350
350
|
select_values = [select_value]
|
351
351
|
select_values += self.select_values unless having_clause.empty?
|
352
352
|
|
353
353
|
select_values.concat group_columns.map { |aliaz, field|
|
354
|
+
aliaz = connection.quote_column_name(aliaz)
|
354
355
|
if field.respond_to?(:as)
|
355
356
|
field.as(aliaz)
|
356
357
|
else
|
@@ -97,6 +97,9 @@ module ActiveRecord
|
|
97
97
|
def missing(*associations)
|
98
98
|
associations.each do |association|
|
99
99
|
reflection = @scope.klass._reflect_on_association(association)
|
100
|
+
unless reflection
|
101
|
+
raise ArgumentError.new("An association named `:#{association}` does not exist on the model `#{@scope.name}`.")
|
102
|
+
end
|
100
103
|
@scope.left_outer_joins!(association)
|
101
104
|
@scope.where!(reflection.table_name => { reflection.association_primary_key => nil })
|
102
105
|
end
|
@@ -424,18 +427,23 @@ module ActiveRecord
|
|
424
427
|
# adapter this will either use a CASE statement or a built-in function.
|
425
428
|
#
|
426
429
|
# User.in_order_of(:id, [1, 5, 3])
|
427
|
-
# # SELECT "users".* FROM "users"
|
430
|
+
# # SELECT "users".* FROM "users"
|
431
|
+
# # ORDER BY FIELD("users"."id", 1, 5, 3)
|
432
|
+
# # WHERE "users"."id" IN (1, 5, 3)
|
428
433
|
#
|
429
434
|
def in_order_of(column, values)
|
430
435
|
klass.disallow_raw_sql!([column], permit: connection.column_name_with_order_matcher)
|
436
|
+
return spawn.none! if values.empty?
|
431
437
|
|
432
438
|
references = column_references([column])
|
433
439
|
self.references_values |= references unless references.empty?
|
434
440
|
|
435
441
|
values = values.map { |value| type_caster.type_cast_for_database(column, value) }
|
436
|
-
|
442
|
+
arel_column = column.is_a?(Symbol) ? order_column(column.to_s) : column
|
437
443
|
|
438
|
-
spawn
|
444
|
+
spawn
|
445
|
+
.order!(connection.field_ordered_value(arel_column, values))
|
446
|
+
.where!(arel_column.in(values))
|
439
447
|
end
|
440
448
|
|
441
449
|
# Replaces any existing order defined on the relation with the specified order.
|
@@ -33,7 +33,6 @@ module ActiveRecord
|
|
33
33
|
@delegate_to_klass = false
|
34
34
|
@future_result = nil
|
35
35
|
@records = nil
|
36
|
-
@limited_count = nil
|
37
36
|
end
|
38
37
|
|
39
38
|
def initialize_copy(other)
|
@@ -647,6 +646,21 @@ module ActiveRecord
|
|
647
646
|
# Schedule the query to be performed from a background thread pool.
|
648
647
|
#
|
649
648
|
# Post.where(published: true).load_async # => #<ActiveRecord::Relation>
|
649
|
+
#
|
650
|
+
# When the +Relation+ is iterated, if the background query wasn't executed yet,
|
651
|
+
# it will be performed by the foreground thread.
|
652
|
+
#
|
653
|
+
# Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
|
654
|
+
# for queries to actually be executed concurrently. Otherwise it defaults to
|
655
|
+
# executing them in the foreground.
|
656
|
+
#
|
657
|
+
# +load_async+ will also fallback to executing in the foreground in the test environment when transactional
|
658
|
+
# fixtures are enabled.
|
659
|
+
#
|
660
|
+
# If the query was actually executed in the background, the Active Record logs will show
|
661
|
+
# it by prefixing the log line with <tt>ASYNC</tt>:
|
662
|
+
#
|
663
|
+
# ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
|
650
664
|
def load_async
|
651
665
|
return load if !connection.async_enabled?
|
652
666
|
|
@@ -699,7 +713,6 @@ module ActiveRecord
|
|
699
713
|
@offsets = @take = nil
|
700
714
|
@cache_keys = nil
|
701
715
|
@records = nil
|
702
|
-
@limited_count = nil
|
703
716
|
self
|
704
717
|
end
|
705
718
|
|
@@ -974,7 +987,7 @@ module ActiveRecord
|
|
974
987
|
end
|
975
988
|
|
976
989
|
def limited_count
|
977
|
-
|
990
|
+
limit_value ? count : limit(2).count
|
978
991
|
end
|
979
992
|
end
|
980
993
|
end
|
@@ -257,8 +257,12 @@ module ActiveRecord
|
|
257
257
|
scope = ENV["SCOPE"]
|
258
258
|
verbose_was, Migration.verbose = Migration.verbose, verbose?
|
259
259
|
|
260
|
-
Base.connection.migration_context.migrate(target_version
|
261
|
-
|
260
|
+
Base.connection.migration_context.migrate(target_version) do |migration|
|
261
|
+
if version.blank?
|
262
|
+
scope.blank? || scope == migration.scope
|
263
|
+
else
|
264
|
+
migration.version == version
|
265
|
+
end
|
262
266
|
end.tap do |migrations_ran|
|
263
267
|
Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
|
264
268
|
end
|
@@ -58,7 +58,7 @@ module ActiveRecord
|
|
58
58
|
end
|
59
59
|
|
60
60
|
args = ["--schema-only", "--no-privileges", "--no-owner"]
|
61
|
-
args << "--no-
|
61
|
+
args << "--no-comments" if connection.database_version >= 110_000
|
62
62
|
args.concat(["--file", filename])
|
63
63
|
|
64
64
|
args.concat(Array(extra_flags)) if extra_flags
|
data/lib/active_record.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright (c) 2004-
|
4
|
+
# Copyright (c) 2004-2022 David Heinemeier Hansson
|
5
5
|
#
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining
|
7
7
|
# a copy of this software and associated documentation files (the
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/active_record"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module Generators # :nodoc:
|
7
|
+
class MultiDbGenerator < ::Rails::Generators::Base # :nodoc:
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
9
|
+
|
10
|
+
def create_multi_db
|
11
|
+
filename = "multi_db.rb"
|
12
|
+
template filename, "config/initializers/#{filename}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Multi-db Configuration
|
2
|
+
#
|
3
|
+
# This file is used for configuration settings related to multiple databases.
|
4
|
+
#
|
5
|
+
# Enable Database Selector
|
6
|
+
#
|
7
|
+
# Inserts middleware to perform automatic connection switching.
|
8
|
+
# The `database_selector` hash is used to pass options to the DatabaseSelector
|
9
|
+
# middleware. The `delay` is used to determine how long to wait after a write
|
10
|
+
# to send a subsequent read to the primary.
|
11
|
+
#
|
12
|
+
# The `database_resolver` class is used by the middleware to determine which
|
13
|
+
# database is appropriate to use based on the time delay.
|
14
|
+
#
|
15
|
+
# The `database_resolver_context` class is used by the middleware to set
|
16
|
+
# timestamps for the last write to the primary. The resolver uses the context
|
17
|
+
# class timestamps to determine how long to wait before reading from the
|
18
|
+
# replica.
|
19
|
+
#
|
20
|
+
# By default Rails will store a last write timestamp in the session. The
|
21
|
+
# DatabaseSelector middleware is designed as such you can define your own
|
22
|
+
# strategy for connection switching and pass that into the middleware through
|
23
|
+
# these configuration options.
|
24
|
+
#
|
25
|
+
# Rails.application.configure do
|
26
|
+
# config.active_record.database_selector = { delay: 2.seconds }
|
27
|
+
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
|
28
|
+
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# Enable Shard Selector
|
32
|
+
#
|
33
|
+
# Inserts middleware to perform automatic shard swapping. The `shard_selector` hash
|
34
|
+
# can be used to pass options to the `ShardSelector` middleware. The `lock` option is
|
35
|
+
# used to determine whether shard swapping should be prohibited for the request.
|
36
|
+
#
|
37
|
+
# The `shard_resolver` option is used by the middleware to determine which shard
|
38
|
+
# to switch to. The application must provide a mechanism for finding the shard name
|
39
|
+
# in a proc. See guides for an example.
|
40
|
+
#
|
41
|
+
# Rails.application.configure do
|
42
|
+
# config.active_record.shard_selector = { lock: true }
|
43
|
+
# config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard }
|
44
|
+
# end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.
|
4
|
+
version: 7.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 7.0.
|
19
|
+
version: 7.0.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 7.0.
|
26
|
+
version: 7.0.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 7.0.
|
33
|
+
version: 7.0.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 7.0.
|
40
|
+
version: 7.0.1
|
41
41
|
description: Databases on Rails. Build a persistent domain model by mapping database
|
42
42
|
tables to Ruby classes. Strong conventions for associations, validations, aggregations,
|
43
43
|
migrations, and testing come baked-in.
|
@@ -427,15 +427,17 @@ files:
|
|
427
427
|
- lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt
|
428
428
|
- lib/rails/generators/active_record/model/templates/model.rb.tt
|
429
429
|
- lib/rails/generators/active_record/model/templates/module.rb.tt
|
430
|
+
- lib/rails/generators/active_record/multi_db/multi_db_generator.rb
|
431
|
+
- lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt
|
430
432
|
homepage: https://rubyonrails.org
|
431
433
|
licenses:
|
432
434
|
- MIT
|
433
435
|
metadata:
|
434
436
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
435
|
-
changelog_uri: https://github.com/rails/rails/blob/v7.0.
|
436
|
-
documentation_uri: https://api.rubyonrails.org/v7.0.
|
437
|
+
changelog_uri: https://github.com/rails/rails/blob/v7.0.1/activerecord/CHANGELOG.md
|
438
|
+
documentation_uri: https://api.rubyonrails.org/v7.0.1/
|
437
439
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
438
|
-
source_code_uri: https://github.com/rails/rails/tree/v7.0.
|
440
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.0.1/activerecord
|
439
441
|
rubygems_mfa_required: 'true'
|
440
442
|
post_install_message:
|
441
443
|
rdoc_options:
|
@@ -450,11 +452,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
450
452
|
version: 2.7.0
|
451
453
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
452
454
|
requirements:
|
453
|
-
- - "
|
455
|
+
- - ">="
|
454
456
|
- !ruby/object:Gem::Version
|
455
|
-
version:
|
457
|
+
version: '0'
|
456
458
|
requirements: []
|
457
|
-
rubygems_version: 3.2.
|
459
|
+
rubygems_version: 3.2.32
|
458
460
|
signing_key:
|
459
461
|
specification_version: 4
|
460
462
|
summary: Object-relational mapper framework (part of Rails).
|