Created
November 21, 2018 00:25
-
-
Save benalavi/159110fd58907c7925fc8bd1958e3ad2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/sequel/plugins/class_table_inheritance.rb b/lib/sequel/plugins/class_table_inheritance.rb | |
index 214f50bb7..40c65d784 100644 | |
--- a/lib/sequel/plugins/class_table_inheritance.rb | |
+++ b/lib/sequel/plugins/class_table_inheritance.rb | |
@@ -198,6 +198,9 @@ module Sequel | |
# Overrides implicit table names. | |
# :ignore_subclass_columns :: Array with column names as symbols that are ignored | |
# on all sub-classes. | |
+ # :qualify_tables :: Boolean true to qualify automatically determined | |
+ # subclass tables with the same qualifier as their | |
+ # superclass. | |
def self.configure(model, opts = OPTS) | |
SingleTableInheritance.configure model, opts[:key], opts | |
@@ -214,6 +217,7 @@ module Sequel | |
source | |
end | |
@cti_ignore_subclass_columns = opts[:ignore_subclass_columns] || [] | |
+ @cti_qualify_tables = !!opts[:qualify_tables] | |
end | |
end | |
@@ -244,6 +248,13 @@ module Sequel | |
# primary key column is always allowed to be duplicated | |
attr_reader :cti_ignore_subclass_columns | |
+ # A boolean indicating whether or not to automatically qualify tables | |
+ # backing subclasses with the same qualifier as their superclass, if | |
+ # the superclass is qualified. Specified with the :qualify_tables | |
+ # option to the plugin and only applied to automatically determined | |
+ # table names (not to the :table_map option). | |
+ attr_reader :cti_qualify_tables | |
+ | |
# Freeze CTI information when freezing model class. | |
def freeze | |
@cti_models.freeze | |
@@ -251,11 +262,12 @@ module Sequel | |
@cti_table_columns.freeze | |
@cti_table_map.freeze | |
@cti_ignore_subclass_columns.freeze | |
+ @cti_qualify_tables.freeze | |
super | |
end | |
- Plugins.inherited_instance_variables(self, :@cti_models=>nil, :@cti_tables=>nil, :@cti_table_columns=>nil, :@cti_instance_dataset=>nil, :@cti_table_map=>nil, :@cti_alias=>nil, :@cti_ignore_subclass_columns=>nil) | |
+ Plugins.inherited_instance_variables(self, :@cti_models=>nil, :@cti_tables=>nil, :@cti_table_columns=>nil, :@cti_instance_dataset=>nil, :@cti_table_map=>nil, :@cti_alias=>nil, :@cti_ignore_subclass_columns=>nil, :@cti_qualify_tables=>nil) | |
def inherited(subclass) | |
ds = sti_dataset | |
@@ -272,7 +284,11 @@ module Sequel | |
if table = cti_table_map[n.to_sym] | |
columns = db.from(table).columns | |
else | |
- table = subclass.implicit_table_name | |
+ table = if table_name.is_a?(SQL::QualifiedIdentifier) && cti_qualify_tables && schema = dataset.schema_and_table(table_name).first | |
+ SQL::QualifiedIdentifier.new schema, subclass.implicit_table_name | |
+ else | |
+ subclass.implicit_table_name | |
+ end | |
columns = check_non_connection_error(false){db.from(table).columns} | |
table = nil if !columns || columns.empty? | |
end | |
diff --git a/spec/extensions/class_table_inheritance_spec.rb b/spec/extensions/class_table_inheritance_spec.rb | |
index d7fa347ac..dc3390221 100644 | |
--- a/spec/extensions/class_table_inheritance_spec.rb | |
+++ b/spec/extensions/class_table_inheritance_spec.rb | |
@@ -56,6 +56,7 @@ describe "class_table_inheritance plugin" do | |
Employee.cti_instance_dataset.frozen?.must_equal true | |
Employee.cti_table_columns.frozen?.must_equal true | |
Employee.cti_table_map.frozen?.must_equal true | |
+ Employee.cti_qualify_tables.frozen?.must_equal true | |
end | |
it "should not attempt to use prepared statements" do | |
@@ -586,38 +587,99 @@ describe "class_table_inheritance plugin with dataset defined with QualifiedIden | |
}[opts[:from] + (opts[:join] || []).map{|x| x.table}] | |
end | |
end | |
- ::Employee = Class.new(Sequel::Model) | |
- ::Employee.db = @db | |
- ::Employee.set_dataset(Sequel[:hr][:employees]) | |
- class ::Employee | |
- def _save_refresh; @values[:id] = 1 end | |
- def self.columns | |
- dataset.columns || dataset.opts[:from].first.expression.columns | |
+ end | |
+ after do | |
+ [:Manager, :Staff, :Employee].each{|s| Object.send(:remove_const, s) if Object.const_defined?(s)} | |
+ end | |
+ | |
+ describe "with table_map used to qualify subclasses" do | |
+ before do | |
+ ::Employee = Class.new(Sequel::Model) | |
+ ::Employee.db = @db | |
+ ::Employee.set_dataset(Sequel[:hr][:employees]) | |
+ class ::Employee | |
+ def _save_refresh; @values[:id] = 1 end | |
+ def self.columns | |
+ dataset.columns || dataset.opts[:from].first.expression.columns | |
+ end | |
+ plugin :class_table_inheritance, :table_map=>{:Manager=>Sequel[:hr][:managers],:Staff=>Sequel[:hr][:staff]} | |
+ end | |
+ class ::Manager < Employee | |
+ one_to_many :staff_members, :class=>:Staff | |
+ end | |
+ class ::Staff < Employee | |
+ many_to_one :manager | |
end | |
- plugin :class_table_inheritance, key: :type, :table_map=>{:Manager=>Sequel[:hr][:managers], :Staff=>Sequel[:hr][:staff]} | |
end | |
- class ::Manager < Employee | |
- one_to_many :staff_members, :class=>:Staff | |
+ | |
+ it "should handle many_to_one relationships correctly" do | |
+ Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E') | |
+ Staff.load(:manager_id=>3).manager.must_equal Manager.load(:id=>3, :name=>'E') | |
+ @db.sqls.must_equal ['SELECT * FROM (SELECT hr.employees.id, hr.employees.name, hr.employees.kind FROM hr.employees INNER JOIN hr.managers ON (hr.managers.id = hr.employees.id)) AS employees WHERE (id = 3) LIMIT 1'] | |
end | |
- class ::Staff < Employee | |
- many_to_one :manager | |
+ | |
+ it "should handle one_to_many relationships correctly" do | |
+ Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :manager_id=>3) | |
+ Manager.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :manager_id=>3)] | |
+ @db.sqls.must_equal ['SELECT * FROM (SELECT hr.employees.id, hr.employees.name, hr.employees.kind, hr.staff.manager_id FROM hr.employees INNER JOIN hr.staff ON (hr.staff.id = hr.employees.id)) AS employees WHERE (employees.manager_id = 3)'] | |
end | |
- @ds = Employee.dataset | |
- @db.sqls | |
- end | |
- after do | |
- [:Manager, :Staff, :Employee].each{|s| Object.send(:remove_const, s)} | |
end | |
- it "should handle many_to_one relationships correctly" do | |
- Manager.dataset = Manager.dataset.with_fetch(:id=>3, :name=>'E') | |
- Staff.load(:manager_id=>3).manager.must_equal Manager.load(:id=>3, :name=>'E') | |
- @db.sqls.must_equal ['SELECT * FROM (SELECT hr.employees.id, hr.employees.name, hr.employees.kind FROM hr.employees INNER JOIN hr.managers ON (hr.managers.id = hr.employees.id)) AS employees WHERE (id = 3) LIMIT 1'] | |
+ describe "without table_map or qualify_tables set" do | |
+ it "should use a non-qualified subquery in subclasses" do | |
+ def @db.schema(table, opts={}) | |
+ {Sequel[:hr][:employees]=>[[:id, {:primary_key=>true, :type=>:integer}], [:name, {:type=>:string}], [:kind, {:type=>:string}]], | |
+ :managers=>[[:id, {:type=>:integer}]], | |
+ }[table.is_a?(Sequel::Dataset) ? table.first_source_table : table] | |
+ end | |
+ @db.extend_datasets do | |
+ def columns | |
+ {[Sequel[:hr][:employees]]=>[:id, :name, :kind], | |
+ [:managers]=>[:id], | |
+ [Sequel[:hr][:employees], :managers]=>[:id, :name, :kind] | |
+ }[opts[:from] + (opts[:join] || []).map{|x| x.table}] | |
+ end | |
+ end | |
+ ::Employee = Class.new(Sequel::Model) | |
+ ::Employee.db = @db | |
+ ::Employee.set_dataset(Sequel[:hr][:employees]) | |
+ class ::Employee | |
+ def _save_refresh; @values[:id] = 1 end | |
+ def self.columns | |
+ dataset.columns || dataset.opts[:from].first.expression.columns | |
+ end | |
+ plugin :class_table_inheritance | |
+ end | |
+ class ::Manager < ::Employee | |
+ end | |
+ | |
+ Employee.dataset.sql.must_equal 'SELECT * FROM hr.employees' | |
+ Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT hr.employees.id, hr.employees.name, hr.employees.kind FROM hr.employees INNER JOIN managers ON (managers.id = hr.employees.id)) AS employees' | |
+ end | |
end | |
- it "should handle one_to_many relationships correctly" do | |
- Staff.dataset = Staff.dataset.with_fetch(:id=>1, :name=>'S', :manager_id=>3) | |
- Manager.load(:id=>3).staff_members.must_equal [Staff.load(:id=>1, :name=>'S', :manager_id=>3)] | |
- @db.sqls.must_equal ['SELECT * FROM (SELECT hr.employees.id, hr.employees.name, hr.employees.kind, hr.staff.manager_id FROM hr.employees INNER JOIN hr.staff ON (hr.staff.id = hr.employees.id)) AS employees WHERE (employees.manager_id = 3)'] | |
+ describe "with qualify_tables option set" do | |
+ it "should use a subquery with the same qualifier in subclasses" do | |
+ ::Employee = Class.new(Sequel::Model) | |
+ ::Employee.db = @db | |
+ ::Employee.set_dataset(Sequel[:hr][:employees]) | |
+ class ::Employee | |
+ def _save_refresh; @values[:id] = 1 end | |
+ def self.columns | |
+ dataset.columns || dataset.opts[:from].first.expression.columns | |
+ end | |
+ plugin :class_table_inheritance, :table_map=>{:Staff=>Sequel[:hr][:staff]}, qualify_tables: true | |
+ end | |
+ class ::Manager < ::Employee | |
+ one_to_many :staff_members, :class=>:Staff | |
+ end | |
+ class ::Staff < ::Employee | |
+ many_to_one :manager | |
+ end | |
+ | |
+ Employee.dataset.sql.must_equal 'SELECT * FROM hr.employees' | |
+ Manager.dataset.sql.must_equal 'SELECT * FROM (SELECT hr.employees.id, hr.employees.name, hr.employees.kind FROM hr.employees INNER JOIN hr.managers ON (hr.managers.id = hr.employees.id)) AS employees' | |
+ Staff.dataset.sql.must_equal 'SELECT * FROM (SELECT hr.employees.id, hr.employees.name, hr.employees.kind, hr.staff.manager_id FROM hr.employees INNER JOIN hr.staff ON (hr.staff.id = hr.employees.id)) AS employees' | |
+ end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment