Merge pull request #46948 from olefriis/add-arel-node-for-simple-sql-construction
Add Arel functionality for "stitching together" SQL
This commit is contained in:
commit
b73fb939de
@ -1,3 +1,9 @@
|
||||
* Multiple `Arel::Nodes::SqlLiteral` nodes can now be added together to
|
||||
form `Arel::Nodes::Fragments` nodes. This allows joining several pieces
|
||||
of SQL.
|
||||
|
||||
*Matthew Draper*, *Ole Friis*
|
||||
|
||||
* `ActiveRecord::Base#signed_id` raises if called on a new record
|
||||
|
||||
Previously it would return an ID that was not usable, since it was based on `id = nil`.
|
||||
|
@ -8,6 +8,7 @@
|
||||
require "arel/nodes/insert_statement"
|
||||
require "arel/nodes/update_statement"
|
||||
require "arel/nodes/bind_param"
|
||||
require "arel/nodes/fragments"
|
||||
|
||||
# terminal
|
||||
|
||||
|
35
activerecord/lib/arel/nodes/fragments.rb
Normal file
35
activerecord/lib/arel/nodes/fragments.rb
Normal file
@ -0,0 +1,35 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Arel # :nodoc: all
|
||||
module Nodes
|
||||
class Fragments < Arel::Nodes::Node
|
||||
attr_reader :values
|
||||
|
||||
def initialize(values = [])
|
||||
super()
|
||||
@values = values
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
super
|
||||
@values = @values.clone
|
||||
end
|
||||
|
||||
def hash
|
||||
[@values].hash
|
||||
end
|
||||
|
||||
def +(other)
|
||||
raise ArgumentError, "Expected Arel node" unless Arel.arel_node?(other)
|
||||
|
||||
self.class.new([*@values, other])
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
self.class == other.class &&
|
||||
self.values == other.values
|
||||
end
|
||||
alias :== :eql?
|
||||
end
|
||||
end
|
||||
end
|
@ -14,6 +14,12 @@ def encode_with(coder)
|
||||
|
||||
def fetch_attribute
|
||||
end
|
||||
|
||||
def +(other)
|
||||
raise ArgumentError, "Expected Arel node" unless Arel.arel_node?(other)
|
||||
|
||||
Fragments.new([self, other])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -791,6 +791,10 @@ def visit_Array(o, collector)
|
||||
end
|
||||
alias :visit_Set :visit_Array
|
||||
|
||||
def visit_Arel_Nodes_Fragments(o, collector)
|
||||
inject_join o.values, collector, " "
|
||||
end
|
||||
|
||||
def quote(value)
|
||||
return value if Arel::Nodes::SqlLiteral === value
|
||||
@connection.quote value
|
||||
|
38
activerecord/test/cases/arel/nodes/fragments_test.rb
Normal file
38
activerecord/test/cases/arel/nodes/fragments_test.rb
Normal file
@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../helper"
|
||||
require "yaml"
|
||||
|
||||
module Arel
|
||||
module Nodes
|
||||
class FragmentsTest < Arel::Spec
|
||||
describe "equality" do
|
||||
it "is equal with equal values" do
|
||||
array = [Fragments.new(["foo", "bar"]), Fragments.new(["foo", "bar"])]
|
||||
assert_equal 1, array.uniq.size
|
||||
end
|
||||
|
||||
it "is not equal with different values" do
|
||||
array = [Fragments.new(["foo"]), Fragments.new(["bar"])]
|
||||
assert_equal 2, array.uniq.size
|
||||
end
|
||||
|
||||
it "can be joined with other nodes" do
|
||||
fragments = Fragments.new(["foo", "bar"])
|
||||
sql = Arel.sql("SELECT")
|
||||
joined_fragments = fragments + sql
|
||||
|
||||
assert_equal ["foo", "bar"], fragments.values
|
||||
assert_equal ["foo", "bar", sql], joined_fragments.values
|
||||
end
|
||||
|
||||
it "fails if joined with something that is not an Arel node" do
|
||||
fragments = Fragments.new
|
||||
assert_raises ArgumentError do
|
||||
fragments + "Not a node"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -70,6 +70,23 @@ def compile(node)
|
||||
assert_equal("foo", YAML.load(yaml_literal))
|
||||
end
|
||||
end
|
||||
|
||||
describe "addition" do
|
||||
it "generates a Fragments node" do
|
||||
sql1 = Arel.sql "SELECT *"
|
||||
sql2 = Arel.sql "FROM users"
|
||||
fragments = sql1 + sql2
|
||||
_(fragments).must_be_kind_of Arel::Nodes::Fragments
|
||||
assert_equal([sql1, sql2], fragments.values)
|
||||
end
|
||||
|
||||
it "fails if joined with something that is not an Arel node" do
|
||||
sql = Arel.sql "SELECT *"
|
||||
assert_raises ArgumentError do
|
||||
sql + "Not a node"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -768,6 +768,20 @@ def dispatch
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "Nodes::Fragments" do
|
||||
it "joins subexpressions" do
|
||||
sql = Arel.sql("SELECT foo, bar") + Arel.sql(" FROM customers")
|
||||
_(compile(sql)).must_be_like "SELECT foo, bar FROM customers"
|
||||
end
|
||||
|
||||
it "can be built by adding SQL fragments one at a time" do
|
||||
sql = Arel.sql("SELECT foo, bar")
|
||||
sql += Arel.sql("FROM customers")
|
||||
sql += Arel.sql("GROUP BY foo")
|
||||
_(compile(sql)).must_be_like "SELECT foo, bar FROM customers GROUP BY foo"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user