# class ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter

### Constants

ER_CANNOT_ADD_FOREIGN
ER_CANNOT_CREATE_TABLE
ER_DATA_TOO_LONG
ER_DO_NOT_HAVE_DEFAULT
ER_DUP_ENTRY

  See dev.mysql.com/doc/refman/5.7/en/error-messages-server.html

ER_LOCK_DEADLOCK
ER_NOT_NULL_VIOLATION
ER_NO_REFERENCED_ROW_2
ER_OUT_OF_RANGE
INDEX_TYPES
INDEX_USINGS
NATIVE_DATABASE_TYPES

### Public Class Methods

emulate_booleans() click to toggle source

By default, the Mysql2Adapter will consider all columns of type `tinyint(1)` as boolean. If you wish to disable this emulation you can add the following line to your application.rb file:

    ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans = false

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 40
    class_attribute :emulate_booleans

new(connection, logger, connection_options, config) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::QueryCache.new

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 68
    def initialize(connection, logger, connection_options, config)
      super(connection, logger, config)

      @statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))

      if version < "5.1.10"
        raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
      end
    end

### Public Instance Methods

begin_db_transaction() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 226
    def begin_db_transaction
      execute "BEGIN"
    end

begin_isolated_db_transaction(isolation) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 230
    def begin_isolated_db_transaction(isolation)
      execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
      begin_db_transaction
    end

charset() click to toggle source

Returns the database character set.

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 298
    def charset
      show_variable "character_set_database"
    end

clear_cache!() click to toggle source

Clears the prepared statements cache.

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 192
    def clear_cache!
      reload_type_map
      @statements.clear
    end

collation() click to toggle source

Returns the database collation strategy.

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 303
    def collation
      show_variable "collation_database"
    end

create_database(name, options = {}) click to toggle source

Create a new MySQL database with optional `:charset` and `:collation`. Charset defaults to utf8.

Example:

    create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
    create_database 'matt_development'
    create_database 'matt_development', charset: :big5

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 277
    def create_database(name, options = {})
      if options[:collation]
        execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')} COLLATE #{quote_table_name(options[:collation])}"
      else
        execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
      end
    end

current_database() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 293
    def current_database
      query_value("SELECT database()", "SCHEMA")
    end

drop_table(table_name, options = {}) click to toggle source

Drops a table from the database.

`:force`

  Set to `:cascade` to drop dependent objects as well. Defaults to false.

`:if_exists`

  Set to `true` to only drop the table if it exists. Defaults to false.

`:temporary`

  Set to `true` to drop temporary table. Defaults to false.

Although this command ignores most `options` and the block if one is given, it can be helpful to provide these in a migration's `change` method so it can be reverted. In that case, `options` and the block will be used by create_table.

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 405
    def drop_table(table_name, options = {})
      execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
    end

empty_insert_statement_value() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 255
    def empty_insert_statement_value
      "VALUES ()"
    end

execute(sql, name = nil) click to toggle source

Executes the SQL statement in the context of this connection.

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 211
    def execute(sql, name = nil)
      log(sql, name) do
        ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
          @connection.query(sql)
        end
      end
    end

explain(arel, binds = []) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 201
    def explain(arel, binds = [])
      sql     = "EXPLAIN #{to_sql(arel, binds)}"
      start   = Time.now
      result  = exec_query(sql, "EXPLAIN", binds)
      elapsed = Time.now - start

      MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
    end

