nalabjp/load_data_infile2

View on GitHub
lib/load_data_infile2/sql.rb

Summary

Maintainability
A
0 mins
Test Coverage
module LoadDataInfile2
  class Sql
    # @param          [String]  file File name
    # @param          [String]  table Table name
    # @param          [Hash]    options Options for `LOAD DATA INFILE` query
    # @option options [Symbol]  :low_priority_or_concurrent :low_priority or :concurrent option
    # @option options [Symbol]  :replace_or_ignore :replace or :ignore for duplicated record
    # @option options [Boolean] :local_infile Use local file
    # @option options [Array]   :partition Partition names
    # @option options [String]  :charset Character set
    # @option options [String]  :fields_terminated_by Column delimiter
    # @option options [String]  :fields_enclosed_by Enclosure character, for example double quote
    # @option options [String]  :fields_optionally_enclosed_by If the input value is not necessarily enclosed, use OPTIONALLY
    # @option options [String]  :fields_escaped_by Escape character
    # @option options [String]  :lines_starting_by Common prefix for all lines to skip over the prefix, and anything before it
    # @option options [String]  :lines_terminated_by Line delimiter
    # @option options [String]  :ignore_lines Number of ignore lines from the start of the file
    # @option options [Array]   :columns Specify a column list, if the order of the fields in the input file differs from the order of the columns in the table
    #                                    The column list can contain either column names or user variables
    # @option options [Hash]    :set Key of Hash should have only column name, value of Hash can use a scalar subquery
    #
    # @see https://dev.mysql.com/doc/refman/5.6/en/load-data.html
    def initialize(file, table, options = {})
      @file = file
      @table = table
      @options = options
    end

    def build
      [
        load_data_infile,
        replace_or_ignore,
        into_table,
        partition,
        character_set,
        fields,
        lines,
        ignore_lines,
        columns,
        set
      ].compact.join(' ').concat(';')
    end

    private

    def load_data_infile
      stmt = 'LOAD DATA '
      stmt.concat("#{@options[:low_priority_or_concurrent].upcase} ") if %i(low_priority concurrent).include?(@options[:low_priority_or_concurrent])
      stmt.concat('LOCAL ') if @options[:local_infile]
      stmt.concat("INFILE '#{@file}'")
      stmt
    end

    def replace_or_ignore
      @options[:replace_or_ignore].to_s.upcase if %i(replace ignore).include?(@options[:replace_or_ignore])
    end

    def into_table
      "INTO TABLE #{@table}"
    end

    def partition
      "PARTITION (#{Array(@options[:partition]).join(', ')})" if @options[:partition] && @options[:partition].size > 0
    end

    def character_set
      "CHARACTER SET #{@options[:charset]}" if @options[:charset]
    end

    def fields
      fields = [
        fields_terminated_by,
        fields_enclosed_by,
        fields_optionally_enclosed_by,
        fields_escaped_by
      ].compact

      return if fields.size == 0

      "FIELDS #{fields.join(' ')}"
    end

    def fields_terminated_by
      "TERMINATED BY '#{@options[:fields_terminated_by]}'" if @options[:fields_terminated_by]
    end

    def fields_enclosed_by
      "ENCLOSED BY '#{@options[:fields_enclosed_by]}'" if @options[:fields_enclosed_by]
    end

    def fields_optionally_enclosed_by
      "OPTIONALLY ENCLOSED BY '#{@options[:fields_optionally_enclosed_by]}'" if @options[:fields_optionally_enclosed_by]
    end

    def fields_escaped_by
      "ESCAPED BY '#{@options[:fields_escaped_by]}'" if @options[:fields_escaped_by]
    end

    def lines
      lines = [
        lines_starting_by,
        lines_terminated_by
      ].compact

      return if lines.size == 0

      "LINES #{lines.join(' ')}"
    end

    def lines_starting_by
      "STARTING BY '#{@options[:lines_starting_by]}'" if @options[:lines_starting_by]
    end

    def lines_terminated_by
      "TERMINATED BY '#{@options[:lines_terminated_by]}'" if @options[:lines_terminated_by]
    end

    def ignore_lines
      "IGNORE #{@options[:ignore_lines].to_i} LINES" if @options[:ignore_lines].to_i > 0
    end

    def columns
      "(#{@options[:columns].join(', ')})" if @options[:columns] && @options[:columns].size > 0
    end

    def set
      if @options[:set] && @options[:set].size > 0
        s = @options[:set].map {|col, val| "#{col} = #{val}" }.join(', ')
        "SET #{s}"
      end
    end
  end
end