diff options
author | Matt Yoho <mby@mattyoho.com> | 2019-03-14 12:57:14 -0700 |
---|---|---|
committer | Matt Yoho <mby@mattyoho.com> | 2019-03-21 20:30:56 -0700 |
commit | f41825809cf3d27f98467bd7bc005e5d6ed94828 (patch) | |
tree | c6a7f1bc8670bffc2c20bc71401b0b58d621d4d1 /activerecord/test/cases/relation_test.rb | |
parent | efb706daad0e2e1039c6abb4879c837ef8bf4d10 (diff) | |
download | rails-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/test/cases/relation_test.rb')
-rw-r--r-- | activerecord/test/cases/relation_test.rb | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 68161f6a84..00a7b3841f 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -307,6 +307,58 @@ module ActiveRecord assert_equal 3, ratings.count end + def test_relation_with_annotation_includes_comment_in_to_sql + post_with_annotation = Post.where(id: 1).annotate("foo") + assert_match %r{= 1 /\* foo \*/}, post_with_annotation.to_sql + end + + def test_relation_with_annotation_includes_comment_in_sql + post_with_annotation = Post.where(id: 1).annotate("foo") + assert_sql(%r{/\* foo \*/}) do + assert post_with_annotation.first, "record should be found" + end + end + + def test_relation_with_annotation_chains_sql_comments + post_with_annotation = Post.where(id: 1).annotate("foo").annotate("bar") + assert_sql(%r{/\* foo \*/ /\* bar \*/}) do + assert post_with_annotation.first, "record should be found" + end + end + + def test_relation_with_annotation_filters_sql_comment_delimiters + post_with_annotation = Post.where(id: 1).annotate("**//foo//**") + assert_match %r{= 1 /\* foo \*/}, post_with_annotation.to_sql + end + + def test_relation_with_annotation_includes_comment_in_count_query + post_with_annotation = Post.annotate("foo") + all_count = Post.all.to_a.count + assert_sql(%r{/\* foo \*/}) do + assert_equal all_count, post_with_annotation.count + end + end + + def test_relation_without_annotation_does_not_include_an_empty_comment + log = capture_sql do + Post.where(id: 1).first + end + + assert_not_predicate log, :empty? + assert_predicate log.select { |query| query.match?(%r{/\*}) }, :empty? + end + + def test_relation_with_optimizer_hints_filters_sql_comment_delimiters + post_with_hint = Post.where(id: 1).optimizer_hints("**//BADHINT//**") + assert_match %r{BADHINT}, post_with_hint.to_sql + assert_no_match %r{\*/BADHINT}, post_with_hint.to_sql + assert_no_match %r{\*//BADHINT}, post_with_hint.to_sql + assert_no_match %r{BADHINT/\*}, post_with_hint.to_sql + assert_no_match %r{BADHINT//\*}, post_with_hint.to_sql + post_with_hint = Post.where(id: 1).optimizer_hints("/*+ BADHINT */") + assert_match %r{/\*\+ BADHINT \*/}, post_with_hint.to_sql + end + class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value def type :string |