query_guard 0.5.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d723cebf19844b600f9e9eac77de7b3f38365528445edad233c64d40ec34c4a9
4
- data.tar.gz: 4d0195a46bb85f9761ffd8aed0da8f5a2826356615cea117b47e27095e99f358
3
+ metadata.gz: 3c49c4ea0d69a65cc4f68add6d1ba70b4ae35f4df1099ccf3d920be37d540b3f
4
+ data.tar.gz: ee58b9a08573ae7d47d15ab7d8188fd768d83c11d8a9e35e71ee0dbe511acf45
5
5
  SHA512:
6
- metadata.gz: 19b5411f363a8fffb31360146221fce7d37014cb6be395e39ca36d14ec8c2bee0247cf357905eeb9e592854201fc414efe9195a314a671674ea2612fb0069d31
7
- data.tar.gz: f07a0a99f4bce9428f26927c05b0bf4e82568a7ae554a1fbfa35eda77a45d9ce69842b021833293a77b7885bf93b30d6c13b307a09438bed45320fedea48b4cb
6
+ metadata.gz: 50d9586e1bb88b11087bc3538c1d89459d99ffdf42001f5998eab5b86d8c6bad006e29d7ca64724693c5fcc6a5087353fb82f101881d9ac9f6a2560306cda858
7
+ data.tar.gz: 8668aa0e2808b835b85dfa5aae308fc3cf4386f696316bd9b89d2c140b93c2cb7bd97d643328030e8737be0a4e6dc30a7f3fa3410fd00feef3083443e348ad33
@@ -12,9 +12,11 @@ module QueryGuard
12
12
 
13
13
  # Run all detectors
14
14
  risks.concat(detect_unsafe_index_additions(migration_content, migration_name))
15
+ risks.concat(detect_concurrent_index_without_disable_ddl(migration_content, migration_name))
15
16
  risks.concat(detect_table_locking_operations(migration_content, migration_name))
16
17
  risks.concat(detect_non_null_additions(migration_content, migration_name))
17
18
  risks.concat(detect_full_table_updates(migration_content, migration_name))
19
+ risks.concat(detect_data_backfill_in_migration(migration_content, migration_name))
18
20
  risks.concat(detect_unsafe_raw_sql(migration_content, migration_name))
19
21
 
20
22
  risks
@@ -29,6 +31,8 @@ module QueryGuard
29
31
  # Find all add_index occurrences
30
32
  lines = content.lines
31
33
  lines.each_with_index do |line, index|
34
+ # Skip commented-out lines
35
+ next if line.strip.start_with?("#")
32
36
  next unless line.include?("add_index")
33
37
 
34
38
  # Check if line includes algorithm: :concurrently
@@ -54,6 +58,105 @@ module QueryGuard
54
58
  risks
55
59
  end
56
60
 
61
+ # Detect algorithm: :concurrently without disable_ddl_transaction!
62
+ # This is necessary for PostgreSQL to allow concurrent index creation
63
+ def self.detect_concurrent_index_without_disable_ddl(content, migration_name)
64
+ risks = []
65
+
66
+ # Check if migration uses algorithm: :concurrently
67
+ has_concurrent_index = content.include?("algorithm: :concurrently")
68
+ return [] unless has_concurrent_index
69
+
70
+ # Check if disable_ddl_transaction! is present (but not in a comment)
71
+ lines = content.lines
72
+ has_disable_ddl = lines.any? do |line|
73
+ # Remove comment part
74
+ code_part = line.split('#').first
75
+ code_part.include?("disable_ddl_transaction!")
76
+ end
77
+
78
+ unless has_disable_ddl
79
+ lines.each_with_index do |line, index|
80
+ # Skip if this line is commented out
81
+ code_part = line.split('#').first
82
+ next unless code_part.include?("algorithm: :concurrently")
83
+
84
+ risks << {
85
+ type: :concurrent_index_no_disable_ddl,
86
+ severity: :error,
87
+ line_number: index + 1,
88
+ migration_name: migration_name,
89
+ title: "algorithm: :concurrently Without disable_ddl_transaction!",
90
+ description: "Using algorithm: :concurrently requires disable_ddl_transaction! in the migration class to avoid transaction errors.",
91
+ message: "algorithm: :concurrently found but disable_ddl_transaction! not set",
92
+ recommendation: "Add `disable_ddl_transaction!` to the migration class definition",
93
+ metadata: {
94
+ operation: "add_index",
95
+ risk_level: :high,
96
+ module: :schema_safety
97
+ }
98
+ }
99
+ end
100
+ end
101
+
102
+ risks
103
+ end
104
+
105
+ # Detect data backfill / app model usage inside migrations
106
+ # Migrations that use ActiveRecord models are risky because:
107
+ # - Models can change independently of migrations
108
+ # - Queries can fail if code changes
109
+ # - Large updates lock tables
110
+ def self.detect_data_backfill_in_migration(content, migration_name)
111
+ risks = []
112
+ lines = content.lines
113
+
114
+ has_model_usage = false
115
+ model_usage_lines = []
116
+
117
+ lines.each_with_index do |line, index|
118
+ next if line.strip.start_with?("#")
119
+ next if line.strip.empty?
120
+
121
+ # Detect direct model class usage (User.find_each, Comment.update_all, etc.)
122
+ if line.match?(/\b[A-Z]\w*\.(find|find_each|find_in_batches|all|where|update|create|delete|update_all|delete_all|execute)\b/)
123
+ # Skip if it's obviously not a model (like Date, Time, etc.)
124
+ unless line.match?(/\b(Date|Time|DateTime|Hash|Array|String|Integer|Float|Symbol|Regexp)\b/)
125
+ has_model_usage = true
126
+ model_usage_lines << { line_num: index + 1, content: line.strip }
127
+ end
128
+ end
129
+
130
+ # Also detect batched iterations (common pattern in data migrations)
131
+ if line.include?("find_each") || line.include?("find_in_batches")
132
+ has_model_usage = true
133
+ model_usage_lines << { line_num: index + 1, content: line.strip }
134
+ end
135
+ end
136
+
137
+ if has_model_usage
138
+ model_usage_lines.each do |item|
139
+ risks << {
140
+ type: :data_backfill_in_migration,
141
+ severity: :error,
142
+ line_number: item[:line_num],
143
+ migration_name: migration_name,
144
+ title: "Data Backfill Using App Models in Migration",
145
+ description: "Using ActiveRecord models in migrations is risky because models can change independently. Large data operations should use batching and happen separately from schema changes.",
146
+ message: "ActiveRecord model usage detected (#{item[:content][0..50]}...)",
147
+ recommendation: "Move data backfill to a separate rake task or post-deploy job. Use raw SQL with batching if migration-embedded, or use a data migration gem.",
148
+ metadata: {
149
+ operation: "data_backfill",
150
+ risk_level: :high,
151
+ module: :schema_safety
152
+ }
153
+ }
154
+ end
155
+ end
156
+
157
+ risks
158
+ end
159
+
57
160
  # Detect operations that lock the table: remove_column, change_column, rename_column
58
161
  def self.detect_table_locking_operations(content, migration_name)
59
162
  risks = []
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QueryGuard
4
- VERSION = "0.5.0"
4
+ VERSION = "0.5.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: query_guard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chitradevi36
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-16 00:00:00.000000000 Z
11
+ date: 2026-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake