switchman 1.13.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/switchman/shard.rb +743 -11
  3. data/db/migrate/20130328224244_create_default_shard.rb +1 -1
  4. data/lib/switchman/active_record/abstract_adapter.rb +2 -6
  5. data/lib/switchman/active_record/association.rb +35 -12
  6. data/lib/switchman/active_record/attribute_methods.rb +3 -0
  7. data/lib/switchman/active_record/base.rb +47 -9
  8. data/lib/switchman/active_record/calculations.rb +2 -1
  9. data/lib/switchman/active_record/connection_handler.rb +23 -20
  10. data/lib/switchman/active_record/connection_pool.rb +17 -15
  11. data/lib/switchman/active_record/log_subscriber.rb +8 -12
  12. data/lib/switchman/active_record/migration.rb +9 -0
  13. data/lib/switchman/active_record/model_schema.rb +1 -1
  14. data/lib/switchman/active_record/postgresql_adapter.rb +22 -47
  15. data/lib/switchman/active_record/query_cache.rb +17 -107
  16. data/lib/switchman/active_record/query_methods.rb +4 -0
  17. data/lib/switchman/active_record/relation.rb +5 -5
  18. data/lib/switchman/active_record/statement_cache.rb +4 -25
  19. data/lib/switchman/active_record/table_definition.rb +2 -2
  20. data/lib/switchman/active_support/cache.rb +16 -0
  21. data/lib/switchman/connection_pool_proxy.rb +38 -22
  22. data/lib/switchman/database_server.rb +32 -19
  23. data/lib/switchman/default_shard.rb +1 -0
  24. data/lib/switchman/engine.rb +16 -14
  25. data/lib/switchman/{shackles → guard_rail}/relation.rb +5 -5
  26. data/lib/switchman/{shackles.rb → guard_rail.rb} +4 -4
  27. data/lib/switchman/r_spec_helper.rb +7 -7
  28. data/lib/switchman/sharded_instrumenter.rb +1 -1
  29. data/lib/switchman/test_helper.rb +3 -3
  30. data/lib/switchman/version.rb +1 -1
  31. data/lib/switchman.rb +1 -1
  32. data/lib/tasks/switchman.rake +23 -17
  33. metadata +35 -22
  34. data/app/models/switchman/shard_internal.rb +0 -714
@@ -3,7 +3,7 @@ class CreateDefaultShard < ActiveRecord::Migration[4.2]
3
3
  unless Switchman::Shard.default.is_a?(Switchman::Shard)
4
4
  Switchman::Shard.reset_column_information
5
5
  Switchman::Shard.create!(:default => true)
6
- Switchman::Shard.default(true)
6
+ Switchman::Shard.default(reload: true)
7
7
  end
8
8
  end
9
9
  end
@@ -4,8 +4,8 @@ module Switchman
4
4
  module ActiveRecord
5
5
  module AbstractAdapter
6
6
  module ForeignKeyCheck
7
- def add_column(table, name, type, options = {})
8
- Engine.foreign_key_check(name, type, options)
7
+ def add_column(table, name, type, limit: nil, **)
8
+ Engine.foreign_key_check(name, type, limit: limit)
9
9
  super
10
10
  end
11
11
  end
@@ -27,10 +27,6 @@ module Switchman
27
27
  quote_table_name(name)
28
28
  end
29
29
 
30
- def use_qualified_names?
31
- false
32
- end
33
-
34
30
  protected
35
31
 
36
32
  def log(*args, &block)
@@ -30,17 +30,14 @@ module Switchman
30
30
  end
31
31
 
32
32
  module CollectionAssociation
33
- method = ::Rails.version < '5.1' ? :get_records : :find_target
34
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
35
- def #{method}
36
- shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [shard]
37
- # activate both the owner and the target's shard category, so that Reflection#join_id_for,
38
- # when called for the owner, will be returned relative to shard the query will execute on
39
- Shard.with_each_shard(shards, [klass.shard_category, owner.class.shard_category].uniq) do
40
- super
41
- end
33
+ def find_target
34
+ shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [shard]
35
+ # activate both the owner and the target's shard category, so that Reflection#join_id_for,
36
+ # when called for the owner, will be returned relative to shard the query will execute on
37
+ Shard.with_each_shard(shards, [klass.shard_category, owner.class.shard_category].uniq) do
38
+ super
42
39
  end
