capture_migration_sql 1.0.4 → 1.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09a2f079879c03ed186a9ee307884faf19826e2f67b9424cd4848322588a3be0'
4
- data.tar.gz: cdc45945a17614ff25f89332fd1fba71872549e190cac1f7d61cd2fedff77a84
3
+ metadata.gz: 244ae0fce5c5654d09321b61daea560a442e213b395fb532145f611f8622f873
4
+ data.tar.gz: 3bb661d211e3a5bf357cdcd447f7b8516ae48e5a7a3c0b27810bc67db388b5c7
5
5
  SHA512:
6
- metadata.gz: 8dd9031cfb5a8e72f94892b6626f3ea8918d75729d4997178b32dbeb53813d6969056bb0634b5f6989bf98a78579952e832819a01ef85655259182f726f97313
7
- data.tar.gz: 02a0bff93314e8a73dd6f8408a8c8e8f81ec93f5498aae72fdd3ddcb5306bd863c4d05d9da3e68c9af035156d29bf2f8edd4b8cf060b09cd1b3335011be73b5c
6
+ metadata.gz: df54712d0d2471fe64222635299652c816cee27309ce42cb4a7d783975cac9e6485fcb3aef192e0cd3403e813992117d1a42eb361668c786a622c23322b992f6
7
+ data.tar.gz: b130cec56900275e6e2da6771814fba7cc0f9ce4fc0d87c5f2303980031a8c67085c2200f87f9741ff92f345ef797b3b0b8b53964a38041b6ec5f8e98ea080c7
data/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.0.5
8
+
9
+ ### Fixed
10
+
11
+ - SQL files are no longer deleted before a down migration runs; if the down migration fails, the SQL file is kept since the migration is still applied.
12
+ - SQL files are now written to a temporary file and atomically renamed into place, so a process killed mid-migration can no longer leave a permanent, truncated SQL file behind.
13
+ - The migration SQL directory is now created with `FileUtils.mkdir_p`, eliminating a race condition between concurrent migration processes and supporting nested custom directories.
14
+ - Attaching the SQL subscriber is now thread safe, preventing duplicate subscribers (and duplicated SQL output) when migrations run in parallel threads.
15
+ - Ignored statement filters are now applied after stripping leading whitespace, so statements with leading whitespace no longer bypass them.
16
+ - The `INSERT INTO schema_migrations` statement written to SQL files now honors custom schema migrations table names and table name prefixes and suffixes.
17
+ - Connection labels from `using_connection` are no longer written to the SQL file when SQL logging is disabled.
18
+
19
+ ### Changed
20
+
21
+ - Rails 5.2 and Ruby 2.6 are the minimum supported versions.
22
+
7
23
  ## 1.0.4
8
24
 
9
25
  ### Fixed
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # Capture Migration SQL
2
2
 
3
3
  [![Continuous Integration](https://github.com/bdurand/capture_migration_sql/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/capture_migration_sql/actions/workflows/continuous_integration.yml)
4
- [![Regression Test](https://github.com/bdurand/capture_migration_sql/actions/workflows/regression_test.yml/badge.svg)](https://github.com/bdurand/capture_migration_sql/actions/workflows/regression_test.yml)
5
4
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
6
5
  [![Gem Version](https://badge.fury.io/rb/capture_migration_sql.svg)](https://badge.fury.io/rb/capture_migration_sql)
7
6
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.0.5
@@ -10,6 +10,12 @@ Gem::Specification.new do |spec|
10
10
  spec.homepage = "https://github.com/bdurand/capture_migration_sql"
11
11
  spec.license = "MIT"
12
12
 
13
+ spec.metadata = {
14
+ "homepage_uri" => spec.homepage,
15
+ "source_code_uri" => spec.homepage,
16
+ "changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md"
17
+ }
18
+
13
19
  # Specify which files should be added to the gem when it is released.
14
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
15
21
  ignore_files = %w[
@@ -27,9 +33,7 @@ Gem::Specification.new do |spec|
27
33
 
28
34
  spec.require_paths = ["lib"]
29
35
 
30
- spec.add_dependency "activerecord", ">= 4.2"
31
-
32
- spec.add_development_dependency "bundler"
36
+ spec.add_dependency "activerecord", ">= 5.2"
33
37
 
34
- spec.required_ruby_version = ">= 2.5"
38
+ spec.required_ruby_version = ">= 2.6"
35
39
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "fileutils"
4
+
3
5
  # Extension methods for ActiveRecord::Migration class.
4
6
  module CaptureMigrationSql
5
7
  module MigrationExtension
@@ -18,7 +20,7 @@ module CaptureMigrationSql
18
20
  end
19
21
 
20
22
  # Enable SQL logging. You can call this method within a block where SQL
21
- # logging was disabled to renable it.
23
+ # logging was disabled to re-enable it.
22
24
  def enable_sql_logging(&block)
23
25
  sql_logging(enabled: true, &block)
24
26
  end
@@ -46,7 +48,7 @@ module CaptureMigrationSql
46
48
  save_connection = @connection
47
49
  begin
48
50
  @connection = connection
49
- stream = CaptureMigrationSql.capture_stream
51
+ stream = CaptureMigrationSql.capture_stream if CaptureMigrationSql.capture_enabled?
50
52
  stream.write("-- BEGIN #{label}\n\n") if label && stream
51
53
  retval = yield
52
54
  stream.write("-- END #{label}\n\n") if label && stream
@@ -73,21 +75,23 @@ module CaptureMigrationSql
73
75
  if File.exist?(output_file)
74
76
  yield
75
77
  else
76
- Dir.mkdir(migration_sql_dir) unless File.exist?(migration_sql_dir)
78
+ FileUtils.mkdir_p(migration_sql_dir)
77
79
  SqlSubscriber.attach_if_necessary
78
- retval = nil
79
- success = false
80
- File.open(output_file, "w") do |f|
81
- retval = capture_migration_sql(f, &block)
82
- success = true
80
+ temp_file = "#{output_file}.tmp.#{Process.pid}.#{Thread.current.object_id}"
81
+ begin
82
+ retval = File.open(temp_file, "w") do |f|
83
+ capture_migration_sql(f, &block)
84
+ end
85
+ File.rename(temp_file, output_file)
86
+ retval
83
87
  ensure
84
- File.unlink(output_file) unless success
88
+ File.unlink(temp_file) if File.exist?(temp_file)
85
89
  end
86
- retval
87
90
  end
88
91
  else
92
+ retval = yield
89
93
  File.unlink(output_file) if output_file && File.exist?(output_file)
90
- yield
94
+ retval
91
95
  end
92
96
  end
93
97
 
@@ -100,7 +104,7 @@ module CaptureMigrationSql
100
104
  ensure
101
105
  Thread.current[:capture_migration_sql_stream] = save_stream
102
106
  end
103
- f.write("INSERT INTO schema_migrations (version) VALUES ('#{version.to_i}');\n")
107
+ f.write("INSERT INTO #{CaptureMigrationSql.schema_migrations_table_name} (version) VALUES ('#{version.to_i}');\n")
104
108
  retval
105
109
  end
106
110
  end
@@ -10,16 +10,19 @@ module CaptureMigrationSql
10
10
 
11
11
  SHOW_STATEMENT = /\ASHOW\b/i
12
12
  EXPLAIN_STATEMENT = /\AEXPLAIN\b/i
13
- SELECT_SCHEMA_MIGRATIONS = /\ASELECT.*FROM.*schema_migrations/i
14
13
  SELECT_INFORMATION_SCHEMA = /\ASELECT.*information_schema/im
15
14
  SQLLITE_VERSION = /\ASELECT sqlite_version\(/i
16
- IGNORE_STATEMENTS = Regexp.union(SHOW_STATEMENT, EXPLAIN_STATEMENT, SELECT_SCHEMA_MIGRATIONS, SELECT_INFORMATION_SCHEMA, SQLLITE_VERSION)
15
+ IGNORE_STATEMENTS = Regexp.union(SHOW_STATEMENT, EXPLAIN_STATEMENT, SELECT_INFORMATION_SCHEMA, SQLLITE_VERSION)
16
+
17
+ ATTACH_MUTEX = Mutex.new
17
18
 
18
19
  class << self
19
20
  def attach_if_necessary
20
- unless defined?(@attached) && @attached
21
- attach_to(:active_record)
22
- @attached = true
21
+ ATTACH_MUTEX.synchronize do
22
+ unless defined?(@attached) && @attached
23
+ attach_to(:active_record)
24
+ @attached = true
25
+ end
23
26
  end
24
27
  end
25
28
  end
@@ -31,11 +34,26 @@ module CaptureMigrationSql
31
34
  payload = event.payload
32
35
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
33
36
  sql = payload[:sql]
34
- return if sql.nil? || IGNORE_STATEMENTS.match(sql)
37
+ return if sql.nil?
35
38
 
36
39
  sql = sql.strip
40
+ return if IGNORE_STATEMENTS.match(sql)
41
+ return if schema_migrations_query?(sql)
42
+
37
43
  sql = "#{sql};" unless sql.end_with?(";")
38
44
  stream.write("#{sql}\n\n")
39
45
  end
46
+
47
+ private
48
+
49
+ # Ignore ActiveRecord's own reads of the schema migrations table. The
50
+ # table name is resolved at runtime so that custom table names and any
51
+ # table name prefix or suffix are matched, staying consistent with the
52
+ # name written to the SQL file.
53
+ def schema_migrations_query?(sql)
54
+ table_name = CaptureMigrationSql.schema_migrations_table_name
55
+ return false if table_name.nil? || table_name.empty?
56
+ /\ASELECT.*FROM.*#{Regexp.escape(table_name)}/im.match?(sql)
57
+ end
40
58
  end
41
59
  end
@@ -18,7 +18,7 @@ module CaptureMigrationSql
18
18
  unless ::ActiveRecord::Migration.include?(MigrationExtension)
19
19
  ::ActiveRecord::Migration.prepend(MigrationExtension)
20
20
  end
21
- @sql_directory = (directory || Rails.root + "db" + "migration_sql")
21
+ @sql_directory = directory || Rails.root + "db" + "migration_sql"
22
22
  @starting_with_version = starting_with.to_i
23
23
  end
24
24
 
@@ -27,7 +27,7 @@ module CaptureMigrationSql
27
27
  @sql_directory if defined?(@sql_directory)
28
28
  end
29
29
 
30
- # Return the migration version number to start capaturing SQL.
30
+ # Return the migration version number to start capturing SQL.
31
31
  def starting_with_version
32
32
  @starting_with_version if defined?(@starting_with_version)
33
33
  end
@@ -37,9 +37,27 @@ module CaptureMigrationSql
37
37
  !!Thread.current[:capture_migration_sql_enabled]
38
38
  end
39
39
 
40
- # Return the strema migration SQL is being written to.
40
+ # Return the stream migration SQL is being written to.
41
41
  def capture_stream
42
42
  Thread.current[:capture_migration_sql_stream]
43
43
  end
44
+
45
+ # Resolve the schema migrations table name so that both the SQL that is
46
+ # written to the file and the statement filter honor custom table names
47
+ # and any table name prefix or suffix. Falls back to "schema_migrations"
48
+ # if the name cannot be determined.
49
+ def schema_migrations_table_name
50
+ if defined?(ActiveRecord::SchemaMigration) && ActiveRecord::SchemaMigration.respond_to?(:table_name)
51
+ ActiveRecord::SchemaMigration.table_name
52
+ else
53
+ connection = ActiveRecord::Base.connection
54
+ schema_migration = if connection.respond_to?(:schema_migration)
55
+ connection.schema_migration
56
+ elsif connection.pool.respond_to?(:schema_migration)
57
+ connection.pool.schema_migration
58
+ end
59
+ schema_migration ? schema_migration.table_name : "schema_migrations"
60
+ end
61
+ end
44
62
  end
45
63
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capture_migration_sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-04-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activerecord
@@ -16,29 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '4.2'
18
+ version: '5.2'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '4.2'
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- description:
25
+ version: '5.2'
42
26
  email:
43
27
  - bbdurand@gmail.com
44
28
  executables: []
@@ -57,8 +41,10 @@ files:
57
41
  homepage: https://github.com/bdurand/capture_migration_sql
58
42
  licenses:
59
43
  - MIT
60
- metadata: {}
61
- post_install_message:
44
+ metadata:
45
+ homepage_uri: https://github.com/bdurand/capture_migration_sql
46
+ source_code_uri: https://github.com/bdurand/capture_migration_sql
47
+ changelog_uri: https://github.com/bdurand/capture_migration_sql/blob/main/CHANGELOG.md
62
48
  rdoc_options: []
63
49
  require_paths:
64
50
  - lib
@@ -66,15 +52,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
52
  requirements:
67
53
  - - ">="
68
54
  - !ruby/object:Gem::Version
69
- version: '2.5'
55
+ version: '2.6'
70
56
  required_rubygems_version: !ruby/object:Gem::Requirement
71
57
  requirements:
72
58
  - - ">="
73
59
  - !ruby/object:Gem::Version
74
60
  version: '0'
75
61
  requirements: []
76
- rubygems_version: 3.4.12
77
- signing_key:
62
+ rubygems_version: 4.0.3
78
63
  specification_version: 4
79
64
  summary: Capture the SQL that is executed when running ActiveRecord migrations so
80
65
  that it can be run in in other environments that don't support migrations.