1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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
114
115
116
117
118
119
120
121
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
|
# $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.6-plus-changes"
require "socket"
require "digest/sha1"
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
SCRAMBLE_LENGTH = 20
SCRAMBLE_LENGTH_323 = 8
# 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_PROTOCOL_41 = 1 << 9
CLIENT_INTERACTIVE = 1 << 10
CLIENT_SSL = 1 << 11
CLIENT_IGNORE_SIGPIPE = 1 << 12
CLIENT_TRANSACTIONS = 1 << 13
CLIENT_RESERVED = 1 << 14
CLIENT_SECURE_CONNECTION = 1 << 15
CLIENT_CAPABILITIES = CLIENT_LONG_PASSWORD|CLIENT_LONG_FLAG|CLIENT_TRANSACTIONS
PROTO_AUTH41 = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION
# 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.slice!(0,3).unpack("cv")
end
flag = 0 if flag == nil
flag |= @client_flag | CLIENT_CAPABILITIES
flag |= CLIENT_CONNECT_WITH_DB if db
@pre_411 = (0 == @server_capabilities & PROTO_AUTH41)
if @pre_411
data = Net::int2str(flag)+Net::int3str(@max_allowed_packet)+
(user||"")+"\0"+
scramble(passwd, @scramble_buff, @protocol_version==9)
else
dummy, @salt2 = a.unpack("a13a12")
@scramble_buff += @salt2
flag |= PROTO_AUTH41
data = Net::int4str(flag) + Net::int4str(@max_allowed_packet) +
([8] + Array.new(23, 0)).pack("c24") + (user||"")+"\0"+
scramble41(passwd, @scramble_buff)
end
if db and @server_capabilities & CLIENT_CONNECT_WITH_DB != 0
data << "\0" if @pre_411
data << db
@db = db.dup
end
write data
pkt = read
handle_auth_fallback(pkt, passwd)
ObjectSpace.define_finalizer(self, Mysql.finalizer(@net))
self
end
alias :connect :real_connect
def handle_auth_fallback(pkt, passwd)
# A packet like this means that we need to send an old-format password
if pkt.size == 1 and pkt[0] == 254 and
@server_capabilities & CLIENT_SECURE_CONNECTION != 0 then
data = scramble(passwd, @scramble_buff, @protocol_version == 9)
write data + "\0"
read
end
end
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="")
if @pre_411
data = user+"\0"+scramble(passwd, @scramble_buff, @protocol_version==9)+"\0"+db
else
data = user+"\0"+scramble41(passwd, @scramble_buff)+db
end
pkt = command COM_CHANGE_USER, data
handle_auth_fallback(pkt, passwd)
@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
if @pre_411
f = read_rows 6
else
f = read_rows 7
end
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
if @pre_411
fields = read_rows 5
else
fields = read_rows 7
end
@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
if data[0] == 254 and data.length == 1 ## EOF
return
elsif data[0] == 254 and data.length == 5
return
end
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::open(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)
if @pre_411
fields = read_rows(5)
else
fields = read_rows(7)
end
@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|
if @pre_411
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
else
catalog = f[0]
db = f[1]
table = f[2]
org_table = f[3]
name = f[4]
org_name = f[5]
length = f[6][2]+f[6][3]*256+f[6][4]*256*256
type = f[6][6]
flags = f[6][7]+f[6][8]*256
decimals = f[6][9]
def_value = ""
max_length = 0
end
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.slice(0,SCRAMBLE_LENGTH_323)
rnd = Random::new hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]
to = []
1.upto(SCRAMBLE_LENGTH_323) 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 scramble41(password, message)
return 0x00.chr if password.nil? or password.empty?
buf = [0x14]
s1 = Digest::SHA1.new(password).digest
s2 = Digest::SHA1.new(s1).digest
x = Digest::SHA1.new(message + s2).digest
(0..s1.length - 1).each {|i| buf.push(s1[i] ^ x[i])}
buf.pack("C*")
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(n)
@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(n)
@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[i].table+"."+@fields[i].name : @fields[i].name
hash[f] = row[i]
end
hash
end
def field_seek(n)
@current_field = n
end
def field_tell()
@current_field
end
def free()
@handle.skip_result
@handle = @fields = @data = nil
end
def num_fields()
@field_count
end
def num_rows()
@data ? @data.length : @row_count
end
def row_seek(n)
@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, :org_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
rescue
errno = Error::CR_SERVER_LOST
raise Error::new(errno, Error::err(errno))
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
rescue
errno = Error::CR_SERVER_LOST
raise Error::new(errno, Error::err(errno))
end
def close()
@sock.close
end
def Net::int2str(n)
[n].pack("v")
end
def Net::int3str(n)
[n%256, n>>8].pack("cv")
end
def Net::int4str(n)
[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 {
begin
net.clear
net.write Mysql::COM_QUIT.chr
net.close
rescue Error => error
# Swallow lost connection errors if connection is already closed.
raise unless error.errno == Error::CR_SERVER_LOST
end
}
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
|