Module: ActiveRecord::Import::MysqlAdapter

Includes:
ImportSupport, OnDuplicateKeyUpdateSupport
Included in:
ConnectionAdapters::SeamlessDatabasePoolAdapter, EMMysql2Adapter, Mysql2Adapter, TrilogyAdapter
Defined in:
lib/activerecord-import/adapters/mysql_adapter.rb

Constant Summary collapse

NO_MAX_PACKET =
0
QUERY_OVERHEAD =

This was shown to be true for MySQL, but it's not clear where the overhead is from.

8

Instance Method Summary collapse

Methods included from OnDuplicateKeyUpdateSupport

#supports_on_duplicate_key_update?

Methods included from ImportSupport

#supports_import?

Instance Method Details

#add_column_for_on_duplicate_key_update(column, options = {}) ⇒ Object

Add a column to be updated on duplicate key update



83
84
85
86
87
88
89
90
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 83

def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
  if (columns = options[:on_duplicate_key_update])
    case columns
    when Array then columns << column.to_sym unless columns.include?(column.to_sym)
    when Hash then columns[column.to_sym] = column.to_sym
    end
  end
end

#duplicate_key_update_error?(exception) ⇒ Boolean

Return true if the statement is a duplicate key record error

Returns:

  • (Boolean)


135
136
137
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 135

def duplicate_key_update_error?(exception) # :nodoc:
  exception.is_a?(ActiveRecord::StatementInvalid) && exception.to_s.include?('Duplicate entry')
end

#increment_locking_column!(table_name, results, locking_column) ⇒ Object



139
140
141
142
143
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 139

def increment_locking_column!(table_name, results, locking_column)
  if locking_column.present?
    results << "`#{locking_column}`=#{table_name}.`#{locking_column}`+1"
  end
end

#insert_many(sql, values, options = {}, *args) ⇒ Object

sql can be a single string or an array. If it is an array all elements that are in position >= 1 will be appended to the final SQL.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 12

def insert_many( sql, values, options = {}, *args ) # :nodoc:
  # the number of inserts default
  number_of_inserts = 0
  affected_rows = nil

  base_sql, post_sql = case sql
                       when String
                         [sql, '']
                       when Array
                         [sql.shift, sql.join( ' ' )]
  end

  sql_size = QUERY_OVERHEAD + base_sql.bytesize + post_sql.bytesize

  # the number of bytes the requested insert statement values will take up
  values_in_bytes = values.sum(&:bytesize)

  # the number of bytes (commas) it will take to comma separate our values
  comma_separated_bytes = values.size - 1

  # the total number of bytes required if this statement is one statement
  total_bytes = sql_size + values_in_bytes + comma_separated_bytes

  max = max_allowed_packet

  # if we can insert it all as one statement
  if max == NO_MAX_PACKET || total_bytes <= max || options[:force_single_insert]
    number_of_inserts += 1
    sql2insert = base_sql + values.join( ',' ) + post_sql
    result = execute( sql2insert, *args )
    if result.respond_to?(:affected_rows)
      affected_rows = (affected_rows || 0) + result.affected_rows
    end
  else
    value_sets = ::ActiveRecord::Import::ValueSetsBytesParser.parse(values,
      reserved_bytes: sql_size,
      max_bytes: max)

    transaction(requires_new: true) do
      value_sets.each do |value_set|
        number_of_inserts += 1
        sql2insert = base_sql + value_set.join( ',' ) + post_sql
        result = execute( sql2insert, *args )
        if result.respond_to?(:affected_rows)
          affected_rows = (affected_rows || 0) + result.affected_rows
        end
      end
    end
  end

  ActiveRecord::Import::Result.new([], number_of_inserts, [], [], affected_rows)
end

#max_allowed_packetObject

Returns the maximum number of bytes that the server will allow in a single packet



67
68
69
70
71
72
73
74
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 67

def max_allowed_packet # :nodoc:
  @max_allowed_packet ||= begin
    result = execute( "SELECT @@max_allowed_packet" )
    # original Mysql gem responds to #fetch_row while Mysql2 responds to #first
    val = result.respond_to?(:fetch_row) ? result.fetch_row[0] : result.first[0]
    val.to_i
  end
end

#pre_sql_statements(options) ⇒ Object



76
77
78
79
80
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 76

def pre_sql_statements( options)
  sql = []
  sql << "IGNORE" if options[:ignore] || options[:on_duplicate_key_ignore]
  sql + super
end

#sql_for_on_duplicate_key_update(table_name, *args) ⇒ Object

Returns a generated ON DUPLICATE KEY UPDATE statement given the passed in args.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 94

def sql_for_on_duplicate_key_update( table_name, *args ) # :nodoc:
  sql = ' ON DUPLICATE KEY UPDATE '.dup
  arg, model, _primary_key, locking_column = args
  case arg
  when Array
    sql << sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arg )
  when Hash
    sql << sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, arg )
  when String
    sql << arg
  else
    raise ArgumentError, "Expected Array or Hash"
  end
  sql
end

#sql_for_on_duplicate_key_update_as_array(table_name, model, locking_column, arr) ⇒ Object

:nodoc:



110
111
112
113
114
115
116
117
118
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 110

def sql_for_on_duplicate_key_update_as_array( table_name, model, locking_column, arr ) # :nodoc:
  results = arr.map do |column|
    original_column_name = model.attribute_alias?( column ) ? model.attribute_alias( column ) : column
    qc = quote_column_name( original_column_name )
    "#{table_name}.#{qc}=VALUES(#{qc})"
  end
  increment_locking_column!(table_name, results, locking_column)
  results.join( ',' )
end

#sql_for_on_duplicate_key_update_as_hash(table_name, model, locking_column, hsh) ⇒ Object

:nodoc:



120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/activerecord-import/adapters/mysql_adapter.rb', line 120

def sql_for_on_duplicate_key_update_as_hash( table_name, model, locking_column, hsh ) # :nodoc:
  results = hsh.map do |column1, column2|
    original_column1_name = model.attribute_alias?( column1 ) ? model.attribute_alias( column1 ) : column1
    qc1 = quote_column_name( original_column1_name )

    original_column2_name = model.attribute_alias?( column2 ) ? model.attribute_alias( column2 ) : column2
    qc2 = quote_column_name( original_column2_name )

    "#{table_name}.#{qc1}=VALUES( #{qc2} )"
  end
  increment_locking_column!(table_name, results, locking_column)
  results.join( ',')
end