class Cutoff::Patch::Mysql2::QueryWithMaxTime
Parses a query and inserts a MAX_EXECUTION_TIME query hint if possible
@private
Public Class Methods
new(query, max_execution_time_ms)
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 33 def initialize(query, max_execution_time_ms) @scanner = StringScanner.new(query.dup) @max_execution_time_ms = max_execution_time_ms @found_select = false @found_hint = false @hint_pos = nil @insert_space = false @insert_trailing_space = false end
Public Instance Methods
to_s()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 43 def to_s return @scanner.string if @scanner.eos? # Loop through tokens like "WORD " or "/* " while @scanner.scan(/(\S+)\s+/) # Get the word part. None of our tokens care about case handle_token(@scanner[1].downcase) end return @scanner.string unless @found_select insert_hint @scanner.string end
Private Instance Methods
block_comment()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 98 def block_comment # Go back to the beginning of the comment then scan until the end # This handles block comments that don't contain whitespace @scanner.unscan @scanner.skip_until(%r{\*/\s*}) end
handle_token(token)
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 64 def handle_token(token) if token.start_with?('--') line_comment elsif token.start_with?('/*+') hint_comment elsif token.start_with?('/*') block_comment elsif token.start_with?('select') select else other end end
hint()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 60 def hint "MAX_EXECUTION_TIME(#{@max_execution_time_ms})" end
hint_comment()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 105 def hint_comment # We can just treat this as a normal block comment if we haven't seen # a select yet return block_comment unless @found_select @found_hint = true # Go back to the beginning of the comment # This is so we can handle comments that don't have internal # whitespace @scanner.unscan # Now skip past just the start of the comment so we don't detect it # on the next line @scanner.skip(%r{/\*\+}) # Scan until the end of the comment # Also detect the last word and trailing whitespace if it exists @scanner.scan_until(%r{(\S*)(\s*)\*/}) # Now step back to the beginning of the */ # If there was trailing whitespace, also subtract that # so that we're at the start of the trailing whitespace # That's where we want to put our hint @hint_pos = @scanner.pos - 2 - @scanner[2].size # We only want to insert an extra space to the left of our # hint if there was already a hint (it's possible to have an # empty hint comment). So check if there was a word there. @insert_space = !@scanner[1].empty? # Once we find our position, we're done @scanner.terminate end
insert_hint()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 78 def insert_hint @scanner.string.insert(@hint_pos, ' ') if @insert_trailing_space if @found_hint # If we found an existing hint, insert our new hint there @scanner.string.insert(@hint_pos, hint) elsif @found_select # Otherwise if we found a select, place our hint right after it @scanner.string.insert(@hint_pos, "/*+ #{hint} */") end @scanner.string.insert(@hint_pos, ' ') if @insert_space end
line_comment()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 92 def line_comment # \R matches cross-platform newlines # so we skip until the end of the line @scanner.skip_until(/\R/) end
other()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 158 def other # If we encounter any other token, we're done # Either we found the select or we found another token # that indicates we should not insert a hint @scanner.terminate end
select()
click to toggle source
# File lib/cutoff/patch/mysql2.rb, line 135 def select # If we encounter a select, we're ready to place our hint comment @scanner.unscan word = @scanner.scan(/\w+/) # Make sure our word is actually select # We only checked that it starts with select before return other unless word.casecmp('select') @found_select = true @hint_pos = @scanner.pos # If the select has space after it, we want to also # insert one later if @scanner.scan(/\s+/) @insert_space = true elsif @scanner.scan(/\*/) # Handle SELECT* since it needs to have an extra space inserted # after the hint comment @insert_trailing_space = true end end