Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rbmysql.rb
Views: 11704
# coding: ascii-8bit1# Copyright (C) 2008-2012 TOMITA Masahiro2# mailto:[email protected]34# MySQL connection class.5# @example6# my = RbMysql.connect('hostname', 'user', 'password', 'dbname')7# res = my.query 'select col1,col2 from tbl where id=123'8# res.each do |c1, c2|9# p c1, c210# end11class RbMysql1213require "rbmysql/constants"14require "rbmysql/error"15require "rbmysql/charset"16require "rbmysql/protocol"17require "rbmysql/packet.rb"18begin19require "mysql/ext.so"20rescue LoadError21end2223VERSION = 20913 # Version number of this library24MYSQL_UNIX_PORT = "/tmp/mysql.sock" # UNIX domain socket filename25MYSQL_TCP_PORT = 3306 # TCP socket port number2627# @return [RbMysql::Charset] character set of MySQL connection28attr_reader :charset29# @private30attr_reader :protocol3132# @return [Boolean] if true, {#query} return {RbMysql::Result}.33attr_accessor :query_with_result3435class << self36# Make RbMysql object without connecting.37# @return [RbMysql]38def init39my = self.allocate40my.instance_eval{initialize}41my42end4344# Make RbMysql object and connect to mysqld.45# @param args same as arguments for {#connect}.46# @return [RbMysql]47def new(*args)48my = self.init49my.connect(*args)50end5152alias real_connect new53alias connect new5455# Escape special character in string.56# @param [String] str57# @return [String]58def escape_string(str)59str.gsub(/[\0\n\r\\\'\"\x1a]/) do |s|60case s61when "\0" then "\\0"62when "\n" then "\\n"63when "\r" then "\\r"64when "\x1a" then "\\Z"65else "\\#{s}"66end67end68end69alias quote escape_string7071# @return [String] client version. This value is dummy for MySQL/Ruby compatibility.72def client_info73"5.0.0"74end75alias get_client_info client_info7677# @return [Integer] client version. This value is dummy for MySQL/Ruby compatibility.78def client_version795000080end81alias get_client_version client_version82end8384def initialize85@fields = nil86@protocol = nil87@charset = nil88@connect_timeout = nil89@read_timeout = nil90@write_timeout = nil91@init_command = nil92@sqlstate = "00000"93@query_with_result = true94@host_info = nil95@last_error = nil96@result_exist = false97@local_infile = nil98end99100# Connect to mysqld.101# @param [String / nil] host hostname mysqld running102# @param [String / nil] user username to connect to mysqld103# @param [String / nil] passwd password to connect to mysqld104# @param [String / nil] db initial database name105# @param [Integer / nil] port port number (used if host is not 'localhost' or nil)106# @param [String / Socket / nil] socket socket file name (used if host is 'localhost' or nil), or an existing ::Socket instance107# @param [Integer / nil] flag connection flag. RbMysql::CLIENT_* ORed108# @return self109def connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=0)110if flag & CLIENT_COMPRESS != 0111warn 'unsupported flag: CLIENT_COMPRESS' if $VERBOSE112flag &= ~CLIENT_COMPRESS113end114@protocol = Protocol.new host, port, socket, @connect_timeout, @read_timeout, @write_timeout115@protocol.authenticate user, passwd, db, (@local_infile ? CLIENT_LOCAL_FILES : 0) | flag, @charset116@charset ||= @protocol.charset117@host_info = (host.nil? || host == "localhost") ? 'Localhost via UNIX socket' : "#{host} via TCP/IP"118query @init_command if @init_command119return self120end121alias real_connect connect122123# Disconnect from mysql.124# @return [RbMysql] self125def close126if @protocol127@protocol.quit_command128@protocol = nil129end130return self131end132133# Disconnect from mysql without QUIT packet.134# @return [RbMysql] self135def close!136if @protocol137@protocol.close138@protocol = nil139end140return self141end142143# Set option for connection.144#145# Available options:146# RbMysql::INIT_COMMAND, RbMysql::OPT_CONNECT_TIMEOUT, RbMysql::OPT_READ_TIMEOUT,147# RbMysql::OPT_WRITE_TIMEOUT, RbMysql::SET_CHARSET_NAME148# @param [Integer] opt option149# @param [Integer] value option value that is depend on opt150# @return [RbMysql] self151def options(opt, value=nil)152case opt153when RbMysql::INIT_COMMAND154@init_command = value.to_s155# when RbMysql::OPT_COMPRESS156when RbMysql::OPT_CONNECT_TIMEOUT157@connect_timeout = value158# when RbMysql::GUESS_CONNECTION159when RbMysql::OPT_LOCAL_INFILE160@local_infile = value161# when RbMysql::OPT_NAMED_PIPE162# when RbMysql::OPT_PROTOCOL163when RbMysql::OPT_READ_TIMEOUT164@read_timeout = value.to_i165# when RbMysql::OPT_RECONNECT166# when RbMysql::SET_CLIENT_IP167# when RbMysql::OPT_SSL_VERIFY_SERVER_CERT168# when RbMysql::OPT_USE_EMBEDDED_CONNECTION169# when RbMysql::OPT_USE_REMOTE_CONNECTION170when RbMysql::OPT_WRITE_TIMEOUT171@write_timeout = value.to_i172# when RbMysql::READ_DEFAULT_FILE173# when RbMysql::READ_DEFAULT_GROUP174# when RbMysql::REPORT_DATA_TRUNCATION175# when RbMysql::SECURE_AUTH176# when RbMysql::SET_CHARSET_DIR177when RbMysql::SET_CHARSET_NAME178@charset = Charset.by_name value.to_s179# when RbMysql::SHARED_MEMORY_BASE_NAME180else181warn "option not implemented: #{opt}" if $VERBOSE182end183self184end185186# Escape special character in MySQL.187#188# In Ruby 1.8, this is not safe for multibyte charset such as 'SJIS'.189# You should use place-holder in prepared-statement.190# @param [String] str191# return [String]192def escape_string(str)193if not defined? Encoding and @charset.unsafe194raise ClientError, 'RbMysql#escape_string is called for unsafe multibyte charset'195end196self.class.escape_string str197end198alias quote escape_string199200# @return [String] client version201def client_info202self.class.client_info203end204alias get_client_info client_info205206# @return [Integer] client version207def client_version208self.class.client_version209end210alias get_client_version client_version211212# Set charset of MySQL connection.213# @param [String / RbMysql::Charset] cs214def charset=(cs)215charset = cs.is_a?(Charset) ? cs : Charset.by_name(cs)216if @protocol217@protocol.charset = charset218query "SET NAMES #{charset.name}"219end220@charset = charset221cs222end223224# @return [String] charset name225def character_set_name226@charset.name227end228229# @return [Integer] last error number230def errno231@last_error ? @last_error.errno : 0232end233234# @return [String] last error message235def error236@last_error && @last_error.error237end238239# @return [String] sqlstate for last error240def sqlstate241@last_error ? @last_error.sqlstate : "00000"242end243244# @return [Integer] number of columns for last query245def field_count246@fields.size247end248249# @return [String] connection type250def host_info251@host_info252end253alias get_host_info host_info254255# @return [Integer] protocol version256def proto_info257RbMysql::Protocol::VERSION258end259alias get_proto_info proto_info260261# @return [String] server version262def server_info263check_connection264@protocol.server_info265end266alias get_server_info server_info267268# @return [Integer] server version269def server_version270check_connection271@protocol.server_version272end273alias get_server_version server_version274275# @return [String] information for last query276def info277@protocol && @protocol.message278end279280# @return [Integer] number of affected records by insert/update/delete.281def affected_rows282@protocol ? @protocol.affected_rows : 0283end284285# @return [Integer] latest auto_increment value286def insert_id287@protocol ? @protocol.insert_id : 0288end289290# @return [Integer] number of warnings for previous query291def warning_count292@protocol ? @protocol.warning_count : 0293end294295# Kill query.296# @param [Integer] pid thread id297# @return [RbMysql] self298def kill(pid)299check_connection300@protocol.kill_command pid301self302end303304# database list.305# @param [String] db database name that may contain wild card.306# @return [Array<String>] database list307def list_dbs(db=nil)308db &&= db.gsub(/[\\\']/){"\\#{$&}"}309query(db ? "show databases like '#{db}'" : "show databases").map(&:first)310end311312# Execute query string.313# @param [String] str Query.314# @yield [RbMysql::Result] evaluated per query.315# @return [RbMysql::Result] If {#query_with_result} is true and result set exist.316# @return [nil] If {#query_with_result} is true and the query does not return result set.317# @return [RbMysql] If {#query_with_result} is false or block is specified318# @example319# my.query("select 1,NULL,'abc'").fetch # => [1, nil, "abc"]320def query(str, &block)321check_connection322@fields = nil323begin324nfields = @protocol.query_command str325if nfields326@fields = @protocol.retr_fields nfields327@result_exist = true328end329if block330while true331block.call store_result if @fields332break unless next_result333end334return self335end336if @query_with_result337return @fields ? store_result : nil338else339return self340end341rescue ServerError => e342@last_error = e343@sqlstate = e.sqlstate344raise345end346end347alias real_query query348349# Get all data for last query if query_with_result is false.350# @return [RbMysql::Result]351def store_result352check_connection353raise ClientError, 'invalid usage' unless @result_exist354res = Result.new @fields, @protocol355@result_exist = false356res357end358359# @return [Integer] Thread ID360def thread_id361check_connection362@protocol.thread_id363end364365# Use result of query. The result data is retrieved when you use RbMysql::Result#fetch.366# @return [RbMysql::Result]367def use_result368store_result369end370371# Set server option.372# @param [Integer] opt {RbMysql::OPTION_MULTI_STATEMENTS_ON} or {RbMysql::OPTION_MULTI_STATEMENTS_OFF}373# @return [RbMysql] self374def set_server_option(opt)375check_connection376@protocol.set_option_command opt377self378end379380# @return [Boolean] true if multiple queries are specified and unexecuted queries exists.381def more_results382@protocol.server_status & SERVER_MORE_RESULTS_EXISTS != 0383end384alias more_results? more_results385386# execute next query if multiple queries are specified.387# @return [Boolean] true if next query exists.388def next_result389return false unless more_results390check_connection391@fields = nil392nfields = @protocol.get_result393if nfields394@fields = @protocol.retr_fields nfields395@result_exist = true396end397return true398end399400# Parse prepared-statement.401# @param [String] str query string402# @return [RbMysql::Stmt] Prepared-statement object403def prepare(str)404st = Stmt.new @protocol, @charset405st.prepare str406st407end408409# @private410# Make empty prepared-statement object.411# @return [RbMysql::Stmt] If block is not specified.412def stmt_init413Stmt.new @protocol, @charset414end415416# Returns RbMysql::Result object that is empty.417# Use fetch_fields to get list of fields.418# @param [String] table table name.419# @param [String] field field name that may contain wild card.420# @return [RbMysql::Result]421def list_fields(table, field=nil)422check_connection423begin424fields = @protocol.field_list_command table, field425return Result.new fields426rescue ServerError => e427@last_error = e428@sqlstate = e.sqlstate429raise430end431end432433# @return [RbMysql::Result] containing process list434def list_processes435check_connection436@fields = @protocol.process_info_command437@result_exist = true438store_result439end440441# @note for Ruby 1.8: This is not multi-byte safe. Don't use for multi-byte charset such as cp932.442# @param [String] table database name that may contain wild card.443# @return [Array<String>] list of table name.444def list_tables(table=nil)445q = table ? "show tables like '#{quote table}'" : "show tables"446query(q).map(&:first)447end448449# Check whether the connection is available.450# @return [RbMysql] self451def ping452check_connection453@protocol.ping_command454self455end456457# Flush tables or caches.458# @param [Integer] op operation. Use RbMysql::REFRESH_* value.459# @return [RbMysql] self460def refresh(op)461check_connection462@protocol.refresh_command op463self464end465466# Reload grant tables.467# @return [RbMysql] self468def reload469refresh RbMysql::REFRESH_GRANT470end471472# Select default database473# @return [RbMysql] self474def select_db(db)475query "use #{db}"476self477end478479# shutdown server.480# @return [RbMysql] self481def shutdown(level=0)482check_connection483@protocol.shutdown_command level484self485end486487# @return [String] statistics message488def stat489@protocol ? @protocol.statistics_command : 'MySQL server has gone away'490end491492# Commit transaction493# @return [RbMysql] self494def commit495query 'commit'496self497end498499# Rollback transaction500# @return [RbMysql] self501def rollback502query 'rollback'503self504end505506# Set autocommit mode507# @param [Boolean] flag508# @return [RbMysql] self509def autocommit(flag)510query "set autocommit=#{flag ? 1 : 0}"511self512end513514private515516def check_connection517raise ClientError::ServerGoneError, 'MySQL server has gone away' unless @protocol518end519520# @!visibility public521# Field class522class Field523# @return [String] database name524attr_reader :db525# @return [String] table name526attr_reader :table527# @return [String] original table name528attr_reader :org_table529# @return [String] field name530attr_reader :name531# @return [String] original field name532attr_reader :org_name533# @return [Integer] charset id number534attr_reader :charsetnr535# @return [Integer] field length536attr_reader :length537# @return [Integer] field type538attr_reader :type539# @return [Integer] flag540attr_reader :flags541# @return [Integer] number of decimals542attr_reader :decimals543# @return [String] default value544attr_reader :default545alias :def :default546547# @private548attr_accessor :result549550# @attr [Protocol::FieldPacket] packet551def initialize(packet)552@db, @table, @org_table, @name, @org_name, @charsetnr, @length, @type, @flags, @decimals, @default =553packet.db, packet.table, packet.org_table, packet.name, packet.org_name, packet.charsetnr, packet.length, packet.type, packet.flags, packet.decimals, packet.default554@flags |= NUM_FLAG if is_num_type?555@max_length = nil556end557558# @return [Hash] field information559def hash560{561"name" => @name,562"table" => @table,563"def" => @default,564"type" => @type,565"length" => @length,566"max_length" => max_length,567"flags" => @flags,568"decimals" => @decimals569}570end571572# @private573def inspect574"#<RbMysql::Field:#{@name}>"575end576577# @return [Boolean] true if numeric field.578def is_num?579@flags & NUM_FLAG != 0580end581582# @return [Boolean] true if not null field.583def is_not_null?584@flags & NOT_NULL_FLAG != 0585end586587# @return [Boolean] true if primary key field.588def is_pri_key?589@flags & PRI_KEY_FLAG != 0590end591592# @return [Integer] maximum width of the field for the result set593def max_length594return @max_length if @max_length595@max_length = 0596@result.calculate_field_max_length if @result597@max_length598end599600attr_writer :max_length601602private603604def is_num_type?605[TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, TYPE_LONGLONG, TYPE_INT24].include?(@type) || (@type == TYPE_TIMESTAMP && (@length == 14 || @length == 8))606end607608end609610# @!visibility public611# Result set612class ResultBase613include Enumerable614615# @return [Array<RbMysql::Field>] field list616attr_reader :fields617618# @param [Array of RbMysql::Field] fields619def initialize(fields)620@fields = fields621@field_index = 0 # index of field622@records = [] # all records623@index = 0 # index of record624@fieldname_with_table = nil625@fetched_record = nil626end627628# ignore629# @return [void]630def free631end632633# @return [Integer] number of record634def size635@records.size636end637alias num_rows size638639# @return [Array] current record data640def fetch641@fetched_record = nil642return nil if @index >= @records.size643@records[@index] = @records[@index].to_a unless @records[@index].is_a? Array644@fetched_record = @records[@index]645@index += 1646return @fetched_record647end648alias fetch_row fetch649650# Return data of current record as Hash.651# The hash key is field name.652# @param [Boolean] with_table if true, hash key is "table_name.field_name".653# @return [Hash] current record data654def fetch_hash(with_table=nil)655row = fetch656return nil unless row657if with_table and @fieldname_with_table.nil?658@fieldname_with_table = @fields.map{|f| [f.table, f.name].join(".")}659end660ret = {}661@fields.each_index do |i|662fname = with_table ? @fieldname_with_table[i] : @fields[i].name663ret[fname] = row[i]664end665ret666end667668# Iterate block with record.669# @yield [Array] record data670# @return [self] self. If block is not specified, this returns Enumerator.671def each(&block)672return enum_for(:each) unless block673while rec = fetch674block.call rec675end676self677end678679# Iterate block with record as Hash.680# @param [Boolean] with_table if true, hash key is "table_name.field_name".681# @yield [Hash] record data682# @return [self] self. If block is not specified, this returns Enumerator.683def each_hash(with_table=nil, &block)684return enum_for(:each_hash, with_table) unless block685while rec = fetch_hash(with_table)686block.call rec687end688self689end690691# Set record position692# @param [Integer] n record index693# @return [self] self694def data_seek(n)695@index = n696self697end698699# @return [Integer] current record position700def row_tell701@index702end703704# Set current position of record705# @param [Integer] n record index706# @return [Integer] previous position707def row_seek(n)708ret = @index709@index = n710ret711end712end713714# @!visibility public715# Result set for simple query716class Result < ResultBase717# @private718# @param [Array<RbMysql::Field>] fields719# @param [RbMysql::Protocol] protocol720def initialize(fields, protocol=nil)721super fields722return unless protocol723@records = protocol.retr_all_records fields724fields.each{|f| f.result = self} # for calculating max_field725end726727# @private728# calculate max_length of all fields729def calculate_field_max_length730max_length = Array.new(@fields.size, 0)731@records.each_with_index do |rec, i|732rec = @records[i] = rec.to_a if rec.is_a? RawRecord733max_length.each_index do |j|734max_length[j] = rec[j].length if rec[j] && rec[j].length > max_length[j]735end736end737max_length.each_with_index do |len, i|738@fields[i].max_length = len739end740end741742# @return [RbMysql::Field] current field743def fetch_field744return nil if @field_index >= @fields.length745ret = @fields[@field_index]746@field_index += 1747ret748end749750# @return [Integer] current field position751def field_tell752@field_index753end754755# Set field position756# @param [Integer] n field index757# @return [Integer] previous position758def field_seek(n)759ret = @field_index760@field_index = n761ret762end763764# Return specified field765# @param [Integer] n field index766# @return [RbMysql::Field] field767def fetch_field_direct(n)768raise ClientError, "invalid argument: #{n}" if n < 0 or n >= @fields.length769@fields[n]770end771772# @return [Array<RbMysql::Field>] all fields773def fetch_fields774@fields775end776777# @return [Array<Integer>] length of each fields778def fetch_lengths779return nil unless @fetched_record780@fetched_record.map{|c|c.nil? ? 0 : c.length}781end782783# @return [Integer] number of fields784def num_fields785@fields.size786end787end788789# @!visibility private790# Result set for prepared statement791class StatementResult < ResultBase792# @private793# @param [Array<RbMysql::Field>] fields794# @param [RbMysql::Protocol] protocol795# @param [RbMysql::Charset] charset796def initialize(fields, protocol, charset)797super fields798@records = protocol.stmt_retr_all_records @fields, charset799end800end801802# @!visibility public803# Prepared statement804# @!attribute [r] affected_rows805# @return [Integer]806# @!attribute [r] insert_id807# @return [Integer]808# @!attribute [r] server_status809# @return [Integer]810# @!attribute [r] warning_count811# @return [Integer]812# @!attribute [r] param_count813# @return [Integer]814# @!attribute [r] fields815# @return [Array<RbMysql::Field>]816# @!attribute [r] sqlstate817# @return [String]818class Stmt819include Enumerable820821attr_reader :affected_rows, :insert_id, :server_status, :warning_count822attr_reader :param_count, :fields, :sqlstate823824# @private825def self.finalizer(protocol, statement_id)826proc do827begin828protocol.gc_stmt statement_id829rescue => e830elog("finalize method for Stmt failed", error: e)831end832end833end834835# @private836# @param [RbMysql::Protocol] protocol837# @param [RbMysql::Charset] charset838def initialize(protocol, charset)839@protocol = protocol840@charset = charset841@statement_id = nil842@affected_rows = @insert_id = @server_status = @warning_count = 0843@sqlstate = "00000"844@param_count = nil845@bind_result = nil846end847848# @private849# parse prepared-statement and return {RbMysql::Stmt} object850# @param [String] str query string851# @return self852def prepare(str)853close854begin855@sqlstate = "00000"856@statement_id, @param_count, @fields = @protocol.stmt_prepare_command(str)857rescue ServerError => e858@last_error = e859@sqlstate = e.sqlstate860raise861end862ObjectSpace.define_finalizer(self, self.class.finalizer(@protocol, @statement_id))863self864end865866# Execute prepared statement.867# @param [Object] values values passed to query868# @return [RbMysql::Stmt] self869def execute(*values)870raise ClientError, "not prepared" unless @param_count871raise ClientError, "parameter count mismatch" if values.length != @param_count872values = values.map{|v| @charset.convert v}873begin874@sqlstate = "00000"875nfields = @protocol.stmt_execute_command @statement_id, values876if nfields877@fields = @protocol.retr_fields nfields878@result = StatementResult.new @fields, @protocol, @charset879else880@affected_rows, @insert_id, @server_status, @warning_count, @info =881@protocol.affected_rows, @protocol.insert_id, @protocol.server_status, @protocol.warning_count, @protocol.message882end883return self884rescue ServerError => e885@last_error = e886@sqlstate = e.sqlstate887raise888end889end890891# Close prepared statement892# @return [void]893def close894ObjectSpace.undefine_finalizer(self)895@protocol.stmt_close_command @statement_id if @statement_id896@statement_id = nil897end898899# @return [Array] current record data900def fetch901row = @result.fetch902return row unless @bind_result903row.zip(@bind_result).map do |col, type|904if col.nil?905nil906elsif [Numeric, Integer, Fixnum].include? type907col.to_i908elsif type == String909col.to_s910elsif type == Float && !col.is_a?(Float)911col.to_i.to_f912elsif type == RbMysql::Time && !col.is_a?(RbMysql::Time)913if col.to_s =~ /\A\d+\z/914i = col.to_s.to_i915if i < 100000000916y = i/10000917m = i/100%100918d = i%100919h, mm, s = 0920else921y = i/10000000000922m = i/100000000%100923d = i/1000000%100924h = i/10000%100925mm= i/100%100926s = i%100927end928if y < 70929y += 2000930elsif y < 100931y += 1900932end933RbMysql::Time.new(y, m, d, h, mm, s)934else935RbMysql::Time.new936end937else938col939end940end941end942943# Return data of current record as Hash.944# The hash key is field name.945# @param [Boolean] with_table if true, hash key is "table_name.field_name".946# @return [Hash] record data947def fetch_hash(with_table=nil)948@result.fetch_hash with_table949end950951# Set retrieve type of value952# @param [Numeric / Fixnum / Integer / Float / String / RbMysql::Time / nil] args value type953# @return [RbMysql::Stmt] self954def bind_result(*args)955if @fields.length != args.length956raise ClientError, "bind_result: result value count(#{@fields.length}) != number of argument(#{args.length})"957end958args.each do |a|959raise TypeError unless [Numeric, Fixnum, Integer, Float, String, RbMysql::Time, nil].include? a960end961@bind_result = args962self963end964965# Iterate block with record.966# @yield [Array] record data967# @return [RbMysql::Stmt] self968# @return [Enumerator] If block is not specified969def each(&block)970return enum_for(:each) unless block971while rec = fetch972block.call rec973end974self975end976977# Iterate block with record as Hash.978# @param [Boolean] with_table if true, hash key is "table_name.field_name".979# @yield [Hash] record data980# @return [RbMysql::Stmt] self981# @return [Enumerator] If block is not specified982def each_hash(with_table=nil, &block)983return enum_for(:each_hash, with_table) unless block984while rec = fetch_hash(with_table)985block.call rec986end987self988end989990# @return [Integer] number of record991def size992@result.size993end994alias num_rows size995996# Set record position997# @param [Integer] n record index998# @return [void]999def data_seek(n)1000@result.data_seek(n)1001end10021003# @return [Integer] current record position1004def row_tell1005@result.row_tell1006end10071008# Set current position of record1009# @param [Integer] n record index1010# @return [Integer] previous position1011def row_seek(n)1012@result.row_seek(n)1013end10141015# @return [Integer] number of columns for last query1016def field_count1017@fields.length1018end10191020# ignore1021# @return [void]1022def free_result1023end10241025# Returns RbMysql::Result object that is empty.1026# Use fetch_fields to get list of fields.1027# @return [RbMysql::Result]1028def result_metadata1029return nil if @fields.empty?1030Result.new @fields1031end1032end10331034# @!visibility public1035# @!attribute [rw] year1036# @return [Integer]1037# @!attribute [rw] month1038# @return [Integer]1039# @!attribute [rw] day1040# @return [Integer]1041# @!attribute [rw] hour1042# @return [Integer]1043# @!attribute [rw] minute1044# @return [Integer]1045# @!attribute [rw] second1046# @return [Integer]1047# @!attribute [rw] neg1048# @return [Boolean] negative flag1049# @!attribute [rw] second_part1050# @return [Integer]1051class Time1052# @param [Integer] year1053# @param [Integer] month1054# @param [Integer] day1055# @param [Integer] hour1056# @param [Integer] minute1057# @param [Integer] second1058# @param [Boolean] neg negative flag1059# @param [Integer] second_part1060def initialize(year=0, month=0, day=0, hour=0, minute=0, second=0, neg=false, second_part=0)1061@date_flag = !(hour && minute && second)1062@year, @month, @day, @hour, @minute, @second, @neg, @second_part =1063year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i, neg, second_part.to_i1064end1065attr_accessor :year, :month, :day, :hour, :minute, :second, :neg, :second_part1066alias mon month1067alias min minute1068alias sec second10691070# @private1071def ==(other)1072other.is_a?(RbMysql::Time) &&1073@year == other.year && @month == other.month && @day == other.day &&1074@hour == other.hour && @minute == other.minute && @second == other.second &&1075@neg == neg && @second_part == other.second_part1076end10771078# @private1079def eql?(other)1080self == other1081end10821083# @return [String] "yyyy-mm-dd HH:MM:SS"1084def to_s1085if @date_flag1086sprintf "%04d-%02d-%02d", year, mon, day1087elsif year == 0 and mon == 0 and day == 01088h = neg ? hour * -1 : hour1089sprintf "%02d:%02d:%02d", h, min, sec1090else1091sprintf "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec1092end1093end10941095# @return [Integer] yyyymmddHHMMSS1096def to_i1097sprintf("%04d%02d%02d%02d%02d%02d", year, mon, day, hour, min, sec).to_i1098end10991100# @private1101def inspect1102sprintf "#<#{self.class.name}:%04d-%02d-%02d %02d:%02d:%02d>", year, mon, day, hour, min, sec1103end11041105end11061107end110811091110