pg_online_schema_change 0.7.4 → 0.8.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.
@@ -30,16 +30,23 @@ module PgOnlineSchemaChange
30
30
  # Set this early on to ensure their creation and cleanup (unexpected)
31
31
  # happens at all times. IOW, the calls from Store.get always return
32
32
  # the same value.
33
- Store.set(:old_primary_table, "pgosc_op_table_#{client.table}")
34
- Store.set(:audit_table, "pgosc_at_#{client.table}_#{pgosc_identifier}")
33
+ Store.set(:old_primary_table, "pgosc_op_table_#{client.table.downcase}")
34
+ Store.set(:audit_table, "pgosc_at_#{client.table.downcase}_#{pgosc_identifier}")
35
35
  Store.set(:operation_type_column, "operation_type_#{pgosc_identifier}")
36
36
  Store.set(:trigger_time_column, "trigger_time_#{pgosc_identifier}")
37
37
  Store.set(:audit_table_pk, "at_#{pgosc_identifier}_id")
38
38
  Store.set(:audit_table_pk_sequence, "#{audit_table}_#{audit_table_pk}_seq")
39
- Store.set(:shadow_table, "pgosc_st_#{client.table}_#{pgosc_identifier}")
40
-
41
- Store.set(:referential_foreign_key_statements, Query.referential_foreign_keys_to_refresh(client, client.table))
42
- Store.set(:self_foreign_key_statements, Query.self_foreign_keys_to_refresh(client, client.table))
39
+ Store.set(:shadow_table, "pgosc_st_#{client.table.downcase}_#{pgosc_identifier}")
40
+
41
+ Store.set(
42
+ :referential_foreign_key_statements,
43
+ Query.referential_foreign_keys_to_refresh(client, client.table_name),
44
+ )
45
+ Store.set(
46
+ :self_foreign_key_statements,
47
+ Query.self_foreign_keys_to_refresh(client, client.table_name),
48
+ )
49
+ Store.set(:trigger_statements, Query.get_triggers_for(client, client.table_name))
43
50
  end
44
51
 
45
52
  def run!(options)
@@ -49,6 +56,7 @@ module PgOnlineSchemaChange
49
56
  raise Error, "Parent table has no primary key, exiting..." if primary_key.nil?
50
57
 
51
58
  setup_audit_table!
59
+
52
60
  setup_trigger!
53
61
  setup_shadow_table! # re-uses transaction with serializable
54
62
  disable_vacuum! # re-uses transaction with serializable
@@ -72,9 +80,7 @@ module PgOnlineSchemaChange
72
80
  def setup_signals!
73
81
  reader, writer = IO.pipe
74
82
 
75
- %w[TERM QUIT INT].each do |sig|
76
- trap(sig) { writer.puts sig }
77
- end
83
+ ['TERM', 'QUIT', 'INT'].each { |sig| trap(sig) { writer.puts sig } }
78
84
 
79
85
  reader
80
86
  end
@@ -83,14 +89,14 @@ module PgOnlineSchemaChange
83
89
  reader = setup_signals!
84
90
  signal = reader.gets.chomp
85
91
 
86
- while !reader.closed? && IO.select([reader]) # rubocop:disable Lint/UnreachableLoop
87
- logger.info "Signal #{signal} received, cleaning up"
92
+ while !reader.closed? && reader.wait_readable # rubocop:disable Lint/UnreachableLoop
93
+ logger.info("Signal #{signal} received, cleaning up")
88
94
 
89
95
  client.connection.cancel
90
96
  drop_and_cleanup!
91
97
  reader.close
92
98
 
93
- exit Signal.list[signal]
99
+ exit(Signal.list[signal])
94
100
  end
95
101
  end
96
102
 
@@ -98,7 +104,7 @@ module PgOnlineSchemaChange
98
104
  logger.info("Setting up audit table", { audit_table: audit_table })
99
105
 
100
106
  sql = <<~SQL
