aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation
diff options
context:
space:
mode:
authorMatt Yoho <mby@mattyoho.com>2019-03-14 12:57:14 -0700
committerMatt Yoho <mby@mattyoho.com>2019-03-21 20:30:56 -0700
commitf41825809cf3d27f98467bd7bc005e5d6ed94828 (patch)
treec6a7f1bc8670bffc2c20bc71401b0b58d621d4d1 /activerecord/lib/active_record/relation
parentefb706daad0e2e1039c6abb4879c837ef8bf4d10 (diff)
downloadrails-f41825809cf3d27f98467bd7bc005e5d6ed94828.tar.gz
rails-f41825809cf3d27f98467bd7bc005e5d6ed94828.tar.bz2
rails-f41825809cf3d27f98467bd7bc005e5d6ed94828.zip
Add Relation#annotate for SQL commenting
This patch has two main portions: 1. Add SQL comment support to Arel via Arel::Nodes::Comment. 2. Implement a Relation#annotate method on top of that. == Adding SQL comment support Adds a new Arel::Nodes::Comment node that represents an optional SQL comment and teachers the relevant visitors how to handle it. Comment nodes may be added to the basic CRUD statement nodes and set through any of the four (Select|Insert|Update|Delete)Manager objects. For example: manager = Arel::UpdateManager.new manager.table table manager.comment("annotation") manager.to_sql # UPDATE "users" /* annotation */ This new node type will be used by ActiveRecord::Relation to enable query annotation via SQL comments. == Implementing the Relation#annotate method Implements `ActiveRecord::Relation#annotate`, which accepts a comment string that will be appeneded to any queries generated by the relation. Some examples: relation = Post.where(id: 123).annotate("metadata string") relation.first # SELECT "posts".* FROM "posts" WHERE "posts"."id" = 123 # LIMIT 1 /* metadata string */ class Tag < ActiveRecord::Base scope :foo_annotated, -> { annotate("foo") } end Tag.foo_annotated.annotate("bar").first # SELECT "tags".* FROM "tags" LIMIT 1 /* foo */ /* bar */ Also wires up the plumbing so this works with `#update_all` and `#delete_all` as well. This feature is useful for instrumentation and general analysis of queries generated at runtime.
Diffstat (limited to 'activerecord/lib/active_record/relation')
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb23
1 files changed, 22 insertions, 1 deletions
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 05ea0850d3..6f0f2125dc 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -349,7 +349,7 @@ module ActiveRecord
end
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
- :limit, :offset, :joins, :left_outer_joins,
+ :limit, :offset, :joins, :left_outer_joins, :annotate,
:includes, :from, :readonly, :having, :optimizer_hints])
# Removes an unwanted relation that is already defined on a chain of relations.
@@ -948,6 +948,26 @@ module ActiveRecord
self
end
+ # Adds an SQL comment to queries generated from this relation. For example:
+ #
+ # User.annotate("selecting user names").select(:name)
+ # # SELECT "users"."name" FROM "users" /* selecting user names */
+ #
+ # User.annotate("selecting", "user", "names").select(:name)
+ # # SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */
+ #
+ # The SQL block comment delimiters, "/*" and "*/", will be added automatically.
+ def annotate(*args)
+ check_if_method_has_arguments!(:annotate, args)
+ spawn.annotate!(*args)
+ end
+
+ # Like #annotate, but modifies relation in place.
+ def annotate!(*args) # :nodoc:
+ self.annotate_values += args
+ self
+ end
+
# Returns the Arel object associated with the relation.
def arel(aliases = nil) # :nodoc:
@arel ||= build_arel(aliases)
@@ -1004,6 +1024,7 @@ module ActiveRecord
arel.distinct(distinct_value)
arel.from(build_from) unless from_clause.empty?
arel.lock(lock_value) if lock_value
+ arel.comment(*annotate_values) unless annotate_values.empty?
arel
end