Class: Exwiw::Adapter::MysqlClient

Inherits:
Object
  • Object
show all
Defined in:
lib/exwiw/adapter/mysql_client.rb

Overview

Thin wrapper over the MySQL driver so MysqlAdapter does not care whether the host app ships the ‘mysql2` gem or the `trilogy` gem. exwiw only runs simple SELECT / EXPLAIN queries, so both drivers are normalized to the same shape: rows as arrays of String|nil plus the column names.

Values are normalized to strings to match mysql2’s ‘cast: false` mode, where every column comes back as a raw string and is quoted uniformly downstream (see MysqlAdapter#escape_value). mysql2 already returns strings in that mode; trilogy always casts to Ruby types (Integer / BigDecimal / Time / Date / …), so its values are stringified back into the same literal form here.

Defined Under Namespace

Classes: Result

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection_config, driver: nil) ⇒ MysqlClient

‘driver:` is mainly a test seam to force a specific driver; in normal use it is auto-detected.



65
66
67
68
69
# File 'lib/exwiw/adapter/mysql_client.rb', line 65

def initialize(connection_config, driver: nil)
  @connection_config = connection_config
  @driver = (driver || self.class.detect_driver).to_sym
  ensure_driver_loaded!
end

Instance Attribute Details

#driverObject (readonly)

Returns the value of attribute driver.



61
62
63
# File 'lib/exwiw/adapter/mysql_client.rb', line 61

def driver
  @driver
end

Class Method Details

.detect_driverObject

Pick the available driver, preferring mysql2 (exwiw’s historical default). require returns false when already loaded, so this is safe to call repeatedly.



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/exwiw/adapter/mysql_client.rb', line 23

def self.detect_driver
  require 'mysql2'
  :mysql2
rescue LoadError
  begin
    require 'trilogy'
    :trilogy
  rescue LoadError
    raise LoadError,
          "exwiw needs the 'mysql2' or 'trilogy' gem to connect to MySQL. " \
          "Add `gem \"mysql2\"` (or `gem \"trilogy\"`) to your Gemfile."
  end
end

.stringify_value(value) ⇒ Object

Render a driver-returned value as the raw string mysql2’s ‘cast: false` would have produced, so trilogy’s typed values quote identically.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/exwiw/adapter/mysql_client.rb', line 39

def self.stringify_value(value)
  case value
  when nil then nil
  when String then value
  when Time
    # Emit fractional seconds only when present. A Time can't tell us the
    # column's declared precision, so a zero fraction on a DATETIME(6)
    # column comes out as "...:00" here whereas mysql2's `cast: false`
    # echoes the raw "...:00.000000"; both re-insert to the same instant.
    value.nsec.zero? ? value.strftime('%Y-%m-%d %H:%M:%S') : value.strftime('%Y-%m-%d %H:%M:%S.%6N')
  when Date then value.strftime('%Y-%m-%d')
  when true then '1'
  when false then '0'
  else
    if defined?(BigDecimal) && value.is_a?(BigDecimal)
      value.to_s('F')
    else
      value.to_s
    end
  end
end

Instance Method Details

#query(sql) ⇒ Result

Returns fields (Array<String>) and rows (Array<Array<String|nil>>).

Parameters:

  • sql (String)

Returns:

  • (Result)

    fields (Array<String>) and rows (Array<Array<String|nil>>)



73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/exwiw/adapter/mysql_client.rb', line 73

def query(sql)
  case @driver
  when :mysql2
    res = raw.query(sql, cast: false, as: :array)
    Result.new(res.fields, res.to_a)
  when :trilogy
    res = raw.query(sql)
    rows = res.rows.map { |row| row.map { |value| self.class.stringify_value(value) } }
    Result.new(res.fields, rows)
  else
    raise "Unsupported MySQL driver: #{@driver.inspect}"
  end
end