foreign_keys(table_name) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 455
          def foreign_keys(table_name)
            raise ArgumentError unless table_name.present?

            scope = quoted_scope(table_name)

            fk_info = exec_query("          SELECT fk.referenced_table_name AS 'to_table',
                     fk.referenced_column_name AS 'primary_key',
                     fk.column_name AS 'column',
                     fk.constraint_name AS 'name',
                     rc.update_rule AS 'on_update',
                     rc.delete_rule AS 'on_delete'
              FROM information_schema.key_column_usage fk
              JOIN information_schema.referential_constraints rc
              USING (constraint_schema, constraint_name)
              WHERE fk.referenced_column_name IS NOT NULL
                AND fk.table_schema = #{scope[:schema]}
                AND fk.table_name = #{scope[:name]}
                AND rc.table_name = #{scope[:name]}
    ".strip_heredoc, "SCHEMA")

            fk_info.map do |row|
              options = {
                column: row["column"],
                name: row["name"],
                primary_key: row["primary_key"]
              }

              options[:on_update] = extract_foreign_key_action(row["on_update"])
              options[:on_delete] = extract_foreign_key_action(row["on_delete"])

              ForeignKeyDefinition.new(table_name, row["to_table"], options)
            end
          end

index_algorithms() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 154
    def index_algorithms
      { default: "ALGORITHM = DEFAULT", copy: "ALGORITHM = COPY", inplace: "ALGORITHM = INPLACE" }
    end

native_database_types() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 150
    def native_database_types
      NATIVE_DATABASE_TYPES
    end

recreate_database(name, options = {}) click to toggle source

Drops the database specified on the `name` attribute and creates it again using the provided `options`.

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 263
    def recreate_database(name, options = {})
      drop_database(name)
      sql = create_database(name, options)
      reconnect!
      sql
    end

rename_index(table_name, old_name, new_name) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::SchemaStatements#rename_index

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 409
    def rename_index(table_name, old_name, new_name)
      if supports_rename_index?
        validate_index_length!(table_name, new_name)

        execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
      else
        super
      end
    end

rename_table(table_name, new_name) click to toggle source

Renames a table.

Example:

    rename_table('octopuses', 'octopi')

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 385
    def rename_table(table_name, new_name)
      execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
      rename_table_indexes(table_name, new_name)
    end

show_variable(name) click to toggle source

SHOW VARIABLES LIKE 'name'

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 536
    def show_variable(name)
      query_value("SELECT @@#{name}", "SCHEMA")
    rescue ActiveRecord::StatementInvalid
      nil
    end

strict_mode?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 585
    def strict_mode?
      self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
    end

supports_advisory_locks?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 138
    def supports_advisory_locks?
      true
    end

supports_datetime_with_precision?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 122
    def supports_datetime_with_precision?
      if mariadb?
        version >= "5.3.0"
      else
        version >= "5.6.4"
      end
    end

supports_explain?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 106
    def supports_explain?
      true
    end

supports_foreign_keys?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 114
    def supports_foreign_keys?
      true
    end

supports_index_sort_order?() click to toggle source

Technically MySQL allows to create indexes with the sort order syntax but at the moment (5.5) it doesn't yet implement them

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 98
    def supports_index_sort_order?
      true
    end

supports_indexes_in_create?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 110
    def supports_indexes_in_create?
      true
    end

supports_statement_cache?() click to toggle source

Returns true, since this connection adapter supports prepared statement caching.

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 92
    def supports_statement_cache?
      true
    end

supports_transaction_isolation?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 102
    def supports_transaction_isolation?
      true
    end

supports_views?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 118
    def supports_views?
      true
    end

supports_virtual_columns?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 130
    def supports_virtual_columns?
      if mariadb?
        version >= "5.2.0"
      else
        version >= "5.7.5"
      end
    end

truncate(table_name, name = nil) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 307
    def truncate(table_name, name = nil)
      execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
    end

### Private Instance Methods

add_column_sql(table_name, column_name, type, options = {}) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 714
    def add_column_sql(table_name, column_name, type, options = {})
      td = create_table_definition(table_name)
      cd = td.new_column_definition(column_name, type, options)
      schema_creation.accept(AddColumnDefinition.new(cd))
    end

add_index_length(quoted_columns, **options) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 657
    def add_index_length(quoted_columns, **options)
      if length = options[:length]
        case length
        when Hash
          length = length.symbolize_keys
          quoted_columns.each { |name, column| column << "(#{length[name]})" if length[name].present? }
        when Integer
          quoted_columns.each { |name, column| column << "(#{length})" }
        end
      end

      quoted_columns
    end

add_index_sql(table_name, column_name, options = {}) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 762
    def add_index_sql(table_name, column_name, options = {})
      index_name, index_type, index_columns, _, index_algorithm, index_using = add_index_options(table_name, column_name, options)
      index_algorithm[0, 0] = ", " if index_algorithm.present?
      "ADD #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_algorithm}"
    end

add_options_for_index_columns(quoted_columns, **options) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::SchemaStatements#add_options_for_index_columns

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 671
    def add_options_for_index_columns(quoted_columns, **options)
      quoted_columns = add_index_length(quoted_columns, options)
      super
    end

add_timestamps_sql(table_name, options = {}) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 773
    def add_timestamps_sql(table_name, options = {})
      [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
    end

can_perform_case_insensitive_comparison_for?(column) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 565
    def can_perform_case_insensitive_comparison_for?(column)
      column.case_sensitive?
    end

change_column_sql(table_name, column_name, type, options = {}) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 720
    def change_column_sql(table_name, column_name, type, options = {})
      column = column_for(table_name, column_name)

      unless options.key?(:default)
        options[:default] = column.default
      end

      unless options.key?(:null)
        options[:null] = column.null
      end

      unless options.key?(:comment)
        options[:comment] = column.comment
      end

      td = create_table_definition(table_name)
      cd = td.new_column_definition(column.name, type, options)
      schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
    end

configure_connection() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 800
    def configure_connection
      variables = @config.fetch(:variables, {}).stringify_keys

      # By default, MySQL 'where id is null' selects the last inserted id; Turn this off.
      variables["sql_auto_is_null"] = 0

      # Increase timeout so the server doesn't disconnect us.
      wait_timeout = self.class.type_cast_config_to_integer(@config[:wait_timeout])
      wait_timeout = 2147483 unless wait_timeout.is_a?(Integer)
      variables["wait_timeout"] = wait_timeout

      defaults = [":default", :default].to_set

      # Make MySQL reject illegal values rather than truncating or blanking them, see
      # http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_strict_all_tables
      # If the user has provided another value for sql_mode, don't replace it.
      if sql_mode = variables.delete("sql_mode")
        sql_mode = quote(sql_mode)
      elsif !defaults.include?(strict_mode?)
        if strict_mode?
          sql_mode = "CONCAT(@@sql_mode, ',STRICT_ALL_TABLES')"
        else
          sql_mode = "REPLACE(@@sql_mode, 'STRICT_TRANS_TABLES', '')"
          sql_mode = "REPLACE(#{sql_mode}, 'STRICT_ALL_TABLES', '')"
          sql_mode = "REPLACE(#{sql_mode}, 'TRADITIONAL', '')"
        end
        sql_mode = "CONCAT(#{sql_mode}, ',NO_AUTO_VALUE_ON_ZERO')"
      end
      sql_mode_assignment = "@@SESSION.sql_mode = #{sql_mode}, " if sql_mode

      # NAMES does not have an equals sign, see
      # http://dev.mysql.com/doc/refman/5.7/en/set-statement.html#id944430
      # (trailing comma because variable_assignments will always have content)
      if @config[:encoding]
        encoding = "NAMES #{@config[:encoding]}"
        encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
        encoding << ", "
      end

      # Gather up all of the SET variables...
      variable_assignments = variables.map do |k, v|
        if defaults.include?(v)
          "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
        elsif !v.nil?
          "@@SESSION.#{k} = #{quote(v)}"
        end
        # or else nil; compact to clear nils out
      end.compact.join(", ")

      # ...and send them all in one query
      execute "SET #{encoding} #{sql_mode_assignment} #{variable_assignments}"
    end

extract_precision(sql_type) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::AbstractAdapter#extract_precision

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 645
    def extract_precision(sql_type)
      if /\A(?:date)?time(?:stamp)?\b/.match?(sql_type)
        super || 0
      else
        super
      end
    end

fetch_type_metadata(sql_type, extra = "") click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::Quoting#fetch_type_metadata

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 653
    def fetch_type_metadata(sql_type, extra = "")
      MySQL::TypeMetadata.new(super(sql_type), extra: extra)
    end

initialize_type_map(m) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::AbstractAdapter#initialize_type_map

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 595
    def initialize_type_map(m)
      super

      register_class_with_limit m, %r(char)i, MysqlString

      m.register_type %r(tinytext)i,   Type::Text.new(limit: 2**8 - 1)
      m.register_type %r(tinyblob)i,   Type::Binary.new(limit: 2**8 - 1)
      m.register_type %r(text)i,       Type::Text.new(limit: 2**16 - 1)
      m.register_type %r(blob)i,       Type::Binary.new(limit: 2**16 - 1)
      m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
      m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
      m.register_type %r(longtext)i,   Type::Text.new(limit: 2**32 - 1)
      m.register_type %r(longblob)i,   Type::Binary.new(limit: 2**32 - 1)
      m.register_type %r(^float)i,     Type::Float.new(limit: 24)
      m.register_type %r(^double)i,    Type::Float.new(limit: 53)
      m.register_type %r(^json)i,      MysqlJson.new

      register_integer_type m, %r(^bigint)i,    limit: 8
      register_integer_type m, %r(^int)i,       limit: 4
      register_integer_type m, %r(^mediumint)i, limit: 3
      register_integer_type m, %r(^smallint)i,  limit: 2
      register_integer_type m, %r(^tinyint)i,   limit: 1

      m.register_type %r(^tinyint\(1\))i, Type::Boolean.new if emulate_booleans
      m.alias_type %r(year)i,          "integer"
      m.alias_type %r(bit)i,           "binary"

      m.register_type(%r(enum)i) do |sql_type|
        limit = sql_type[/^enum\((.+)\)/i, 1]
          .split(",").map { |enum| enum.strip.length - 2 }.max
        MysqlString.new(limit: limit)
      end

      m.register_type(%r(^set)i) do |sql_type|
        limit = sql_type[/^set\((.+)\)/i, 1]
          .split(",").map { |set| set.strip.length - 1 }.sum - 1
        MysqlString.new(limit: limit)
      end
    end

mismatched_foreign_key(message) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 874
    def mismatched_foreign_key(message)
      parts = message.scan(/`(\w+)`[ $)]/).flatten
      MismatchedForeignKey.new(
        self,
        message: message,
        table: parts[0],
        foreign_key: parts[1],
        target_table: parts[2],
        primary_key: parts[3],
      )
    end

register_integer_type(mapping, key, options) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 635
    def register_integer_type(mapping, key, options)
      mapping.register_type(key) do |sql_type|
        if /\bunsigned\b/.match?(sql_type)
          Type::UnsignedInteger.new(options)
        else
          Type::Integer.new(options)
        end
      end
    end

remove_column_sql(table_name, column_name, type = nil, options = {}) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 754
    def remove_column_sql(table_name, column_name, type = nil, options = {})
      "DROP #{quote_column_name(column_name)}"
    end

remove_columns_sql(table_name, *column_names) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 758
    def remove_columns_sql(table_name, *column_names)
      column_names.map { |column_name| remove_column_sql(table_name, column_name) }
    end

remove_index_sql(table_name, options = {}) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 768
    def remove_index_sql(table_name, options = {})
      index_name = index_name_for_remove(table_name, options)
      "DROP INDEX #{index_name}"
    end

remove_timestamps_sql(table_name, options = {}) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 777
    def remove_timestamps_sql(table_name, options = {})
      [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
    end

rename_column_sql(table_name, column_name, new_column_name) click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 740
    def rename_column_sql(table_name, column_name, new_column_name)
      column  = column_for(table_name, column_name)
      options = {
        default: column.default,
        null: column.null,
        auto_increment: column.auto_increment?
      }

      current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
      td = create_table_definition(table_name)
      cd = td.new_column_definition(new_column_name, current_type, options)
      schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
    end

subquery_for(key, select) click to toggle source

MySQL is too stupid to create a temporary table for use subquery, so we have to give it some prompting in the form of a subsubquery. Ugh!

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 783
    def subquery_for(key, select)
      subsubselect = select.clone
      subsubselect.projections = [key]

      # Materialize subquery by adding distinct
      # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
      subsubselect.distinct unless select.limit || select.offset || select.orders.any?

      subselect = Arel::SelectManager.new(select.engine)
      subselect.project Arel.sql(key.name)
      subselect.from subsubselect.as("__active_record_temp")
    end

supports_rename_index?() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 796
    def supports_rename_index?
      mariadb? ? false : version >= "5.7.6"
    end

translate_exception(exception, message) click to toggle source
Calls superclass method ActiveRecord::ConnectionAdapters::AbstractAdapter#translate_exception

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 687
    def translate_exception(exception, message)
      case error_number(exception)
      when ER_DUP_ENTRY
        RecordNotUnique.new(message)
      when ER_NO_REFERENCED_ROW_2
        InvalidForeignKey.new(message)
      when ER_CANNOT_ADD_FOREIGN
        mismatched_foreign_key(message)
      when ER_CANNOT_CREATE_TABLE
        if message.include?("errno: 150")
          mismatched_foreign_key(message)
        else
          super
        end
      when ER_DATA_TOO_LONG
        ValueTooLong.new(message)
      when ER_OUT_OF_RANGE
        RangeError.new(message)
      when ER_NOT_NULL_VIOLATION, ER_DO_NOT_HAVE_DEFAULT
        NotNullViolation.new(message)
      when ER_LOCK_DEADLOCK
        Deadlocked.new(message)
      else
        super
      end
    end

version_string() click to toggle source

    # File lib/active_record/connection_adapters/abstract_mysql_adapter.rb, line 917
    def version_string
      full_version.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)[1]
    end