module RequestLogAnalyzer::FileFormat
  # PostgresQL spec 8.3.7
  class Postgresql < Base
    extend CommonRegularExpressions

    line_definition :query do |line|
      line.header = true
      line.teaser = /.*LOG\:/
      line.regexp = /(#{timestamp('%Y-%m-%d %k:%M:%S')})\ \S+ \[\d+\]\:\ \[.*\]\ LOG\:\ \ \d+\:\ duration\: (.*)\ ms\ \ statement:\ (.*)/

      line.capture(:timestamp).as(:timestamp)
      line.capture(:query_time).as(:duration, unit: :sec)
      line.capture(:query_fragment)
    end

    line_definition :location do |line|
      line.footer = true
      line.teaser = /.*LOCATION:/
      line.regexp = /.*(\ )LOCATION:/

      line.capture(:query).as(:sql) # Hack to gather up fragments
    end

    line_definition :query_fragment do |line|
      line.regexp = /^(?!.*LOG)\s*(.*)\s*/
      line.capture(:query_fragment)
    end

    report do |analyze|
      analyze.timespan
      analyze.hourly_spread
      analyze.duration :query_time, category: :query, title: 'Query time'
    end

    class Request < RequestLogAnalyzer::Request
      def convert_sql(value, _definition)
        # Recreate the full SQL query by joining all the previous parts and this last line
        sql = every(:query_fragment).join("\n") + value

        # Sanitize an SQL query so that it can be used as a category field.
        # sql.gsub!(/\/\*.*\*\//, '')                                       # remove comments
        sql.gsub!(/\s+/, ' ')                                             # remove excessive whitespace
        sql.gsub!(/"([^"]+)"/, '\1')                                      # remove quotes from field names
        sql.gsub!(/'\d{4}-\d{2}-\d{2}'/, ':date')                         # replace dates
        sql.gsub!(/'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}'/, ':datetime')   # replace timestamps
        sql.gsub!(/'[^']*'/, ':string')                                   # replace strings
        sql.gsub!(/\b\d+\b/, ':int')                                      # replace integers
        sql.gsub!(/(:int,)+:int/, ':ints')                                # replace multiple ints by a list
        sql.gsub!(/(:string,)+:string/, ':strings')                       # replace multiple strings by a list

        sql.lstrip.rstrip
      end

      def host
        self[:host] == '' || self[:host].nil? ? self[:ip] : self[:host]
      end

      # Convert the timestamp to an integer
      def convert_timestamp(value, _definition)
        _, y, m, d, h, i, s = value.split(/(\d\d)-(\d\d)-(\d\d)\s+(\d?\d):(\d\d):(\d\d)/)
        ('20%s%s%s%s%s%s' % [y, m, d, h.rjust(2, '0'), i, s]).to_i
      end
    end
  end
end