mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
3.6 KiB
132 lines
3.6 KiB
# frozen_string_literal: true
|
|
|
|
require "socket"
|
|
require "trilogy/version"
|
|
require "trilogy/error"
|
|
require "trilogy/result"
|
|
require "trilogy/cext"
|
|
require "trilogy/encoding"
|
|
|
|
class Trilogy
|
|
IO_TIMEOUT_ERROR =
|
|
if defined?(IO::TimeoutError)
|
|
IO::TimeoutError
|
|
else
|
|
Class.new(StandardError)
|
|
end
|
|
private_constant :IO_TIMEOUT_ERROR
|
|
|
|
module Synchronization
|
|
def initialize(...)
|
|
@mutex = Mutex.new
|
|
super
|
|
end
|
|
|
|
synchronized_methods = Trilogy.public_instance_methods(false) - %i(closed?)
|
|
source = synchronized_methods.flat_map do |method|
|
|
[
|
|
"def #{method}(...)",
|
|
"raise SynchronizationError unless @mutex.try_lock",
|
|
"begin",
|
|
"super",
|
|
"ensure",
|
|
"@mutex.unlock",
|
|
"end",
|
|
"end",
|
|
]
|
|
end
|
|
class_eval(source.join(";"), __FILE__, __LINE__)
|
|
end
|
|
|
|
prepend(Synchronization)
|
|
|
|
def initialize(options = {})
|
|
options[:port] = options[:port].to_i if options[:port]
|
|
mysql_encoding = options[:encoding] || "utf8mb4"
|
|
encoding = Trilogy::Encoding.find(mysql_encoding)
|
|
charset = Trilogy::Encoding.charset(mysql_encoding)
|
|
@connection_options = options
|
|
@connected_host = nil
|
|
|
|
socket = nil
|
|
begin
|
|
if host = options[:host]
|
|
port = options[:port] || 3306
|
|
connect_timeout = options[:connect_timeout] || options[:write_timeout]
|
|
|
|
socket = TCPSocket.new(host, port, connect_timeout: connect_timeout)
|
|
|
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
|
|
if keepalive_enabled = options[:keepalive_enabled]
|
|
keepalive_idle = options[:keepalive_idle]
|
|
keepalive_interval = options[:keepalive_interval]
|
|
keepalive_count = options[:keepalive_count]
|
|
|
|
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
|
|
|
if keepalive_idle > 0 && defined?(Socket::TCP_KEEPIDLE)
|
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, keepalive_idle)
|
|
end
|
|
if keepalive_interval > 0 && defined?(Socket::TCP_KEEPINTVL)
|
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, keepalive_interval)
|
|
end
|
|
if keepalive_count > 0 && defined?(Socket::TCP_KEEPCNT)
|
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, keepalive_count)
|
|
end
|
|
end
|
|
else
|
|
path = options[:socket] ||= "/tmp/mysql.sock"
|
|
socket = UNIXSocket.new(path)
|
|
end
|
|
rescue Errno::ETIMEDOUT, IO_TIMEOUT_ERROR => e
|
|
raise Trilogy::TimeoutError, e.message
|
|
rescue SocketError => e
|
|
connection_str = host ? "#{host}:#{port}" : path
|
|
raise Trilogy::BaseConnectionError, "unable to connect to \"#{connection_str}\": #{e.message}"
|
|
rescue => e
|
|
if e.respond_to?(:errno)
|
|
raise Trilogy::SyscallError.from_errno(e.errno, e.message)
|
|
else
|
|
raise
|
|
end
|
|
end
|
|
|
|
_connect(socket, encoding, charset, options)
|
|
ensure
|
|
# Socket's fd will be dup'd in C
|
|
socket&.close
|
|
end
|
|
|
|
def connection_options
|
|
@connection_options.dup.freeze
|
|
end
|
|
|
|
def in_transaction?
|
|
(server_status & SERVER_STATUS_IN_TRANS) != 0
|
|
end
|
|
|
|
def server_info
|
|
version_str = server_version
|
|
|
|
if /\A(\d+)\.(\d+)\.(\d+)/ =~ version_str
|
|
version_num = ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
|
|
end
|
|
|
|
{ :version => version_str, :id => version_num }
|
|
end
|
|
|
|
def connected_host
|
|
@connected_host ||= query_with_flags("select @@hostname", query_flags | QUERY_FLAGS_FLATTEN_ROWS).rows.first
|
|
end
|
|
|
|
def query_with_flags(sql, flags)
|
|
old_flags = query_flags
|
|
self.query_flags = flags
|
|
|
|
query(sql)
|
|
ensure
|
|
self.query_flags = old_flags
|
|
end
|
|
end
|