class Atatus::Sql::Signature

@api private

Public Class Methods

new(sql) click to toggle source
# File lib/atatus/sql/signature.rb, line 38
def initialize(sql)
  @sql = sql
  @tokenizer = Tokenizer.new(sql)
end
parse(sql) click to toggle source
# File lib/atatus/sql/signature.rb, line 56
def self.parse(sql)
  new(sql).parse
end

Public Instance Methods

parse() click to toggle source
# File lib/atatus/sql/signature.rb, line 43
def parse
  @tokenizer.scan # until tokenizer.token != COMMENT

  parsed = parse_tokens
  return parsed if parsed

  # If all else fails, just return the first token of the query.
  parts = @sql.split
  return '' unless parts.any?

  parts.first.upcase
end

Private Instance Methods

parse_tokens() click to toggle source

rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity

# File lib/atatus/sql/signature.rb, line 64
def parse_tokens
  t = @tokenizer

  case t.token

  when CALL
    return unless scan_until IDENT
    "CALL #{t.text}"

  when DELETE
    return unless scan_until FROM
    return unless scan_token IDENT
    table = scan_dotted_identifier
    "DELETE FROM #{table}"

  when INSERT, REPLACE
    action = t.text
    return unless scan_until INTO
    return unless scan_token IDENT
    table = scan_dotted_identifier
    "#{action} INTO #{table}"

  when SELECT
    level = 0
    while t.scan
      case t.token
      when LPAREN then level += 1
      when RPAREN then level -= 1
      when FROM
        next unless level == 0
        break unless scan_token IDENT
        table = scan_dotted_identifier
        return "SELECT FROM #{table}"
      end
    end

  when UPDATE
    # Scan for the table name. Some dialects allow option keywords before
    # the table name.
    return 'UPDATE' unless scan_token IDENT

    table = t.text

    period = false
    first_period = false

    while t.scan
      case t.token
      when IDENT
        if period
          table += t.text
          period = false
        end

        unless first_period
          table = t.text
        end

        # Two adjacent identifiers found after the first period. Ignore
        # the secondary ones, in case they are unknown keywords.
      when PERIOD
        period = true
        first_period = true
        table += '.'
      else
        return "UPDATE #{table}"
      end
    end
  end
end
scan_dotted_identifier() click to toggle source
# File lib/atatus/sql/signature.rb, line 158
def scan_dotted_identifier
  table = @tokenizer.text

  while scan_token(PERIOD) && scan_token(IDENT)
    table += ".#{@tokenizer.text}"
  end

  table
end
scan_token(kind) click to toggle source

Scans next token, ignoring comments Returns whether next token is of `kind`

# File lib/atatus/sql/signature.rb, line 147
def scan_token(kind)
  while @tokenizer.scan
    next if @tokenizer.token == COMMENT
    break
  end

  return true if @tokenizer.token == kind

  false
end
scan_until(kind) click to toggle source

Scans until finding token of `kind`

# File lib/atatus/sql/signature.rb, line 138
def scan_until(kind)
  while @tokenizer.scan
    break true if @tokenizer.token == kind
    false
  end
end