Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
Brian Hempel | 3cb6e38a12 | |
Brian Hempel | 19f1137329 | |
Brian Hempel | ac7754ae76 | |
Brian Hempel | 4e14a9f3ae | |
Brian Hempel | 242d02f339 | |
Brian Hempel | 7fc35a4e4e | |
Brian Hempel | c7f0cfd22f | |
Brian Hempel | c5a2213c3d | |
Brian Hempel | 28824c3c39 | |
Gleb Mazovetskiy | dec1205c14 | |
Gleb Mazovetskiy | d4c7269b61 | |
Gleb Mazovetskiy | b0a2d4affc | |
Anuj | 00fca9a9d1 | |
Brian Hempel | c4e29e573e | |
Brian Hempel | 8695300f26 | |
Brian Hempel | 5a2e7a537f | |
Gleb Mazovetskiy | 5a47ade141 | |
Brian Hempel | 74b21c6ba1 | |
Brian Hempel | 30c171b84f |
|
@ -4,6 +4,7 @@
|
|||
.config
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
*.gemfile.lock
|
||||
InstalledFiles
|
||||
_yardoc
|
||||
coverage
|
||||
|
|
10
.travis.yml
10
.travis.yml
|
@ -2,7 +2,9 @@ language: ruby
|
|||
addons:
|
||||
postgresql: "9.4"
|
||||
rvm:
|
||||
- 2.3.0
|
||||
- 2.2.4
|
||||
- 2.1.8
|
||||
- 2.0.0
|
||||
- 2.3.6
|
||||
gemfile:
|
||||
- rails_5_0.gemfile
|
||||
- rails_5_1.gemfile
|
||||
- rails_5_2.gemfile
|
||||
script: bundle exec rspec
|
||||
|
|
30
README.md
30
README.md
|
@ -17,6 +17,10 @@ user_1.posts.union(user_2.posts).union(Post.published)
|
|||
user_1.posts.union_all(user_2.posts)
|
||||
```
|
||||
|
||||
ActiveRecordUnion is tested against Rails 5.0, 5.1, and 5.2. It should also work on Rails 4.2. It may or may not work on Rails 4.0/4.1.
|
||||
|
||||
If you are using Postgres, you might alternatively check out [ActiveRecordExtended](https://github.com/georgekaraszi/ActiveRecordExtended) which includes support for unions as well as other goodies.
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
@ -65,7 +69,7 @@ SELECT "posts".* FROM (
|
|||
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
||||
UNION
|
||||
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:04:21.918366')
|
||||
) posts
|
||||
) "posts"
|
||||
```
|
||||
|
||||
Because the `union` method returns another `ActiveRecord::Relation`, we can run further queries on the union.
|
||||
|
@ -78,7 +82,7 @@ SELECT "posts".* FROM (
|
|||
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
||||
UNION
|
||||
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:06:04.460771')
|
||||
) posts WHERE "posts"."id" IN (6, 7)
|
||||
) "posts" WHERE "posts"."id" IN (6, 7)
|
||||
```
|
||||
|
||||
The `union` method can also accept anything that `where` does.
|
||||
|
@ -102,10 +106,10 @@ SELECT "posts".* FROM (
|
|||
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
||||
UNION
|
||||
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 2
|
||||
) posts
|
||||
) "posts"
|
||||
UNION
|
||||
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:12:45.882648')
|
||||
) posts
|
||||
) "posts"
|
||||
```
|
||||
|
||||
### UNION ALL
|
||||
|
@ -120,14 +124,14 @@ SELECT "posts".* FROM (
|
|||
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
||||
UNION ALL
|
||||
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 2
|
||||
) posts
|
||||
) "posts"
|
||||
```
|
||||
|
||||
## Caveats
|
||||
|
||||
There's a couple things to be aware of when using ActiveRecordUnion:
|
||||
|
||||
1. ActiveRecordUnion with raise an error if you try to UNION any relations that do any preloading/eager-loading. There's no sensible way to do the preloading in the subselects. If enough people complain maybe we can change ActiveRecordUnion to let the queries run anyway but without preloading any records.
|
||||
1. ActiveRecordUnion will raise an error if you try to UNION any relations that do any preloading/eager-loading. There's no sensible way to do the preloading in the subselects. If enough people complain, maybe, we can change ActiveRecordUnion to let the queries run anyway but without preloading any records.
|
||||
2. There's no easy way to get SQLite to allow ORDER BY in the UNION subselects. If you get a syntax error, you can either write `my_relation.reorder(nil).union(other.reorder(nil))` or switch to Postgres.
|
||||
|
||||
## Another nifty way to reduce extra queries
|
||||
|
@ -184,8 +188,14 @@ This is a gem not a Rails pull request because the standard of code quality for
|
|||
|
||||
## Changelog
|
||||
|
||||
**1.3.0** - January 14, 2018
|
||||
- Ready for Rails 5.2! Updates provided by [@glebm](https://github.com/glebm).
|
||||
|
||||
**1.2.0** - June 26, 2016
|
||||
- Ready for Rails 5.0! Updates provided by [@glebm](https://github.com/glebm).
|
||||
|
||||
**1.1.1** - Mar 19, 2016
|
||||
- Fix broken polymorphic associations and joins due to improper handling of bind values. Fix by [@efradelos](https://github.com/efradelos), reported by [@Machiaweliczny](https://github.com/Machiaweliczny).
|
||||
- Fix broken polymorphic associations and joins due to improper handling of bind values. Fix by [@efradelos](https://github.com/efradelos), reported by [@Machiaweliczny](https://github.com/Machiaweliczny) and [@seandougall](https://github.com/seandougall).
|
||||
- Quote table name aliases properly. Reported by [@odedniv](https://github.com/odedniv).
|
||||
|
||||
**1.1.0** - Mar 29, 2015 - Add UNION ALL support, courtesy of [@pic](https://github.com/pic).
|
||||
|
@ -204,7 +214,11 @@ This public domain dedication follows the the CC0 1.0 at https://creativecommons
|
|||
|
||||
1. Fork it ( https://github.com/brianhempel/active_record_union/fork )
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Run the tests with `rspec`
|
||||
3. Run the tests:
|
||||
1. Install MySQL and PostgreSQL.
|
||||
2. You need to be able to connect to a local MySQL and Postgres database as the default user, so the specs can create a `test_active_record_union` database. From a vanilla install of MariaDB from Homebrew, this just works. For Postgres installed by Homebrew, you may need to run `$ echo "create database my_computer_user_name;" | psql postgres` since the initial database created by Homebrew is named "postgres" but PG defaults to connecting to a database named after your username.
|
||||
3. Run `rake` to test with all supported Rails versions. All needed dependencies will be installed via Bundler (`gem install bundler` if you happen not to have Bundler yet).
|
||||
4. Run `rake test_rails_4_2` or `rake test_rails_5_2` etc. to test a specific Rails version.
|
||||
4. There is also a `bin/console` command to load up a REPL for playing around
|
||||
5. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
6. Push to the branch (`git push origin my-new-feature`)
|
||||
|
|
61
Rakefile
61
Rakefile
|
@ -1,8 +1,61 @@
|
|||
require "bundler/gem_tasks"
|
||||
|
||||
task :default => :spec
|
||||
require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
task :default => :test_all_gemfiles
|
||||
|
||||
desc "Run the tests"
|
||||
task :spec do
|
||||
exec("bundle exec rspec")
|
||||
module TestTasks
|
||||
module_function
|
||||
|
||||
TEST_CMD = 'bundle exec rspec'
|
||||
|
||||
def run_all(envs, cmd = "bundle install && #{TEST_CMD}", success_message)
|
||||
statuses = envs.map { |env| run(env, cmd) }
|
||||
failed = statuses.reject(&:first).map(&:last)
|
||||
if failed.empty?
|
||||
$stderr.puts success_message
|
||||
else
|
||||
$stderr.puts "❌ FAILING (#{failed.size}):\n#{failed.map { |env| to_bash_cmd_with_env(cmd, env) } * "\n"}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
def run_one(env, cmd = "bundle install && #{TEST_CMD}")
|
||||
full_cmd = to_bash_cmd_with_env(cmd, env)
|
||||
exec(full_cmd)
|
||||
end
|
||||
|
||||
def run(env, cmd)
|
||||
Bundler.with_clean_env do
|
||||
full_cmd = to_bash_cmd_with_env(cmd, env)
|
||||
$stderr.puts full_cmd
|
||||
isSuccess = system(full_cmd)
|
||||
[isSuccess, env]
|
||||
end
|
||||
end
|
||||
|
||||
def gemfiles
|
||||
Dir.glob('*.gemfile').sort
|
||||
end
|
||||
|
||||
def to_bash_cmd_with_env(cmd, env)
|
||||
"(export #{env.map { |k, v| "#{k}=#{v}" }.join(' ')}; #{cmd})"
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Test all Gemfiles'
|
||||
task :test_all_gemfiles do
|
||||
envs = TestTasks.gemfiles.map { |gemfile| { 'BUNDLE_GEMFILE' => gemfile } }
|
||||
TestTasks.run_all envs, "✓ Tests pass with all #{envs.size} gemfiles"
|
||||
end
|
||||
|
||||
|
||||
TestTasks.gemfiles.each do |gemfile|
|
||||
rails_version_underscored = gemfile[/rails_(.+)\.gemfile/, 1]
|
||||
|
||||
desc "Test Rails #{rails_version_underscored.gsub("_", ".")}"
|
||||
task :"test_rails_#{rails_version_underscored}" do
|
||||
env = { 'BUNDLE_GEMFILE' => gemfile }
|
||||
TestTasks.run_one(env)
|
||||
end
|
||||
end
|
|
@ -20,11 +20,9 @@ Gem::Specification.new do |spec|
|
|||
|
||||
spec.add_dependency "activerecord", ">= 4.0"
|
||||
|
||||
spec.add_development_dependency "bundler", "~> 1.6"
|
||||
spec.add_development_dependency "bundler"
|
||||
spec.add_development_dependency "rake"
|
||||
spec.add_development_dependency "rspec", "~> 3.0"
|
||||
spec.add_development_dependency "pry"
|
||||
spec.add_development_dependency "sqlite3"
|
||||
spec.add_development_dependency "pg"
|
||||
spec.add_development_dependency "mysql2"
|
||||
end
|
||||
|
|
|
@ -7,42 +7,75 @@ module ActiveRecord
|
|||
union_all: Arel::Nodes::UnionAll
|
||||
}
|
||||
|
||||
def union(relation_or_where_arg, *args)
|
||||
def union(relation_or_where_arg, *args)
|
||||
set_operation(:union, relation_or_where_arg, *args)
|
||||
end
|
||||
|
||||
def union_all(relation_or_where_arg, *args)
|
||||
def union_all(relation_or_where_arg, *args)
|
||||
set_operation(:union_all, relation_or_where_arg, *args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_operation(operation, relation_or_where_arg, *args)
|
||||
other = if args.size == 0 && Relation === relation_or_where_arg
|
||||
relation_or_where_arg
|
||||
else
|
||||
@klass.where(relation_or_where_arg, *args)
|
||||
end
|
||||
other = if args.empty? && relation_or_where_arg.is_a?(Relation)
|
||||
relation_or_where_arg
|
||||
else
|
||||
@klass.where(relation_or_where_arg, *args)
|
||||
end
|
||||
|
||||
verify_relations_for_set_operation!(operation, self, other)
|
||||
|
||||
left = self.arel.ast
|
||||
right = other.arel.ast
|
||||
|
||||
# Postgres allows ORDER BY in the UNION subqueries if each subquery is surrounded by parenthesis
|
||||
# but SQLite does not allow parens around the subqueries; you will have to explicitly do `relation.reorder(nil)` in SQLite
|
||||
if Arel::Visitors::SQLite === self.visitor
|
||||
left, right = self.ast, other.ast
|
||||
else
|
||||
left, right = Arel::Nodes::Grouping.new(self.ast), Arel::Nodes::Grouping.new(other.ast)
|
||||
# but SQLite does not allow parens around the subqueries
|
||||
unless self.connection.visitor.is_a?(Arel::Visitors::SQLite)
|
||||
left = Arel::Nodes::Grouping.new(left)
|
||||
right = Arel::Nodes::Grouping.new(right)
|
||||
end
|
||||
|
||||
set = SET_OPERATION_TO_AREL_CLASS[operation].new(left, right)
|
||||
from = Arel::Nodes::TableAlias.new(
|
||||
set,
|
||||
@klass.arel_table.name
|
||||
)
|
||||
set = SET_OPERATION_TO_AREL_CLASS[operation].new(left, right)
|
||||
from = Arel::Nodes::TableAlias.new(set, @klass.arel_table.name)
|
||||
build_union_relation(from, other)
|
||||
end
|
||||
|
||||
relation = @klass.unscoped.from(from)
|
||||
relation.bind_values = self.arel.bind_values + self.bind_values + other.arel.bind_values + other.bind_values
|
||||
relation
|
||||
if ActiveRecord.gem_version >= Gem::Version.new('5.2.0.beta2')
|
||||
# Since Rails 5.2, binds are maintained only in the Arel AST.
|
||||
def build_union_relation(arel_table_alias, _other)
|
||||
@klass.unscoped.from(arel_table_alias)
|
||||
end
|
||||
elsif ActiveRecord::VERSION::MAJOR >= 5
|
||||
# In Rails >= 5.0, < 5.2, binds are maintained only in ActiveRecord
|
||||
# relations and clauses.
|
||||
def build_union_relation(arel_table_alias, other)
|
||||
relation = @klass.unscoped.spawn
|
||||
relation.from_clause =
|
||||
UnionFromClause.new(arel_table_alias, nil,
|
||||
self.bound_attributes + other.bound_attributes)
|
||||
relation
|
||||
end
|
||||
|
||||
class UnionFromClause < ActiveRecord::Relation::FromClause
|
||||
def initialize(value, name, bound_attributes)
|
||||
super(value, name)
|
||||
@bound_attributes = bound_attributes
|
||||
end
|
||||
|
||||
def binds
|
||||
@bound_attributes
|
||||
end
|
||||
end
|
||||
else
|
||||
# In Rails 4.x, binds are maintained in both ActiveRecord relations and
|
||||
# clauses and also in their Arel ASTs.
|
||||
def build_union_relation(arel_table_alias, other)
|
||||
relation = @klass.unscoped.from(arel_table_alias)
|
||||
relation.bind_values = self.arel.bind_values + self.bind_values +
|
||||
other.arel.bind_values + other.bind_values
|
||||
relation
|
||||
end
|
||||
end
|
||||
|
||||
def verify_relations_for_set_operation!(operation, *relations)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module ActiveRecordUnion
|
||||
VERSION = "1.1.1"
|
||||
VERSION = "1.3.0"
|
||||
end
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in active_record_union.gemspec
|
||||
gemspec
|
||||
|
||||
gem 'rails', '~> 5.0.0'
|
||||
|
||||
# On Rails < 5.2, only pg < v1 is supported. See:
|
||||
# https://github.com/rails/rails/pull/31671
|
||||
# https://bitbucket.org/ged/ruby-pg/issues/270/pg-100-x64-mingw32-rails-server-not-start
|
||||
gem 'pg', '~> 0.21'
|
||||
|
||||
gem 'sqlite3', '~> 1.3.6'
|
|
@ -0,0 +1,13 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in active_record_union.gemspec
|
||||
gemspec
|
||||
|
||||
gem 'rails', '~> 5.1.0'
|
||||
|
||||
# On Rails < 5.2, only pg < v1 is supported. See:
|
||||
# https://github.com/rails/rails/pull/31671
|
||||
# https://bitbucket.org/ged/ruby-pg/issues/270/pg-100-x64-mingw32-rails-server-not-start
|
||||
gem 'pg', '~> 0.21'
|
||||
|
||||
gem 'sqlite3', '~> 1.3.6'
|
|
@ -2,3 +2,9 @@ source 'https://rubygems.org'
|
|||
|
||||
# Specify your gem's dependencies in active_record_union.gemspec
|
||||
gemspec
|
||||
|
||||
gem 'rails', '~> 5.2.0'
|
||||
|
||||
gem 'pg'
|
||||
|
||||
gem 'sqlite3', '~> 1.3.6'
|
|
@ -1,6 +1,9 @@
|
|||
require 'spec_helper'
|
||||
require "spec_helper"
|
||||
|
||||
describe ActiveRecord::Relation do
|
||||
TIME = Time.utc(2014, 7, 19, 0, 0, 0)
|
||||
SQL_TIME = ActiveRecord::VERSION::MAJOR >= 5 ? "2014-07-19 00:00:00" : "2014-07-19 00:00:00.000000"
|
||||
|
||||
describe ".union" do
|
||||
it "returns an ActiveRecord::Relation" do
|
||||
expect(User.all.union(User.all)).to be_kind_of(ActiveRecord::Relation)
|
||||
|
@ -26,17 +29,29 @@ describe ActiveRecord::Relation do
|
|||
end
|
||||
|
||||
it "works" do
|
||||
union = User.new(id: 1).posts.union(Post.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)))
|
||||
union = User.new(id: 1).posts.union(Post.where("created_at > ?", TIME))
|
||||
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ) \"posts\""
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
||||
)
|
||||
expect(union.arel.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ) \"posts\""
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
||||
)
|
||||
expect{union.to_a}.to_not raise_error
|
||||
end
|
||||
|
||||
def bind_values_from_relation(relation)
|
||||
if ActiveRecord.gem_version >= Gem::Version.new('5.2.0.beta2')
|
||||
relation.arel_table.class.engine.connection.visitor.accept(
|
||||
relation.arel.ast, Arel::Collectors::Bind.new
|
||||
).value.map(&:value)
|
||||
elsif ActiveRecord::VERSION::MAJOR >= 5
|
||||
relation.bound_attributes.map { |a| a.value_for_database }
|
||||
else
|
||||
(relation.arel.bind_values + relation.bind_values).map { |_column, value| value }
|
||||
end
|
||||
end
|
||||
|
||||
it "binds values properly" do
|
||||
user1 = User.new(id: 1)
|
||||
user2 = User.new(id: 2)
|
||||
|
@ -46,7 +61,7 @@ describe ActiveRecord::Relation do
|
|||
|
||||
# Inside ActiveRecord the bind value list is
|
||||
# (union.arel.bind_values + union.bind_values)
|
||||
bind_values = (union.arel.bind_values + union.bind_values).map { |column, value| value }
|
||||
bind_values = bind_values_from_relation union
|
||||
|
||||
expect(bind_values).to eq([1, 2, 3])
|
||||
end
|
||||
|
@ -54,7 +69,7 @@ describe ActiveRecord::Relation do
|
|||
it "binds values properly on joins" do
|
||||
union = User.joins(:drafts).union(User.where(id: 11))
|
||||
|
||||
bind_values = (union.arel.bind_values + union.bind_values).map { |column, value| value }
|
||||
bind_values = bind_values_from_relation union
|
||||
expect(bind_values).to eq([true, 11])
|
||||
|
||||
|
||||
|
@ -66,32 +81,39 @@ describe ActiveRecord::Relation do
|
|||
|
||||
it "doesn't repeat default scopes" do
|
||||
expect(Time).to receive(:now) { Time.utc(2014, 7, 24, 0, 0, 0) }
|
||||
sql_now = "2014-07-24 00:00:00#{".000000" if ActiveRecord::VERSION::MAJOR < 5}"
|
||||
|
||||
class PublishedPost < ActiveRecord::Base
|
||||
self.table_name = "posts"
|
||||
default_scope { where("published_at < ?", Time.now) }
|
||||
end
|
||||
|
||||
union = PublishedPost.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)).union(User.new(id: 1).posts)
|
||||
union = PublishedPost.where("created_at > ?", TIME).union(User.new(id: 1).posts)
|
||||
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE (published_at < '2014-07-24 00:00:00.000000') AND (created_at > '2014-07-19 00:00:00.000000') UNION SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ) \"posts\""
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE (published_at < '#{sql_now}') AND (created_at > '#{SQL_TIME}') UNION SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ) \"posts\""
|
||||
)
|
||||
expect{union.to_a}.to_not raise_error
|
||||
end
|
||||
|
||||
context "with ORDER BY in subselects" do
|
||||
def union
|
||||
let :union do
|
||||
User.new(id: 1).posts.order(:created_at).union(
|
||||
Post.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)).order(:created_at)
|
||||
Post.where("created_at > ?", TIME).order(:created_at)
|
||||
).order(:created_at)
|
||||
end
|
||||
|
||||
context "in SQLite" do
|
||||
it "lets ORDER BY in query subselects throw a syntax error" do
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ORDER BY \"posts\".\"created_at\" ASC ) \"posts\" ORDER BY \"posts\".\"created_at\" ASC"
|
||||
)
|
||||
if ([ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR] <=> [5, 2]) >= 0
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00') ORDER BY \"posts\".\"created_at\" ASC ) \"posts\" ORDER BY \"created_at\" ASC"
|
||||
)
|
||||
else
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ORDER BY \"posts\".\"created_at\" ASC ) \"posts\" ORDER BY \"posts\".\"created_at\" ASC"
|
||||
)
|
||||
end
|
||||
expect{union.to_a}.to raise_error(ActiveRecord::StatementInvalid)
|
||||
end
|
||||
end
|
||||
|
@ -99,9 +121,15 @@ describe ActiveRecord::Relation do
|
|||
context "in Postgres" do
|
||||
it "wraps query subselects in parentheses to allow ORDER BY clauses" do
|
||||
Databases.with_postgres do
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( (SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC) UNION (SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ORDER BY \"posts\".\"created_at\" ASC) ) \"posts\" ORDER BY \"posts\".\"created_at\" ASC"
|
||||
)
|
||||
if ([ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR] <=> [5, 2]) >= 0
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( (SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC) UNION (SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00') ORDER BY \"posts\".\"created_at\" ASC) ) \"posts\" ORDER BY \"created_at\" ASC"
|
||||
)
|
||||
else
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( (SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC) UNION (SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ORDER BY \"posts\".\"created_at\" ASC) ) \"posts\" ORDER BY \"posts\".\"created_at\" ASC"
|
||||
)
|
||||
end
|
||||
expect{union.to_a}.to_not raise_error
|
||||
end
|
||||
end
|
||||
|
@ -110,9 +138,15 @@ describe ActiveRecord::Relation do
|
|||
context "in MySQL" do
|
||||
it "wraps query subselects in parentheses to allow ORDER BY clauses" do
|
||||
Databases.with_mysql do
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT `posts`.* FROM ( (SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 1 ORDER BY `posts`.`created_at` ASC) UNION (SELECT `posts`.* FROM `posts` WHERE (created_at > '2014-07-19 00:00:00.000000') ORDER BY `posts`.`created_at` ASC) ) `posts` ORDER BY `posts`.`created_at` ASC"
|
||||
)
|
||||
if ([ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR] <=> [5, 2]) >= 0
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT `posts`.* FROM ( (SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 1 ORDER BY `posts`.`created_at` ASC) UNION (SELECT `posts`.* FROM `posts` WHERE (created_at > '2014-07-19 00:00:00') ORDER BY `posts`.`created_at` ASC) ) `posts` ORDER BY `created_at` ASC"
|
||||
)
|
||||
else
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT `posts`.* FROM ( (SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 1 ORDER BY `posts`.`created_at` ASC) UNION (SELECT `posts`.* FROM `posts` WHERE (created_at > '#{SQL_TIME}') ORDER BY `posts`.`created_at` ASC) ) `posts` ORDER BY `posts`.`created_at` ASC"
|
||||
)
|
||||
end
|
||||
expect{union.to_a}.to_not raise_error
|
||||
end
|
||||
end
|
||||
|
@ -130,10 +164,10 @@ describe ActiveRecord::Relation do
|
|||
end
|
||||
|
||||
it "multiple arguments" do
|
||||
union = User.new(id: 1).posts.union("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0))
|
||||
union = User.new(id: 1).posts.union("created_at > ?", TIME)
|
||||
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ) \"posts\""
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
||||
)
|
||||
expect{union.to_a}.to_not raise_error
|
||||
end
|
||||
|
@ -151,10 +185,10 @@ describe ActiveRecord::Relation do
|
|||
|
||||
describe ".union_all" do
|
||||
it "works" do
|
||||
union = User.new(id: 1).posts.union_all(Post.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)))
|
||||
union = User.new(id: 1).posts.union_all(Post.where("created_at > ?", TIME))
|
||||
|
||||
expect(union.to_sql.squish).to eq(
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION ALL SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ) \"posts\""
|
||||
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION ALL SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
||||
)
|
||||
expect{union.to_a}.to_not raise_error
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue