File Coverage

blib/lib/AnyEvent/MySQL/Imp.pm
Criterion Covered Total %
statement 51 354 14.4
branch 0 176 0.0
condition n/a
subroutine 17 51 33.3
pod 24 24 100.0
total 92 605 15.2


line stmt bran cond sub pod time code
1             package AnyEvent::MySQL::Imp;
2              
3 1     1   6 use strict;
  1         1  
  1         23  
4 1     1   4 use warnings;
  1         2  
  1         65  
5              
6 1     1   464 use AE;
  1         5243  
  1         31  
7 1     1   554 use AnyEvent::Socket;
  1         21489  
  1         110  
8 1     1   824 use AnyEvent::Handle;
  1         5858  
  1         41  
9 1     1   490 use Digest::SHA1 qw(sha1);
  1         617  
  1         60  
10 1     1   7 use List::Util qw(reduce);
  1         2  
  1         75  
11 1     1   6 use Scalar::Util qw(dualvar);
  1         2  
  1         64  
12              
13             use constant {
14 1         150 DEV => 0,
15 1     1   5 };
  1         2  
16              
17             use constant {
18 1         319 CLIENT_LONG_PASSWORD => 1, # new more secure passwords +
19             CLIENT_FOUND_ROWS => 2, # Found instead of affected rows *
20             CLIENT_LONG_FLAG => 4, # Get all column flags * +
21             CLIENT_CONNECT_WITH_DB => 8, # One can specify db on connect +
22             CLIENT_NO_SCHEMA => 16, # Don't allow database.table.column
23             CLIENT_COMPRESS => 32, # Can use compression protocol *
24             CLIENT_ODBC => 64, # Odbc client
25             CLIENT_LOCAL_FILES => 128, # Can use LOAD DATA LOCAL *
26             CLIENT_IGNORE_SPACE => 256, # Ignore spaces before '(' *
27             CLIENT_PROTOCOL_41 => 512, # New 4.1 protocol +
28             CLIENT_INTERACTIVE => 1024, # This is an interactive client * +
29             CLIENT_SSL => 2048, # Switch to SSL after handshake *
30             CLIENT_IGNORE_SIGPIPE => 4096, # IGNORE sigpipes
31             CLIENT_TRANSACTIONS => 8192, # Client knows about transactions +
32             CLIENT_RESERVED => 16384, # Old flag for 4.1 protocol
33             CLIENT_SECURE_CONNECTION => 32768, # New 4.1 authentication * +
34             CLIENT_MULTI_STATEMENTS => 65536, # Enable/disable multi-stmt support * +
35             CLIENT_MULTI_RESULTS => 131072, # Enable/disable multi-results * +
36 1     1   6 };
  1         2  
37              
38             use constant {
39 1         249 COM_SLEEP => "\x00", # (none, this is an internal thread state)
40             COM_QUIT => "\x01", # mysql_close
41             COM_INIT_DB => "\x02", # mysql_select_db
42             COM_QUERY => "\x03", # mysql_real_query
43             COM_FIELD_LIST => "\x04", # mysql_list_fields
44             COM_CREATE_DB => "\x05", # mysql_create_db (deprecated)
45             COM_DROP_DB => "\x06", # mysql_drop_db (deprecated)
46             COM_REFRESH => "\x07", # mysql_refresh
47             COM_SHUTDOWN => "\x08", # mysql_shutdown
48             COM_STATISTICS => "\x09", # mysql_stat
49             COM_PROCESS_INFO => "\x0a", # mysql_list_processes
50             COM_CONNECT => "\x0b", # (none, this is an internal thread state)
51             COM_PROCESS_KILL => "\x0c", # mysql_kill
52             COM_DEBUG => "\x0d", # mysql_dump_debug_info
53             COM_PING => "\x0e", # mysql_ping
54             COM_TIME => "\x0f", # (none, this is an internal thread state)
55             COM_DELAYED_INSERT => "\x10", # (none, this is an internal thread state)
56             COM_CHANGE_USER => "\x11", # mysql_change_user
57             COM_BINLOG_DUMP => "\x12", # sent by the slave IO thread to request a binlog
58             COM_TABLE_DUMP => "\x13", # LOAD TABLE ... FROM MASTER (deprecated)
59             COM_CONNECT_OUT => "\x14", # (none, this is an internal thread state)
60             COM_REGISTER_SLAVE => "\x15", # sent by the slave to register with the master (optional)
61             COM_STMT_PREPARE => "\x16", # mysql_stmt_prepare
62             COM_STMT_EXECUTE => "\x17", # mysql_stmt_execute
63             COM_STMT_SEND_LONG_DATA => "\x18", # mysql_stmt_send_long_data
64             COM_STMT_CLOSE => "\x19", # mysql_stmt_close
65             COM_STMT_RESET => "\x1a", # mysql_stmt_reset
66             COM_SET_OPTION => "\x1b", # mysql_set_server_option
67             COM_STMT_FETCH => "\x1c", # mysql_stmt_fetch
68 1     1   7 };
  1         1  
69              
70             use constant {
71 1         250 MYSQL_TYPE_BIT => 16,
72             MYSQL_TYPE_BLOB => 252,
73             MYSQL_TYPE_DATE => 10,
74             MYSQL_TYPE_DATETIME => 12,
75             MYSQL_TYPE_DECIMAL => 0,
76             MYSQL_TYPE_DOUBLE => 5,
77             MYSQL_TYPE_ENUM => 247,
78             MYSQL_TYPE_FLOAT => 4,
79             MYSQL_TYPE_GEOMETRY => 255,
80             MYSQL_TYPE_INT24 => 9,
81             MYSQL_TYPE_LONG => 3,
82             MYSQL_TYPE_LONGLONG => 8,
83             MYSQL_TYPE_LONG_BLOB => 251,
84             MYSQL_TYPE_MEDIUM_BLOB => 250,
85             MYSQL_TYPE_NEWDATE => 14,
86             MYSQL_TYPE_NEWDECIMAL => 246,
87             MYSQL_TYPE_NULL => 6,
88             MYSQL_TYPE_SET => 248,
89             MYSQL_TYPE_SHORT => 2,
90             MYSQL_TYPE_STRING => 254,
91             MYSQL_TYPE_TIME => 11,
92             MYSQL_TYPE_TIMESTAMP => 7,
93             MYSQL_TYPE_TINY => 1,
94             MYSQL_TYPE_TINY_BLOB => 249,
95             MYSQL_TYPE_VARCHAR => 15,
96             MYSQL_TYPE_VAR_STRING => 253,
97             MYSQL_TYPE_YEAR => 13,
98 1     1   6 };
  1         2  
99              
100             use constant {
101 1         166 NOT_NULL_FLAG => 1, # Field can't be NULL
102             PRI_KEY_FLAG => 2, # Field is part of a primary key
103             UNIQUE_KEY_FLAG => 4, # Field is part of a unique key
104             MULTIPLE_KEY_FLAG => 8, # Field is part of a key
105             BLOB_FLAG => 16, # Field is a blob
106             UNSIGNED_FLAG => 32, # Field is unsigned
107             ZEROFILL_FLAG => 64, # Field is zerofill
108             BINARY_FLAG => 128, # Field is binary
109             ENUM_FLAG => 256, # Field is an enum
110             AUTO_INCREMENT_FLAG => 512, # Field is a autoincrement field
111             TIMESTAMP_FLAG => 1024, # Field is a timestamp
112             SET_FLAG => 2048, # Field is a set
113             NO_DEFAULT_VALUE_FLAG => 4096, # Field doesn't have default value
114             ON_UPDATE_NOW_FLAG => 8192, # Field is set to NOW on UPDATE
115             NUM_FLAG => 32768, # Field is num (for clients)
116             PART_KEY_FLAG => 16384, # Intern; Part of some key
117             GROUP_FLAG => 32768, # Intern: Group field
118             UNIQUE_FLAG => 65536, # Intern: Used by sql_yacc
119             BINCMP_FLAG => 131072, # Intern: Used by sql_yacc
120             GET_FIXED_FIELDS_FLAG => (1<<18), # Used to get fields in item tree
121             FIELD_IN_PART_FUNC_FLAG => (1<<19), # Field part of partition func
122             FIELD_IN_ADD_INDEX => (1<<20), # Intern: Field used in ADD INDEX
123             FIELD_IS_RENAMED => (1<<21), # Intern: Field is being renamed
124 1     1   5 };
  1         2  
125              
126             use constant {
127 1         764 RES_OK => 0,
128             RES_ERROR => 255,
129             RES_RESULT => 1,
130             RES_PREPARE => 2,
131 1     1   6 };
  1         2  
132              
133             =head2 $str = take_zstring($data(modified))
134             null terminated string
135             =cut
136             sub take_zstr {
137 0     0 1   $_[0] =~ s/(.*?)\x00//s;
138 0           return $1;
139             }
140              
141             =head2 $num = take_lcb($data(modifed))
142             length coded binary
143             =cut
144             sub take_lcb {
145 0     0 1   my $fb = substr($_[0], 0, 1, '');
146 0 0         if( $fb le "\xFA" ) { # 0-250
147 0           return ord($fb);
148             }
149 0 0         if( $fb eq "\xFB" ) { # 251
150 0           return undef;
151             }
152 0 0         if( $fb eq "\xFC" ) { # 252
153 0           return unpack('v', substr($_[0], 0, 2, ''));
154             }
155 0 0         if( $fb eq "\xFD" ) { # 253
156 0           return unpack('V', substr($_[0], 0, 3, '')."\x00");
157             }
158 0 0         if( $fb eq "\xFE" ) { # 254
159 0           return unpack('Q<', substr($_[0], 0, 8, ''));
160             }
161 0           return undef; # error
162             }
163              
164             =head2 $str = take_lcs($data(modified))
165             length coded string
166             =cut
167             sub take_lcs {
168 0     0 1   my $len = &take_lcb;
169 0 0         if( defined $len ) {
170 0           return substr($_[0], 0, $len, '');
171             }
172             else {
173 0           return undef;
174             }
175             }
176              
177             =head2 $num = take_num($data(modified), $len)
178             =cut
179             sub take_num {
180 0     0 1   return unpack('V', substr($_[0], 0, $_[1], '')."\x00\x00\x00");
181             }
182              
183             =head2 $str = take_str($data(modified), $len)
184             =cut
185             sub take_str {
186 0     0 1   return substr($_[0], 0, $_[1], '');
187             }
188              
189             =head2 () = take_filler($data(modified), $len)
190             =cut
191             sub take_filler {
192 0     0 1   substr($_[0], 0, $_[1], '');
193 0           return ();
194             }
195              
196             =head2 $cell = take_type($data(modified), $type, $length, $flag)
197             WARN: some MySQL data types are not implemented
198             =cut
199             sub take_type {
200 0 0   0 1   if( $_[1]==MYSQL_TYPE_TINY ) { # tinyint
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
201 0 0         if( $_[3] & UNSIGNED_FLAG ) {
202 0           return ord(substr($_[0], 0, 1, ''));
203             }
204             else {
205 0           return unpack('c', substr($_[0], 0, 1, ''));
206             }
207             } elsif( $_[1]==MYSQL_TYPE_SHORT ) { # smallint
208 0 0         if( $_[3] & UNSIGNED_FLAG ) {
209 0           return unpack('v', substr($_[0], 0, 2, ''));
210             }
211             else {
212 0           return unpack('s<', substr($_[0], 0, 2, ''));
213             }
214             } elsif( $_[1]==MYSQL_TYPE_INT24 ) { # mediumint
215 0 0         if( $_[3] & UNSIGNED_FLAG ) {
216 0           return unpack('V', substr($_[0], 0, 3, '')."\0");
217             }
218             else {
219 0           return unpack('l<', substr($_[0], 0, 3, '')."\0");
220             }
221             } elsif( $_[1]==MYSQL_TYPE_LONG ) { # int
222 0 0         if( $_[3] & UNSIGNED_FLAG ) {
223 0           return unpack('V', substr($_[0], 0, 4, ''));
224             }
225             else {
226 0           return unpack('l<', substr($_[0], 0, 4, ''));
227             }
228             } elsif( $_[1]==MYSQL_TYPE_LONGLONG ) { # bigint
229 0 0         if( $_[3] & UNSIGNED_FLAG ) {
230 0           return unpack('Q<', substr($_[0], 0, 8, ''));
231             }
232             else {
233 0           return unpack('q<', substr($_[0], 0, 8, ''));
234             }
235             } elsif( $_[1]==MYSQL_TYPE_FLOAT ) { # float
236 0           return unpack('f<', substr($_[0], 0, 4, ''));
237             } elsif( $_[1]==MYSQL_TYPE_DOUBLE ) { # double
238 0           return unpack('d<', substr($_[0], 0, 8, ''));
239             } elsif( $_[1]==MYSQL_TYPE_NEWDECIMAL ) { # decimal, numeric
240 0           warn "Not implement DECIMAL,NUMERIC yet";
241 0           return;
242             } elsif( $_[1]==MYSQL_TYPE_BIT ) { # bit(n)
243 0           warn "Not implement BIT yet";
244 0           return;
245             } elsif( $_[1]==MYSQL_TYPE_DATE ) { # date
246 0           warn "Not implement DATE yet";
247 0           return;
248             } elsif( $_[1]==MYSQL_TYPE_TIME ) { # time
249 0           warn "Not implement TIME yet";
250 0           return;
251             } elsif( $_[1]==MYSQL_TYPE_DATETIME ) { # datetime
252 0           warn "Not implement DATETIME yet";
253 0           return;
254             } elsif( $_[1]==MYSQL_TYPE_TIMESTAMP ) { # timestamp
255 0           warn "Not implement TIMESTAMP yet";
256 0           return;
257             } elsif( $_[1]==MYSQL_TYPE_YEAR ) { # year
258 0           return 1901+ord(substr($_[0], 0, 1, ''));
259             } elsif( $_[1]==MYSQL_TYPE_STRING ) { # char(n), binary(n), enum(), set()
260 0 0         if( $_[3] & ENUM_FLAG ) {
    0          
261 0           warn "Not implement ENUM yet";
262 0           return;
263             }
264             elsif( $_[3] & SET_FLAG ) {
265 0           warn "Not implement SET yet";
266 0           return;
267             }
268             else {
269 0           my $data = substr($_[0], 0, $_[2], '');
270 0           $data =~ s/ +$//;
271 0           return $data;
272             }
273             } elsif( $_[1]==[ MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_BLOB]) { # varchar(n), varbinary(n) | tinyblob, tinytext, blob, text, mediumblob, mediumtext, longblob, longtext
274 0           my $len;
275 0 0         if( $_[2]<=0xFF ) {
    0          
    0          
276 0           $len = ord(substr($_[0], 0, 1, ''));
277             }
278             elsif( $_[2]<=0xFFFF ) {
279 0           $len = unpack('v', substr($_[0], 0, 2, ''));
280             }
281             elsif( $_[2]<=0xFFFFFF ) {
282 0           $len = unpack('V', substr($_[0], 0, 3, '')."\0");
283             }
284             else {
285 0           $len = unpack('V', substr($_[0], 0, 4, ''));
286             }
287 0           return substr($_[0], 0, $len, '');
288             } else {
289 0           warn "Unsupported type: $_";
290 1     1   460 use Devel::StackTrace;
  1         4310  
  1         518  
291 0           print Devel::StackTrace->new->as_string;
292 0           return;
293             }
294             }
295              
296             =head2 put_type($data(modified), $cell, $type, $len, $flag)
297             =cut
298             sub put_type {
299 0 0   0 1   if( $_[2]==MYSQL_TYPE_TINY ) { # tinyint
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
300 0 0         if( $_[4] & UNSIGNED_FLAG ) {
301 0           $_[0] .= chr($_[1]);
302             }
303             else {
304 0           $_[0] .= pack('c', $_[1]);
305             }
306             } elsif( $_[2]==MYSQL_TYPE_SHORT ) { # smallint
307 0 0         $_[0] .= pack( $_[4] & UNSIGNED_FLAG ? 'v' : 's<' , $_[1]);
308             } elsif( $_[2]==MYSQL_TYPE_INT24 ) { # mediumint
309 0 0         $_[0] .= substr(pack( $_[4] & UNSIGNED_FLAG ? 'V' : 'l<' , $_[1]), 0, 3);
310             } elsif( $_[2]==MYSQL_TYPE_LONG ) { # int
311 0 0         $_[0] .= pack( $_[4] & UNSIGNED_FLAG ? 'V' : 'l<' , $_[1] );
312             } elsif( $_[2]==MYSQL_TYPE_LONGLONG ) { # bigint
313 0 0         $_[0] .= pack( $_[4] & UNSIGNED_FLAG ? 'Q<' : 'q<' , $_[1] );
314             } elsif( $_[2]==MYSQL_TYPE_FLOAT ) { # float
315 0           $_[0] .= pack('f<', $_[1]);
316             } elsif( $_[2]==MYSQL_TYPE_DOUBLE ) { # double
317 0           $_[0] .= pack('d<', $_[1]);
318             } elsif( $_[2]==MYSQL_TYPE_NEWDECIMAL ) { # decimal, numeric
319 0           warn "Not implement DECIMAL,NUMERIC yet";
320 0           return;
321             } elsif( $_[2]==MYSQL_TYPE_BIT ) { # bit(n)
322 0           warn "Not implement BIT yet";
323 0           return;
324             } elsif( $_[2]==MYSQL_TYPE_DATE ) { # date
325 0           warn "Not implement DATE yet";
326 0           return;
327             } elsif( $_[2]==MYSQL_TYPE_TIME ) { # time
328 0           warn "Not implement TIME yet";
329 0           return;
330             } elsif( $_[2]==MYSQL_TYPE_DATETIME ) { # datetime
331 0           warn "Not implement DATETIME yet";
332 0           return;
333             } elsif( $_[2]==MYSQL_TYPE_TIMESTAMP ) { # timestamp
334 0           warn "Not implement TIMESTAMP yet";
335 0           return;
336             } elsif( $_[2]==MYSQL_TYPE_YEAR ) { # year
337 0           $_[0] .= chr($_[1]-1901);
338             } elsif( $_[2]==MYSQL_TYPE_STRING ) { # char(n), binary(n), enum(), set()
339 0 0         if( $_[4] & ENUM_FLAG ) {
    0          
340 0           warn "Not implement ENUM yet";
341 0           return;
342             }
343             elsif( $_[4] & SET_FLAG ) {
344 0           warn "Not implement SET yet";
345 0           return;
346             }
347             else {
348 0 0         if( length($_[1]) >= $_[3] ) {
349 0           $_[0] .= substr($_[1], 0, $_[3]);
350             }
351             else {
352 0           $_[0] .= $_[1];
353 0           $_[0] .= ' ' x ($_[3] - length $_[1]);
354             }
355             }
356             } elsif( $_[2]==[ MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_BLOB]) { # varchar(n), varbinary(n) | tinyblob, tinytext, blob, text, mediumblob, mediumtext, longblob, longtext
357 0           my $len;
358 0 0         $_[1] = substr($_[1], 0, $_[3]) if( length($_[1]) > $_[3] );
359 0 0         if( $_[3]<=0xFF ) {
    0          
    0          
360 0           $_[0] .= chr(length($_[1]));
361             }
362             elsif( $_[3]<=0xFFFF ) {
363 0           $_[0] .= pack('v', length($_[1]));
364             }
365             elsif( $_[3]<=0xFFFFFF ) {
366 0           $_[0] .= substr(pack('V', length($_[1])), 0, 3);
367             }
368             else {
369 0           $_[0] .= pack('V', length($_[1]));
370             }
371 0           $_[0] .= $_[1];
372             } else {
373 0           warn "Unsupported type: $_[2]";
374 0           return;
375             }
376             }
377              
378             =head2 put_num($data(modified), $num, $len)
379             =cut
380             sub put_num {
381 0     0 1   $_[0] .= substr(pack('V', $_[1]), 0, $_[2]);
382             }
383              
384             =head2 put_str($data(modified), $str, $len)
385             =cut
386             sub put_str {
387 0     0 1   $_[0] .= substr($_[1].("\x00" x $_[2]), 0, $_[2]);
388             }
389              
390             =head2 put_zstr($data(modified), $str)
391             =cut
392             sub put_zstr {
393 1     1   7 no warnings 'uninitialized';
  1         3  
  1         840  
394 0     0 1   $_[0] .= $_[1];
395 0           $_[0] .= "\x00";
396             }
397              
398             =head2 put_lcb($data(modified), $num)
399             =cut
400             sub put_lcb {
401 0 0   0 1   if( $_[1] <= 250 ) {
    0          
    0          
    0          
402 0           $_[0] .= chr($_[1]);
403             }
404             elsif( !defined($_[1]) ) {
405 0           $_[0] .= "\xFB"; # 251
406             }
407             elsif( $_[1] <= 65535 ) {
408 0           $_[0] .= "\xFC"; # 252
409 0           $_[0] .= pack('v', $_[1]);
410             }
411             elsif( $_[1] <= 16777215 ) {
412 0           $_[0] .= "\xFD"; # 253
413 0           $_[0] .= substr(pack('V', $_[1]), 0, 3);
414             }
415             else {
416 0           $_[0] .= "\xFE"; # 254
417 0           $_[0] .= pack('Q<', $_[1]);
418             }
419             }
420              
421             =head2 put_lcs($data(modified), $str)
422             =cut
423             sub put_lcs {
424 0     0 1   put_lcb($_[0], length($_[1]));
425 0           $_[0] .= $_[1];
426             }
427              
428             =head2 ($affected_rows, $insert_id, $server_status, $warning_count, $message) | $is = parse_ok($data(modified))
429             =cut
430             sub parse_ok {
431 0 0   0 1   if( substr($_[0], 0, 1) eq "\x00" ) {
432 0 0         if( wantarray ) {
433 0           substr($_[0], 0, 1, '');
434             return (
435 0           take_lcb($_[0]),
436             take_lcb($_[0]),
437             take_num($_[0], 2),
438             take_num($_[0], 2),
439             $_[0],
440             );
441             }
442             else {
443 0           return 1;
444             }
445             }
446             else {
447 0           return;
448             }
449             }
450              
451             =head2 ($errno, $sqlstate, $message) = parse_error($data(modified))
452             =cut
453             sub parse_error {
454 0 0   0 1   if( substr($_[0], 0, 1) eq "\xFF" ) {
455 0 0         if( wantarray ) {
456 0           substr($_[0], 0, 1, '');
457             return (
458 0 0         take_num($_[0], 2),
459             ( substr($_[0], 0, 1) eq '#' ?
460             ( substr($_[0], 1, 5), substr($_[0], 6) ) :
461             ( '', $_[0] )
462             )
463             );
464             }
465             else {
466 0           return 1;
467             }
468             }
469             else {
470 0           return;
471             }
472             }
473              
474             ## ($field_count, $extra) = parse_result_set_header($data(modified))
475             #sub parse_result_set_header {
476             # if( $substr($_[0], 0, 1)
477             #}
478              
479             =head2 recv_packet($hd, $cb->($packet))
480             =cut
481             sub recv_packet {
482 0     0 1   my $cb = pop;
483 0           my($hd) = @_;
484 0 0         if( $hd ) {
485             $hd->push_read( chunk => 4, sub {
486 0     0     my $len = unpack("V", $_[1]);
487 0           my $num = $len >> 24;
488 0           $len &= 0xFFFFFF;
489 0           print "pack_len=$len, pack_num=$num\n" if DEV;
490             $_[0]->unshift_read( chunk => $len, sub {
491 0           $cb->($_[1]);
492 0           } );
493 0           } );
494             }
495             }
496              
497             =head2 skip_until_eof($hd, $cb->())
498             =cut
499             sub skip_until_eof {
500 0     0 1   my($hd, $cb) = @_;
501             recv_packet($hd, sub {
502 0 0   0     if( substr($_[0], 0, 1) eq "\xFE" ) {
503 0           $cb->();
504             }
505             else {
506 0           skip_until_eof($hd, $cb);
507             }
508 0           });
509             }
510              
511             =head2 send_packet($hd, $packet_num, $packet_frag1, $pack_frag2, ...)
512             =cut
513             sub send_packet {
514 0 0   0 1   return if !$_[0];
515 0           local $_[0] = $_[0];
516 0     0     my $len = reduce { $a + length($b) } 0, @_[2..$#_];
  0            
517 0           $_[0]->push_write(substr(pack('V', $len), 0, 3) . chr($_[1]) . join('', @_[2..$#_]));
518             }
519              
520             =head2 _recv_field($hd, \@field)
521             =cut
522             sub _recv_field {
523 0     0     warn "get field." if DEV;
524 0           my $field = $_[1];
525             recv_packet($_[0], sub {
526 0     0     warn "got field!" if DEV;
527 0           push @$field, [
528             take_lcs($_[0]), take_lcs($_[0]), take_lcs($_[0]),
529             take_lcs($_[0]), take_lcs($_[0]), take_lcs($_[0]),
530             take_filler($_[0], 1),
531             take_num($_[0], 2),
532             take_num($_[0], 4),
533             take_num($_[0], 1),
534             take_num($_[0], 2),
535             take_num($_[0], 1),
536             take_filler($_[0], 2),
537             take_lcb($_[0]),
538             ];
539 0           });
540             }
541              
542             =head2 recv_response($hd, %opt, $cb->(TYPE, data...))
543             RES_OK, $affected_rows, $insert_id, $server_status, $warning_count, $message
544             RES_ERROR, $errno, $sqlstate, $message
545             RES_RESULT, \@field, \@row
546             $field[$i] = [$catalog, $db, $table, $org_table, $name, $org_name, $charsetnr, $length, $type, $flags, $decimals, $default]
547             $row[$i] = [$field, $field, $field, ...]
548             RES_PREPARE, $stmt_id, \@param, \@column, $warning_count
549             $param[$i] = [$catalog, $db, $table, $org_table, $name, $org_name, $charsetnr, $length, $type, $flags, $decimals, $default]
550             $column[$i] = [$catalog, $db, $table, $org_table, $name, $org_name, $charsetnr, $length, $type, $flags, $decimals, $default]
551             opt:
552             prepare (set to truthy to recv prepare_ok)
553             =cut
554             sub recv_response {
555 0 0   0 1   my $cb = ref($_[-1]) eq 'CODE' ? pop : sub {};
        0      
556 0           my($hd, %opt) = @_;
557              
558 0           if( DEV ) {
559             my $cb0 = $cb;
560             $cb = sub {
561 1     1   556 use Data::Dumper;
  1         5475  
  1         1712  
562 0     0     warn "recv_response: ".Dumper(\@_);
563 0           &$cb0;
564             };
565             }
566              
567             recv_packet($hd, sub {
568 0     0     my $head = substr($_[0], 0, 1);
569 0 0         if( $head eq "\x00" ) { # OK
    0          
570 0           substr($_[0], 0, 1, '');
571 0 0         if( $opt{prepare} ) {
572 0           my $stmt_id = take_num($_[0], 4);
573 0           my $column_count = take_num($_[0], 2);
574 0           my $param_count = take_num($_[0], 2);
575 0           take_filler($_[0], 1);
576 0           my $warning_count = take_num($_[0], 2);
577 0           warn "stmt_id=$stmt_id, column_count=$column_count, param_count=$param_count, warning_count=$warning_count" if DEV;
578              
579 0           my(@param, @column);
580              
581             my $end_cv = AE::cv {
582 0           $cb->(RES_PREPARE, $stmt_id, \@param, \@column, $warning_count);
583 0           };
584              
585 0           $end_cv->begin;
586              
587 0 0         if( $param_count ) {
588 0           $end_cv->begin;
589 0           for(my $i=0; $i<$param_count; ++$i) {
590 0           _recv_field($hd, \@param);
591             }
592 0           recv_packet($hd, sub { $end_cv->end }); # EOF
  0            
593             }
594              
595 0 0         if( $column_count ) {
596 0           $end_cv->begin;
597 0           for(my $i=0; $i<$column_count; ++$i) {
598 0           _recv_field($hd, \@column);
599             }
600 0           recv_packet($hd, sub { $end_cv->end }); # EOF
  0            
601             }
602              
603 0           $end_cv->end;
604             }
605             else {
606 0           $cb->(
607             RES_OK,
608             take_lcb($_[0]),
609             take_lcb($_[0]),
610             take_num($_[0], 2),
611             take_num($_[0], 2),
612             $_[0],
613             );
614             }
615             }
616             elsif( $head eq "\xFF" ) { # Error
617 0           substr($_[0], 0, 1, '');
618 0 0         $cb->(
619             RES_ERROR,
620             take_num($_[0], 2),
621             ( substr($_[0], 0, 1) eq '#' ?
622             ( substr($_[0], 1, 5), substr($_[0], 6) ) : # ver 4.1
623             ( undef, $_[0] ) # ver 4.0
624             )
625             );
626             }
627             else { # Others (EOF shouldn't be here)
628 0           my $field_count = take_lcb($_[0]);
629 0 0         my $extra = $_[0] eq '' ? undef : take_lcb($_[0]);
630              
631 0           warn "field_count=$field_count" if DEV;
632              
633 0           my @field;
634 0           for(my $i=0; $i<$field_count; ++$i) {
635 0           _recv_field($hd, \@field);
636             }
637 0           recv_packet($hd, sub{ warn "got EOF" if DEV }); # EOF
  0            
638              
639 0           my @row;
640             my $fetch_row; $fetch_row = sub { # text format
641 0           warn "get row." if DEV;
642             recv_packet($hd, sub {
643 0 0         if( substr($_[0], 0, 1) eq "\xFE" ) { # EOF
644 0           warn "got EOF!" if DEV;
645 0           undef $fetch_row;
646 0           $cb->(
647             RES_RESULT,
648             \@field,
649             \@row,
650             );
651             }
652             else {
653 0           warn "got row!" if DEV;
654 0           my @cell;
655 0 0         if( $opt{execute} ) {
656 0           take_filler($_[0], 1);
657 0           my $null_bit_map = substr($_[0], 0, $field_count + 9 >> 3, '');
658 0           for(my $i=0; $i<$field_count; ++$i) {
659 0 0         if( vec($null_bit_map, 2+$i, 1) ) {
660 0           push @cell, undef;
661             }
662             else {
663 0           push @cell, take_type($_[0], $field[$i][8], $field[$i][7], $field[$i][9]);
664             }
665             }
666             }
667             else {
668 0           for(my $i=0; $i<$field_count; ++$i) {
669 0           push @cell, take_lcs($_[0]);
670             }
671             }
672 0           push @row, \@cell;
673 0           $fetch_row->();
674             }
675 0           });
676 0           };
677 0           $fetch_row->();
678             }
679 0           });
680             }
681              
682             =head2 do_auth($hd, $username, [$password, [$database,]] $cb->($success, $err_num_and_msg, $thread_id))
683             =cut
684             sub do_auth {
685 0 0   0 1   my $cb = ref($_[-1]) eq 'CODE' ? pop : sub {};
        0      
686 0           my($hd, $username, $password, $database) = @_;
687              
688             recv_packet($hd, sub {
689 0     0     if( DEV ) {
690             my $hex = $_[0];
691             $hex =~ s/(.)/sprintf"%02X ",ord$1/ges;
692             my $ascii = $_[0];
693             $ascii =~ s/([^\x20-\x7E])/./g;
694             warn $hex, $ascii;
695             }
696 0           my $proto_ver = take_num($_[0], 1); warn "proto_ver:$proto_ver" if DEV;
  0            
697 0           my $server_ver = take_zstr($_[0]); warn "server_ver:$server_ver" if DEV;
  0            
698 0           my $thread_id = take_num($_[0], 4); warn "thread_id:$thread_id" if DEV;
  0            
699 0           my $scramble_buff = take_str($_[0], 8).substr($_[0], 19, 12); warn "scramble_buff:$scramble_buff" if DEV;
  0            
700 0           my $filler = take_num($_[0], 1); warn "filler:$filler" if DEV;
  0            
701 0           my $server_cap = take_num($_[0], 2);
702 0           my $server_lang = take_num($_[0], 1); warn "server_lang:$server_lang" if DEV;
  0            
703 0           my $server_status = take_num($_[0], 2); warn "server_status:$server_status" if DEV;
  0            
704 0           $server_cap += take_num($_[0], 2) << 16;
705 0           if( DEV ) {
706             warn "server_cap:";
707             warn " CLIENT_LONG_PASSWORD" if( $server_cap & CLIENT_LONG_PASSWORD );
708             warn " CLIENT_FOUND_ROWS" if( $server_cap & CLIENT_FOUND_ROWS );
709             warn " CLIENT_LONG_FLAG" if( $server_cap & CLIENT_LONG_FLAG );
710             warn " CLIENT_CONNECT_WITH_DB" if( $server_cap & CLIENT_CONNECT_WITH_DB );
711             warn " CLIENT_NO_SCHEMA" if( $server_cap & CLIENT_NO_SCHEMA );
712             warn " CLIENT_COMPRESS" if( $server_cap & CLIENT_COMPRESS );
713             warn " CLIENT_ODBC" if( $server_cap & CLIENT_ODBC );
714             warn " CLIENT_LOCAL_FILES" if( $server_cap & CLIENT_LOCAL_FILES );
715             warn " CLIENT_IGNORE_SPACE" if( $server_cap & CLIENT_IGNORE_SPACE );
716             warn " CLIENT_PROTOCOL_41" if( $server_cap & CLIENT_PROTOCOL_41 );
717             warn " CLIENT_INTERACTIVE" if( $server_cap & CLIENT_INTERACTIVE );
718             warn " CLIENT_SSL" if( $server_cap & CLIENT_SSL );
719             warn " CLIENT_IGNORE_SIGPIPE" if( $server_cap & CLIENT_IGNORE_SIGPIPE );
720             warn " CLIENT_TRANSACTIONS" if( $server_cap & CLIENT_TRANSACTIONS );
721             warn " CLIENT_RESERVED" if( $server_cap & CLIENT_RESERVED );
722             warn " CLIENT_SECURE_CONNECTION" if( $server_cap & CLIENT_SECURE_CONNECTION );
723             warn " CLIENT_MULTI_STATEMENTS" if( $server_cap & CLIENT_MULTI_STATEMENTS );
724             warn " CLIENT_MULTI_RESULTS" if( $server_cap & CLIENT_MULTI_RESULTS );
725             }
726 0           my $scramble_len = take_num($_[0], 1); warn "scramble_len:$scramble_len" if DEV;
  0            
727              
728 0           my $packet = '';
729 0           put_num($packet, $server_cap & (
730             CLIENT_LONG_PASSWORD | # new more secure passwords
731             CLIENT_FOUND_ROWS | # Found instead of affected rows
732             CLIENT_LONG_FLAG | # Get all column flags
733             CLIENT_CONNECT_WITH_DB | # One can specify db on connect
734             # CLIENT_NO_SCHEMA | # Don't allow database.table.column
735             # CLIENT_COMPRESS | # Can use compression protocol
736             # CLIENT_ODBC | # Odbc client
737             # CLIENT_LOCAL_FILES | # Can use LOAD DATA LOCAL
738             # CLIENT_IGNORE_SPACE | # Ignore spaces before '('
739             CLIENT_PROTOCOL_41 | # New 4.1 protocol
740             # CLIENT_INTERACTIVE | # This is an interactive client
741             # CLIENT_SSL | # Switch to SSL after handshake
742             # CLIENT_IGNORE_SIGPIPE | # IGNORE sigpipes
743             CLIENT_TRANSACTIONS | # Client knows about transactions
744             # CLIENT_RESERVED | # Old flag for 4.1 protocol
745             CLIENT_SECURE_CONNECTION | # New 4.1 authentication
746             CLIENT_MULTI_STATEMENTS | # Enable/disable multi-stmt support
747             CLIENT_MULTI_RESULTS | # Enable/disable multi-results
748             0
749             ), 4); # client_flags
750 0           put_num($packet, 0x1000000, 4); # max_packet_size
751 0           put_num($packet, $server_lang, 1); # charset_number
752 0           $packet .= "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; # filler
753 0           put_zstr($packet, $username); # username
754 0 0         if( $password eq '' ) {
755 0           put_lcs($packet, '');
756             }
757             else {
758 0           my $stage1_hash = sha1($password);
759 0           put_lcs($packet, sha1($scramble_buff.sha1($stage1_hash)) ^ $stage1_hash); # scramble buff
760             }
761 0           put_zstr($packet, $database); # database name
762              
763 0           send_packet($hd, 1, $packet);
764             recv_packet($hd, sub {
765 0 0         if( parse_ok($_[0]) ) {
766 0           $cb->(1, undef, $thread_id);
767             }
768             else {
769 0           my($errno, $sqlstate, $message) = parse_error($_[0]);
770 0           warn "$errno [$sqlstate] $message" if DEV;
771 0           $cb->(0, dualvar($errno, $message));
772             }
773 0           });
774 0           });
775             }
776              
777             =head2 do_reset_stmt($hd, $stmt_id)
778             =cut
779             sub do_reset_stmt {
780 0     0 1   my $packet = '';
781 0           put_num($packet, $_[1], 4);
782 0           send_packet($_[0], 0, COM_STMT_RESET, $packet);
783             }
784              
785             =head2 do_long_data_packet($hd, $stmt_id, $param_num, $type, $data, $len, $flag, $packet_num)
786             =cut
787             sub do_long_data_packet {
788 0     0 1   my $packet = '';
789 0           put_num($packet, $_[1], 4);
790 0           put_num($packet, $_[2], 2);
791 0           put_num($packet, $_[3], 2);
792 0           put_type($packet, $_[4], $_[3], $_[5], $_[6]);
793 0           send_packet($_[0], $_[7], COM_STMT_SEND_LONG_DATA, $packet);
794             }
795              
796             =head2 do_execute($hd, $stmt_id, $null_bit_map, $packet_num)
797             =cut
798             sub do_execute {
799 0     0 1   my $packet = '';
800 0           put_num($packet, $_[1], 4);
801 0           $packet .= "\0\1\0\0\0";
802 0           $packet .= $_[2];
803 0           $packet .= "\0";
804 0           send_packet($_[0], $_[3], COM_STMT_EXECUTE, $packet);
805             }
806              
807             =head2 do_execute_param($hd, $stmt_id, \@param, \@param_config)
808             =cut
809             sub do_execute_param {
810 0 0   0 1   my $null_bit_map = pack('b*', join '', map { defined($_) ? '0' : '1' } @{$_[2]});
  0            
  0            
811 0           my $packet = '';
812 0           put_num($packet, $_[1], 4);
813 0           $packet .= "\0\1\0\0\0";
814 0           $packet .= $null_bit_map;
815 0           $packet .= "\1";
816 0           for(my $i=0; $i<@{$_[2]}; ++$i) {
  0            
817             #put_num($packet, $_[3][$i][8], 2);
818 0           put_num($packet, MYSQL_TYPE_BLOB, 2);
819             }
820 0           for(my $i=0; $i<@{$_[2]}; ++$i) {
  0            
821 0 0         if( defined($_[2][$i]) ) {
822             #put_type($packet, $_[2][$i], $_[3][$i][8], $_[3][$i][7], $_[3][$i][9]);
823 0           put_type($packet, $_[2][$i], MYSQL_TYPE_BLOB, length($_[2][$i]), $_[3][$i][9]);
824             }
825             }
826 0           send_packet($_[0], 0, COM_STMT_EXECUTE, $packet);
827             }
828              
829             1;