# $Id: mysql.rb,v 1.24 2005/02/12 11:37:15 tommy Exp $
#
# Copyright (C) 2003-2005 TOMITA Masahiro
#
tommy@tmtm.org
#
class Mysql
VERSION = "4.0-ruby-0.2.5"
MAX_PACKET_LENGTH = 256*256*256-1
MAX_ALLOWED_PACKET = 1024*1024*1024
MYSQL_UNIX_ADDR = "/tmp/mysql.sock"
MYSQL_PORT = 3306
PROTOCOL_VERSION = 10
# Command
COM_SLEEP = 0
COM_QUIT = 1
COM_INIT_DB = 2
COM_QUERY = 3
COM_FIELD_LIST = 4
COM_CREATE_DB = 5
COM_DROP_DB = 6
COM_REFRESH = 7
COM_SHUTDOWN = 8
COM_STATISTICS = 9
COM_PROCESS_INFO = 10
COM_CONNECT = 11
COM_PROCESS_KILL = 12
COM_DEBUG = 13
COM_PING = 14
COM_TIME = 15
COM_DELAYED_INSERT = 16
COM_CHANGE_USER = 17
COM_BINLOG_DUMP = 18
COM_TABLE_DUMP = 19
COM_CONNECT_OUT = 20
COM_REGISTER_SLAVE = 21
# Client flag
CLIENT_LONG_PASSWORD = 1
CLIENT_FOUND_ROWS = 1 << 1
CLIENT_LONG_FLAG = 1 << 2
CLIENT_CONNECT_WITH_DB= 1 << 3
CLIENT_NO_SCHEMA = 1 << 4
CLIENT_COMPRESS = 1 << 5
CLIENT_ODBC = 1 << 6
CLIENT_LOCAL_FILES = 1 << 7
CLIENT_IGNORE_SPACE = 1 << 8
CLIENT_INTERACTIVE = 1 << 10
CLIENT_SSL = 1 << 11
CLIENT_IGNORE_SIGPIPE = 1 << 12
CLIENT_TRANSACTIONS = 1 << 13
CLIENT_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
# Connection Option
OPT_CONNECT_TIMEOUT = 0
OPT_COMPRESS = 1
OPT_NAMED_PIPE = 2
INIT_COMMAND = 3
READ_DEFAULT_FILE = 4
READ_DEFAULT_GROUP = 5
SET_CHARSET_DIR = 6
SET_CHARSET_NAME = 7
OPT_LOCAL_INFILE = 8
# Server Status
SERVER_STATUS_IN_TRANS = 1
SERVER_STATUS_AUTOCOMMIT = 2
# Refresh parameter
REFRESH_GRANT = 1
REFRESH_LOG = 2
REFRESH_TABLES = 4
REFRESH_HOSTS = 8
REFRESH_STATUS = 16
REFRESH_THREADS = 32
REFRESH_SLAVE = 64
REFRESH_MASTER = 128
def initialize(*args)
@client_flag = 0
@max_allowed_packet = MAX_ALLOWED_PACKET
@query_with_result = true
@status = :STATUS_READY
if args[0] != :INIT then
real_connect(*args)
end
end
def real_connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=nil)
@server_status = SERVER_STATUS_AUTOCOMMIT
if (host == nil or host == "localhost") and defined? UNIXSocket then
unix_socket = socket || ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_ADDR
sock = UNIXSocket::new(unix_socket)
@host_info = Error::err(Error::CR_LOCALHOST_CONNECTION)
@unix_socket = unix_socket
else
sock = TCPSocket::new(host, port||ENV["MYSQL_TCP_PORT"]||(Socket::getservbyname("mysql","tcp") rescue MYSQL_PORT))
@host_info = sprintf Error::err(Error::CR_TCP_CONNECTION), host
end
@host = host ? host.dup : nil
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true
@net = Net::new sock
a = read
@protocol_version = a.slice!(0)
@server_version, a = a.split(/\0/,2)
@thread_id, @scramble_buff = a.slice!(0,13).unpack("La8")
if a.size >= 2 then
@server_capabilities, = a.slice!(0,2).unpack("v")
end
if a.size >= 16 then
@server_language, @server_status = a.unpack("cv")
end
flag = 0 if flag == nil
flag |= @client_flag | CLIENT_CAPABILITIES
flag |= CLIENT_CONNECT_WITH_DB if db
data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+(user||"")+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)
if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0 then
data << "\0"+db
@db = db.dup
end
write data
read
ObjectSpace.define_finalizer(self, Mysql.finalizer(@net))
self
end
alias :connect :real_connect
def escape_string(str)
Mysql::escape_string str
end
alias :quote :escape_string
def get_client_info()
VERSION
end
alias :client_info :get_client_info
def options(option, arg=nil)
if option == OPT_LOCAL_INFILE then
if arg == false or arg == 0 then
@client_flag &= ~CLIENT_LOCAL_FILES
else
@client_flag |= CLIENT_LOCAL_FILES
end
else
raise "not implemented"
end
end
def real_query(query)
command COM_QUERY, query, true
read_query_result
self
end
def use_result()
if @status != :STATUS_GET_RESULT then
error Error::CR_COMMANDS_OUT_OF_SYNC
end
res = Result::new self, @fields, @field_count
@status = :STATUS_USE_RESULT
res
end
def store_result()
if @status != :STATUS_GET_RESULT then
error Error::CR_COMMANDS_OUT_OF_SYNC
end
@status = :STATUS_READY
data = read_rows @field_count
res = Result::new self, @fields, @field_count, data
@fields = nil
@affected_rows = data.length
res
end
def change_user(user="", passwd="", db="")
data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
command COM_CHANGE_USER, data
@user = user
@passwd = passwd
@db = db
end
def character_set_name()
raise "not implemented"
end
def close()
@status = :STATUS_READY
command COM_QUIT, nil, true
@net.close
self
end
def create_db(db)
command COM_CREATE_DB, db
self
end
def drop_db(db)
command COM_DROP_DB, db
self
end
def dump_debug_info()
command COM_DEBUG
self
end
def get_host_info()
@host_info
end
alias :host_info :get_host_info
def get_proto_info()
@protocol_version
end
alias :proto_info :get_proto_info
def get_server_info()
@server_version
end
alias :server_info :get_server_info
def kill(id)
command COM_PROCESS_KILL, Net::int4str(id)
self
end
def list_dbs(db=nil)
real_query "show databases #{db}"
@status = :STATUS_READY
read_rows(1).flatten
end
def list_fields(table, field=nil)
command COM_FIELD_LIST, "#{table}\0#{field}", true
f = read_rows 6
fields = unpack_fields(f, @server_capabilities & CLIENT_LONG_FLAG != 0)
res = Result::new self, fields, f.length
res.eof = true
res
end
def list_processes()
data = command COM_PROCESS_INFO
@field_count = get_length data
fields = read_rows 5
@fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
@status = :STATUS_GET_RESULT
store_result
end
def list_tables(table=nil)
real_query "show tables #{table}"
@status = :STATUS_READY
read_rows(1).flatten
end
def ping()
command COM_PING
self
end
def query(query)
real_query query
if not @query_with_result then
return self
end
if @field_count == 0 then
return nil
end
store_result
end
def refresh(r)
command COM_REFRESH, r.chr
self
end
def reload()
refresh REFRESH_GRANT
self
end
def select_db(db)
command COM_INIT_DB, db
@db = db
self
end
def shutdown()
command COM_SHUTDOWN
self
end
def stat()
command COM_STATISTICS
end
attr_reader :info, :insert_id, :affected_rows, :field_count, :thread_id
attr_accessor :query_with_result, :status
def read_one_row(field_count)
data = read
return if data[0] == 254 and data.length == 1
rec = []
field_count.times do
len = get_length data
if len == nil then
rec << len
else
rec << data.slice!(0,len)
end
end
rec
end
def skip_result()
if @status == :STATUS_USE_RESULT then
loop do
data = read
break if data[0] == 254 and data.length == 1
end
@status = :STATUS_READY
end
end
def inspect()
"#<#{self.class}>"
end
private
def read_query_result()
data = read
@field_count = get_length(data)
if @field_count == nil then # LOAD DATA LOCAL INFILE
File:
pen(data) do |f|
write f.read
end
write "" # mark EOF
data = read
@field_count = get_length(data)
end
if @field_count == 0 then
@affected_rows = get_length(data, true)
@insert_id = get_length(data, true)
if @server_capabilities & CLIENT_TRANSACTIONS != 0 then
a = data.slice!(0,2)
@server_status = a[0]+a[1]*256
end
if data.size > 0 and get_length(data) then
@info = data
end
else
@extra_info = get_length(data, true)
fields = read_rows 5
@fields = unpack_fields(fields, @server_capabilities & CLIENT_LONG_FLAG != 0)
@status = :STATUS_GET_RESULT
end
self
end
def unpack_fields(data, long_flag_protocol)
ret = []
data.each do |f|
table = org_table = f[0]
name = f[1]
length = f[2][0]+f[2][1]*256+f[2][2]*256*256
type = f[3][0]
if long_flag_protocol then
flags = f[4][0]+f[4][1]*256
decimals = f[4][2]
else
flags = f[4][0]
decimals = f[4][1]
end
def_value = f[5]
max_length = 0
ret << Field::new(table, org_table, name, length, type, flags, decimals, def_value, max_length)
end
ret
end
def read_rows(field_count)
ret = []
while rec = read_one_row(field_count) do
ret << rec
end
ret
end
def get_length(data, longlong=nil)
return if data.length == 0
c = data.slice!(0)
case c
when 251
return nil
when 252
a = data.slice!(0,2)
return a[0]+a[1]*256
when 253
a = data.slice!(0,3)
return a[0]+a[1]*256+a[2]*256**2
when 254
a = data.slice!(0,8)
if longlong then
return a[0]+a[1]*256+a[2]*256**2+a[3]*256**3+
a[4]*256**4+a[5]*256**5+a[6]*256**6+a[7]*256**7
else
return a[0]+a[1]*256+a[2]*256**2+a[3]*256**3
end
else
c
end
end
def command(cmd, arg=nil, skip_check=nil)
unless @net then
error Error::CR_SERVER_GONE_ERROR
end
if @status != :STATUS_READY then
error Error::CR_COMMANDS_OUT_OF_SYNC
end
@net.clear
write cmd.chr+(arg||"")
read unless skip_check
end
def read()
unless @net then
error Error::CR_SERVER_GONE_ERROR
end
a = @net.read
if a[0] == 255 then
if a.length > 3 then
@errno = a[1]+a[2]*256
@error = a[3 .. -1]
else
@errno = Error::CR_UNKNOWN_ERROR
@error = Error::err @errno
end
raise Error::new(@errno, @error)
end
a
end
def write(arg)
unless @net then
error Error::CR_SERVER_GONE_ERROR
end
@net.write arg
end
def hash_password(password)
nr = 1345345333
add = 7
nr2 = 0x12345671
password.each_byte do |i|
next if i == 0x20 or i == 9
nr ^= (((nr & 63) + add) * i) + (nr << 8)
nr2 += (nr2 << 8) ^ nr
add += i
end
[nr & ((1 << 31) - 1), nr2 & ((1 << 31) - 1)]
end
def scramble(password, message, old_ver)
return "" if password == nil or password == ""
raise "old version password is not implemented" if old_ver
hash_pass = hash_password password
hash_message = hash_password message
rnd = Random::new hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]
to = []
1.upto(message.length) do
to << ((rnd.rnd*31)+64).floor
end
extra = (rnd.rnd*31).floor
to.map! do |t| (t ^ extra).chr end
to.join
end
def error(errno)
@errno = errno
@error = Error::err errno
raise Error::new(@errno, @error)
end
class Result
def initialize(mysql, fields, field_count, data=nil)
@handle = mysql
@fields = fields
@field_count = field_count
@data = data
@current_field = 0
@current_row = 0
@eof = false
@row_count = 0
end
attr_accessor :eof
def data_seek
@current_row = n
end
def fetch_field()
return if @current_field >= @field_count
f = @fields[@current_field]
@current_field += 1
f
end
def fetch_fields()
@fields
end
def fetch_field_direct
@fields[n]
end
def fetch_lengths()
@data ? @data[@current_row].map{|i| i ? i.length : 0} : @lengths
end
def fetch_row()
if @data then
if @current_row >= @data.length then
@handle.status = :STATUS_READY
return
end
ret = @data[@current_row]
@current_row += 1
else
return if @eof
ret = @handle.read_one_row @field_count
if ret == nil then
@eof = true
return
end
@lengths = ret.map{|i| i ? i.length : 0}
@row_count += 1
end
ret
end
def fetch_hash(with_table=nil)
row = fetch_row
return if row == nil
hash = {}
@fields.each_index do |i|
f = with_table ? @fields
.table+"."+@fields.name : @fields.name
hash[f] = row
end
hash
end
def field_seek
@current_field = n
end
def field_tell()
@current_field
end
def free()
@handle.skip_result
@handle = @fields = @data = nil
GC::start
end
def num_fields()
@field_count
end
def num_rows()
@data ? @data.length : @row_count
end
def row_seek
@current_row = n
end
def row_tell()
@current_row
end
def each()
while row = fetch_row do
yield row
end
end
def each_hash(with_table=nil)
while hash = fetch_hash(with_table) do
yield hash
end
end
def inspect()
"#<#{self.class}>"
end
end
class Field
# Field type
TYPE_DECIMAL = 0
TYPE_TINY = 1
TYPE_SHORT = 2
TYPE_LONG = 3
TYPE_FLOAT = 4
TYPE_DOUBLE = 5
TYPE_NULL = 6
TYPE_TIMESTAMP = 7
TYPE_LONGLONG = 8
TYPE_INT24 = 9
TYPE_DATE = 10
TYPE_TIME = 11
TYPE_DATETIME = 12
TYPE_YEAR = 13
TYPE_NEWDATE = 14
TYPE_ENUM = 247
TYPE_SET = 248
TYPE_TINY_BLOB = 249
TYPE_MEDIUM_BLOB = 250
TYPE_LONG_BLOB = 251
TYPE_BLOB = 252
TYPE_VAR_STRING = 253
TYPE_STRING = 254
TYPE_GEOMETRY = 255
TYPE_CHAR = TYPE_TINY
TYPE_INTERVAL = TYPE_ENUM
# Flag
NOT_NULL_FLAG = 1
PRI_KEY_FLAG = 2
UNIQUE_KEY_FLAG = 4
MULTIPLE_KEY_FLAG = 8
BLOB_FLAG = 16
UNSIGNED_FLAG = 32
ZEROFILL_FLAG = 64
BINARY_FLAG = 128
ENUM_FLAG = 256
AUTO_INCREMENT_FLAG = 512
TIMESTAMP_FLAG = 1024
SET_FLAG = 2048
NUM_FLAG = 32768
PART_KEY_FLAG = 16384
GROUP_FLAG = 32768
UNIQUE_FLAG = 65536
def initialize(table, org_table, name, length, type, flags, decimals, def_value, max_length)
@table = table
@org_table = org_table
@name = name
@length = length
@type = type
@flags = flags
@decimals = decimals
@def = def_value
@max_length = max_length
if (type <= TYPE_INT24 and (type != TYPE_TIMESTAMP or length == 14 or length == 8)) or type == TYPE_YEAR then
@flags |= NUM_FLAG
end
end
attr_reader :table, rg_table, :name, :length, :type, :flags, :decimals, :def, :max_length
def inspect()
"#<#{self.class}:#{@name}>"
end
end
class Error < StandardError
# Server Error
ER_HASHCHK = 1000
ER_NISAMCHK = 1001
ER_NO = 1002
ER_YES = 1003
ER_CANT_CREATE_FILE = 1004
ER_CANT_CREATE_TABLE = 1005
ER_CANT_CREATE_DB = 1006
ER_DB_CREATE_EXISTS = 1007
ER_DB_DROP_EXISTS = 1008
ER_DB_DROP_DELETE = 1009
ER_DB_DROP_RMDIR = 1010
ER_CANT_DELETE_FILE = 1011
ER_CANT_FIND_SYSTEM_REC = 1012
ER_CANT_GET_STAT = 1013
ER_CANT_GET_WD = 1014
ER_CANT_LOCK = 1015
ER_CANT_OPEN_FILE = 1016
ER_FILE_NOT_FOUND = 1017
ER_CANT_READ_DIR = 1018
ER_CANT_SET_WD = 1019
ER_CHECKREAD = 1020
ER_DISK_FULL = 1021
ER_DUP_KEY = 1022
ER_ERROR_ON_CLOSE = 1023
ER_ERROR_ON_READ = 1024
ER_ERROR_ON_RENAME = 1025
ER_ERROR_ON_WRITE = 1026
ER_FILE_USED = 1027
ER_FILSORT_ABORT = 1028
ER_FORM_NOT_FOUND = 1029
ER_GET_ERRNO = 1030
ER_ILLEGAL_HA = 1031
ER_KEY_NOT_FOUND = 1032
ER_NOT_FORM_FILE = 1033
ER_NOT_KEYFILE = 1034
ER_OLD_KEYFILE = 1035
ER_OPEN_AS_READONLY = 1036
ER_OUTOFMEMORY = 1037
ER_OUT_OF_SORTMEMORY = 1038
ER_UNEXPECTED_EOF = 1039
ER_CON_COUNT_ERROR = 1040
ER_OUT_OF_RESOURCES = 1041
ER_BAD_HOST_ERROR = 1042
ER_HANDSHAKE_ERROR = 1043
ER_DBACCESS_DENIED_ERROR = 1044
ER_ACCESS_DENIED_ERROR = 1045
ER_NO_DB_ERROR = 1046
ER_UNKNOWN_COM_ERROR = 1047
ER_BAD_NULL_ERROR = 1048
ER_BAD_DB_ERROR = 1049
ER_TABLE_EXISTS_ERROR = 1050
ER_BAD_TABLE_ERROR = 1051
ER_NON_UNIQ_ERROR = 1052
ER_SERVER_SHUTDOWN = 1053
ER_BAD_FIELD_ERROR = 1054
ER_WRONG_FIELD_WITH_GROUP = 1055
ER_WRONG_GROUP_FIELD = 1056
ER_WRONG_SUM_SELECT = 1057
ER_WRONG_VALUE_COUNT = 1058
ER_TOO_LONG_IDENT = 1059
ER_DUP_FIELDNAME = 1060
ER_DUP_KEYNAME = 1061
ER_DUP_ENTRY = 1062
ER_WRONG_FIELD_SPEC = 1063
ER_PARSE_ERROR = 1064
ER_EMPTY_QUERY = 1065
ER_NONUNIQ_TABLE = 1066
ER_INVALID_DEFAULT = 1067
ER_MULTIPLE_PRI_KEY = 1068
ER_TOO_MANY_KEYS = 1069
ER_TOO_MANY_KEY_PARTS = 1070
ER_TOO_LONG_KEY = 1071
ER_KEY_COLUMN_DOES_NOT_EXITS = 1072
ER_BLOB_USED_AS_KEY = 1073
ER_TOO_BIG_FIELDLENGTH = 1074
ER_WRONG_AUTO_KEY = 1075
ER_READY = 1076
ER_NORMAL_SHUTDOWN = 1077
ER_GOT_SIGNAL = 1078
ER_SHUTDOWN_COMPLETE = 1079
ER_FORCING_CLOSE = 1080
ER_IPSOCK_ERROR = 1081
ER_NO_SUCH_INDEX = 1082
ER_WRONG_FIELD_TERMINATORS = 1083
ER_BLOBS_AND_NO_TERMINATED = 1084
ER_TEXTFILE_NOT_READABLE = 1085
ER_FILE_EXISTS_ERROR = 1086
ER_LOAD_INFO = 1087
ER_ALTER_INFO = 1088
ER_WRONG_SUB_KEY = 1089
ER_CANT_REMOVE_ALL_FIELDS = 1090
ER_CANT_DROP_FIELD_OR_KEY = 1091
ER_INSERT_INFO = 1092
ER_INSERT_TABLE_USED = 1093
ER_NO_SUCH_THREAD = 1094
ER_KILL_DENIED_ERROR = 1095
ER_NO_TABLES_USED = 1096
ER_TOO_BIG_SET = 1097
ER_NO_UNIQUE_LOGFILE = 1098
ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099
ER_TABLE_NOT_LOCKED = 1100
ER_BLOB_CANT_HAVE_DEFAULT = 1101
ER_WRONG_DB_NAME = 1102
ER_WRONG_TABLE_NAME = 1103
ER_TOO_BIG_SELECT = 1104
ER_UNKNOWN_ERROR = 1105
ER_UNKNOWN_PROCEDURE = 1106
ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107
ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108
ER_UNKNOWN_TABLE = 1109
ER_FIELD_SPECIFIED_TWICE = 1110
ER_INVALID_GROUP_FUNC_USE = 1111
ER_UNSUPPORTED_EXTENSION = 1112
ER_TABLE_MUST_HAVE_COLUMNS = 1113
ER_RECORD_FILE_FULL = 1114
ER_UNKNOWN_CHARACTER_SET = 1115
ER_TOO_MANY_TABLES = 1116
ER_TOO_MANY_FIELDS = 1117
ER_TOO_BIG_ROWSIZE = 1118
ER_STACK_OVERRUN = 1119
ER_WRONG_OUTER_JOIN = 1120
ER_NULL_COLUMN_IN_INDEX = 1121
ER_CANT_FIND_UDF = 1122
ER_CANT_INITIALIZE_UDF = 1123
ER_UDF_NO_PATHS = 1124
ER_UDF_EXISTS = 1125
ER_CANT_OPEN_LIBRARY = 1126
ER_CANT_FIND_DL_ENTRY = 1127
ER_FUNCTION_NOT_DEFINED = 1128
ER_HOST_IS_BLOCKED = 1129
ER_HOST_NOT_PRIVILEGED = 1130
ER_PASSWORD_ANONYMOUS_USER = 1131
ER_PASSWORD_NOT_ALLOWED = 1132
ER_PASSWORD_NO_MATCH = 1133
ER_UPDATE_INFO = 1134
ER_CANT_CREATE_THREAD = 1135
ER_WRONG_VALUE_COUNT_ON_ROW = 1136
ER_CANT_REOPEN_TABLE = 1137
ER_INVALID_USE_OF_NULL = 1138
ER_REGEXP_ERROR = 1139
ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140
ER_NONEXISTING_GRANT = 1141
ER_TABLEACCESS_DENIED_ERROR = 1142
ER_COLUMNACCESS_DENIED_ERROR = 1143
ER_ILLEGAL_GRANT_FOR_TABLE = 1144
ER_GRANT_WRONG_HOST_OR_USER = 1145
ER_NO_SUCH_TABLE = 1146
ER_NONEXISTING_TABLE_GRANT = 1147
ER_NOT_ALLOWED_COMMAND = 1148
ER_SYNTAX_ERROR = 1149
ER_DELAYED_CANT_CHANGE_LOCK = 1150
ER_TOO_MANY_DELAYED_THREADS = 1151
ER_ABORTING_CONNECTION = 1152
ER_NET_PACKET_TOO_LARGE = 1153
ER_NET_READ_ERROR_FROM_PIPE = 1154
ER_NET_FCNTL_ERROR = 1155
ER_NET_PACKETS_OUT_OF_ORDER = 1156
ER_NET_UNCOMPRESS_ERROR = 1157
ER_NET_READ_ERROR = 1158
ER_NET_READ_INTERRUPTED = 1159
ER_NET_ERROR_ON_WRITE = 1160
ER_NET_WRITE_INTERRUPTED = 1161
ER_TOO_LONG_STRING = 1162
ER_TABLE_CANT_HANDLE_BLOB = 1163
ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164
ER_DELAYED_INSERT_TABLE_LOCKED = 1165
ER_WRONG_COLUMN_NAME = 1166
ER_WRONG_KEY_COLUMN = 1167
ER_WRONG_MRG_TABLE = 1168
ER_DUP_UNIQUE = 1169
ER_BLOB_KEY_WITHOUT_LENGTH = 1170
ER_PRIMARY_CANT_HAVE_NULL = 1171
ER_TOO_MANY_ROWS = 1172
ER_REQUIRES_PRIMARY_KEY = 1173
ER_NO_RAID_COMPILED = 1174
ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175
ER_KEY_DOES_NOT_EXITS = 1176
ER_CHECK_NO_SUCH_TABLE = 1177
ER_CHECK_NOT_IMPLEMENTED = 1178
ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179
ER_ERROR_DURING_COMMIT = 1180
ER_ERROR_DURING_ROLLBACK = 1181
ER_ERROR_DURING_FLUSH_LOGS = 1182
ER_ERROR_DURING_CHECKPOINT = 1183
ER_NEW_ABORTING_CONNECTION = 1184
ER_DUMP_NOT_IMPLEMENTED = 1185
ER_FLUSH_MASTER_BINLOG_CLOSED = 1186
ER_INDEX_REBUILD = 1187
ER_MASTER = 1188
ER_MASTER_NET_READ = 1189
ER_MASTER_NET_WRITE = 1190
ER_FT_MATCHING_KEY_NOT_FOUND = 1191
ER_LOCK_OR_ACTIVE_TRANSACTION = 1192
ER_UNKNOWN_SYSTEM_VARIABLE = 1193
ER_CRASHED_ON_USAGE = 1194
ER_CRASHED_ON_REPAIR = 1195
ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196
ER_TRANS_CACHE_FULL = 1197
ER_SLAVE_MUST_STOP = 1198
ER_SLAVE_NOT_RUNNING = 1199
ER_BAD_SLAVE = 1200
ER_MASTER_INFO = 1201
ER_SLAVE_THREAD = 1202
ER_TOO_MANY_USER_CONNECTIONS = 1203
ER_SET_CONSTANTS_ONLY = 1204
ER_LOCK_WAIT_TIMEOUT = 1205
ER_LOCK_TABLE_FULL = 1206
ER_READ_ONLY_TRANSACTION = 1207
ER_DROP_DB_WITH_READ_LOCK = 1208
ER_CREATE_DB_WITH_READ_LOCK = 1209
ER_WRONG_ARGUMENTS = 1210
ER_NO_PERMISSION_TO_CREATE_USER = 1211
ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212
ER_LOCK_DEADLOCK = 1213
ER_TABLE_CANT_HANDLE_FULLTEXT = 1214
ER_CANNOT_ADD_FOREIGN = 1215
ER_NO_REFERENCED_ROW = 1216
ER_ROW_IS_REFERENCED = 1217
ER_CONNECT_TO_MASTER = 1218
ER_QUERY_ON_MASTER = 1219
ER_ERROR_WHEN_EXECUTING_COMMAND = 1220
ER_WRONG_USAGE = 1221
ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222
ER_CANT_UPDATE_WITH_READLOCK = 1223
ER_MIXING_NOT_ALLOWED = 1224
ER_DUP_ARGUMENT = 1225
ER_USER_LIMIT_REACHED = 1226
ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227
ER_LOCAL_VARIABLE = 1228
ER_GLOBAL_VARIABLE = 1229
ER_NO_DEFAULT = 1230
ER_WRONG_VALUE_FOR_VAR = 1231
ER_WRONG_TYPE_FOR_VAR = 1232
ER_VAR_CANT_BE_READ = 1233
ER_CANT_USE_OPTION_HERE = 1234
ER_NOT_SUPPORTED_YET = 1235
ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236
ER_SLAVE_IGNORED_TABLE = 1237
ER_ERROR_MESSAGES = 238
# Client Error
CR_MIN_ERROR = 2000
CR_MAX_ERROR = 2999
CR_UNKNOWN_ERROR = 2000
CR_SOCKET_CREATE_ERROR = 2001
CR_CONNECTION_ERROR = 2002
CR_CONN_HOST_ERROR = 2003
CR_IPSOCK_ERROR = 2004
CR_UNKNOWN_HOST = 2005
CR_SERVER_GONE_ERROR = 2006
CR_VERSION_ERROR = 2007
CR_OUT_OF_MEMORY = 2008
CR_WRONG_HOST_INFO = 2009
CR_LOCALHOST_CONNECTION = 2010
CR_TCP_CONNECTION = 2011
CR_SERVER_HANDSHAKE_ERR = 2012
CR_SERVER_LOST = 2013
CR_COMMANDS_OUT_OF_SYNC = 2014
CR_NAMEDPIPE_CONNECTION = 2015
CR_NAMEDPIPEWAIT_ERROR = 2016
CR_NAMEDPIPEOPEN_ERROR = 2017
CR_NAMEDPIPESETSTATE_ERROR = 2018
CR_CANT_READ_CHARSET = 2019
CR_NET_PACKET_TOO_LARGE = 2020
CR_EMBEDDED_CONNECTION = 2021
CR_PROBE_SLAVE_STATUS = 2022
CR_PROBE_SLAVE_HOSTS = 2023
CR_PROBE_SLAVE_CONNECT = 2024
CR_PROBE_MASTER_CONNECT = 2025
CR_SSL_CONNECTION_ERROR = 2026
CR_MALFORMED_PACKET = 2027
CLIENT_ERRORS = [
"Unknown MySQL error",
"Can't create UNIX socket (%d)",
"Can't connect to local MySQL server through socket '%-.64s' (%d)",
"Can't connect to MySQL server on '%-.64s' (%d)",
"Can't create TCP/IP socket (%d)",
"Unknown MySQL Server Host '%-.64s' (%d)",
"MySQL server has gone away",
"Protocol mismatch. Server Version = %d Client Version = %d",
"MySQL client run out of memory",
"Wrong host info",
"Localhost via UNIX socket",
"%-.64s via TCP/IP",
"Error in server handshake",
"Lost connection to MySQL server during query",
"Commands out of sync; You can't run this command now",
"%-.64s via named pipe",
"Can't wait for named pipe to host: %-.64s pipe: %-.32s (%lu)",
"Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)",
"Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)",
"Can't initialize character set %-.64s (path: %-.64s)",
"Got packet bigger than 'max_allowed_packet'",
"Embedded server",
"Error on SHOW SLAVE STATUS:",
"Error on SHOW SLAVE HOSTS:",
"Error connecting to slave:",
"Error connecting to master:",
"SSL connection error",
"Malformed packet"
]
def initialize(errno, error)
@errno = errno
@error = error
super error
end
attr_reader :errno, :error
def Error::err(errno)
CLIENT_ERRORS[errno - Error::CR_MIN_ERROR]
end
end
class Net
def initialize(sock)
@sock = sock
@pkt_nr = 0
end
def clear()
@pkt_nr = 0
end
def read()
buf = []
len = nil
@sock.sync = false
while len == nil or len == MAX_PACKET_LENGTH do
a = @sock.read(4)
len = a[0]+a[1]*256+a[2]*256*256
pkt_nr = a[3]
if @pkt_nr != pkt_nr then
raise "Packets out of order: #{@pkt_nr}<>#{pkt_nr}"
end
@pkt_nr = @pkt_nr + 1 & 0xff
buf << @sock.read(len)
end
@sock.sync = true
buf.join
end
def write(data)
if data.is_a? Array then
data = data.join
end
@sock.sync = false
ptr = 0
while data.length >= MAX_PACKET_LENGTH do
@sock.write Net::int3str(MAX_PACKET_LENGTH)+@pkt_nr.chr+data[ptr, MAX_PACKET_LENGTH]
@pkt_nr = @pkt_nr + 1 & 0xff
ptr += MAX_PACKET_LENGTH
end
@sock.write Net::int3str(data.length-ptr)+@pkt_nr.chr+data[ptr .. -1]
@pkt_nr = @pkt_nr + 1 & 0xff
@sock.sync = true
@sock.flush
end
def close()
@sock.close
end
def Net::int2str
[n].pack("v")
end
def Net::int3str
[n%256, n>>8].pack("cv")
end
def Net::int4str
[n].pack("V")
end
end
class Random
def initialize(seed1, seed2)
@max_value = 0x3FFFFFFF
@seed1 = seed1 % @max_value
@seed2 = seed2 % @max_value
end
def rnd()
@seed1 = (@seed1*3+@seed2) % @max_value
@seed2 = (@seed1+@seed2+33) % @max_value
@seed1.to_f / @max_value
end
end
end
class << Mysql
def init()
Mysql::new :INIT
end
def real_connect(*args)
Mysql::new(*args)
end
alias :connect :real_connect
def finalizer(net)
proc {
net.clear
net.write Mysql::COM_QUIT.chr
}
end
def escape_string(str)
str.gsub(/([\0\n\r\032\'\"\\])/) do
case $1
when "\0" then "\\0"
when "\n" then "\\n"
when "\r" then "\\r"
when "\032" then "\\Z"
else "\\"+$1
end
end
end
alias :quote :escape_string
def get_client_info()
Mysql::VERSION
end
alias :client_info :get_client_info
def debug(str)
raise "not implemented"
end
end
#
# for compatibility
#
MysqlRes = Mysql::Result
MysqlField = Mysql::Field
MysqlError = Mysql::Error