Compare commits

...

19 Commits

Author SHA1 Message Date
Brian Hempel 3cb6e38a12 bah travis 2019-04-02 14:52:13 -05:00
Brian Hempel 19f1137329 Vain attempts to get Travis to build again 2019-04-02 14:19:28 -05:00
Brian Hempel ac7754ae76 Unbreak Travis for Rails 5.x 2019-04-02 14:04:57 -05:00
Brian Hempel 4e14a9f3ae Fix Travis? Relax bundler development dependency 2019-04-02 13:55:27 -05:00
Brian Hempel 242d02f339
README: We do test against Rails 5.2 2019-04-02 13:48:06 -05:00
Brian Hempel 7fc35a4e4e
README: Link to ActiveRecordExtended 2019-04-02 13:46:57 -05:00
Brian Hempel c7f0cfd22f Version bump 1.3.0 (Ready for Rails 5.2!) 2018-01-14 17:29:39 -06:00
Brian Hempel c5a2213c3d README: Update Changelog (Ready for Rails 5.2!) 2018-01-14 17:27:55 -06:00
Brian Hempel 28824c3c39 README: Clarify how to run tests. 2018-01-14 17:27:27 -06:00
Gleb Mazovetskiy dec1205c14
Rails 5.2 support (#19)
Since Rails 5.2, binds are maintained only in the Arel AST.
2018-01-14 20:59:37 +00:00
Gleb Mazovetskiy d4c7269b61 Fix spec failure 2016-12-11 22:29:36 +00:00
Gleb Mazovetskiy b0a2d4affc update Rails versions 2016-12-11 22:23:47 +00:00
Anuj 00fca9a9d1 fix readme typo (#14) 2016-12-10 20:20:29 +00:00
Brian Hempel c4e29e573e README: Note that we only test Rails 4.2/5.0 2016-06-26 19:48:35 -05:00
Brian Hempel 8695300f26 Changelog/version bump 1.2.0 - Rails 5.0 compatibility, no functionality changes 2016-06-26 19:26:40 -05:00
Brian Hempel 5a2e7a537f Add `rake test_rails_4_2` and `rake test_rails_5_0` commands. 2016-06-26 19:26:40 -05:00
Gleb Mazovetskiy 5a47ade141 Support Rails 5 2016-06-26 19:26:38 -05:00
Brian Hempel 74b21c6ba1 Also credit @seandougall in README (for providing a helpful example of the bug) 2016-03-19 22:40:05 -05:00
Brian Hempel 30c171b84f Quote tables in README, since we now quote properly 2016-03-19 22:38:34 -05:00
11 changed files with 230 additions and 63 deletions

1
.gitignore vendored
View File

@ -4,6 +4,7 @@
.config
.yardoc
Gemfile.lock
*.gemfile.lock
InstalledFiles
_yardoc
coverage

View File

@ -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

View File

@ -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`)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -1,3 +1,3 @@
module ActiveRecordUnion
VERSION = "1.1.1"
VERSION = "1.3.0"
end

13
rails_5_0.gemfile Normal file
View File

@ -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'

13
rails_5_1.gemfile Normal file
View File

@ -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'

View File

@ -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'

View File

@ -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