Class: MysqlFramework::Connector

Inherits:
Object
  • Object
show all
Defined in:
lib/mysql_framework/connector.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ void

Initializes a connector instance with MySQL client options.

Parameters:

  • options (Hash) (defaults to: {})

    custom MySQL client options that override defaults



13
14
15
16
# File 'lib/mysql_framework/connector.rb', line 13

def initialize(options = {})
  @options = default_options.merge(options)
  Mysql2::Client.default_query_options.merge!(symbolize_keys: true, cast_booleans: true)
end

Instance Attribute Details

#connection_poolObject (readonly)

Returns the value of attribute connection_pool.



7
8
9
# File 'lib/mysql_framework/connector.rb', line 7

def connection_pool
  @connection_pool
end

Instance Method Details

#check_in(client) ⇒ void

This method returns an undefined value.

Returns a MySQL client back to the pool or closes it when pooling is disabled.

Parameters:

  • client (Mysql2::Client, nil)

    client to return or close



53
54
55
56
57
# File 'lib/mysql_framework/connector.rb', line 53

def check_in(client)
  return client&.close unless connection_pool_enabled?

  @connection_pool.check_in(client)
end

#check_outMysql2::Client

Checks out a MySQL client, sanitizing it before use.

Returns:

  • (Mysql2::Client)

    checked-out client

Raises:

  • (ConnectionSanitizationError)

    when sanitization repeatedly fails

  • (Mysql2::Error)

    when checkout or sanitization fails due to MySQL errors



43
44
45
46
47
# File 'lib/mysql_framework/connector.rb', line 43

def check_out
  return new_client unless connection_pool_enabled?

  @connection_pool.check_out
end

#disposevoid

This method returns an undefined value.

Disposes of the connection pool and closes pooled connections.



31
32
33
34
35
36
# File 'lib/mysql_framework/connector.rb', line 31

def dispose
  return unless connection_pool_enabled?

  @connection_pool&.dispose
  @connection_pool = nil
end

#execute(query, provided_client = nil) ⇒ Array<Hash>?

Executes a prepared statement.

NOTE: We must always free the result and close the prepared statement. Otherwise MySQL may raise “Commands out of sync” when the same connection is reused (e.g. via connection pooling).

The connection itself must NOT be closed here because it is managed by the connection pool.

Parameters:

  • query (Object)

    query object responding to sql and params

  • provided_client (Mysql2::Client, nil) (defaults to: nil)

    optional existing client

Returns:

  • (Array<Hash>, nil)

    query result rows

Raises:

  • (Mysql2::Error)

    when statement preparation or execution fails



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/mysql_framework/connector.rb', line 88

def execute(query, provided_client = nil)
  with_client(provided_client) do |client|
    statement = nil
    result = nil

    begin
      statement = client.prepare(query.sql)
      result = statement.execute(
        *query.params, symbolize_keys: true, cast_booleans: true
      )
      final = result&.to_a
      final
    ensure
      client&.abandon_results!
      result&.free
      statement&.close
    end
  end
end

#query(query_string, provided_client = nil) ⇒ Mysql2::Result

Executes a SQL query.

Parameters:

  • query_string (String)

    SQL query to execute

  • provided_client (Mysql2::Client, nil) (defaults to: nil)

    optional existing client

Returns:

  • (Mysql2::Result)

    raw MySQL result

Raises:

  • (Mysql2::Error)

    when query execution fails



114
115
116
# File 'lib/mysql_framework/connector.rb', line 114

def query(query_string, provided_client = nil)
  with_client(provided_client) { |conn| conn.query(query_string) }
end

#query_multiple_results(query_string, provided_client = nil) ⇒ Array<Array<Hash>>

Executes a multi-statement SQL query and collects all result sets.

Parameters:

  • query_string (String)

    multi-statement SQL query

  • provided_client (Mysql2::Client, nil) (defaults to: nil)

    optional existing client

Returns:

  • (Array<Array<Hash>>)

    list of result sets

Raises:

  • (Mysql2::Error)

    when query execution or result fetching fails



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/mysql_framework/connector.rb', line 124

def query_multiple_results(query_string, provided_client = nil)
  results = nil

  # Multiple statement query is buggy and client cannot be reused after calling next_result/store_result
  # Client's state gets corrupted and leaks into next queries. The reason is unknown.
  # As a result we do not return client back to the pool but instead close connection which is not optimal.
  with_client(provided_client, discard_current_pool_connection: true) do |client|
    raw_results = []
    query_call = client.query(query_string)
    raw_results << query_call&.to_a
    query_call&.free

    while client.more_results?
      client.next_result
      query_call = client.store_result
      raw_results << query_call&.to_a
      query_call&.free
    end

    results = raw_results.compact
    results
  ensure
    client&.abandon_results!
  end

  results
end

#setupConnectionPool?

Sets up the MySQL connection pool when pooling is enabled.

Returns:

  • (ConnectionPool, nil)

    configured pool, or nil when pooling is disabled



21
22
23
24
25
26
# File 'lib/mysql_framework/connector.rb', line 21

def setup
  return unless connection_pool_enabled?

  @connection_pool = MysqlFramework::MysqlConnectionPool.new(@options)
  @connection_pool.setup
end

#transaction {|client| ... } ⇒ Object

Executes a block within a database transaction.

Yields:

  • (client)

    block executed between BEGIN and COMMIT

Yield Parameters:

  • client (Mysql2::Client)

Returns:

  • (Object)

    block result

Raises:

  • (ArgumentError)

    when no block is given

  • (StandardError)

    re-raises any exception after rollback



159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/mysql_framework/connector.rb', line 159

def transaction
  raise ArgumentError, 'No block was given' unless block_given?

  with_client do |client|
    client.query('BEGIN')
    yield client
    client.query('COMMIT')
  rescue StandardError => e
    client.query('ROLLBACK')
    raise e
  end
end

#with_client(provided_client = nil, discard_current_pool_connection: false) {|client| ... } ⇒ Object

Yields a MySQL client from the pool, or yields the provided client directly.

Parameters:

  • provided_client (Mysql2::Client, nil) (defaults to: nil)

    existing client to yield without pool checkout

  • discard_current_pool_connection (Boolean) (defaults to: false)

    whether to discard the pooled connection after use

Yields:

  • (client)

    block that performs work with a MySQL client

Yield Parameters:

  • client (Mysql2::Client)

Returns:

  • (Object)

    block result

Raises:

  • (Mysql2::Error)

    re-raises MySQL errors from the block



67
68
69
70
71
72
# File 'lib/mysql_framework/connector.rb', line 67

def with_client(provided_client = nil, discard_current_pool_connection: false)
  return yield provided_client if provided_client
  return with_new_client { |c| yield c } unless connection_pool_enabled?

  @connection_pool.with_client(discard_current_pool_connection:) { |c| yield c }
end