101
- CREATE TABLE #{audit_table} (#{audit_table_pk} SERIAL PRIMARY KEY, #{operation_type_column} text, #{trigger_time_column} timestamp, LIKE #{client.table});
107
+ CREATE TABLE #{audit_table} (#{audit_table_pk} SERIAL PRIMARY KEY, #{operation_type_column} text, #{trigger_time_column} timestamp, LIKE #{client.table_name});
102
108
  SQL
103
109
 
104
110
  Query.run(client.connection, sql)
@@ -108,34 +114,34 @@ module PgOnlineSchemaChange
108
114
  # acquire access exclusive lock to ensure audit triggers
109
115
  # are setup fine. This also calls kill_backends (if opted in via flag)
110
116
  # so any competing backends will be killed to setup the trigger
111
- opened = Query.open_lock_exclusive(client, client.table)
117
+ opened = Query.open_lock_exclusive(client, client.table_name)
112
118
 
113
119
  raise AccessExclusiveLockNotAcquired unless opened
114
120
 
115
121
  logger.info("Setting up triggers")
116
122
 
117
123
  sql = <<~SQL
118
- DROP TRIGGER IF EXISTS primary_to_audit_table_trigger ON #{client.table};
124
+ DROP TRIGGER IF EXISTS primary_to_audit_table_trigger ON #{client.table_name};
119
125
 
120
126
  CREATE OR REPLACE FUNCTION primary_to_audit_table_trigger()
121
127
  RETURNS TRIGGER AS
122
128
  $$
123
129
  BEGIN
124
130
  IF ( TG_OP = 'INSERT') THEN
125
- INSERT INTO \"#{audit_table}\" select nextval(\'#{audit_table_pk_sequence}\'), 'INSERT', clock_timestamp(), NEW.* ;
131
+ INSERT INTO "#{audit_table}" select nextval('#{audit_table_pk_sequence}'), 'INSERT', clock_timestamp(), NEW.* ;
126
132
  RETURN NEW;
127
133
  ELSIF ( TG_OP = 'UPDATE') THEN
128
- INSERT INTO \"#{audit_table}\" select nextval(\'#{audit_table_pk_sequence}\'), 'UPDATE', clock_timestamp(), NEW.* ;
134
+ INSERT INTO "#{audit_table}" select nextval('#{audit_table_pk_sequence}'), 'UPDATE', clock_timestamp(), NEW.* ;
129
135
  RETURN NEW;
130
136
  ELSIF ( TG_OP = 'DELETE') THEN
131
- INSERT INTO \"#{audit_table}\" select nextval(\'#{audit_table_pk_sequence}\'), 'DELETE', clock_timestamp(), OLD.* ;
137
+ INSERT INTO "#{audit_table}" select nextval('#{audit_table_pk_sequence}'), 'DELETE', clock_timestamp(), OLD.* ;
132
138
  RETURN NEW;
133
139
  END IF;
134
140
  END;
135
141
  $$ LANGUAGE PLPGSQL SECURITY DEFINER;
136
142
 
137
143
  CREATE TRIGGER primary_to_audit_table_trigger
138
- AFTER INSERT OR UPDATE OR DELETE ON #{client.table}
144
+ AFTER INSERT OR UPDATE OR DELETE ON #{client.table_name}
139
145
  FOR EACH ROW EXECUTE PROCEDURE primary_to_audit_table_trigger();
140
146
  SQL
141
147
 
