Class: Mysql::Protocol
- Inherits:
-
Object
- Object
- Mysql::Protocol
- Defined in:
- lib/mysql/protocol.rb
Overview
MySQL network protocol
Defined Under Namespace
Classes: AuthenticationPacket, ExecutePacket, FetchPacket, FieldListPacket, FieldPacket, InitialPacket, PreparePacket, PrepareResultPacket, QueryPacket, QuitPacket, ResultPacket, RxPacket, StmtClosePacket, TxPacket
Constant Summary collapse
- VERSION =
10
- MAX_PACKET_LENGTH =
2**24-1
Instance Attribute Summary collapse
-
#sqlstate ⇒ Object
readonly
Returns the value of attribute sqlstate.
Class Method Summary collapse
- .eof_packet?(data) ⇒ Boolean
-
.lcb(num) ⇒ Object
convert Numeric to LengthCodedBinary.
-
.lcb2int!(lcb) ⇒ Object
- convert LengthCodedBinary to Integer === Argument lcb
- String
-
LengthCodedBinary.
-
.lcs(str) ⇒ Object
convert String to LengthCodedString.
-
.lcs2str!(lcs) ⇒ Object
- convert LengthCodedString to String === Argument lcs
- String
-
LengthCodedString.
-
.net2value(data, type, unsigned) ⇒ Object
- Convert netdata to Ruby value === Argument data
- String
-
packet data.
-
.value2net(v) ⇒ Object
- convert Ruby value to netdata === Argument v
- Object
-
Ruby value.
Instance Method Summary collapse
- #close ⇒ Object
-
#initialize(host, port, socket, conn_timeout, read_timeout, write_timeout) ⇒ Protocol
constructor
make socket connection to server.
-
#read ⇒ Object
- Read one packet data === Return String === Exception ProtocolError
-
invalid packet sequence number.
-
#read_eof_packet ⇒ Object
- Read EOF packet === Exception ProtocolError
-
packet is not EOF.
-
#read_field_packet ⇒ Object
- Read field packet === Return FieldPacket
- packet data === Exception ProtocolError
-
invalid packet.
-
#read_initial_packet ⇒ Object
- Read initial packet === Return InitialPacket
-
Exception ProtocolError :: invalid packet.
-
#read_prepare_result_packet ⇒ Object
- Read prepare result packet === Return PrepareResultPacket
-
Exception ProtocolError :: invalid packet.
-
#read_result_packet ⇒ Object
Read result packet === Return ResultPacket ::.
-
#reset ⇒ Object
Reset sequence number.
-
#send_packet(packet) ⇒ Object
- Send one packet === Argument packet
-
[*Packet].
- #synchronize ⇒ Object
-
#write(data) ⇒ Object
Write one packet data === Argument data [String / IO] ::.
Constructor Details
#initialize(host, port, socket, conn_timeout, read_timeout, write_timeout) ⇒ Protocol
make socket connection to server.
Argument
- host
- String
-
if “localhost” or “” nil then use UNIXSocket. Otherwise use TCPSocket
- port
- Integer
-
port number using by TCPSocket
- socket
- String
-
socket file name using by UNIXSocket
- conn_timeout
- Integer
-
connect timeout (sec).
- read_timeout
- Integer
-
read timeout (sec).
- write_timeout
- Integer
-
write timeout (sec).
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/mysql/protocol.rb', line 186 def initialize(host, port, socket, conn_timeout, read_timeout, write_timeout) begin Timeout.timeout conn_timeout do if host.nil? or host.empty? or host == "localhost" socket ||= ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_PORT @sock = UNIXSocket.new socket else port ||= ENV["MYSQL_TCP_PORT"] || (Socket.getservbyname("mysql","tcp") rescue MYSQL_TCP_PORT) @sock = TCPSocket.new host, port end end rescue Timeout::Error raise ClientError, "connection timeout" end @read_timeout = read_timeout @write_timeout = write_timeout @seq = 0 # packet counter. reset by each command @mutex = Mutex.new end |
Instance Attribute Details
#sqlstate ⇒ Object (readonly)
Returns the value of attribute sqlstate.
176 177 178 |
# File 'lib/mysql/protocol.rb', line 176 def sqlstate @sqlstate end |
Class Method Details
.eof_packet?(data) ⇒ Boolean
65 66 67 |
# File 'lib/mysql/protocol.rb', line 65 def self.eof_packet?(data) data[0] == ?\xfe && data.length == 5 end |
.lcb(num) ⇒ Object
convert Numeric to LengthCodedBinary
18 19 20 21 22 23 24 |
# File 'lib/mysql/protocol.rb', line 18 def self.lcb(num) return "\xfb" if num.nil? return [num].pack("C") if num < 251 return [252, num].pack("Cv") if num < 65536 return [253, num&0xffff, num>>16].pack("CvC") if num < 16777216 return [254, num&0xffffffff, num>>32].pack("CVV") end |
.lcb2int!(lcb) ⇒ Object
convert LengthCodedBinary to Integer
Argument
- lcb
- String
-
LengthCodedBinary. This value will be broken.
Return
Integer or nil
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/mysql/protocol.rb', line 37 def self.lcb2int!(lcb) return nil if lcb.empty? case v = lcb.slice!(0) when ?\xfb return nil when ?\xfc return lcb.slice!(0,2).unpack("v").first when ?\xfd c, v = lcb.slice!(0,3).unpack("Cv") return (v<<8)+c when ?\xfe v1, v2 = lcb.slice!(0,8).unpack("VV") return (v2<<32)+v1 else return v.ord end end |
.lcs(str) ⇒ Object
convert String to LengthCodedString
27 28 29 30 |
# File 'lib/mysql/protocol.rb', line 27 def self.lcs(str) str = Charset.to_binary str lcb(str.length)+str end |
.lcs2str!(lcs) ⇒ Object
convert LengthCodedString to String
Argument
- lcs
- String
-
LengthCodedString. This value will be broken.
Return
String or nil
60 61 62 63 |
# File 'lib/mysql/protocol.rb', line 60 def self.lcs2str!(lcs) len = lcb2int! lcs return len && lcs.slice!(0, len) end |
.net2value(data, type, unsigned) ⇒ Object
Convert netdata to Ruby value
Argument
- data
- String
-
packet data. This will be broken.
- type
- Integer
-
field type
- unsigned
- true or false
-
true if value is unsigned
Return
- Object
-
converted value.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/mysql/protocol.rb', line 76 def self.net2value(data, type, unsigned) case type when Field::TYPE_STRING, Field::TYPE_VAR_STRING, Field::TYPE_NEWDECIMAL, Field::TYPE_BLOB return Protocol.lcs2str!(data) when Field::TYPE_TINY v = data.slice!(0).ord return unsigned ? v : v < 128 ? v : v-256 when Field::TYPE_SHORT v = data.slice!(0,2).unpack("v").first return unsigned ? v : v < 32768 ? v : v-65536 when Field::TYPE_INT24, Field::TYPE_LONG v = data.slice!(0,4).unpack("V").first return unsigned ? v : v < 2**32/2 ? v : v-2**32 when Field::TYPE_LONGLONG n1, n2 = data.slice!(0,8).unpack("VV") v = (n2<<32) | n1 return unsigned ? v : v < 2**64/2 ? v : v-2**64 when Field::TYPE_FLOAT return data.slice!(0,4).unpack("e").first when Field::TYPE_DOUBLE return data.slice!(0,8).unpack("E").first when Field::TYPE_DATE, Field::TYPE_DATETIME, Field::TYPE_TIMESTAMP len = data.slice!(0).ord y, m, d, h, mi, s, bs = data.slice!(0,len).unpack("vCCCCCV") return Mysql::Time.new(y, m, d, h, mi, s, bs) when Field::TYPE_TIME len = data.slice!(0).ord sign, d, h, mi, s, sp = data.slice!(0,len).unpack("CVCCCV") h = d.to_i * 24 + h.to_i return Mysql::Time.new(0, 0, 0, h, mi, s, sign!=0, sp) when Field::TYPE_YEAR return data.slice!(0,2).unpack("v").first when Field::TYPE_BIT return Protocol.lcs2str!(data) else raise "not implemented: type=#{type}" end end |
.value2net(v) ⇒ Object
convert Ruby value to netdata
Argument
- v
- Object
-
Ruby value.
Return
- String
-
netdata
Exception
- ProtocolError
-
value too large / value is not supported
122 123 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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/mysql/protocol.rb', line 122 def self.value2net(v) case v when nil type = Field::TYPE_NULL val = "" when Integer if v >= 0 if v < 256 type = Field::TYPE_TINY | 0x8000 val = [v].pack("C") elsif v < 256**2 type = Field::TYPE_SHORT | 0x8000 val = [v].pack("v") elsif v < 256**4 type = Field::TYPE_LONG | 0x8000 val = [v].pack("V") elsif v < 256**8 type = Field::TYPE_LONGLONG | 0x8000 val = [v&0xffffffff, v>>32].pack("VV") else raise ProtocolError, "value too large: #{v}" end else if -v <= 256/2 type = Field::TYPE_TINY val = [v].pack("C") elsif -v <= 256**2/2 type = Field::TYPE_SHORT val = [v].pack("v") elsif -v <= 256**4/2 type = Field::TYPE_LONG val = [v].pack("V") elsif -v <= 256**8/2 type = Field::TYPE_LONGLONG val = [v&0xffffffff, v>>32].pack("VV") else raise ProtocolError, "value too large: #{v}" end end when Float type = Field::TYPE_DOUBLE val = [v].pack("E") when String type = Field::TYPE_STRING val = Protocol.lcs(v) when Mysql::Time, ::Time type = Field::TYPE_DATETIME val = [7, v.year, v.month, v.day, v.hour, v.min, v.sec].pack("CvCCCCC") else raise ProtocolError, "class #{v.class} is not supported" end return type, val end |
Instance Method Details
#close ⇒ Object
206 207 208 |
# File 'lib/mysql/protocol.rb', line 206 def close @sock.close end |
#read ⇒ Object
Read one packet data
Return
String
Exception
- ProtocolError
-
invalid packet sequence number
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/mysql/protocol.rb', line 226 def read ret = "" len = nil begin Timeout.timeout @read_timeout do header = @sock.read(4) len1, len2, seq = header.unpack("CvC") len = (len2 << 8) + len1 raise ProtocolError, "invalid packet: sequence number mismatch(#{seq} != #{@seq}(expected))" if @seq != seq @seq = (@seq + 1) % 256 ret.concat @sock.read(len) end rescue Timeout::Error raise ClientError, "read timeout" end while len == MAX_PACKET_LENGTH @sqlstate = "00000" # Error packet if ret[0] == ?\xff f, errno, marker, @sqlstate, = ret.unpack("Cvaa5a*") unless marker == "#" f, errno, = ret.unpack("Cva*") # Version 4.0 Error @sqlstate = "" end if Mysql::ServerError::ERROR_MAP.key? errno raise Mysql::ServerError::ERROR_MAP[errno].new(, @sqlstate) end raise Mysql::ServerError.new(, @sqlstate) end ret end |
#read_eof_packet ⇒ Object
Read EOF packet
Exception
- ProtocolError
-
packet is not EOF
292 293 294 295 |
# File 'lib/mysql/protocol.rb', line 292 def read_eof_packet data = read raise ProtocolError, "packet is not EOF" unless Protocol.eof_packet? data end |
#read_field_packet ⇒ Object
Read field packet
Return
- FieldPacket
-
packet data
Exception
- ProtocolError
-
invalid packet
318 319 320 |
# File 'lib/mysql/protocol.rb', line 318 def read_field_packet FieldPacket.parse read end |
#read_initial_packet ⇒ Object
Read initial packet
Return
- InitialPacket
-
Exception
- ProtocolError
-
invalid packet
302 303 304 |
# File 'lib/mysql/protocol.rb', line 302 def read_initial_packet InitialPacket.parse read end |
#read_prepare_result_packet ⇒ Object
Read prepare result packet
Return
- PrepareResultPacket
-
Exception
- ProtocolError
-
invalid packet
327 328 329 |
# File 'lib/mysql/protocol.rb', line 327 def read_prepare_result_packet PrepareResultPacket.parse read end |
#read_result_packet ⇒ Object
Read result packet
Return
- ResultPacket
309 310 311 |
# File 'lib/mysql/protocol.rb', line 309 def read_result_packet ResultPacket.parse read end |
#reset ⇒ Object
Reset sequence number
217 218 219 |
# File 'lib/mysql/protocol.rb', line 217 def reset @seq = 0 end |
#send_packet(packet) ⇒ Object
Send one packet
Argument
- packet
- *Packet
285 286 287 |
# File 'lib/mysql/protocol.rb', line 285 def send_packet(packet) write packet.serialize end |
#synchronize ⇒ Object
210 211 212 213 214 |
# File 'lib/mysql/protocol.rb', line 210 def synchronize @mutex.synchronize do return yield end end |
#write(data) ⇒ Object
Write one packet data
Argument
- data [String / IO]
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/mysql/protocol.rb', line 262 def write(data) begin @sock.sync = false data = StringIO.new data if data.is_a? String while d = data.read(MAX_PACKET_LENGTH) Timeout.timeout @write_timeout do @sock.write [d.length%256, d.length/256, @seq].pack("CvC") @sock.write d end @seq = (@seq + 1) % 256 end @sock.sync = true Timeout.timeout @write_timeout do @sock.flush end rescue Timeout::Error raise ClientError, "write timeout" end end |