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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +10 -2
  3. data/app/models/switchman/shard.rb +235 -270
  4. data/app/models/switchman/unsharded_record.rb +7 -0
  5. data/db/migrate/20130328212039_create_switchman_shards.rb +1 -1
  6. data/db/migrate/20130328224244_create_default_shard.rb +5 -5
  7. data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +1 -0
  8. data/db/migrate/20180828183945_add_default_shard_index.rb +2 -2
  9. data/db/migrate/20180828192111_add_timestamps_to_shards.rb +7 -5
  10. data/db/migrate/20190114212900_add_unique_name_indexes.rb +5 -3
  11. data/lib/switchman.rb +3 -3
  12. data/lib/switchman/action_controller/caching.rb +2 -2
  13. data/lib/switchman/active_record/abstract_adapter.rb +1 -0
  14. data/lib/switchman/active_record/association.rb +78 -89
  15. data/lib/switchman/active_record/attribute_methods.rb +106 -52
  16. data/lib/switchman/active_record/base.rb +58 -59
  17. data/lib/switchman/active_record/calculations.rb +73 -66
  18. data/lib/switchman/active_record/connection_pool.rb +14 -41
  19. data/lib/switchman/active_record/database_configurations.rb +34 -0
  20. data/lib/switchman/active_record/database_configurations/database_config.rb +13 -0
  21. data/lib/switchman/active_record/finder_methods.rb +11 -16
  22. data/lib/switchman/active_record/log_subscriber.rb +4 -8
  23. data/lib/switchman/active_record/migration.rb +18 -15
  24. data/lib/switchman/active_record/model_schema.rb +1 -1
  25. data/lib/switchman/active_record/persistence.rb +4 -6
  26. data/lib/switchman/active_record/postgresql_adapter.rb +45 -144
  27. data/lib/switchman/active_record/predicate_builder.rb +1 -1
  28. data/lib/switchman/active_record/query_cache.rb +18 -19
  29. data/lib/switchman/active_record/query_methods.rb +172 -181
  30. data/lib/switchman/active_record/reflection.rb +6 -10
  31. data/lib/switchman/active_record/relation.rb +27 -21
  32. data/lib/switchman/active_record/spawn_methods.rb +27 -29
  33. data/lib/switchman/active_record/statement_cache.rb +18 -35
  34. data/lib/switchman/active_record/tasks/database_tasks.rb +16 -0
  35. data/lib/switchman/active_support/cache.rb +3 -5
  36. data/lib/switchman/arel.rb +13 -8
  37. data/lib/switchman/database_server.rb +122 -144
  38. data/lib/switchman/default_shard.rb +52 -16
  39. data/lib/switchman/engine.rb +61 -57
  40. data/lib/switchman/environment.rb +4 -8
  41. data/lib/switchman/errors.rb +1 -0
  42. data/lib/switchman/guard_rail.rb +6 -19
  43. data/lib/switchman/guard_rail/relation.rb +5 -7
  44. data/lib/switchman/r_spec_helper.rb +29 -37
  45. data/lib/switchman/rails.rb +14 -12
  46. data/lib/switchman/sharded_instrumenter.rb +1 -1
  47. data/lib/switchman/standard_error.rb +15 -3
  48. data/lib/switchman/test_helper.rb +7 -11
  49. data/lib/switchman/version.rb +1 -1
  50. data/lib/tasks/switchman.rake +54 -69
  51. metadata +90 -49
  52. data/lib/switchman/active_record/batches.rb +0 -11
  53. data/lib/switchman/active_record/connection_handler.rb +0 -172
  54. data/lib/switchman/active_record/where_clause_factory.rb +0 -36
  55. data/lib/switchman/connection_pool_proxy.rb +0 -169
  56. 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
- Thread.current[tls_key] || Shard.default
9
+ shard_stack.last || Shard.default
10
10
  end
11
11
 
12
- def shard=(value)
13
- Thread.current[tls_key] = value
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 = synchronize do
26
- # ideally I would just keep a thread-local spec that I could modify
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
- switch_database(conn) if conn.shard != self.shard && switch_shard
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
- if spec.config[:idle_timeout]
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' && !self.shard.database_server.config[:shard_name]
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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ module DatabaseConfigurations
6
+ module DatabaseConfig
7
+ def for_current_env?
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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.shard_category)
11
- result = self.activate do |relation, shard|
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 && self.all_shards.length > 1
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.shard_category)
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 if !conditions
47
+ return false unless conditions
49
48
 
50
- relation = ::Rails.version >= "5.2" ?
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("1 AS one").limit(1)
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 ::Rails.version >= "5.2" ?
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(' '.freeze)
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
- use_old_format = (::Rails.version < '5.1.5')
24
- args = use_old_format ?
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
- }.inspect
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
- options[:id] = :bigserial
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.current_pool.shard
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("#{Shard.current.id}:#{Shard.current.name}")
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(shard_category).id] ||= connection.quote_table_name(table_name)
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.shard_category) { super }
8
+ shard.activate(self.class.connection_classes) { super }
9
9
  end
10
10
 
11
- if ::Rails.version >= '5.2'
12
- def update_columns(*)
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
- 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
- ""
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("SELECT * FROM unnest(current_schemas(false))")
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
- def view_exists?(name)
60
- name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
61
- return false unless name.identifier
62
- if !name.schema
63
- name.instance_variable_set(:@schema, shard.name)
64
- end
65
-
66
- select_values(<<-SQL, 'SCHEMA').any?
67
- SELECT c.relname
68
- FROM pg_class c
69
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
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
- end.compact
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 index_name_exists?(table_name, index_name, _default = nil)
123
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
124
- SELECT COUNT(*)
125
- FROM pg_class t
126
- INNER JOIN pg_index d ON t.oid = d.indrelid
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
- if !name.schema
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
- index_name, index_type, index_columns, index_options, algorithm, using = super
199
- algorithm = nil if DatabaseServer.creating_new_shard && algorithm == "CONCURRENTLY"
200
- [index_name, index_type, index_columns, index_options, algorithm, using]
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)