class Masking::InsertStatement

Constants

BINARY_REGEXP
COLUMNS_REGEXP
NULL_REGEXP
NUMBER_REGEXP

NOTE: in mysqldump,

integer/float/NULL type has dumped without single quote. e.g. -123 / 2.4 / NULL
string/time type has dumped with single quote. e.g. 'string' / '2018-08-22 13:27:34'
binary/blob type has dumped with _binary prefix. e.g. _binary 'binarydata'
if there is single quote inside of value, it will dumped with escape. e.g. 'chikahiro\'s item'
in number, there could be include Scientific notation e.g. 1.2E3 / -1.2E-3 / 1e+030 / 9.71726e-17
  refs: https://dev.mysql.com/doc/refman/5.7/en/precision-math-numbers.html
PARSE_REGEXP
STRING_TIME_REGEXP
VALUE_REGEXP
VALUE_ROW_SPLITTER

Attributes

columns_section[R]
raw_statement[R]
sql_builder[R]
table[R]
values_section[R]

Public Class Methods

new(raw_statement, sql_builder: SQLBuilder) click to toggle source
# File lib/masking/insert_statement.rb, line 10
def initialize(raw_statement, sql_builder: SQLBuilder)
  @raw_statement = raw_statement
  @sql_builder = sql_builder

  PARSE_REGEXP.match(raw_statement).tap do |match_data|
    raise Error::InsertStatementParseError if match_data.nil?

    @table           = match_data[:table]
    @columns_section = match_data[:columns_section]
    @values_section  = match_data[:values_section]
  end
end

Public Instance Methods

column_index(column_name) click to toggle source
# File lib/masking/insert_statement.rb, line 27
def column_index(column_name)
  columns.index(column_name)
end
columns() click to toggle source
# File lib/masking/insert_statement.rb, line 23
def columns
  @columns ||= columns_section.scan(COLUMNS_REGEXP).flatten.map(&:to_sym)
end
mask_value(column_index:, mask_method:) click to toggle source
# File lib/masking/insert_statement.rb, line 37
def mask_value(column_index:, mask_method:)
  values.each do |value|
    value[column_index] = mask_method.call
  end
end
sql() click to toggle source
# File lib/masking/insert_statement.rb, line 43
def sql
  sql_builder.new(table: table, columns: columns, values: values).sql
end
values() click to toggle source
# File lib/masking/insert_statement.rb, line 31
def values
  @values ||= values_section.split(VALUE_ROW_SPLITTER)
                            .tap { |rows| rows.each_with_index { |_, i| recursive_pattern_value_concat(rows, i) } }
                            .flat_map { |row| row.scan(values_regexp) }
end

Private Instance Methods

recursive_pattern_value_concat(value_rows, index) click to toggle source

Check single quote count on each value, and just continue if it's even number. if it's odd, concat with next row (it means a value contains “),(” pattern)

e.g. INSERT ... VALUES (123,'string ),( abc'),(456,'ab');

refs: implementation of parsing CSV on ruby standard library FasterCSV (ja): www.clear-code.com/blog/2018/12/25.html

# File lib/masking/insert_statement.rb, line 77
def recursive_pattern_value_concat(value_rows, index)
  return if value_rows[index].gsub(/\\\\/, '').gsub(/\\'/, '').count(?').even?

  value_rows[index] += VALUE_ROW_SPLITTER + value_rows.delete_at(index + 1)
  recursive_pattern_value_concat(value_rows, index)
end
values_regexp() click to toggle source
# File lib/masking/insert_statement.rb, line 69
def values_regexp
  @values_regexp ||= /^\(?#{([VALUE_REGEXP] * columns.count).join(?,)}\)?$/
end