Class: Flydata::Mysql::MysqlDumpParser

Inherits:
Object
  • Object
show all
Defined in:
lib/flydata/command/sync.rb

Defined Under Namespace

Modules: State Classes: InsertParser

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_path, option = {}) ⇒ MysqlDumpParser

Returns a new instance of MysqlDumpParser.



714
715
716
717
718
719
# File 'lib/flydata/command/sync.rb', line 714

def initialize(file_path, option = {})
  @file_path = file_path
  raise "Dump file does not exist. file_path:#{file_path}" unless File.exist?(file_path)
  @binlog_pos = option[:binlog_pos]
  @option = option
end

Instance Attribute Details

#binlog_posObject

Returns the value of attribute binlog_pos.



712
713
714
# File 'lib/flydata/command/sync.rb', line 712

def binlog_pos
  @binlog_pos
end

Instance Method Details

#parse(create_table_block, insert_record_block, check_point_block) ⇒ Object



721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
# File 'lib/flydata/command/sync.rb', line 721

def parse(create_table_block, insert_record_block, check_point_block)
  invalid_file = false
  current_state = State::START
  substate = nil

  state_start = Proc.new do |f|
    line = f.readline.strip
    # -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=120;
    m = /^\-\- CHANGE MASTER TO MASTER_LOG_FILE='(?<binfile>[^']+)', MASTER_LOG_POS=(?<pos>\d+)/.match(line)
    if m
      @binlog_pos = {binfile: m[:binfile], pos: m[:pos].to_i}
      current_state = State::CREATE_TABLE
      check_point_block.call(nil, f.pos, @binlog_pos, current_state)
    end
  end

  current_table = nil
  state_create_table = Proc.new do |f|
    line = f.readline.strip
    # CREATE TABLE `active_admin_comments` (
    m = /^CREATE TABLE `(?<table_name>[^`]+)`/.match(line)
    if m
      current_table = MysqlTable.new(m[:table_name])
      current_state = State::CREATE_TABLE_COLUMNS
    end
  end

  state_create_table_constraints = Proc.new do |f|
    line = f.readline.strip
    #  PRIMARY KEY (`id`),
    if line.start_with?(')')
      create_table_block.call(current_table)
      current_state = State::INSERT_RECORD
      check_point_block.call(current_table, f.pos, @binlog_pos, current_state)
    elsif m = /^PRIMARY KEY \((?<primary_keys>[^\)]+)\)/.match(line)
      current_table.primary_keys = m[:primary_keys].split(',').collect do |pk_str|
        pk_str[1..-2]
      end
    end
  end

  state_create_table_columns = Proc.new do |f|
    start_pos = f.pos
    line = f.readline.strip
    #  `author_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    if line.start_with?("\`")
      column = {}

      # parse column line
      line = line[0..-2] if line.end_with?(',')
      items = line.split
      column[:column_name] = items.shift[1..-2]
      column[:format_type_str] = format_type_str = items.shift
      pos = format_type_str.index('(')
      if pos
        ft = column[:format_type] = format_type_str[0..pos-1]
        if ft == 'decimal'
          precision, scale = format_type_str[pos+1..-2].split(',').collect{|v| v.to_i}
          column[:decimal_precision] = precision
          column[:decimal_scale] = scale
        else
          column[:format_size] = format_type_str[pos+1..-2].to_i
        end
      else
        column[:format_type] = format_type_str
      end
      while (item = items.shift) do
        case item
        when 'DEFAULT'
          value = items.shift
          value = value.start_with?('\'') ? value[1..-2] : value
          value = nil if value == 'NULL'
          column[:default] = value
        when 'NOT'
          if items[1] == 'NULL'
            items.shift
            column[:not_null] = true
          end
        when 'unsigned'
          column[:unsigned] = true
        else
          #ignore other options
        end
      end

      current_table.add_column(column)
    else
      current_state = State::CREATE_TABLE_CONSTRAINTS
      f.pos = start_pos
      state_create_table_constraints.call(f)
    end
  end

  state_insert_record = Proc.new do |f|
    original_pos = f.pos
    command = f.read(6)
    if command == 'INSERT'
      current_state = State::PARSING_INSERT_RECORD
    else
      f.pos = original_pos
      f.readline
      if command == 'UNLOCK'
        current_state = State::CREATE_TABLE
        check_point_block.call(current_table, f.pos, @binlog_pos, current_state)
      end
    end
  end

  state_parsing_insert_record = Proc.new do |f|
    values_set = InsertParser.new(f).parse
    current_state = State::INSERT_RECORD
    if insert_record_block.call(current_table, values_set)
      check_point_block.call(current_table, f.pos, @binlog_pos, current_state)
    end
  end

  # Start reading file from top
  File.open(@file_path, 'r') do |f|
    last_saved_pos = 0

    # resume
    if @option[:last_pos]
      f.pos = @option[:last_pos].to_i
      current_state = @option[:state]
      substate = @option[:substate]
      current_table = @option[:mysql_table]
    end

    until f.eof? do
      case current_state
      when State::START
        state_start.call(f)
      when State::CREATE_TABLE
        state_create_table.call(f)
      when State::CREATE_TABLE_COLUMNS
        state_create_table_columns.call(f)
      when State::CREATE_TABLE_CONSTRAINTS
        state_create_table_constraints.call(f)
      when State::INSERT_RECORD
        state_insert_record.call(f)
      when State::PARSING_INSERT_RECORD
        state_parsing_insert_record.call(f)
      end
    end
  end
  @binlog_pos
end