cutoff 1.1.0 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62b041df9110341cc0f55dc67e59098c03caa41f96661bd95a73689e0e508606
4
- data.tar.gz: c61f6dd25c2781924d9f98bf20ca1deedc701f2d4aae143dad97eae987006a47
3
+ metadata.gz: 8e267a7090c7eee2e054ccc060c18a38a4f6d27277477d7fcfc3263b7f16091a
4
+ data.tar.gz: f7a34c730fe930080da8a539b5c864b4b4af467a042c7ddf075ba7df7242da79
5
5
  SHA512:
6
- metadata.gz: 853b7822a150ea5c55b345694919299a839f06d8f414dde5fd285d30789a3a6f4e3da234dc32fafd6411b358827ee0f25184da28fff1140d5d86aca4df7f286c
7
- data.tar.gz: df37414f0271365e2fdb0adfe5e8a20e805b353f6c37d8b391271edc1ddf4f96a82cf7c340d59ca56c56b722a9873f5031d84f5b6eba5e532a54baa363da1dbd
6
+ metadata.gz: 1853882757fc2da5bae7c836423893dcd2b673f09ff4ebb191dcababd8b44fbf114617664a7974bf67b2a615dbb2fb2f8c2da31876bf5a580d47f9b908d9af79
7
+ data.tar.gz: 75eec1648d14ac62203989bb51ab2b4e3e6710006c0c072d4e98933c367ebd96fde88b7a33705983f1ba09cc44b5e486dacde80e06b9e47291a804883c16872b
data/CHANGELOG.md CHANGED
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.0] - 2026-08-07
11
+
12
+ ### Fixed
13
+
14
+ - The Mysql2 patch now finds the first `SELECT` when a statement begins with a
15
+ parenthesized query block, as in `(SELECT ...) UNION (SELECT ...)`.
16
+ Previously the leading `(` was treated as an unrecognized token and the
17
+ parser bailed out, so `MAX_EXECUTION_TIME` was silently never inserted and
18
+ the query ran with no server-side ceiling. MySQL applies a hint in the first
19
+ query block to the whole statement, so these queries are now capped
20
+ correctly.
21
+ - Fixed a check in the Mysql2 patch that used `String#casecmp` where a boolean
22
+ was expected. Because `casecmp` returns `0` for a match (which is truthy in
23
+ Ruby), the guard never fired and any leading token merely *starting with*
24
+ `select` (such as `selections`) had a hint comment spliced into it,
25
+ producing invalid SQL. It now uses `casecmp?`.
26
+
27
+ ### Upgrading
28
+
29
+ Queries beginning with a parenthesized query block were previously never given
30
+ a `MAX_EXECUTION_TIME` hint, so they ran unbounded on the server even with an
31
+ active cutoff. They are now capped, which means a query that used to slowly
32
+ succeed may instead raise `Mysql2::Error`. That is the intended behavior of the
33
+ patch, but it can surface as new errors on upgrade. If you have queries in this
34
+ shape, check that their callers handle the error, or give them a longer cutoff.
35
+
36
+ Statements beginning with `WITH` (common table expressions) are still not
37
+ given a hint.
38
+
10
39
  ## [1.1.0] - 2026-05-12
11
40
 
12
41
  ### Fixed
@@ -107,7 +136,8 @@ to `Timeout::Error`. `CutoffError` changes from a class to a module.
107
136
  - Cutoff class
108
137
  - Mysql2 patch
109
138
 
110
- [Unreleased]: https://github.com/justinhoward/cutoff/compare/v1.1.0...HEAD
139
+ [Unreleased]: https://github.com/justinhoward/cutoff/compare/v1.2.0...HEAD
140
+ [1.2.0]: https://github.com/justinhoward/cutoff/compare/v1.1.0...v1.2.0
111
141
  [1.1.0]: https://github.com/justinhoward/cutoff/compare/v1.0.0...v1.1.0
112
142
  [1.0.0]: https://github.com/justinhoward/cutoff/compare/v0.5.2...v1.0.0
113
143
  [0.5.2]: https://github.com/justinhoward/cutoff/compare/v0.5.1...v0.5.2
@@ -69,6 +69,8 @@ class Cutoff
69
69
  hint_comment
70
70
  elsif token.start_with?('/*')
71
71
  block_comment
72
+ elsif !@found_select && token.start_with?('(')
73
+ open_paren
72
74
  elsif token.match?(/^select/i)
73
75
  select
74
76
  else
@@ -133,6 +135,21 @@ class Cutoff
133
135
  @scanner.terminate
134
136
  end
135
137
 
138
+ def open_paren
139
+ # A statement can begin with a parenthesized query block, as in
140
+ # `(SELECT ...) UNION (SELECT ...)`. MySQL accepts an optimizer hint
141
+ # at the start of the first block and applies MAX_EXECUTION_TIME to
142
+ # the whole statement, so consume the parentheses and keep looking
143
+ # for the select.
144
+ #
145
+ # Go back to the beginning of the token and skip only the parens
146
+ # (and any whitespace after them) so the rest of the token is
147
+ # re-examined as its own token. This always advances the scanner by
148
+ # at least one character, so the token loop can't spin.
149
+ @scanner.unscan
150
+ @scanner.skip(/\(+\s*/)
151
+ end
152
+
136
153
  def select
137
154
  # If we encounter a select, we're ready to place our hint comment
138
155
  @scanner.unscan
@@ -140,7 +157,7 @@ class Cutoff
140
157
 
141
158
  # Make sure our word is actually select
142
159
  # We only checked that it starts with select before
143
- return other unless word.casecmp('select')
160
+ return other unless word.casecmp?('select')
144
161
 
145
162
  @found_select = true
146
163
  @hint_pos = @scanner.pos
@@ -3,6 +3,6 @@
3
3
  class Cutoff
4
4
  # @return [Gem::Version] The current version of the cutoff gem
5
5
  def self.version
6
- Gem::Version.new('1.1.0')
6
+ Gem::Version.new('1.2.0')
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cutoff
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Howard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-12 00:00:00.000000000 Z
11
+ date: 2026-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -84,7 +84,7 @@ licenses:
84
84
  - MIT
85
85
  metadata:
86
86
  changelog_uri: https://github.com/justinhoward/cutoff/blob/master/CHANGELOG.md
87
- documentation_uri: https://www.rubydoc.info/gems/cutoff/1.1.0
87
+ documentation_uri: https://www.rubydoc.info/gems/cutoff/1.2.0
88
88
  rubygems_mfa_required: 'true'
89
89
  post_install_message:
90
90
  rdoc_options: []