43
- RUBY
40
+ end
44
41
  end
45
42
 
46
43
  module BelongsToAssociation
@@ -76,15 +73,34 @@ module Switchman
76
73
 
77
74
  module Preloader
78
75
  module Association
79
- if ::Rails.version >= "5.2"
76
+ if ::Rails.version >= "5.2" and ::Rails.version < "6.0"
80
77
  def run(preloader)
81
- # TODO - can move associated_records_by_owner into this after 5.1 is gonzo
82
78
  associated_records_by_owner(preloader).each do |owner, records|
83
79
  associate_records_to_owner(owner, records)
84
80
  end
85
81
  end
86
82
  end
87
83
 
84
+ if ::Rails.version >= "6.0"
85
+ # Copypasta from Activerecord but with added global_id_for goodness.
86
+ def records_for(ids)
87
+ scope.where(association_key_name => ids).load do |record|
88
+ global_key = if record.class.shard_category == :unsharded
89
+ convert_key(record[association_key_name])
90
+ else
91
+ Shard.global_id_for(record[association_key_name], record.shard)
92
+ end
93
+ owner = owners_by_key[global_key.to_s].first
94
+ association = owner.association(reflection.name)
95
+ association.set_inverse_instance(record)
96
+ end
97
+ end
98
+
99
+ def records_by_owner
100
+ associated_records_by_owner
101
+ end
102
+ end
103
+
88
104
  def associated_records_by_owner(preloader = nil)
89
105
  owners_map = owners_by_key
90
106
 
@@ -183,6 +199,13 @@ module Switchman
183
199
  (record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key) || # have to use send instead of [] because sharding
184
200
  record.attribute_changed?(reflection.foreign_key)
185
201
  end
202
+
203
+ def save_belongs_to_association(reflection)
204
+ # this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
205
+ # after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
206
+ # category of the associated record to match Shard.current for the category of self
207
+ shard.activate(shard_category_for_reflection(reflection)) { super }
208
+ end
186
209
  end
187
210
  end
188
211
  end
@@ -61,6 +61,9 @@ module Switchman
61
61
  end
62
62
  end
63
63
 
64
+ # see also Base#shard_category_for_reflection
65
+ # the difference being this will output static strings for the common cases, making them
66
+ # more performant
64
67
  def shard_category_code_for_reflection(reflection)
65
68
  if reflection
66
69
  if reflection.options[:polymorphic]
@@ -31,20 +31,20 @@ module Switchman
31
31
  @integral_id
32
32
  end
33
33
 
34
- def transaction(*args)
34
+ def transaction(**)
35
35
  if self != ::ActiveRecord::Base && current_scope
36
36
  current_scope.activate do
37
37
  db = Shard.current(shard_category).database_server
38
- if ::Shackles.environment != db.shackles_environment
39
- db.unshackle { super }
38
+ if ::GuardRail.environment != db.guard_rail_environment
39
+ db.unguard { super }
40
40
  else
41
41
  super
42
42
  end
43
43
  end
44
44
  else
45
45
  db = Shard.current(shard_category).database_server
46
- if ::Shackles.environment != db.shackles_environment
47
- db.unshackle { super }
46
+ if ::GuardRail.environment != db.guard_rail_environment
47
+ db.unguard { super }
48
48
  else
49
49
  super
50
50
  end
@@ -68,6 +68,14 @@ module Switchman
68
68
  result
69
69
  end
70
70
  end
71
+
72
+ def clear_query_caches_for_current_thread
73
+ ::ActiveRecord::Base.connection_handlers.each_value do |handler|
74
+ handler.connection_pool_list.each do |pool|
75
+ pool.connection(switch_shard: false).clear_query_cache if pool.active_connection?
76
+ end
77
+ end
78
+ end
71
79
  end
72
80
 
73
81
  def self.included(klass)
@@ -97,14 +105,14 @@ module Switchman
97
105
  end
98
106
  end
99
107
 
100
- def save(*args)
108
+ def save(*, **)
101
109
  @shard_set_in_stone = true
