activerecord 5.2.3
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 +7 -0
- data/CHANGELOG.md +937 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +217 -0
- data/examples/performance.rb +185 -0
- data/examples/simple.rb +15 -0
- data/lib/active_record.rb +188 -0
- data/lib/active_record/aggregations.rb +283 -0
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations.rb +1860 -0
- data/lib/active_record/associations/alias_tracker.rb +81 -0
- data/lib/active_record/associations/association.rb +299 -0
- data/lib/active_record/associations/association_scope.rb +168 -0
- data/lib/active_record/associations/belongs_to_association.rb +130 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
- data/lib/active_record/associations/builder/association.rb +140 -0
- data/lib/active_record/associations/builder/belongs_to.rb +163 -0
- data/lib/active_record/associations/builder/collection_association.rb +82 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +135 -0
- data/lib/active_record/associations/builder/has_many.rb +17 -0
- data/lib/active_record/associations/builder/has_one.rb +30 -0
- data/lib/active_record/associations/builder/singular_association.rb +42 -0
- data/lib/active_record/associations/collection_association.rb +513 -0
- data/lib/active_record/associations/collection_proxy.rb +1131 -0
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +144 -0
- data/lib/active_record/associations/has_many_through_association.rb +227 -0
- data/lib/active_record/associations/has_one_association.rb +120 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency.rb +262 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +60 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/preloader.rb +193 -0
- data/lib/active_record/associations/preloader/association.rb +131 -0
- data/lib/active_record/associations/preloader/through_association.rb +107 -0
- data/lib/active_record/associations/singular_association.rb +73 -0
- data/lib/active_record/associations/through_association.rb +121 -0
- data/lib/active_record/attribute_assignment.rb +88 -0
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods.rb +492 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +78 -0
- data/lib/active_record/attribute_methods/dirty.rb +150 -0
- data/lib/active_record/attribute_methods/primary_key.rb +143 -0
- data/lib/active_record/attribute_methods/query.rb +42 -0
- data/lib/active_record/attribute_methods/read.rb +85 -0
- data/lib/active_record/attribute_methods/serialization.rb +90 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
- data/lib/active_record/attribute_methods/write.rb +68 -0
- data/lib/active_record/attributes.rb +266 -0
- data/lib/active_record/autosave_association.rb +498 -0
- data/lib/active_record/base.rb +329 -0
- data/lib/active_record/callbacks.rb +353 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +50 -0
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1068 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +72 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +540 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +145 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +200 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +685 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1396 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +628 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +887 -0
- data/lib/active_record/connection_adapters/column.rb +91 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +129 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +863 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +118 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +573 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
- data/lib/active_record/connection_handling.rb +145 -0
- data/lib/active_record/core.rb +559 -0
- data/lib/active_record/counter_cache.rb +218 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +122 -0
- data/lib/active_record/enum.rb +244 -0
- data/lib/active_record/errors.rb +380 -0
- data/lib/active_record/explain.rb +50 -0
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixtures.rb +1065 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +283 -0
- data/lib/active_record/integration.rb +155 -0
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +198 -0
- data/lib/active_record/locking/pessimistic.rb +89 -0
- data/lib/active_record/log_subscriber.rb +137 -0
- data/lib/active_record/migration.rb +1378 -0
- data/lib/active_record/migration/command_recorder.rb +240 -0
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/model_schema.rb +521 -0
- data/lib/active_record/nested_attributes.rb +600 -0
- data/lib/active_record/no_touching.rb +58 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +763 -0
- data/lib/active_record/query_cache.rb +45 -0
- data/lib/active_record/querying.rb +70 -0
- data/lib/active_record/railtie.rb +226 -0
- data/lib/active_record/railties/console_sandbox.rb +7 -0
- data/lib/active_record/railties/controller_runtime.rb +56 -0
- data/lib/active_record/railties/databases.rake +377 -0
- data/lib/active_record/readonly_attributes.rb +24 -0
- data/lib/active_record/reflection.rb +1044 -0
- data/lib/active_record/relation.rb +629 -0
- data/lib/active_record/relation/batches.rb +287 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/calculations.rb +417 -0
- data/lib/active_record/relation/delegation.rb +147 -0
- data/lib/active_record/relation/finder_methods.rb +565 -0
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder.rb +152 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +1231 -0
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +77 -0
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/result.rb +149 -0
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +222 -0
- data/lib/active_record/schema.rb +70 -0
- data/lib/active_record/schema_dumper.rb +255 -0
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping.rb +106 -0
- data/lib/active_record/scoping/default.rb +152 -0
- data/lib/active_record/scoping/named.rb +213 -0
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/statement_cache.rb +121 -0
- data/lib/active_record/store.rb +211 -0
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +337 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
- data/lib/active_record/timestamp.rb +153 -0
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +502 -0
- data/lib/active_record/translation.rb +24 -0
- data/lib/active_record/type.rb +79 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/validations.rb +93 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +60 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +238 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/rails/generators/active_record.rb +19 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration.rb +35 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +78 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
- metadata +333 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Relation
|
5
|
+
module RecordFetchWarning
|
6
|
+
# When this module is prepended to ActiveRecord::Relation and
|
7
|
+
# +config.active_record.warn_on_records_fetched_greater_than+ is
|
8
|
+
# set to an integer, if the number of records a query returns is
|
9
|
+
# greater than the value of +warn_on_records_fetched_greater_than+,
|
10
|
+
# a warning is logged. This allows for the detection of queries that
|
11
|
+
# return a large number of records, which could cause memory bloat.
|
12
|
+
#
|
13
|
+
# In most cases, fetching large number of records can be performed
|
14
|
+
# efficiently using the ActiveRecord::Batches methods.
|
15
|
+
# See ActiveRecord::Batches for more information.
|
16
|
+
def exec_queries
|
17
|
+
QueryRegistry.reset
|
18
|
+
|
19
|
+
super.tap do
|
20
|
+
if logger && warn_on_records_fetched_greater_than
|
21
|
+
if @records.length > warn_on_records_fetched_greater_than
|
22
|
+
logger.warn "Query fetched #{@records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# :stopdoc:
|
29
|
+
ActiveSupport::Notifications.subscribe("sql.active_record") do |*, payload|
|
30
|
+
QueryRegistry.queries << payload[:sql]
|
31
|
+
end
|
32
|
+
# :startdoc:
|
33
|
+
|
34
|
+
class QueryRegistry # :nodoc:
|
35
|
+
extend ActiveSupport::PerThreadRegistry
|
36
|
+
|
37
|
+
attr_reader :queries
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@queries = []
|
41
|
+
end
|
42
|
+
|
43
|
+
def reset
|
44
|
+
@queries.clear
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
ActiveRecord::Relation.prepend ActiveRecord::Relation::RecordFetchWarning
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/except"
|
4
|
+
require "active_support/core_ext/hash/slice"
|
5
|
+
require "active_record/relation/merger"
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module SpawnMethods
|
9
|
+
# This is overridden by Associations::CollectionProxy
|
10
|
+
def spawn #:nodoc:
|
11
|
+
@delegate_to_klass ? klass.all : clone
|
12
|
+
end
|
13
|
+
|
14
|
+
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
|
15
|
+
# Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
|
16
|
+
#
|
17
|
+
# Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
|
18
|
+
# # Performs a single join query with both where conditions.
|
19
|
+
#
|
20
|
+
# recent_posts = Post.order('created_at DESC').first(5)
|
21
|
+
# Post.where(published: true).merge(recent_posts)
|
22
|
+
# # Returns the intersection of all published posts with the 5 most recently created posts.
|
23
|
+
# # (This is just an example. You'd probably want to do this with a single query!)
|
24
|
+
#
|
25
|
+
# Procs will be evaluated by merge:
|
26
|
+
#
|
27
|
+
# Post.where(published: true).merge(-> { joins(:comments) })
|
28
|
+
# # => Post.where(published: true).joins(:comments)
|
29
|
+
#
|
30
|
+
# This is mainly intended for sharing common conditions between multiple associations.
|
31
|
+
def merge(other)
|
32
|
+
if other.is_a?(Array)
|
33
|
+
records & other
|
34
|
+
elsif other
|
35
|
+
spawn.merge!(other)
|
36
|
+
else
|
37
|
+
raise ArgumentError, "invalid argument: #{other.inspect}."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def merge!(other) # :nodoc:
|
42
|
+
if other.is_a?(Hash)
|
43
|
+
Relation::HashMerger.new(self, other).merge
|
44
|
+
elsif other.is_a?(Relation)
|
45
|
+
Relation::Merger.new(self, other).merge
|
46
|
+
elsif other.respond_to?(:to_proc)
|
47
|
+
instance_exec(&other)
|
48
|
+
else
|
49
|
+
raise ArgumentError, "#{other.inspect} is not an ActiveRecord::Relation"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Removes from the query the condition(s) specified in +skips+.
|
54
|
+
#
|
55
|
+
# Post.order('id asc').except(:order) # discards the order condition
|
56
|
+
# Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
|
57
|
+
def except(*skips)
|
58
|
+
relation_with values.except(*skips)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Removes any condition from the query other than the one(s) specified in +onlies+.
|
62
|
+
#
|
63
|
+
# Post.order('id asc').only(:where) # discards the order condition
|
64
|
+
# Post.order('id asc').only(:where, :order) # uses the specified order
|
65
|
+
def only(*onlies)
|
66
|
+
relation_with values.slice(*onlies)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def relation_with(values)
|
72
|
+
result = Relation.create(klass, values: values)
|
73
|
+
result.extend(*extending_values) if extending_values.any?
|
74
|
+
result
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Relation
|
5
|
+
class WhereClause # :nodoc:
|
6
|
+
delegate :any?, :empty?, to: :predicates
|
7
|
+
|
8
|
+
def initialize(predicates)
|
9
|
+
@predicates = predicates
|
10
|
+
end
|
11
|
+
|
12
|
+
def +(other)
|
13
|
+
WhereClause.new(
|
14
|
+
predicates + other.predicates,
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def -(other)
|
19
|
+
WhereClause.new(
|
20
|
+
predicates - other.predicates,
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge(other)
|
25
|
+
WhereClause.new(
|
26
|
+
predicates_unreferenced_by(other) + other.predicates,
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def except(*columns)
|
31
|
+
WhereClause.new(except_predicates(columns))
|
32
|
+
end
|
33
|
+
|
34
|
+
def or(other)
|
35
|
+
left = self - other
|
36
|
+
common = self - left
|
37
|
+
right = other - common
|
38
|
+
|
39
|
+
if left.empty? || right.empty?
|
40
|
+
common
|
41
|
+
else
|
42
|
+
or_clause = WhereClause.new(
|
43
|
+
[left.ast.or(right.ast)],
|
44
|
+
)
|
45
|
+
common + or_clause
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_h(table_name = nil)
|
50
|
+
equalities = equalities(predicates)
|
51
|
+
if table_name
|
52
|
+
equalities = equalities.select do |node|
|
53
|
+
node.left.relation.name == table_name
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
equalities.map { |node|
|
58
|
+
name = node.left.name.to_s
|
59
|
+
value = extract_node_value(node.right)
|
60
|
+
[name, value]
|
61
|
+
}.to_h
|
62
|
+
end
|
63
|
+
|
64
|
+
def ast
|
65
|
+
Arel::Nodes::And.new(predicates_with_wrapped_sql_literals)
|
66
|
+
end
|
67
|
+
|
68
|
+
def ==(other)
|
69
|
+
other.is_a?(WhereClause) &&
|
70
|
+
predicates == other.predicates
|
71
|
+
end
|
72
|
+
|
73
|
+
def invert
|
74
|
+
WhereClause.new(inverted_predicates)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.empty
|
78
|
+
@empty ||= new([])
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
attr_reader :predicates
|
84
|
+
|
85
|
+
def referenced_columns
|
86
|
+
@referenced_columns ||= begin
|
87
|
+
equality_nodes = predicates.select { |n| equality_node?(n) }
|
88
|
+
Set.new(equality_nodes, &:left)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def equalities(predicates)
|
94
|
+
equalities = []
|
95
|
+
|
96
|
+
predicates.each do |node|
|
97
|
+
case node
|
98
|
+
when Arel::Nodes::Equality
|
99
|
+
equalities << node
|
100
|
+
when Arel::Nodes::And
|
101
|
+
equalities.concat equalities(node.children)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
equalities
|
106
|
+
end
|
107
|
+
|
108
|
+
def predicates_unreferenced_by(other)
|
109
|
+
predicates.reject do |n|
|
110
|
+
equality_node?(n) && other.referenced_columns.include?(n.left)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def equality_node?(node)
|
115
|
+
node.respond_to?(:operator) && node.operator == :==
|
116
|
+
end
|
117
|
+
|
118
|
+
def inverted_predicates
|
119
|
+
predicates.map { |node| invert_predicate(node) }
|
120
|
+
end
|
121
|
+
|
122
|
+
def invert_predicate(node)
|
123
|
+
case node
|
124
|
+
when NilClass
|
125
|
+
raise ArgumentError, "Invalid argument for .where.not(), got nil."
|
126
|
+
when Arel::Nodes::In
|
127
|
+
Arel::Nodes::NotIn.new(node.left, node.right)
|
128
|
+
when Arel::Nodes::Equality
|
129
|
+
Arel::Nodes::NotEqual.new(node.left, node.right)
|
130
|
+
when String
|
131
|
+
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(node))
|
132
|
+
else
|
133
|
+
Arel::Nodes::Not.new(node)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def except_predicates(columns)
|
138
|
+
predicates.reject do |node|
|
139
|
+
case node
|
140
|
+
when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
141
|
+
subrelation = (node.left.kind_of?(Arel::Attributes::Attribute) ? node.left : node.right)
|
142
|
+
columns.include?(subrelation.name.to_s)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def predicates_with_wrapped_sql_literals
|
148
|
+
non_empty_predicates.map do |node|
|
149
|
+
case node
|
150
|
+
when Arel::Nodes::SqlLiteral, ::String
|
151
|
+
wrap_sql_literal(node)
|
152
|
+
else node
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
ARRAY_WITH_EMPTY_STRING = [""]
|
158
|
+
def non_empty_predicates
|
159
|
+
predicates - ARRAY_WITH_EMPTY_STRING
|
160
|
+
end
|
161
|
+
|
162
|
+
def wrap_sql_literal(node)
|
163
|
+
if ::String === node
|
164
|
+
node = Arel.sql(node)
|
165
|
+
end
|
166
|
+
Arel::Nodes::Grouping.new(node)
|
167
|
+
end
|
168
|
+
|
169
|
+
def extract_node_value(node)
|
170
|
+
case node
|
171
|
+
when Array
|
172
|
+
node.map { |v| extract_node_value(v) }
|
173
|
+
when Arel::Nodes::Casted, Arel::Nodes::Quoted
|
174
|
+
node.val
|
175
|
+
when Arel::Nodes::BindParam
|
176
|
+
value = node.value
|
177
|
+
if value.respond_to?(:value_before_type_cast)
|
178
|
+
value.value_before_type_cast
|
179
|
+
else
|
180
|
+
value
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Relation
|
5
|
+
class WhereClauseFactory # :nodoc:
|
6
|
+
def initialize(klass, predicate_builder)
|
7
|
+
@klass = klass
|
8
|
+
@predicate_builder = predicate_builder
|
9
|
+
end
|
10
|
+
|
11
|
+
def build(opts, other)
|
12
|
+
case opts
|
13
|
+
when String, Array
|
14
|
+
parts = [klass.sanitize_sql(other.empty? ? opts : ([opts] + other))]
|
15
|
+
when Hash
|
16
|
+
attributes = predicate_builder.resolve_column_aliases(opts)
|
17
|
+
attributes.stringify_keys!
|
18
|
+
|
19
|
+
parts = predicate_builder.build_from_hash(attributes)
|
20
|
+
when Arel::Nodes::Node
|
21
|
+
parts = [opts]
|
22
|
+
else
|
23
|
+
raise ArgumentError, "Unsupported argument type: #{opts} (#{opts.class})"
|
24
|
+
end
|
25
|
+
|
26
|
+
WhereClause.new(parts)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
attr_reader :klass, :predicate_builder
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
###
|
5
|
+
# This class encapsulates a result returned from calling
|
6
|
+
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
|
7
|
+
# on any database connection adapter. For example:
|
8
|
+
#
|
9
|
+
# result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
|
10
|
+
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
11
|
+
#
|
12
|
+
# # Get the column names of the result:
|
13
|
+
# result.columns
|
14
|
+
# # => ["id", "title", "body"]
|
15
|
+
#
|
16
|
+
# # Get the record values of the result:
|
17
|
+
# result.rows
|
18
|
+
# # => [[1, "title_1", "body_1"],
|
19
|
+
# [2, "title_2", "body_2"],
|
20
|
+
# ...
|
21
|
+
# ]
|
22
|
+
#
|
23
|
+
# # Get an array of hashes representing the result (column => value):
|
24
|
+
# result.to_hash
|
25
|
+
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
|
26
|
+
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
|
27
|
+
# ...
|
28
|
+
# ]
|
29
|
+
#
|
30
|
+
# # ActiveRecord::Result also includes Enumerable.
|
31
|
+
# result.each do |row|
|
32
|
+
# puts row['title'] + " " + row['body']
|
33
|
+
# end
|
34
|
+
class Result
|
35
|
+
include Enumerable
|
36
|
+
|
37
|
+
attr_reader :columns, :rows, :column_types
|
38
|
+
|
39
|
+
def initialize(columns, rows, column_types = {})
|
40
|
+
@columns = columns
|
41
|
+
@rows = rows
|
42
|
+
@hash_rows = nil
|
43
|
+
@column_types = column_types
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the number of elements in the rows array.
|
47
|
+
def length
|
48
|
+
@rows.length
|
49
|
+
end
|
50
|
+
|
51
|
+
# Calls the given block once for each element in row collection, passing
|
52
|
+
# row as parameter.
|
53
|
+
#
|
54
|
+
# Returns an +Enumerator+ if no block is given.
|
55
|
+
def each
|
56
|
+
if block_given?
|
57
|
+
hash_rows.each { |row| yield row }
|
58
|
+
else
|
59
|
+
hash_rows.to_enum { @rows.size }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns an array of hashes representing each row record.
|
64
|
+
def to_hash
|
65
|
+
hash_rows
|
66
|
+
end
|
67
|
+
|
68
|
+
alias :map! :map
|
69
|
+
alias :collect! :map
|
70
|
+
|
71
|
+
# Returns true if there are no records, otherwise false.
|
72
|
+
def empty?
|
73
|
+
rows.empty?
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns an array of hashes representing each row record.
|
77
|
+
def to_ary
|
78
|
+
hash_rows
|
79
|
+
end
|
80
|
+
|
81
|
+
def [](idx)
|
82
|
+
hash_rows[idx]
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns the first record from the rows collection.
|
86
|
+
# If the rows collection is empty, returns +nil+.
|
87
|
+
def first
|
88
|
+
return nil if @rows.empty?
|
89
|
+
Hash[@columns.zip(@rows.first)]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the last record from the rows collection.
|
93
|
+
# If the rows collection is empty, returns +nil+.
|
94
|
+
def last
|
95
|
+
return nil if @rows.empty?
|
96
|
+
Hash[@columns.zip(@rows.last)]
|
97
|
+
end
|
98
|
+
|
99
|
+
def cast_values(type_overrides = {}) # :nodoc:
|
100
|
+
types = columns.map { |name| column_type(name, type_overrides) }
|
101
|
+
result = rows.map do |values|
|
102
|
+
types.zip(values).map { |type, value| type.deserialize(value) }
|
103
|
+
end
|
104
|
+
|
105
|
+
columns.one? ? result.map!(&:first) : result
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize_copy(other)
|
109
|
+
@columns = columns.dup
|
110
|
+
@rows = rows.dup
|
111
|
+
@column_types = column_types.dup
|
112
|
+
@hash_rows = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def column_type(name, type_overrides = {})
|
118
|
+
type_overrides.fetch(name) do
|
119
|
+
column_types.fetch(name, Type.default_value)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def hash_rows
|
124
|
+
@hash_rows ||=
|
125
|
+
begin
|
126
|
+
# We freeze the strings to prevent them getting duped when
|
127
|
+
# used as keys in ActiveRecord::Base's @attributes hash
|
128
|
+
columns = @columns.map { |c| c.dup.freeze }
|
129
|
+
@rows.map { |row|
|
130
|
+
# In the past we used Hash[columns.zip(row)]
|
131
|
+
# though elegant, the verbose way is much more efficient
|
132
|
+
# both time and memory wise cause it avoids a big array allocation
|
133
|
+
# this method is called a lot and needs to be micro optimised
|
134
|
+
hash = {}
|
135
|
+
|
136
|
+
index = 0
|
137
|
+
length = columns.length
|
138
|
+
|
139
|
+
while index < length
|
140
|
+
hash[columns[index]] = row[index]
|
141
|
+
index += 1
|
142
|
+
end
|
143
|
+
|
144
|
+
hash
|
145
|
+
}
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|