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.
- checksums.yaml +4 -4
- data/app/models/switchman/shard.rb +743 -11
- data/db/migrate/20130328224244_create_default_shard.rb +1 -1
- data/lib/switchman/active_record/abstract_adapter.rb +2 -6
- data/lib/switchman/active_record/association.rb +35 -12
- data/lib/switchman/active_record/attribute_methods.rb +3 -0
- data/lib/switchman/active_record/base.rb +47 -9
- data/lib/switchman/active_record/calculations.rb +2 -1
- data/lib/switchman/active_record/connection_handler.rb +23 -20
- data/lib/switchman/active_record/connection_pool.rb +17 -15
- data/lib/switchman/active_record/log_subscriber.rb +8 -12
- data/lib/switchman/active_record/migration.rb +9 -0
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/postgresql_adapter.rb +22 -47
- data/lib/switchman/active_record/query_cache.rb +17 -107
- data/lib/switchman/active_record/query_methods.rb +4 -0
- data/lib/switchman/active_record/relation.rb +5 -5
- data/lib/switchman/active_record/statement_cache.rb +4 -25
- data/lib/switchman/active_record/table_definition.rb +2 -2
- data/lib/switchman/active_support/cache.rb +16 -0
- data/lib/switchman/connection_pool_proxy.rb +38 -22
- data/lib/switchman/database_server.rb +32 -19
- data/lib/switchman/default_shard.rb +1 -0
- data/lib/switchman/engine.rb +16 -14
- data/lib/switchman/{shackles → guard_rail}/relation.rb +5 -5
- data/lib/switchman/{shackles.rb → guard_rail.rb} +4 -4
- data/lib/switchman/r_spec_helper.rb +7 -7
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/test_helper.rb +3 -3
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +1 -1
- data/lib/tasks/switchman.rake +23 -17
- metadata +35 -22
- 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,
|
|
8
|
-
Engine.foreign_key_check(name, type,
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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(
|
|
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 ::
|
|
39
|
-
db.
|
|
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 ::
|
|
47
|
-
db.
|
|
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(
|
|
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!(
|
|
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
|
-
|
|
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 < '
|
|
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 ||= { [:
|
|
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[:
|
|
64
|
-
Shard.default.database_server.
|
|
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[:
|
|
68
|
-
Shard.default.database_server.
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 =
|
|
45
|
+
WHERE schemaname = '#{shard.name}'
|
|
52
46
|
SQL
|
|
53
47
|
end
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
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 = #{
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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,
|
|
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
|