102
- self.class.shard(shard, :implicit).scoping { super }
110
+ (self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
103
111
  end
104
112
 
105
- def save!(*args)
113
+ def save!(*, **)
106
114
  @shard_set_in_stone = true
107
- self.class.shard(shard, :implicit).scoping { super }
115
+ (self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
108
116
  end
109
117
 
110
118
  def destroy
@@ -146,6 +154,36 @@ module Switchman
146
154
  # do this the Rails 4.2 way, so that if Shard.current != self.shard, the id gets transposed
147
155
  self.class.connection.quote(id)
148
156
  end
157
+
158
+ def update_columns(*)
159
+ db = Shard.current(self.class.shard_category).database_server
160
+ if ::GuardRail.environment != db.guard_rail_environment
161
+ return db.unguard { super }
162
+ else
163
+ super
164
+ end
165
+ end
166
+
167
+ protected
168
+
169
+ # see also AttributeMethods#shard_category_code_for_reflection
170
+ def shard_category_for_reflection(reflection)
171
+ if reflection
172
+ if reflection.options[:polymorphic]
173
+ begin
174
+ read_attribute(reflection.foreign_type)&.constantize&.shard_category || :primary
175
+ rescue NameError
176
+ # in case someone is abusing foreign_type to not point to an actual class
177
+ :primary
178
+ end
179
+ else
180
+ # otherwise we can just return a symbol for the statically known type of the association
181
+ reflection.klass.shard_category
182
+ end
183
+ else
184
+ shard_category
185
+ end
186
+ end
149
187
  end
150
188
  end
151
189
  end
@@ -121,7 +121,8 @@ module Switchman
121
121
  group_fields = group_attrs
122
122
  end
123
123
 
124
- group_aliases = group_fields.map { |field| column_alias_for(field) }
124
+ # to_s is because Rails 5 returns a string but Rails 6 returns a symbol.
125
+ group_aliases = group_fields.map { |field| column_alias_for(field.downcase.to_s).to_s }
125
126
  group_columns = group_aliases.zip(group_fields).map { |aliaz, field|
126
127
  [aliaz, type_for(field), column_name_for(field)]
127
128
  }
@@ -4,8 +4,6 @@ module Switchman
4
4
  module ActiveRecord
5
5
  module ConnectionHandler
6
6
  def self.make_sharing_automagic(config, shard = Shard.current)
7
- key = config[:adapter] == 'postgresql' ? :schema_search_path : :database
8
-
9
7
  # only load the shard name from the db if we have to
10
8
  if !config[:shard_name]
11
9
  # we may not be able to connect to this shard yet, cause it might be an empty database server
@@ -15,15 +13,13 @@ module Switchman
15
13
 
16
14
  config[:shard_name] ||= shard_name
17
15
  end
18
-
19
- if !config[key] || config[key] == shard_name
20
- # this may truncate the schema_search_path if it was not specified in database.yml
21
- # but that's what our old behavior was anyway, so I guess it's okay
22
- config[key] = '%{shard_name}'
23
- end
24
16
  end
25
17
 
26
18
  def establish_connection(spec)
19
+ # Just skip establishing a sharded connection if sharding isn't loaded; we'll do it again later
20
+ # This only can happen when loading ActiveRecord::Base; after everything is loaded Shard will
21
+ # be defined and this will actually establish a connection
22
+ return unless defined?(Shard)
27
23
  pool = super
28
24
 
29
25
  # this is the first place that the adapter would have been required; but now we
@@ -39,19 +35,27 @@ module Switchman
39
35
  # to sharding will recurse onto itself trying to access column information
40
36
  Shard.default
41
37
 
38
+ config = pool.spec.config
42
39
  # automatically change config to allow for sharing connections with simple config
43
- config = ::Rails.version < '5.1' ? spec.config : pool.spec.config
44
40
  ConnectionHandler.make_sharing_automagic(config)
45
41
  ConnectionHandler.make_sharing_automagic(Shard.default.database_server.config)
46
42
 
47
- if ::Rails.version < '5.1'
48
- ::ActiveRecord::Base.configurations[::Rails.env] = spec.instance_variable_get(:@config).stringify_keys
49
- else
43
+ if ::Rails.version < '6.0'
50
44
  ::ActiveRecord::Base.configurations[::Rails.env] = config.stringify_keys
45
+ else
46
+ # Adopted from the deprecated code that currently lives in rails proper
47
+ remaining_configs = ::ActiveRecord::Base.configurations.configurations.reject { |db_config| db_config.env_name == ::Rails.env }
48
+ new_config = ::ActiveRecord::DatabaseConfigurations.new(::Rails.env => config.stringify_keys).configurations
49
+ new_configs = remaining_configs + new_config
50
+
51
+ ::ActiveRecord::Base.configurations = new_configs
51
52
  end
53
+ else
54
+ # this is probably wrong now
55
+ Shard.default.remove_instance_variable(:@name) if Shard.default.instance_variable_defined?(:@name)
52
56
  end
53
57
 
54
- @shard_connection_pools ||= { [:master, Shard.default.database_server.shareable? ? ::Rails.env : Shard.default] => pool}
58
+ @shard_connection_pools ||= { [:primary, Shard.default.database_server.shareable? ? ::Rails.env : Shard.default] => pool}
55
59
 
56
60
  category = pool.spec.name.to_sym
57
61
  proxy = ConnectionPoolProxy.new(category,
@@ -60,13 +64,13 @@ module Switchman
60
64
  owner_to_pool[pool.spec.name] = proxy
61
65
 
62
66
  if first_time
63
- if Shard.default.database_server.config[:prefer_slave]
64
- Shard.default.database_server.shackle!
67
+ if Shard.default.database_server.config[:prefer_secondary]
68
+ Shard.default.database_server.guard!
65
69
  end
66
70
 
67
- if Shard.default.is_a?(DefaultShard) && Shard.default.database_server.config[:slave]
68
- Shard.default.database_server.shackle!
69
- Shard.default(true)
71
+ if Shard.default.is_a?(DefaultShard) && Shard.default.database_server.config[:secondary]
72
+ Shard.default.database_server.guard!
73
+ Shard.default(reload: true)
70
74
  end
71
75
  end
72
76
 
@@ -122,8 +126,7 @@ module Switchman
122
126
  else
123
127
  ancestor_pool.spec
124
128
  end
125
- spec = spec.to_hash if ::Rails.version >= '5.1'
126
- pool = establish_connection spec
129
+ pool = establish_connection(spec.to_hash)
127
130
  pool.instance_variable_set(:@schema_cache, ancestor_pool.schema_cache) if ancestor_pool.schema_cache
128
131
  pool
129
132
  elsif spec_name != "primary"
@@ -32,10 +32,10 @@ module Switchman
32
32
  conn
33
33
  end
34
34
 
35
- def connection
36
- conn = super
35
+ def connection(switch_shard: true)
36
+ conn = super()
37
37
  raise NonExistentShardError if shard.new_record?
38
- switch_database(conn) if conn.shard != self.shard
38
+ switch_database(conn) if conn.shard != self.shard && switch_shard
39
39
  conn
40
40
  end
41
41
 
@@ -47,6 +47,20 @@ module Switchman
47
47
  end
48
48
  end
49
49
 
50
+ def remove_shard!(shard)
51
+ synchronize do
52
+ # The shard might be currently active, so we need to update our own shard
53
+ if self.shard == shard
54
+ self.shard = Shard.default
55
+ end
56
+ # Update out any connections that may be using this shard
57
+ @connections.each do |conn|
58
+ # This will also update the connection's shard to the default shard
59
+ switch_database(conn) if conn.shard == shard
60
+ end
61
+ end
62
+ end
63
+
50
64
  def clear_idle_connections!(since_when)
51
65
  synchronize do
52
66
  @connections.reject! do |conn|
@@ -70,18 +84,6 @@ module Switchman
70
84
  end
71
85
 
72
86
  spec.config[:shard_name] = self.shard.name
73
- case conn.adapter_name
74
- when 'MySQL', 'Mysql2'
75
- conn.execute("USE #{spec.config[:database]}")
76
- when 'PostgreSQL'
77
- if conn.schema_search_path != spec.config[:schema_search_path]
78
- conn.schema_search_path = spec.config[:schema_search_path]
79
- end
80
- when 'SQLite'
81
- # This is an artifact of the adapter modifying the path to be an absolute path when it is instantiated; just let it slide
82
- else
83
- raise("Cannot switch databases on same DatabaseServer with adapter type: #{conn.adapter_name}. Limit one Shard per DatabaseServer.")
84
- end
85
87
  conn.shard = shard
86
88
  end
87
89
 
@@ -18,18 +18,14 @@ module Switchman
18
18
  shard = " [#{shard[:database_server_id]}:#{shard[:id]} #{shard[:env]}]" if shard
19
19
 
20
20
  unless (payload[:binds] || []).empty?
21
- if ::Rails.version < '5.0.3'
22
- binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect
23
- else
24
- use_old_format = (::Rails.version < '5.1') ? (::Rails.version < '5.0.7') : (::Rails.version < '5.1.5')
25
- args = use_old_format ?
26
- [payload[:binds], payload[:type_casted_binds]] :
27
- [payload[:type_casted_binds]]
28
- casted_params = type_casted_binds(*args)
29
- binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
30
- render_bind(attr, value)
31
- }.inspect
32
- end
21
+ use_old_format = (::Rails.version < '5.1.5')
22
+ args = use_old_format ?
23
+ [payload[:binds], payload[:type_casted_binds]] :
24
+ [payload[:type_casted_binds]]
25
+ casted_params = type_casted_binds(*args)
26
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
27
+ render_bind(attr, value)
28
+ }.inspect
33
29
  end
34
30
 
35
31
  name = colorize_payload_name(name, payload[:name])
@@ -33,5 +33,14 @@ module Switchman
33
33
  ::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
34
34
  end
35
35
  end
36
+
37
+ module MigrationContext
38
+ def migrations
39
+ return @migrations if instance_variable_defined?(:@migrations)
40
+ migrations_cache = Thread.current[:migrations_cache] ||= {}
41
+ key = Digest::MD5.hexdigest(migration_files.sort.join(','))
42
+ @migrations = migrations_cache[key] ||= super
43
+ end
44
+ end
36
45
  end
37
46
  end
@@ -4,7 +4,7 @@ module Switchman
4
4
  module ClassMethods
5
5
  def quoted_table_name
6
6
  @quoted_table_name ||= {}
7
- @quoted_table_name[Shard.current.id] ||= connection.quote_table_name(table_name)
7
+ @quoted_table_name[Shard.current(shard_category).id] ||= connection.quote_table_name(table_name)
8
8
  end
9
9
  end
10
10
  end
@@ -38,51 +38,26 @@ module Switchman
38
38
  select_values("SELECT * FROM unnest(current_schemas(false))")
39
39
  end
40
40
 
41
- def use_qualified_names?
42
- @config[:use_qualified_names]
43
- end
44
-
45
41
  def tables(name = nil)
46
- schema = shard.name if use_qualified_names?
47
-
48
42
  query(<<-SQL, 'SCHEMA').map { |row| row[0] }
49
43
  SELECT tablename
50
44
  FROM pg_tables
51
- WHERE schemaname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
45
+ WHERE schemaname = '#{shard.name}'
52
46
  SQL
53
47
  end
54
48
 
55
- if ::Rails.version >= '5.1'
56
- def extract_schema_qualified_name(string)
57
- name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(string.to_s)
58
- if string && !name.schema && use_qualified_names?
59
- name.instance_variable_set(:@schema, shard.name)
60
- end
61
- [name.schema, name.identifier]
62
- end
63
- else
64
- def data_source_exists?(name)
65
- name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
66
- return false unless name.identifier
67
- if !name.schema && use_qualified_names?
68
- name.instance_variable_set(:@schema, shard.name)
69
- end
70
-
71
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
72
- SELECT COUNT(*)
73
- FROM pg_class c
74
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
75
- WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
76
- AND c.relname = '#{name.identifier}'
77
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
78
- SQL
49
+ def extract_schema_qualified_name(string)
50
+ name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(string.to_s)
51
+ if string && !name.schema
52
+ name.instance_variable_set(:@schema, shard.name)
79
53
  end
54
+ [name.schema, name.identifier]
80
55
  end
81
56
 
82
57
  def view_exists?(name)
83
58
  name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
84
59
  return false unless name.identifier
85
- if !name.schema && use_qualified_names?
60
+ if !name.schema
86
61
  name.instance_variable_set(:@schema, shard.name)
87
62
  end
88
63
 
@@ -92,13 +67,11 @@ module Switchman
92
67
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
93
68
  WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
94
69
  AND c.relname = '#{name.identifier}'
95
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
70
+ AND n.nspname = '#{shard.name}'
96
71
  SQL
97
72
  end
98
73
 
99
74
  def indexes(table_name)
100
- schema = shard.name if use_qualified_names?
101
-
102
75
  result = query(<<-SQL, 'SCHEMA')
103
76
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
104
77
  FROM pg_class t
@@ -107,7 +80,7 @@ module Switchman
107
80
  WHERE i.relkind = 'i'
108
81
  AND d.indisprimary = 'f'
109
82
  AND t.relname = '#{table_name}'
110
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'} )
83
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
111
84
  ORDER BY i.relname
112
85
  SQL
113
86
 
@@ -145,8 +118,6 @@ module Switchman
145
118
  end
146
119
 
147
120
  def index_name_exists?(table_name, index_name, _default = nil)
148
- schema = shard.name if use_qualified_names?
149
-
150
121
  exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
151
122
  SELECT COUNT(*)
152
123
  FROM pg_class t
@@ -155,7 +126,7 @@ module Switchman
155
126
  WHERE i.relkind = 'i'
156
127
  AND i.relname = '#{index_name}'
157
128
  AND t.relname = '#{table_name}'
158
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'} )
129
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
159
130
  SQL
160
131
  end
161
132
 
@@ -169,21 +140,21 @@ module Switchman
169
140
  def quote_table_name(name)
170
141
  return quote_local_table_name(name) if @use_local_table_name
171
142
  name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
172
- if !name.schema && use_qualified_names?
143
+ if !name.schema
173
144
  name.instance_variable_set(:@schema, shard.name)
174
145
  end
175
146
  name.quoted
176
147
  end
177
148
 
178
- def with_local_table_name
179
- @use_local_table_name = true
149
+ def with_local_table_name(enable = true)
150
+ old_value = @use_local_table_name
151
+ @use_local_table_name = enable
180
152
  yield
181
153
  ensure
182
- @use_local_table_name = false
154
+ @use_local_table_name = old_value
183
155
  end
184
156
 
185
157
  def foreign_keys(table_name)
186
- schema = shard.name if use_qualified_names?
187
158
 
188
159
  # mostly copy-pasted from AR - only change is to the nspname condition for qualified names support
189
160
  fk_info = select_all <<-SQL.strip_heredoc
@@ -196,7 +167,7 @@ module Switchman
196
167
  JOIN pg_namespace t3 ON c.connamespace = t3.oid
197
168
  WHERE c.contype = 'f'
198
169
  AND t1.relname = #{quote(table_name)}
199
- AND t3.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
170
+ AND t3.nspname = '#{shard.name}'
200
171
  ORDER BY c.conname
201
172
  SQL
202
173
 
@@ -213,7 +184,7 @@ module Switchman
213
184
  # strip the schema name from to_table if it matches
214
185
  to_table = row['to_table']
215
186
  to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(to_table)
216
- if use_qualified_names? && to_table_qualified_name.schema == shard.name
187
+ if to_table_qualified_name.schema == shard.name
217
188
  to_table = to_table_qualified_name.identifier
218
189
  end
219
190
 
@@ -221,7 +192,7 @@ module Switchman
221
192
  end
222
193
  end
223
194
 
224
- def add_index_options(_table_name, _column_name, _options = {})
195
+ def add_index_options(_table_name, _column_name, **)
225
196
  index_name, index_type, index_columns, index_options, algorithm, using = super
226
197
  algorithm = nil if DatabaseServer.creating_new_shard && algorithm == "CONCURRENTLY"
227
198
  [index_name, index_type, index_columns, index_options, algorithm, using]
@@ -248,6 +219,10 @@ module Switchman
248
219
 
249
220
  execute "ALTER INDEX #{quote_table_name(old_name)} RENAME TO #{quote_local_table_name(new_name)}"
250
221
  end
222
+
223
+ def columns(*)
224
+ with_local_table_name(false) { super }
225
+ end
251
226
  end
252
227
  end
253
228
  end