lib/baza/driver/mysql2.rb
Baza.load_driver("mysql")
class Baza::Driver::Mysql2 < Baza::MysqlBaseDriver
AutoAutoloader.autoload_sub_classes(self, __FILE__)
attr_reader :conn, :conns, :encoding
# Helper to enable automatic registering of database using Baza::Db.from_object
def self.from_object(args)
if args[:object].class.name == "Mysql2::Client"
return {
type: :success,
args: {
type: :mysql2,
conn: args[:object],
query_args: {
symbolize_keys: true
}
}
}
end
nil
end
def initialize(db)
super
@opts = @db.opts
require "monitor"
@mutex = Monitor.new
if @opts[:encoding]
@encoding = @opts[:encoding]
else
@encoding = "utf8"
end
if @db.opts.key?(:port)
@port = @db.opts[:port].to_i
else
@port = 3306
end
reconnect
end
# Cleans the wref-map holding the tables.
def clean
tables.clean if tables
end
# Respawns the connection to the MySQL-database.
def reconnect
@mutex.synchronize do
require "mysql2" unless ::Object.const_defined?(:Mysql2)
args = {
host: @db.opts[:host],
username: @db.opts[:user],
password: @db.opts[:pass],
database: @db.opts[:db],
port: @port,
symbolize_keys: true,
cache_rows: false,
encoding: encoding
}
# Symbolize keys should also be given here, else table-data wont be symbolized for some reason - knj.
@query_args = {symbolize_keys: true}
@query_args[:cast] = false unless @db.opts[:type_translation]
@query_args.merge!(@db.opts[:query_args]) if @db.opts[:query_args]
pos_args = [:as, :async, :cast_booleans, :database_timezone, :application_timezone, :cache_rows, :connect_flags, :cast]
pos_args.each do |key|
args[key] = @db.opts[key] if @db.opts.key?(key)
end
args[:as] = :array
if @db.opts[:conn]
@conn = @db.opts[:conn]
else
@conn = ::Mysql2::Client.new(args)
end
query("SET NAMES '#{esc(encoding)}'") if encoding
end
end
# Executes a query and returns the result.
def query(str)
str = str.to_s
str = str.force_encoding("UTF-8") if encoding == "utf8" && str.respond_to?(:force_encoding)
tries = 0
begin
tries += 1
@mutex.synchronize do
return Baza::Driver::Mysql2::Result.new(self, @conn.query(str, @query_args))
end
rescue => e
if tries <= 3
if e.message == "MySQL server has gone away" || e.message == "closed MySQL connection" || e.message == "Can't connect to local MySQL server through socket"
sleep 0.5
reconnect
retry
elsif e.message.include?("No operations allowed after connection closed") || e.message == "This connection is still waiting for a result, try again once you have the result" || e.message == "Lock wait timeout exceeded; try restarting transaction"
reconnect
retry
end
end
raise e
end
end
# Executes an unbuffered query and returns the result that can be used to access the data.
def query_ubuf(str, _args = nil, &_blk)
@mutex.synchronize do
return Baza::Driver::Mysql2::Result.new(self, @conn.query(str, @query_args.merge(stream: true)))
end
end
# Escapes a string to be safe to use in a query.
def escape(string)
@conn.escape(string.to_s)
end
# Closes the connection threadsafe.
def close
@mutex.synchronize { @conn.close }
end
# Destroyes the connection.
def destroy
@conn = nil
@db = nil
@mutex = nil
@encoding = nil
@query_args = nil
@port = nil
end
end