switchman 2.0.6 → 3.0.1
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/Rakefile +10 -2
- data/app/models/switchman/shard.rb +235 -270
- data/app/models/switchman/unsharded_record.rb +7 -0
- data/db/migrate/20130328212039_create_switchman_shards.rb +1 -1
- data/db/migrate/20130328224244_create_default_shard.rb +5 -5
- data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +1 -0
- data/db/migrate/20180828183945_add_default_shard_index.rb +2 -2
- data/db/migrate/20180828192111_add_timestamps_to_shards.rb +7 -5
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +5 -3
- data/lib/switchman.rb +3 -3
- data/lib/switchman/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +1 -0
- data/lib/switchman/active_record/association.rb +78 -89
- data/lib/switchman/active_record/attribute_methods.rb +106 -52
- data/lib/switchman/active_record/base.rb +58 -59
- data/lib/switchman/active_record/calculations.rb +73 -66
- data/lib/switchman/active_record/connection_pool.rb +14 -41
- data/lib/switchman/active_record/database_configurations.rb +34 -0
- data/lib/switchman/active_record/database_configurations/database_config.rb +13 -0
- data/lib/switchman/active_record/finder_methods.rb +11 -16
- data/lib/switchman/active_record/log_subscriber.rb +4 -8
- data/lib/switchman/active_record/migration.rb +18 -15
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/persistence.rb +4 -6
- data/lib/switchman/active_record/postgresql_adapter.rb +45 -144
- data/lib/switchman/active_record/predicate_builder.rb +1 -1
- data/lib/switchman/active_record/query_cache.rb +18 -19
- data/lib/switchman/active_record/query_methods.rb +172 -181
- data/lib/switchman/active_record/reflection.rb +6 -10
- data/lib/switchman/active_record/relation.rb +27 -21
- data/lib/switchman/active_record/spawn_methods.rb +27 -29
- data/lib/switchman/active_record/statement_cache.rb +18 -35
- data/lib/switchman/active_record/tasks/database_tasks.rb +16 -0
- data/lib/switchman/active_support/cache.rb +3 -5
- data/lib/switchman/arel.rb +13 -8
- data/lib/switchman/database_server.rb +122 -144
- data/lib/switchman/default_shard.rb +52 -16
- data/lib/switchman/engine.rb +61 -57
- data/lib/switchman/environment.rb +4 -8
- data/lib/switchman/errors.rb +1 -0
- data/lib/switchman/guard_rail.rb +6 -19
- data/lib/switchman/guard_rail/relation.rb +5 -7
- data/lib/switchman/r_spec_helper.rb +29 -37
- data/lib/switchman/rails.rb +14 -12
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/standard_error.rb +15 -3
- data/lib/switchman/test_helper.rb +7 -11
- data/lib/switchman/version.rb +1 -1
- data/lib/tasks/switchman.rake +54 -69
- metadata +90 -49
- data/lib/switchman/active_record/batches.rb +0 -11
- data/lib/switchman/active_record/connection_handler.rb +0 -172
- data/lib/switchman/active_record/where_clause_factory.rb +0 -36
- data/lib/switchman/connection_pool_proxy.rb +0 -169
- data/lib/switchman/schema_cache.rb +0 -20
@@ -6,15 +6,18 @@ module Switchman
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionPool
|
8
8
|
def shard
|
9
|
-
|
9
|
+
shard_stack.last || Shard.default
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
Thread.current
|
12
|
+
def shard_stack
|
13
|
+
unless (shard_stack = Thread.current.thread_variable_get(tls_key))
|
14
|
+
shard_stack = Concurrent::Array.new
|
15
|
+
Thread.current.thread_variable_set(tls_key, shard_stack)
|
16
|
+
end
|
17
|
+
shard_stack
|
14
18
|
end
|
15
19
|
|
16
20
|
def default_schema
|
17
|
-
raise "Not postgres!" unless self.spec.config[:adapter] == 'postgresql'
|
18
21
|
connection unless @schemas
|
19
22
|
# default shard will not switch databases immediately, so it won't be set yet
|
20
23
|
@schemas ||= connection.current_schemas
|
@@ -22,39 +25,29 @@ module Switchman
|
|
22
25
|
end
|
23
26
|
|
24
27
|
def checkout_new_connection
|
25
|
-
conn =
|
26
|
-
|
27
|
-
# without locking anything, but if spec returns not-the-object passed
|
28
|
-
# to initialize this pool, things break
|
29
|
-
spec.config[:shard_name] = self.shard.name
|
30
|
-
|
31
|
-
super
|
32
|
-
end
|
33
|
-
conn.shard = self.shard
|
28
|
+
conn = super
|
29
|
+
conn.shard = shard
|
34
30
|
conn
|
35
31
|
end
|
36
32
|
|
37
33
|
def connection(switch_shard: true)
|
38
34
|
conn = super()
|
39
35
|
raise NonExistentShardError if shard.new_record?
|
40
|
-
|
36
|
+
|
37
|
+
switch_database(conn) if conn.shard != shard && switch_shard
|
41
38
|
conn
|
42
39
|
end
|
43
40
|
|
44
41
|
def release_connection(with_id = Thread.current)
|
45
42
|
super(with_id)
|
46
43
|
|
47
|
-
|
48
|
-
clear_idle_connections!(Time.now - spec.config[:idle_timeout].to_i)
|
49
|
-
end
|
44
|
+
flush
|
50
45
|
end
|
51
46
|
|
52
47
|
def remove_shard!(shard)
|
53
48
|
synchronize do
|
54
49
|
# The shard might be currently active, so we need to update our own shard
|
55
|
-
if self.shard == shard
|
56
|
-
self.shard = Shard.default
|
57
|
-
end
|
50
|
+
self.shard = Shard.default if self.shard == shard
|
58
51
|
# Update out any connections that may be using this shard
|
59
52
|
@connections.each do |conn|
|
60
53
|
# This will also update the connection's shard to the default shard
|
@@ -63,29 +56,9 @@ module Switchman
|
|
63
56
|
end
|
64
57
|
end
|
65
58
|
|
66
|
-
def clear_idle_connections!(since_when)
|
67
|
-
synchronize do
|
68
|
-
@connections.reject! do |conn|
|
69
|
-
if conn.last_query_at < since_when && !conn.in_use?
|
70
|
-
conn.disconnect!
|
71
|
-
true
|
72
|
-
else
|
73
|
-
false
|
74
|
-
end
|
75
|
-
end
|
76
|
-
@available.clear
|
77
|
-
@connections.each do |conn|
|
78
|
-
@available.add conn
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
59
|
def switch_database(conn)
|
84
|
-
if !@schemas && conn.adapter_name == 'PostgreSQL' && !
|
85
|
-
@schemas = conn.current_schemas
|
86
|
-
end
|
60
|
+
@schemas = conn.current_schemas if !@schemas && conn.adapter_name == 'PostgreSQL' && !shard.database_server.config[:shard_name]
|
87
61
|
|
88
|
-
spec.config[:shard_name] = self.shard.name
|
89
62
|
conn.shard = shard
|
90
63
|
end
|
91
64
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Switchman
|
4
|
+
module ActiveRecord
|
5
|
+
module DatabaseConfigurations
|
6
|
+
private
|
7
|
+
|
8
|
+
# key difference: assumes a hybrid two-tier structure; each third tier
|
9
|
+
# is implicitly named, and their config is constructing by merging into
|
10
|
+
# its parent
|
11
|
+
def build_configs(configs)
|
12
|
+
return configs.configurations if configs.is_a?(DatabaseConfigurations)
|
13
|
+
return configs if configs.is_a?(Array)
|
14
|
+
|
15
|
+
db_configs = configs.flat_map do |env_name, config|
|
16
|
+
roles = config.keys.select { |k| config[k].is_a?(Hash) }
|
17
|
+
base_config = config.except(*roles)
|
18
|
+
|
19
|
+
name = "#{env_name}/primary"
|
20
|
+
name = 'primary' if env_name == default_env
|
21
|
+
base_db = build_db_config_from_raw_config(env_name, name, base_config)
|
22
|
+
[base_db] + roles.map do |role|
|
23
|
+
build_db_config_from_raw_config(env_name, "#{env_name}/#{role}",
|
24
|
+
base_config.merge(config[role]).merge(replica: true))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
db_configs << environment_url_config(default_env, 'primary', {}) unless db_configs.find(&:for_current_env?)
|
29
|
+
|
30
|
+
merge_db_environment_variables(default_env, db_configs.compact)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -7,19 +7,18 @@ module Switchman
|
|
7
7
|
return super(id) unless klass.integral_id?
|
8
8
|
|
9
9
|
if shard_source_value != :implicit
|
10
|
-
current_shard = Shard.current(klass.
|
11
|
-
result =
|
10
|
+
current_shard = Shard.current(klass.connection_classes)
|
11
|
+
result = activate do |relation, shard|
|
12
12
|
current_id = Shard.relative_id_for(id, current_shard, shard)
|
13
13
|
# current_id will be nil for non-integral id
|
14
14
|
next unless current_id
|
15
15
|
# skip the shard if the object can't be on it. unless we're only looking at one shard;
|
16
16
|
# we might be expecting a shadow object
|
17
|
-
next if current_id > Shard::IDS_PER_SHARD &&
|
17
|
+
next if current_id > Shard::IDS_PER_SHARD && all_shards.length > 1
|
18
|
+
|
18
19
|
relation.call_super(:find_one, FinderMethods, current_id)
|
19
20
|
end
|
20
|
-
if result.is_a?(Array)
|
21
|
-
result = result.first
|
22
|
-
end
|
21
|
+
result = result.first if result.is_a?(Array)
|
23
22
|
# we may have skipped all shards
|
24
23
|
raise_record_not_found_exception!(id, 0, 1) unless result
|
25
24
|
return result
|
@@ -34,8 +33,8 @@ module Switchman
|
|
34
33
|
end
|
35
34
|
|
36
35
|
def find_some_ordered(ids)
|
37
|
-
current_shard = Shard.current(klass.
|
38
|
-
ids = ids.map{|id| Shard.relative_id_for(id, current_shard, current_shard)}
|
36
|
+
current_shard = Shard.current(klass.connection_classes)
|
37
|
+
ids = ids.map { |id| Shard.relative_id_for(id, current_shard, current_shard) }
|
39
38
|
super(ids)
|
40
39
|
end
|
41
40
|
|
@@ -45,14 +44,12 @@ module Switchman
|
|
45
44
|
|
46
45
|
def exists?(conditions = :none)
|
47
46
|
conditions = conditions.id if ::ActiveRecord::Base === conditions
|
48
|
-
return false
|
47
|
+
return false unless conditions
|
49
48
|
|
50
|
-
relation =
|
51
|
-
apply_join_dependency(eager_loading: false) :
|
52
|
-
apply_join_dependency(self, construct_join_dependency)
|
49
|
+
relation = apply_join_dependency(eager_loading: false)
|
53
50
|
return false if ::ActiveRecord::NullRelation === relation
|
54
51
|
|
55
|
-
relation = relation.except(:select, :order).select(
|
52
|
+
relation = relation.except(:select, :order).select('1 AS one').limit(1)
|
56
53
|
|
57
54
|
case conditions
|
58
55
|
when Array, Hash
|
@@ -62,9 +59,7 @@ module Switchman
|
|
62
59
|
end
|
63
60
|
|
64
61
|
relation.activate do |shard_rel|
|
65
|
-
return true if
|
66
|
-
connection.select_value(shard_rel.arel, "#{name} Exists") :
|
67
|
-
connection.select_value(shard_rel, "#{name} Exists", shard_rel.bound_attributes)
|
62
|
+
return true if connection.select_value(shard_rel.arel, "#{name} Exists")
|
68
63
|
end
|
69
64
|
false
|
70
65
|
end
|
@@ -14,20 +14,16 @@ module Switchman
|
|
14
14
|
|
15
15
|
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
16
16
|
name = "CACHE #{name}" if payload[:cached]
|
17
|
-
sql = payload[:sql].squeeze(' '
|
17
|
+
sql = payload[:sql].squeeze(' ')
|
18
18
|
binds = nil
|
19
19
|
shard = payload[:shard]
|
20
20
|
shard = " [#{shard[:database_server_id]}:#{shard[:id]} #{shard[:env]}]" if shard
|
21
21
|
|
22
22
|
unless (payload[:binds] || []).empty?
|
23
|
-
|
24
|
-
|
25
|
-
[payload[:binds], payload[:type_casted_binds]] :
|
26
|
-
[payload[:type_casted_binds]]
|
27
|
-
casted_params = type_casted_binds(*args)
|
28
|
-
binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
|
23
|
+
casted_params = type_casted_binds(payload[:type_casted_binds])
|
24
|
+
binds = ' ' + payload[:binds].zip(casted_params).map do |attr, value|
|
29
25
|
render_bind(attr, value)
|
30
|
-
|
26
|
+
end.inspect
|
31
27
|
end
|
32
28
|
|
33
29
|
name = colorize_payload_name(name, payload[:name])
|
@@ -4,41 +4,44 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module Migration
|
6
6
|
module Compatibility
|
7
|
-
module V5_0
|
7
|
+
module V5_0 # rubocop:disable Naming/ClassAndModuleCamelCase
|
8
8
|
def create_table(*args, **options)
|
9
|
-
unless options.key?(:id)
|
10
|
-
|
11
|
-
end
|
12
|
-
if block_given?
|
13
|
-
super do |td|
|
14
|
-
yield td
|
15
|
-
end
|
16
|
-
else
|
17
|
-
super
|
18
|
-
end
|
9
|
+
options[:id] = :bigserial unless options.key?(:id)
|
10
|
+
super
|
19
11
|
end
|
20
12
|
end
|
21
13
|
end
|
22
14
|
|
23
15
|
def connection
|
24
16
|
conn = super
|
25
|
-
if conn.shard != ::ActiveRecord::Base.connection_pool.
|
26
|
-
::ActiveRecord::Base.connection_pool.current_pool.switch_database(conn)
|
27
|
-
end
|
17
|
+
::ActiveRecord::Base.connection_pool.switch_database(conn) if conn.shard != ::ActiveRecord::Base.connection_pool.shard
|
28
18
|
conn
|
29
19
|
end
|
30
20
|
end
|
31
21
|
|
32
22
|
module Migrator
|
23
|
+
# significant change: hash shard name, not database name
|
33
24
|
def generate_migrator_advisory_lock_id
|
34
|
-
shard_name_hash = Zlib.crc32(
|
25
|
+
shard_name_hash = Zlib.crc32(Shard.current.name)
|
35
26
|
::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
|
36
27
|
end
|
28
|
+
|
29
|
+
# significant change: strip out prefer_secondary from config
|
30
|
+
def with_advisory_lock_connection
|
31
|
+
pool = ::ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
|
32
|
+
::ActiveRecord::Base.connection_db_config.configuration_hash.except(:prefer_secondary)
|
33
|
+
)
|
34
|
+
|
35
|
+
pool.with_connection { |connection| yield(connection) } # rubocop:disable Style/ExplicitBlockArgument
|
36
|
+
ensure
|
37
|
+
pool&.disconnect!
|
38
|
+
end
|
37
39
|
end
|
38
40
|
|
39
41
|
module MigrationContext
|
40
42
|
def migrations
|
41
43
|
return @migrations if instance_variable_defined?(:@migrations)
|
44
|
+
|
42
45
|
migrations_cache = Thread.current[:migrations_cache] ||= {}
|
43
46
|
key = Digest::MD5.hexdigest(migration_files.sort.join(','))
|
44
47
|
@migrations = migrations_cache[key] ||= super
|
@@ -6,7 +6,7 @@ module Switchman
|
|
6
6
|
module ClassMethods
|
7
7
|
def quoted_table_name
|
8
8
|
@quoted_table_name ||= {}
|
9
|
-
@quoted_table_name[Shard.current(
|
9
|
+
@quoted_table_name[Shard.current(connection_classes).id] ||= connection.quote_table_name(table_name)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -5,14 +5,12 @@ module Switchman
|
|
5
5
|
module Persistence
|
6
6
|
# touch reads the id attribute directly, so it's not relative to the current shard
|
7
7
|
def touch(*, **)
|
8
|
-
shard.activate(self.class.
|
8
|
+
shard.activate(self.class.connection_classes) { super }
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
shard.activate(self.class.shard_category) { super }
|
14
|
-
end
|
11
|
+
def update_columns(*)
|
12
|
+
shard.activate(self.class.connection_classes) { super }
|
15
13
|
end
|
16
14
|
end
|
17
15
|
end
|
18
|
-
end
|
16
|
+
end
|
@@ -9,22 +9,22 @@ module Switchman
|
|
9
9
|
|
10
10
|
option_string = options.sum do |key, value|
|
11
11
|
case key
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
when :owner
|
13
|
+
" OWNER = \"#{value}\""
|
14
|
+
when :template
|
15
|
+
" TEMPLATE = \"#{value}\""
|
16
|
+
when :encoding
|
17
|
+
" ENCODING = '#{value}'"
|
18
|
+
when :collation
|
19
|
+
" LC_COLLATE = '#{value}'"
|
20
|
+
when :ctype
|
21
|
+
" LC_CTYPE = '#{value}'"
|
22
|
+
when :tablespace
|
23
|
+
" TABLESPACE = \"#{value}\""
|
24
|
+
when :connection_limit
|
25
|
+
" CONNECTION LIMIT = #{value}"
|
26
|
+
else
|
27
|
+
''
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -37,99 +37,39 @@ module Switchman
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def current_schemas
|
40
|
-
select_values(
|
41
|
-
end
|
42
|
-
|
43
|
-
def tables(name = nil)
|
44
|
-
query(<<-SQL, 'SCHEMA').map { |row| row[0] }
|
45
|
-
SELECT tablename
|
46
|
-
FROM pg_tables
|
47
|
-
WHERE schemaname = '#{shard.name}'
|
48
|
-
SQL
|
40
|
+
select_values('SELECT * FROM unnest(current_schemas(false))')
|
49
41
|
end
|
50
42
|
|
51
43
|
def extract_schema_qualified_name(string)
|
52
44
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(string.to_s)
|
53
|
-
if string && !name.schema
|
54
|
-
name.instance_variable_set(:@schema, shard.name)
|
55
|
-
end
|
45
|
+
name.instance_variable_set(:@schema, shard.name) if string && !name.schema
|
56
46
|
[name.schema, name.identifier]
|
57
47
|
end
|
58
48
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
71
|
-
AND c.relname = '#{name.identifier}'
|
72
|
-
AND n.nspname = '#{shard.name}'
|
73
|
-
SQL
|
74
|
-
end
|
75
|
-
|
76
|
-
def indexes(table_name)
|
77
|
-
result = query(<<-SQL, 'SCHEMA')
|
78
|
-
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
79
|
-
FROM pg_class t
|
80
|
-
INNER JOIN pg_index d ON t.oid = d.indrelid
|
81
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
82
|
-
WHERE i.relkind = 'i'
|
83
|
-
AND d.indisprimary = 'f'
|
84
|
-
AND t.relname = '#{table_name}'
|
85
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
86
|
-
ORDER BY i.relname
|
87
|
-
SQL
|
88
|
-
|
89
|
-
|
90
|
-
result.map do |row|
|
91
|
-
index_name = row[0]
|
92
|
-
unique = row[1] == true || row[1] == 't'
|
93
|
-
indkey = row[2].split(" ")
|
94
|
-
inddef = row[3]
|
95
|
-
oid = row[4]
|
96
|
-
|
97
|
-
columns = Hash[query(<<-SQL, "SCHEMA")]
|
98
|
-
SELECT a.attnum, a.attname
|
99
|
-
FROM pg_attribute a
|
100
|
-
WHERE a.attrelid = #{oid}
|
101
|
-
AND a.attnum IN (#{indkey.join(",")})
|
102
|
-
SQL
|
103
|
-
|
104
|
-
column_names = columns.stringify_keys.values_at(*indkey).compact
|
105
|
-
|
106
|
-
unless column_names.empty?
|
107
|
-
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
108
|
-
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
109
|
-
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
|
110
|
-
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
111
|
-
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
112
|
-
|
113
|
-
if ::Rails.version >= "5.2"
|
114
|
-
::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, orders: orders, where: where, using: using)
|
115
|
-
else
|
116
|
-
::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
|
117
|
-
end
|
49
|
+
# significant change: use the shard name if no explicit schema
|
50
|
+
def quoted_scope(name = nil, type: nil)
|
51
|
+
schema, name = extract_schema_qualified_name(name)
|
52
|
+
type = \
|
53
|
+
case type # rubocop:disable Style/HashLikeCase
|
54
|
+
when 'BASE TABLE'
|
55
|
+
"'r','p'"
|
56
|
+
when 'VIEW'
|
57
|
+
"'v','m'"
|
58
|
+
when 'FOREIGN TABLE'
|
59
|
+
"'f'"
|
118
60
|
end
|
119
|
-
|
61
|
+
scope = {}
|
62
|
+
scope[:schema] = quote(schema || shard.name)
|
63
|
+
scope[:name] = quote(name) if name
|
64
|
+
scope[:type] = type if type
|
65
|
+
scope
|
120
66
|
end
|
121
67
|
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
128
|
-
WHERE i.relkind = 'i'
|
129
|
-
AND i.relname = '#{index_name}'
|
130
|
-
AND t.relname = '#{table_name}'
|
131
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
132
|
-
SQL
|
68
|
+
def foreign_keys(table_name)
|
69
|
+
super.each do |fk|
|
70
|
+
to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(fk.to_table)
|
71
|
+
fk.to_table = to_table_qualified_name.identifier if to_table_qualified_name.schema == shard.name
|
72
|
+
end
|
133
73
|
end
|
134
74
|
|
135
75
|
def quote_local_table_name(name)
|
@@ -141,14 +81,13 @@ module Switchman
|
|
141
81
|
|
142
82
|
def quote_table_name(name)
|
143
83
|
return quote_local_table_name(name) if @use_local_table_name
|
84
|
+
|
144
85
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
145
|
-
|
146
|
-
name.instance_variable_set(:@schema, shard.name)
|
147
|
-
end
|
86
|
+
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
148
87
|
name.quoted
|
149
88
|
end
|
150
89
|
|
151
|
-
def with_local_table_name(enable = true)
|
90
|
+
def with_local_table_name(enable = true) # rubocop:disable Style/OptionalBooleanParameter
|
152
91
|
old_value = @use_local_table_name
|
153
92
|
@use_local_table_name = enable
|
154
93
|
yield
|
@@ -156,48 +95,10 @@ module Switchman
|
|
156
95
|
@use_local_table_name = old_value
|
157
96
|
end
|
158
97
|
|
159
|
-
def foreign_keys(table_name)
|
160
|
-
|
161
|
-
# mostly copy-pasted from AR - only change is to the nspname condition for qualified names support
|
162
|
-
fk_info = select_all <<-SQL.strip_heredoc
|
163
|
-
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
|
164
|
-
FROM pg_constraint c
|
165
|
-
JOIN pg_class t1 ON c.conrelid = t1.oid
|
166
|
-
JOIN pg_class t2 ON c.confrelid = t2.oid
|
167
|
-
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
|
168
|
-
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
169
|
-
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
170
|
-
WHERE c.contype = 'f'
|
171
|
-
AND t1.relname = #{quote(table_name)}
|
172
|
-
AND t3.nspname = '#{shard.name}'
|
173
|
-
ORDER BY c.conname
|
174
|
-
SQL
|
175
|
-
|
176
|
-
fk_info.map do |row|
|
177
|
-
options = {
|
178
|
-
column: row['column'],
|
179
|
-
name: row['name'],
|
180
|
-
primary_key: row['primary_key']
|
181
|
-
}
|
182
|
-
|
183
|
-
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
184
|
-
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
185
|
-
|
186
|
-
# strip the schema name from to_table if it matches
|
187
|
-
to_table = row['to_table']
|
188
|
-
to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(to_table)
|
189
|
-
if to_table_qualified_name.schema == shard.name
|
190
|
-
to_table = to_table_qualified_name.identifier
|
191
|
-
end
|
192
|
-
|
193
|
-
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, to_table, options)
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
98
|
def add_index_options(_table_name, _column_name, **)
|
198
|
-
|
199
|
-
algorithm = nil if DatabaseServer.creating_new_shard && algorithm ==
|
200
|
-
[
|
99
|
+
index, algorithm, if_not_exists = super
|
100
|
+
algorithm = nil if DatabaseServer.creating_new_shard && algorithm == 'CONCURRENTLY'
|
101
|
+
[index, algorithm, if_not_exists]
|
201
102
|
end
|
202
103
|
|
203
104
|
def rename_table(table_name, new_name)
|