@@ -156,20 +162,30 @@ module PgOnlineSchemaChange
156
162
  Query.run(client.connection, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", true)
157
163
  logger.info("Setting up shadow table", { shadow_table: shadow_table })
158
164
 
159
- Query.run(client.connection, "SELECT create_table_all('#{client.table}', '#{shadow_table}');", true)
165
+ Query.run(
166
+ client.connection,
167
+ "SELECT create_table_all('#{client.table_name}', '#{shadow_table}');",
168
+ true,
169
+ )
160
170
 
161
171
  # update serials
162
- Query.run(client.connection, "SELECT fix_serial_sequence('#{client.table}', '#{shadow_table}');", true)
172
+ Query.run(
173
+ client.connection,
174
+ "SELECT fix_serial_sequence('#{client.table_name}', '#{shadow_table}');",
175
+ true,
176
+ )
163
177
  end
164
178
 
165
179
  def disable_vacuum!
166
180
  # re-uses transaction with serializable
167
181
  # Disabling vacuum to avoid any issues during the process
168
- result = Query.storage_parameters_for(client, client.table, true) || ""
182
+ result = Query.storage_parameters_for(client, client.table_name, true) || ""
169
183
  Store.set(:primary_table_storage_parameters, result)
170
184
 
171
- logger.debug("Disabling vacuum on shadow and audit table",
172
- { shadow_table: shadow_table, audit_table: audit_table })
185
+ logger.debug(
186
+ "Disabling vacuum on shadow and audit table",
187
+ { shadow_table: shadow_table, audit_table: audit_table },
188
+ )
173
189
  sql = <<~SQL
174
190
  ALTER TABLE #{shadow_table} SET (
175
191
  autovacuum_enabled = false, toast.autovacuum_enabled = false
@@ -185,8 +201,10 @@ module PgOnlineSchemaChange
185
201
  def run_alter_statement!
186
202
  # re-uses transaction with serializable
187
203
  statement = Query.alter_statement_for(client, shadow_table)
188
- logger.info("Running alter statement on shadow table",
189
- { shadow_table: shadow_table, parent_table: client.table })
204
+ logger.info(
205
+ "Running alter statement on shadow table",
206
+ { shadow_table: shadow_table, parent_table: client.table_name },
207
+ )
190
208
  Query.run(client.connection, statement, true)
191
209
 
192
210
  Store.set(:dropped_columns_list, Query.dropped_columns(client))
@@ -198,10 +216,16 @@ module PgOnlineSchemaChange
198
216
  # Begin the process to copy data into copy table
199
217
  # depending on the size of the table, this can be a time
200
218
  # taking operation.
201
- logger.info("Clearing contents of audit table before copy..", { shadow_table: shadow_table, parent_table: client.table })
219
+ logger.info(
220
+ "Clearing contents of audit table before copy..",
221
+ { shadow_table: shadow_table, parent_table: client.table_name },
222
+ )
202
223
  Query.run(client.connection, "DELETE FROM #{audit_table}", true)
203
224
 
204
- logger.info("Copying contents..", { shadow_table: shadow_table, parent_table: client.table })
225
+ logger.info(
226
+ "Copying contents..",
227
+ { shadow_table: shadow_table, parent_table: client.table_name },
228
+ )
205
229
  if client.copy_statement
206
230
  query = format(client.copy_statement, shadow_table: shadow_table)
207
231
  return Query.run(client.connection, query, true)
@@ -223,31 +247,34 @@ module PgOnlineSchemaChange
223
247
 
224
248
  def swap!
225
249
  logger.info("Performing swap!")
226
-
227
- storage_params_reset = primary_table_storage_parameters.empty? ? "" : "ALTER TABLE #{client.table} SET (#{primary_table_storage_parameters});"
228
-
229
250
  # From here on, all statements are carried out in a single
230
251
  # transaction with access exclusive lock
231
252
 
232
- opened = Query.open_lock_exclusive(client, client.table)
253
+ opened = Query.open_lock_exclusive(client, client.table_name)
233
254
 
234
255
  raise AccessExclusiveLockNotAcquired unless opened
235
256
 
236
- Query.run(client.connection, "SET statement_timeout to '#{SWAP_STATEMENT_TIMEOUT}';", opened)
257
+ Query.run(
258
+ client.connection,
259
+ "SET statement_timeout to '#{SWAP_STATEMENT_TIMEOUT}';",
260
+ opened,
261
+ )
237
262
 
238
263
  rows = Replay.rows_to_play(opened)
239
264
  Replay.play!(rows, opened)
240
265
 
241
- query_for_primary_key_refresh = Query.query_for_primary_key_refresh(shadow_table, primary_key, client.table, opened)
266
+ query_for_primary_key_refresh =
267
+ Query.query_for_primary_key_refresh(shadow_table, primary_key, client.table_name, opened)
242
268
 
243
269
  sql = <<~SQL
244
270
  #{query_for_primary_key_refresh};
245
- ALTER TABLE #{client.table} RENAME to #{old_primary_table};
246
- ALTER TABLE #{shadow_table} RENAME to #{client.table};
271
+ ALTER TABLE #{client.table_name} RENAME to #{old_primary_table};
272
+ ALTER TABLE #{shadow_table} RENAME to #{client.table_name};
247
273
  #{referential_foreign_key_statements}
248
274
  #{self_foreign_key_statements}
275
+ #{trigger_statements}
249
276
  #{storage_params_reset}
250
- DROP TRIGGER IF EXISTS primary_to_audit_table_trigger ON #{client.table};
277
+ DROP TRIGGER IF EXISTS primary_to_audit_table_trigger ON #{client.table_name};
251
278
  SQL
252
279
 
253
280
  Query.run(client.connection, sql, opened)
@@ -259,13 +286,13 @@ module PgOnlineSchemaChange
259
286
  def run_analyze!
260
287
  logger.info("Performing ANALYZE!")
261
288
 
262
- Query.run(client.connection, "ANALYZE VERBOSE #{client.table};")
289
+ Query.run(client.connection, "ANALYZE VERBOSE #{client.table_name};")
263
290
  end
264
291
 
265
292
  def validate_constraints!
266
293
  logger.info("Validating constraints!")
267
294
 
268
- validate_statements = Query.get_foreign_keys_to_validate(client, client.table)
295
+ validate_statements = Query.get_foreign_keys_to_validate(client, client.table_name)
269
296
 
270
297
  Query.run(client.connection, validate_statements)
271
298
  end
@@ -276,7 +303,7 @@ module PgOnlineSchemaChange
276
303
  shadow_table_drop = shadow_table ? "DROP TABLE IF EXISTS #{shadow_table}" : ""
277
304
 
278
305
  sql = <<~SQL
279
- DROP TRIGGER IF EXISTS primary_to_audit_table_trigger ON #{client.table};
306
+ DROP TRIGGER IF EXISTS primary_to_audit_table_trigger ON #{client.table_name};
280
307
  #{audit_table_drop};
281
308
  #{shadow_table_drop};
282
309
  #{primary_drop}
@@ -293,6 +320,17 @@ module PgOnlineSchemaChange
293
320
  def pgosc_identifier
294
321
  @pgosc_identifier ||= SecureRandom.hex(3)
295
322
  end
323
+
324
+ def storage_params_reset
325
+ "ALTER TABLE #{client.table_name} RESET (autovacuum_enabled, toast.autovacuum_enabled);" +
326
+ (
327
+ if primary_table_storage_parameters.empty?
328
+ ""
329
+ else
330
+ "ALTER TABLE #{client.table_name} SET (#{primary_table_storage_parameters});"
331
+ end
332
+ )
333
+ end
296
334
  end
297
335
  end
298
336
  end
@@ -14,21 +14,31 @@ module PgOnlineSchemaChange
14
14
 
15
15
  class << self
16
16
  def alter_statement?(query)
17
- PgQuery.parse(query).tree.stmts.all? do |statement|
18
- statement.stmt.alter_table_stmt.instance_of?(PgQuery::AlterTableStmt) || statement.stmt.rename_stmt.instance_of?(PgQuery::RenameStmt)
19
- end
17
+ PgQuery
18
+ .parse(query)
19
+ .tree
20
+ .stmts
21
+ .all? do |statement|
22
+ statement.stmt.alter_table_stmt.instance_of?(PgQuery::AlterTableStmt) ||
23
+ statement.stmt.rename_stmt.instance_of?(PgQuery::RenameStmt)
24
+ end
20
25
  rescue PgQuery::ParseError
21
26
  false
22
27
  end
23
28
 
24
29
  def same_table?(query)
25
- tables = PgQuery.parse(query).tree.stmts.filter_map do |statement|
26
- if statement.stmt.alter_table_stmt.instance_of?(PgQuery::AlterTableStmt)
27
- statement.stmt.alter_table_stmt.relation.relname
28
- elsif statement.stmt.rename_stmt.instance_of?(PgQuery::RenameStmt)
29
- statement.stmt.rename_stmt.relation.relname
30
- end
31
- end
30
+ tables =
31
+ PgQuery
32
+ .parse(query)
33
+ .tree
34
+ .stmts
35
+ .filter_map do |statement|
36
+ if statement.stmt.alter_table_stmt.instance_of?(PgQuery::AlterTableStmt)
37
+ statement.stmt.alter_table_stmt.relation.relname
38
+ elsif statement.stmt.rename_stmt.instance_of?(PgQuery::RenameStmt)
39
+ statement.stmt.rename_stmt.relation.relname
40
+ end
41
+ end
32
42
 
33
43
  tables.uniq.count == 1
34
44
  rescue PgQuery::ParseError
@@ -36,14 +46,30 @@ module PgOnlineSchemaChange
36
46
  end
37
47
 
38
48
  def table(query)
39
- from_rename_statement = PgQuery.parse(query).tree.stmts.filter_map do |statement|
40
- statement.stmt.rename_stmt&.relation&.relname
41
- end[0]
49
+ from_rename_statement =
50
+ PgQuery
51
+ .parse(query)
52
+ .tree
53
+ .stmts
54
+ .filter_map { |statement| statement.stmt.rename_stmt&.relation&.relname }[
55
+ 0
56
+ ]
42
57
  PgQuery.parse(query).tables[0] || from_rename_statement
43
58
  end
44
59
 
60
+ def table_name(query, table)
61
+ table_name = "\"#{table}\""
62
+ if table =~ /[A-Z]/ && query.include?(table_name) && table[0] != '"'
63
+ table_name
64
+ else
65
+ table
66
+ end
67
+ end
68
+
45
69
  def run(connection, query, reuse_trasaction = false, &block)
46
- connection.cancel if [PG::PQTRANS_INERROR, PG::PQTRANS_UNKNOWN].include?(connection.transaction_status)
70
+ if [PG::PQTRANS_INERROR, PG::PQTRANS_UNKNOWN].include?(connection.transaction_status)
71
+ connection.cancel
72
+ end
47
73
 
48
74
  logger.debug("Running query", { query: query })
49
75
 
@@ -65,18 +91,19 @@ module PgOnlineSchemaChange
65
91
  def table_columns(client, table = nil, reuse_trasaction = false)
66
92
  sql = <<~SQL
67
93
  SELECT attname as column_name, format_type(atttypid, atttypmod) as type, attnum as column_position FROM pg_attribute
68
- WHERE attrelid = \'#{table || client.table}\'::regclass AND attnum > 0 AND NOT attisdropped
94
+ WHERE attrelid = '#{table || client.table_name}'::regclass AND attnum > 0 AND NOT attisdropped
69
95
  ORDER BY attnum;
70
96
  SQL
71
97
  mapped_columns = []
72
98
 
73
99
  run(client.connection, sql, reuse_trasaction) do |result|
74
- mapped_columns = result.map do |row|
75
- row["column_name_regular"] = row["column_name"]
76
- row["column_name"] = client.connection.quote_ident(row["column_name"])
77
- row["column_position"] = row["column_position"].to_i
78
- row
79
- end
100
+ mapped_columns =
101
+ result.map do |row|
102
+ row["column_name_regular"] = row["column_name"]
103
+ row["column_name"] = client.connection.quote_ident(row["column_name"])
104
+ row["column_position"] = row["column_position"].to_i
105
+ row
106
+ end
80
107
  end
81
108
 
82
109
  mapped_columns
@@ -86,7 +113,9 @@ module PgOnlineSchemaChange
86
113
  parsed_query = PgQuery.parse(client.alter_statement)
87
114
 
88
115
  parsed_query.tree.stmts.each do |statement|
89
- statement.stmt.alter_table_stmt.relation.relname = shadow_table if statement.stmt.alter_table_stmt
116
+ if statement.stmt.alter_table_stmt
117
+ statement.stmt.alter_table_stmt.relation.relname = shadow_table
118
+ end
90
119
 
91
120
  statement.stmt.rename_stmt.relation.relname = shadow_table if statement.stmt.rename_stmt
92
121
  end
@@ -97,17 +126,27 @@ module PgOnlineSchemaChange
97
126
  query = <<~SQL
98
127
  SELECT indexdef, schemaname
99
128
  FROM pg_indexes
100
- WHERE schemaname = \'#{client.schema}\' AND tablename = \'#{table}\'
129
+ WHERE schemaname = '#{client.schema}' AND tablename = '#{table}'
101
130
  SQL
102
131
 
103
132
  indexes = []
104
- run(client.connection, query) do |result|
105
- indexes = result.map { |row| row["indexdef"] }
106
- end
133
+ run(client.connection, query) { |result| indexes = result.map { |row| row["indexdef"] } }
107
134
 
108
135
  indexes
109
136
  end
110
137
 
138
+ def get_triggers_for(client, table)
139
+ query = <<~SQL
140
+ SELECT pg_get_triggerdef(oid) as tdef FROM pg_trigger
141
+ WHERE tgrelid = '#{client.schema}.#{table}'::regclass AND tgisinternal = FALSE;
142
+ SQL
143
+
144
+ triggers = []
145
+ run(client.connection, query) { |result| triggers = result.map { |row| "#{row["tdef"]};" } }
146
+
147
+ triggers.join(";")
148
+ end
149
+
111
150
  def get_all_constraints_for(client)
112
151
  query = <<~SQL
113
152
  SELECT conrelid::regclass AS table_on,
@@ -121,9 +160,7 @@ module PgOnlineSchemaChange
121
160
  SQL
122
161
 
123
162
  constraints = []
124
- run(client.connection, query) do |result|
125
- constraints = result.map { |row| row }
126
- end
163
+ run(client.connection, query) { |result| constraints = result.map { |row| row } }
127
164
 
128
165
  constraints
129
166
  end
@@ -141,72 +178,93 @@ module PgOnlineSchemaChange
141
178
  end
142
179
 
143
180
  def referential_foreign_keys_to_refresh(client, table)
144
- references = get_all_constraints_for(client).select do |row|
145
- row["table_from"] == table && row["constraint_type"] == "f"
146
- end
181
+ references =
182
+ get_all_constraints_for(client).select do |row|
183
+ row["table_from"] == table && row["constraint_type"] == "f"
184
+ end
147
185
 
148
- references.map do |row|
149
- add_statement = if row["definition"].end_with?("NOT VALID")
150
- "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]};"
151
- else
152
- "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]} NOT VALID;"
153
- end
186
+ references
187
+ .map do |row|
188
+ add_statement =
189
+ if row["definition"].end_with?("NOT VALID")
190
+ "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]};"
191
+ else
192
+ "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]} NOT VALID;"
193
+ end
154
194
 
155
- drop_statement = "ALTER TABLE #{row["table_on"]} DROP CONSTRAINT #{row["constraint_name"]};"
195
+ drop_statement =
196
+ "ALTER TABLE #{row["table_on"]} DROP CONSTRAINT #{row["constraint_name"]};"
156
197
 
157
- "#{drop_statement} #{add_statement}"
158
- end.join
198
+ "#{drop_statement} #{add_statement}"
199
+ end
200
+ .join
159
201
  end
160
202
 
161
203
  def self_foreign_keys_to_refresh(client, table)
162
- references = get_all_constraints_for(client).select do |row|
163
- row["table_on"] == table && row["constraint_type"] == "f"
164
- end
204
+ references =
205
+ get_all_constraints_for(client).select do |row|
206
+ row["table_on"] == table && row["constraint_type"] == "f"
207
+ end
165
208
 
166
- references.map do |row|
167
- add_statement = if row["definition"].end_with?("NOT VALID")
168
- "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]};"
169
- else
170
- "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]} NOT VALID;"
171
- end
172
- add_statement
173
- end.join
209
+ references
210
+ .map do |row|
211
+ add_statement =
212
+ if row["definition"].end_with?("NOT VALID")
213
+ "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]};"
214
+ else
215
+ "ALTER TABLE #{row["table_on"]} ADD CONSTRAINT #{row["constraint_name"]} #{row["definition"]} NOT VALID;"
216
+ end
217
+ add_statement
218
+ end
219
+ .join
174
220
  end
175
221
 
176
222
  def get_foreign_keys_to_validate(client, table)
177
223
  constraints = get_all_constraints_for(client)
178
- referential_foreign_keys = constraints.select do |row|
179
- row["table_from"] == table && row["constraint_type"] == "f"
180
- end
224
+ referential_foreign_keys =
225
+ constraints.select { |row| row["table_from"] == table && row["constraint_type"] == "f" }
181
226
 
182
- self_foreign_keys = constraints.select do |row|
183
- row["table_on"] == table && row["constraint_type"] == "f"
184
- end
227
+ self_foreign_keys =
228
+ constraints.select { |row| row["table_on"] == table && row["constraint_type"] == "f" }
185
229
 
186
- [referential_foreign_keys, self_foreign_keys].flatten.map do |row|
187
- "ALTER TABLE #{row["table_on"]} VALIDATE CONSTRAINT #{row["constraint_name"]};"
188
- end.join
230
+ [referential_foreign_keys, self_foreign_keys].flatten
231
+ .map do |row|
232
+ "ALTER TABLE #{row["table_on"]} VALIDATE CONSTRAINT #{row["constraint_name"]};"
233
+ end
234
+ .join
189
235
  end
190
236
 
191
237
  def dropped_columns(client)
192
- PgQuery.parse(client.alter_statement).tree.stmts.map do |statement|
193
- next if statement.stmt.alter_table_stmt.nil?
194
-
195
- statement.stmt.alter_table_stmt.cmds.map do |cmd|
196
- cmd.alter_table_cmd.name if cmd.alter_table_cmd.subtype == DROPPED_COLUMN_TYPE
238
+ PgQuery
239
+ .parse(client.alter_statement)
240
+ .tree
241
+ .stmts
242
+ .map do |statement|
243
+ next if statement.stmt.alter_table_stmt.nil?
244
+
245
+ statement.stmt.alter_table_stmt.cmds.map do |cmd|
246
+ cmd.alter_table_cmd.name if cmd.alter_table_cmd.subtype == DROPPED_COLUMN_TYPE
247
+ end
197
248
  end
198
- end.flatten.compact
249
+ .flatten
250
+ .compact
199
251
  end
200
252
 
201
253
  def renamed_columns(client)
202
- PgQuery.parse(client.alter_statement).tree.stmts.map do |statement|
203
- next if statement.stmt.rename_stmt.nil?
204
-
205
- {
206
- old_name: statement.stmt.rename_stmt.subname,
207
- new_name: statement.stmt.rename_stmt.newname,
208
- }
209
- end.flatten.compact
254
+ PgQuery
255
+ .parse(client.alter_statement)
256
+ .tree
257
+ .stmts
258
+ .map do |statement|
259
+ next if statement.stmt.rename_stmt.nil?
260
+
261
+ {
262
+ old_name: statement.stmt.rename_stmt.subname,
263
+ new_name: statement.stmt.rename_stmt.newname,
264
+ }
265
+ end
266
+ .flatten
267
+ .compact
210
268
  end
211
269
 
212
270
  def primary_key_for(client, table)
@@ -215,9 +273,9 @@ module PgOnlineSchemaChange
215
273
  pg_attribute.attname as column_name
216
274
  FROM pg_index, pg_class, pg_attribute, pg_namespace
217
275
  WHERE
218
- pg_class.oid = \'#{table}\'::regclass AND
276
+ pg_class.oid = '#{table}'::regclass AND
219
277
  indrelid = pg_class.oid AND
220
- nspname = \'#{client.schema}\' AND
278
+ nspname = '#{client.schema}' AND
221
279
  pg_class.relnamespace = pg_namespace.oid AND
222
280
  pg_attribute.attrelid = pg_class.oid AND
223
281
  pg_attribute.attnum = any(pg_index.indkey)
@@ -225,16 +283,14 @@ module PgOnlineSchemaChange
225
283
  SQL
226
284
 
227
285
  columns = []
228
- run(client.connection, query) do |result|
229
- columns = result.map { |row| row["column_name"] }
230
- end
286
+ run(client.connection, query) { |result| columns = result.map { |row| row["column_name"] } }
231
287
 
232
288
  columns.first
233
289
  end
234
290
 
235
291
  def storage_parameters_for(client, table, reuse_trasaction = false)
236
292
  query = <<~SQL
237
- SELECT array_to_string(reloptions, ',') as params FROM pg_class WHERE relname=\'#{table}\';
293
+ SELECT array_to_string(reloptions, ',') as params FROM pg_class WHERE relname='#{table}';
238
294
  SQL
239
295
 
240
296
  columns = []
@@ -255,7 +311,7 @@ module PgOnlineSchemaChange
255
311
 
256
312
  query = <<~SQL
257
313
  SET lock_timeout = '#{client.wait_time_for_lock}s';
258
- LOCK TABLE #{client.table} IN ACCESS EXCLUSIVE MODE;
314
+ LOCK TABLE #{client.table_name} IN ACCESS EXCLUSIVE MODE;
259
315
  SQL
260
316
  run(client.connection, query, true)
261
317
 
@@ -282,16 +338,17 @@ module PgOnlineSchemaChange
282
338
  logger.info("Terminating other backends")
283
339
 
284
340
  query = <<~SQL
285
- SELECT pg_terminate_backend(pid) FROM pg_locks WHERE locktype = 'relation' AND relation = \'#{table}\'::regclass::oid AND pid <> pg_backend_pid()
341
+ SELECT pg_terminate_backend(pid) FROM pg_locks WHERE locktype = 'relation' AND relation = '#{table}'::regclass::oid AND pid <> pg_backend_pid()
286
342
  SQL
287
343
 
288
344
  run(client.connection, query, true)
289
345
  end
290
346
 
291
347
  def copy_data_statement(client, shadow_table, reuse_trasaction = false)
292
- select_columns = table_columns(client, client.table, reuse_trasaction).map do |entry|
293
- entry["column_name_regular"]
294
- end
348
+ select_columns =
349
+ table_columns(client, client.table_name, reuse_trasaction).map do |entry|
350
+ entry["column_name_regular"]
351
+ end
295
352
 
296
353
  select_columns -= dropped_columns_list if dropped_columns_list.any?
297
354
 
@@ -309,27 +366,23 @@ module PgOnlineSchemaChange
309
366
  client.connection.quote_ident(insert_into_column)
310
367
  end
311
368
 
312
- select_columns.map! do |select_column|
313
- client.connection.quote_ident(select_column)
314
- end
369
+ select_columns.map! { |select_column| client.connection.quote_ident(select_column) }
315
370
 
316
371
  <<~SQL
317
372
  INSERT INTO #{shadow_table}(#{insert_into_columns.join(", ")})
318
373
  SELECT #{select_columns.join(", ")}
319
- FROM ONLY #{client.table}
374
+ FROM ONLY #{client.table_name}
320
375
  SQL
321
376
  end
322
377
 
323
378
  def primary_key_sequence(shadow_table, primary_key, opened)
324
379
  query = <<~SQL
325
- SELECT pg_get_serial_sequence(\'#{shadow_table}\', \'#{primary_key}\') as sequence_name
380
+ SELECT pg_get_serial_sequence('#{shadow_table}', '#{primary_key}') as sequence_name
326
381
  SQL
327
382
 
328
383
  result = run(client.connection, query, opened)
329
384
 
330
- result.map do |row|
331
- row["sequence_name"]
332
- end&.first
385
+ result.map { |row| row["sequence_name"] }&.first
333
386
  end
334
387
 
335
388
  def query_for_primary_key_refresh(shadow_table, primary_key, table, opened)
@@ -338,7 +391,7 @@ module PgOnlineSchemaChange
338
391
  return "" if sequence_name.nil?
339
392
 
340
393
  <<~SQL
341
- SELECT setval((select pg_get_serial_sequence(\'#{shadow_table}\', \'#{primary_key}\')), (SELECT max(#{primary_key}) FROM #{table}));
394
+ SELECT setval((select pg_get_serial_sequence('#{shadow_table}', '#{primary_key}')), (SELECT max(#{primary_key}) FROM #{table}));
342
395
  SQL
343
396
  end
344
397
  end