Permalink
Browse files

Add Dataset#unused_table_alias, for generating a table alias that has…

… not yet been used in the query

This method already existed in a slightly different form as one
of the protected eager_graph helper methods named
eager_unique_table_alias.  However, since it is a generally useful
method, it is being made one of the public core dataset methods.

What this method allows you do to give the dataset a table
identifier (any object acceptable by alias_symbol), and have it
return a symbol representing an alias that has yet been used in the
dataset:

  DB[:test].unused_table_alias(:test) # => :test_0

It's expected use case is where you want to join a table to a
dataset, but you aren't sure if the table you are joining is
already been referenced elsewhere in the dataset, and you want
a unique identifier to refer to it:

  aliaz = ds.unused_table_alias(:table_name)
  ds.join(:table_name.as(aliaz), ...)

Before, there wasn't an easy way to do this on a non-model
dataset (since only model datasets had the eager_unique_table_alias
defined).

Since eager_unique_table_alias is no longer needed, it has been
removed.
  • Loading branch information...
1 parent 83baf16 commit bfd4f2d145d8e50be557c884786f92484b0f38c3 @jeremyevans committed Feb 21, 2010
Showing with 54 additions and 22 deletions.
  1. +2 −0 CHANGELOG
  2. +23 −0 lib/sequel/dataset/convenience.rb
  3. +2 −22 lib/sequel/model/associations.rb
  4. +27 −0 spec/core/dataset_spec.rb
View
@@ -1,5 +1,7 @@
=== HEAD
+* Add Dataset#unused_table_alias, for generating a table alias that has not yet been used in the query (jeremyevans)
+
* Support an empty database argument in bin/sequel, useful for testing things without a real database (jeremyevans)
* Support for schemas and aliases when eager graphing (jeremyevans)
@@ -274,6 +274,29 @@ def to_hash(key_column, value_column = nil)
end
end
+ # Creates a unique table alias that hasn't already been used in the dataset.
+ # table_alias can be any type of object accepted by alias_symbol.
+ # The symbol returned will be the implicit alias in the argument,
+ # possibly appended with "_N" if the implicit alias has already been
+ # used, where N is an integer starting at 0 and increasing until an
+ # unused one is found.
+ def unused_table_alias(table_alias)
+ table_alias = alias_symbol(table_alias)
+ used_aliases = []
+ used_aliases += opts[:from].map{|t| alias_symbol(t)} if opts[:from]
+ used_aliases += opts[:join].map{|j| j.table_alias ? alias_alias_symbol(j.table_alias) : alias_symbol(j.table)} if opts[:join]
+ if used_aliases.include?(table_alias)
+ i = 0
+ loop do
+ ta = :"#{table_alias}_#{i}"
+ return ta unless used_aliases.include?(ta)
+ i += 1
+ end
+ else
+ table_alias
+ end
+ end
+
private
# Return a plain symbol given a potentially qualified or aliased symbol,
@@ -763,7 +763,7 @@ def def_many_to_many(opts)
jt_join_type = opts[:graph_join_table_join_type]
jt_graph_block = opts[:graph_join_table_block]
opts[:eager_grapher] ||= proc do |ds, assoc_alias, table_alias|
- ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(lcpks) + graph_jt_conds, :select=>false, :table_alias=>ds.send(:eager_unique_table_alias, ds, join_table), :join_type=>jt_join_type, :implicit_qualifier=>table_alias, :from_self_alias=>ds.opts[:eager_graph][:master], &jt_graph_block)
+ ds = ds.graph(join_table, use_jt_only_conditions ? jt_only_conditions : lcks.zip(lcpks) + graph_jt_conds, :select=>false, :table_alias=>ds.unused_table_alias(join_table), :join_type=>jt_join_type, :implicit_qualifier=>table_alias, :from_self_alias=>ds.opts[:eager_graph][:master], &jt_graph_block)
ds.graph(opts.associated_class, use_only_conditions ? only_conditions : opts.right_primary_keys.zip(rcks) + conditions, :select=>select, :table_alias=>assoc_alias, :join_type=>join_type, &graph_block)
end
@@ -1251,7 +1251,7 @@ def ungraphed
def eager_graph_association(ds, model, ta, requirements, r, *associations)
klass = r.associated_class
assoc_name = r[:name]
- assoc_table_alias = ds.eager_unique_table_alias(ds, assoc_name)
+ assoc_table_alias = ds.unused_table_alias(assoc_name)
ds = r[:eager_grapher].call(ds, assoc_table_alias, ta)
ds = ds.order_more(*Array(r[:order]).map{|c| eager_graph_qualify_order(assoc_table_alias, c)}) if r[:order] and r[:order_eager_graph]
eager_graph = ds.opts[:eager_graph]
@@ -1352,26 +1352,6 @@ def eager_graph_build_associations(record_graphs)
record_graphs.replace(records)
end
- # Creates a unique table alias that hasn't already been used in the query.
- # Will either be the table_alias itself or table_alias_N for some integer
- # N (starting at 0 and increasing until an unused one is found).
- def eager_unique_table_alias(ds, table_alias)
- table_alias = alias_symbol(table_alias)
- used_aliases = ds.opts[:from].map{|t| alias_symbol(t)}
- used_aliases += ds.opts[:join].map{|j| j.table_alias ? alias_alias_symbol(j.table_alias) : alias_symbol(j.table)} if ds.opts[:join]
- graph = ds.opts[:graph]
- used_aliases += graph[:table_aliases].keys if graph
- if used_aliases.include?(table_alias)
- i = 0
- loop do
- ta = :"#{table_alias}_#{i}"
- return ta unless used_aliases.include?(ta)
- i += 1
- end
- end
- table_alias
- end
-
private
# Make sure the association is valid for this model, and return the related AssociationReflection.
@@ -267,6 +267,33 @@ def v.values; {}; end
end
end
+context "Dataset#unused_table_alias" do
+ before do
+ @ds = Sequel::Dataset.new(nil).from(:test)
+ end
+
+ specify "should return given symbol if it hasn't already been used" do
+ @ds.unused_table_alias(:blah).should == :blah
+ end
+
+ specify "should return a symbol specifying an alias that hasn't already been used if it has already been used" do
+ @ds.unused_table_alias(:test).should == :test_0
+ @ds.from(:test, :test_0).unused_table_alias(:test).should == :test_1
+ @ds.from(:test, :test_0).cross_join(:test_1).unused_table_alias(:test).should == :test_2
+ end
+
+ specify "should return an appropriate symbol if given other forms of identifiers" do
+ @ds.unused_table_alias('test').should == :test_0
+ @ds.unused_table_alias(:b__t___test).should == :test_0
+ @ds.unused_table_alias(:b__test).should == :test_0
+ @ds.unused_table_alias(:test.qualify(:b)).should == :test_0
+ @ds.unused_table_alias(:b.as(:test)).should == :test_0
+ @ds.unused_table_alias(:b.as(:test.identifier)).should == :test_0
+ @ds.unused_table_alias(:b.as('test')).should == :test_0
+ @ds.unused_table_alias(:test.identifier).should == :test_0
+ end
+end
+
context "Dataset#exists" do
before do
@ds1 = Sequel::Dataset.new(nil).from(:test)

0 comments on commit bfd4f2d

Please sign in to comment.