File Coverage

blib/lib/MySQL/Packet.pm
Criterion Covered Total %
statement 16 174 9.2
branch 1 134 0.7
condition 1 14 7.1
subroutine 6 25 24.0
pod 18 19 94.7
total 42 366 11.4


line stmt bran cond sub pod time code
1             package MySQL::Packet;
2              
3             our $VERSION = qw(0.2007054);
4              
5             # todo: check all unpack calls for possibility of fatal exceptions
6              
7             =head1 NAME
8              
9             MySQL::Packet - encode and decode the MySQL binary protocol
10              
11             =head1 VERSION
12              
13             Version 0.2007054
14              
15             =head1 SYNOPSIS
16              
17             Sorry for the absurdly verbose synopsis. I don't have a proper example
18             script for you at the moment.
19              
20             use MySQL::Packet qw(:debug); # dumping packet contents etc.
21             use MySQL::Packet qw(:test :decode); # decoding subs
22             use MySQL::Packet qw(:encode); # encoding subs
23              
24             use MySQL::Packet qw(:COM :CLIENT :SERVER); # constants
25              
26             my $packet;
27             my $greeting;
28             my $result;
29             my $field_end;
30              
31             my $mysql_socket = whatever_i_do_to_connect();
32              
33             while (read $mysql_socket, $_, 1000, length) {
34             if (not $packet) {
35             my $_packet = {};
36             my $rc = mysql_decode_header $_packet;
37             if ($rc < 0) {
38             die 'bad header';
39             }
40             elsif ($rc > 0) {
41             $packet = $_packet;
42             redo;
43             }
44             }
45             elsif (not $greeting) {
46             my $rc = mysql_decode_greeting $_packet;
47             if ($rc < 0) {
48             die 'bad greeting';
49             }
50             elsif ($rc > 0) {
51             mysql_debug_packet $packet;
52             $greeting = $packet;
53             undef $packet;
54             send_client_auth();
55             redo;
56             }
57             }
58             elsif (not $result) {
59             my $rc = mysql_decode_result $packet;
60             if ($rc < 0) {
61             die 'bad result';
62             }
63             elsif ($rc > 0) {
64             mysql_debug_packet $packet;
65             if ($packet->{error}) {
66             die 'the server hates me';
67             }
68             elsif ($packet->{end}) {
69             die 'this should never happen';
70             }
71             else {
72             if ($packet->{field_count}) {
73             $result = $packet;
74             # fields and rows to come
75             }
76             elsif (not $packet->{server_status} & SERVER_MORE_RESULTS_EXISTS) {
77             # that's that..
78             send_some_query();
79             }
80             }
81             undef $packet;
82             redo;
83             }
84             }
85             elsif (not $field_end) {
86             my $rc = do {
87             (mysql_test_var $packet}) ? (mysql_decode_field $packet)
88             : (mysql_decode_result $packet)
89             };
90             if ($rc < 0) {
91             die 'bad field packet';
92             }
93             elsif ($rc > 0) {
94             mysql_debug_packet $packet;
95             if ($packet->{error}) {
96             die 'the server hates me';
97             }
98             elsif ($packet->{end}) {
99             $field_end = $packet;
100             }
101             else {
102             do_something_with_field_metadata($packet);
103             }
104             undef $packet;
105             redo;
106             }
107             }
108             else {
109             my $rc = do {
110             (mysql_test_var $packet ? (mysql_decode_row $packet)
111             : (mysql_decode_result $packet)
112             };
113             if ($rc < 0) {
114             die 'bad row packet';
115             }
116             elsif ($rc > 0) {
117             mysql_debug_packet $packet;
118             if ($packet->{error}) {
119             die 'the server hates me';
120             }
121             elsif ($packet->{end}) {
122             undef $result;
123             undef $field_end;
124             unless ($packet->{server_status} & SERVER_MORE_RESULTS_EXISTS) {
125             # that's that..
126             send_some_query();
127             }
128             }
129             else {
130             my @row = @{ $packet->{row} };
131             do_something_with_row_data(@row);
132             }
133             undef $packet;
134             redo;
135             }
136             }
137             }
138              
139             sub send_client_auth {
140             my $flags = CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_PROTOCOL_41 | CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION;
141             $flags |= CLIENT_CONNECT_WITH_DB if $i_want_to;
142             my $pw_crypt = mysql_crypt 'my_password', $greeting->{crypt_seed};
143             my $packet_body = mysql_encode_client_auth (
144             $flags, # $client_flags
145             0x01000000, # $max_packet_size
146             $greeting->{server_lang}, # $charset_no
147             'my_username', # $username
148             $pw_crypt, # $pw_crypt
149             'my_database', # $database
150             );
151             my $packet_head = mysql_encode_header $packet_body, 1;
152             print $mysql_socket $packet_head, $packet_body;
153             }
154              
155             sub send_some_query {
156             my $packet_body = mysql_encode_com_query 'SELECT * FROM foo';
157             my $packet_head = mysql_encode_header $packet_body;
158             print $mysql_socket $packet_head, $packet_body;
159             }
160              
161             =head1 DESCRIPTION
162              
163             This module exports various functions for encoding and decoding binary packets
164             pertinent to the MySQL client/server protocol. It also exports some useful
165             constants. It does NOT wrap an IO::Socket handle for you.
166              
167             This is ALPHA code. It currently groks only the new v4.1+ protocol. It
168             currently handles only authentication, the COM_QUERY and COM_QUIT commands,
169             and the associated server responses. In other words, just enough to send
170             plain SQL and get the results.
171              
172             For what it does, it seems to be quite stable, by my own yardstick.
173              
174             This module should eventually grow to support statement prepare and execute,
175             the pre-v4.1 protocol, compression, and so on.
176              
177             =cut
178              
179             BEGIN {
180             # i am not sure whether to prefer Digest::SHA or Digest::SHA1 here
181 1 50 33 1   22445 eval 'use Digest::SHA qw(sha1); 1' or
  1     1   1718  
  1         15661  
  1         107  
182             eval 'use Digest::SHA1 qw(sha1); 1' or die;
183             }
184              
185 1     1   11 use Exporter qw(import);
  1         3  
  1         284  
186              
187             our %EXPORT_TAGS = (
188             COM => [qw/
189             COM_SLEEP
190             COM_QUIT
191             COM_INIT_DB
192             COM_QUERY
193             COM_FIELD_LIST
194             COM_CREATE_DB
195             COM_DROP_DB
196             COM_REFRESH
197             COM_SHUTDOWN
198             COM_STATISTICS
199             COM_PROCESS_INFO
200             COM_CONNECT
201             COM_PROCESS_KILL
202             COM_DEBUG
203             COM_PING
204             COM_TIME
205             COM_DELAYED_INSERT
206             COM_CHANGE_USER
207             COM_BINLOG_DUMP
208             COM_TABLE_DUMP
209             COM_CONNECT_OUT
210             COM_REGISTER_SLAVE
211             COM_STMT_PREPARE
212             COM_STMT_EXECUTE
213             COM_STMT_SEND_LONG_DATA
214             COM_STMT_CLOSE
215             COM_STMT_RESET
216             COM_SET_OPTION
217             COM_STMT_FETCH
218             /],
219             CLIENT => [qw/
220             CLIENT_LONG_PASSWORD
221             CLIENT_FOUND_ROWS
222             CLIENT_LONG_FLAG
223             CLIENT_CONNECT_WITH_DB
224             CLIENT_NO_SCHEMA
225             CLIENT_COMPRESS
226             CLIENT_ODBC
227             CLIENT_LOCAL_FILES
228             CLIENT_IGNORE_SPACE
229             CLIENT_PROTOCOL_41
230             CLIENT_INTERACTIVE
231             CLIENT_SSL
232             CLIENT_IGNORE_SIGPIPE
233             CLIENT_TRANSACTIONS
234             CLIENT_RESERVED
235             CLIENT_SECURE_CONNECTION
236             CLIENT_MULTI_STATEMENTS
237             CLIENT_MULTI_RESULTS
238             /],
239             SERVER => [qw/
240             SERVER_STATUS_IN_TRANS
241             SERVER_STATUS_AUTOCOMMIT
242             SERVER_MORE_RESULTS_EXISTS
243             SERVER_QUERY_NO_GOOD_INDEX_USED
244             SERVER_QUERY_NO_INDEX_USED
245             SERVER_STATUS_CURSOR_EXISTS
246             SERVER_STATUS_LAST_ROW_SENT
247             SERVER_STATUS_DB_DROPPED
248             SERVER_STATUS_NO_BACKSLASH_ESCAPES
249             /],
250             debug => [qw/
251             mysql_debug_packet
252             /],
253             test => [qw/
254             mysql_test_var
255             mysql_test_end
256             mysql_test_error
257             /],
258             decode => [qw/
259             mysql_decode_header
260             mysql_decode_skip
261             mysql_decode_varnum
262             mysql_decode_varstr
263             mysql_decode_greeting
264             mysql_decode_result
265             mysql_decode_field
266             mysql_decode_row
267             /],
268             encode => [qw/
269             mysql_encode_header
270             mysql_encode_varnum
271             mysql_encode_varstr
272             mysql_encode_client_auth
273             mysql_encode_com_query
274             /],
275             crypt => [qw/
276             mysql_crypt
277             /],
278             );
279              
280             our @EXPORT_OK = map {@$_} values %EXPORT_TAGS;
281              
282             =head1 EXPORTABLE CONSTANTS
283              
284             =cut
285              
286             #### constants ####
287              
288             =head2 Commands (Tag :COM)
289              
290             COM_SLEEP
291             COM_QUIT
292             COM_INIT_DB
293             COM_QUERY
294             COM_FIELD_LIST
295             COM_CREATE_DB
296             COM_DROP_DB
297             COM_REFRESH
298             COM_SHUTDOWN
299             COM_STATISTICS
300             COM_PROCESS_INFO
301             COM_CONNECT
302             COM_PROCESS_KILL
303             COM_DEBUG
304             COM_PING
305             COM_TIME
306             COM_DELAYED_INSERT
307             COM_CHANGE_USER
308             COM_BINLOG_DUMP
309             COM_TABLE_DUMP
310             COM_CONNECT_OUT
311             COM_REGISTER_SLAVE
312             COM_STMT_PREPARE
313             COM_STMT_EXECUTE
314             COM_STMT_SEND_LONG_DATA
315             COM_STMT_CLOSE
316             COM_STMT_RESET
317             COM_SET_OPTION
318             COM_STMT_FETCH
319              
320             =cut
321              
322             use constant {
323 1         426 COM_SLEEP => 0x00,
324             COM_QUIT => 0x01,
325             COM_INIT_DB => 0x02,
326             COM_QUERY => 0x03,
327             COM_FIELD_LIST => 0x04,
328             COM_CREATE_DB => 0x05,
329             COM_DROP_DB => 0x06,
330             COM_REFRESH => 0x07,
331             COM_SHUTDOWN => 0x08,
332             COM_STATISTICS => 0x09,
333             COM_PROCESS_INFO => 0x0a,
334             COM_CONNECT => 0x0b,
335             COM_PROCESS_KILL => 0x0c,
336             COM_DEBUG => 0x0d,
337             COM_PING => 0x0e,
338             COM_TIME => 0x0f,
339             COM_DELAYED_INSERT => 0x10,
340             COM_CHANGE_USER => 0x11,
341             COM_BINLOG_DUMP => 0x12,
342             COM_TABLE_DUMP => 0x13,
343             COM_CONNECT_OUT => 0x14,
344             COM_REGISTER_SLAVE => 0x15,
345             COM_STMT_PREPARE => 0x16,
346             COM_STMT_EXECUTE => 0x17,
347             COM_STMT_SEND_LONG_DATA => 0x18,
348             COM_STMT_CLOSE => 0x19,
349             COM_STMT_RESET => 0x1a,
350             COM_SET_OPTION => 0x1b,
351             COM_STMT_FETCH => 0x1c,
352 1     1   6 };
  1         2  
353              
354             =head2 Client Flags / Server Capabilities (Tag :CLIENT)
355              
356             CLIENT_LONG_PASSWORD
357             CLIENT_FOUND_ROWS
358             CLIENT_LONG_FLAG
359             CLIENT_CONNECT_WITH_DB
360             CLIENT_NO_SCHEMA
361             CLIENT_COMPRESS
362             CLIENT_ODBC
363             CLIENT_LOCAL_FILES
364             CLIENT_IGNORE_SPACE
365             CLIENT_PROTOCOL_41
366             CLIENT_INTERACTIVE
367             CLIENT_SSL
368             CLIENT_IGNORE_SIGPIPE
369             CLIENT_TRANSACTIONS
370             CLIENT_RESERVED
371             CLIENT_SECURE_CONNECTION
372             CLIENT_MULTI_STATEMENTS
373             CLIENT_MULTI_RESULTS
374              
375             =cut
376              
377             use constant {
378 1         238 CLIENT_LONG_PASSWORD => 1,
379             CLIENT_FOUND_ROWS => 2,
380             CLIENT_LONG_FLAG => 4,
381             CLIENT_CONNECT_WITH_DB => 8,
382             CLIENT_NO_SCHEMA => 16,
383             CLIENT_COMPRESS => 32,
384             CLIENT_ODBC => 64,
385             CLIENT_LOCAL_FILES => 128,
386             CLIENT_IGNORE_SPACE => 256,
387             CLIENT_PROTOCOL_41 => 512,
388             CLIENT_INTERACTIVE => 1024,
389             CLIENT_SSL => 2048,
390             CLIENT_IGNORE_SIGPIPE => 4096,
391             CLIENT_TRANSACTIONS => 8192,
392             CLIENT_RESERVED => 16384,
393             CLIENT_SECURE_CONNECTION => 32768,
394             CLIENT_MULTI_STATEMENTS => 65536,
395             CLIENT_MULTI_RESULTS => 131072,
396 1     1   7 };
  1         2  
397              
398             =head2 Server Status Flags (Tag :SERVER)
399              
400             SERVER_STATUS_IN_TRANS
401             SERVER_STATUS_AUTOCOMMIT
402             SERVER_MORE_RESULTS_EXISTS
403             SERVER_QUERY_NO_GOOD_INDEX_USED
404             SERVER_QUERY_NO_INDEX_USED
405             SERVER_STATUS_CURSOR_EXISTS
406             SERVER_STATUS_LAST_ROW_SENT
407             SERVER_STATUS_DB_DROPPED
408             SERVER_STATUS_NO_BACKSLASH_ESCAPES
409              
410             =cut
411              
412             use constant {
413 1         7519 SERVER_STATUS_IN_TRANS => 1,
414             SERVER_STATUS_AUTOCOMMIT => 2,
415             SERVER_MORE_RESULTS_EXISTS => 8,
416             SERVER_QUERY_NO_GOOD_INDEX_USED => 16,
417             SERVER_QUERY_NO_INDEX_USED => 32,
418             SERVER_STATUS_CURSOR_EXISTS => 64,
419             SERVER_STATUS_LAST_ROW_SENT => 128,
420             SERVER_STATUS_DB_DROPPED => 256,
421             SERVER_STATUS_NO_BACKSLASH_ESCAPES => 512,
422 1     1   7 };
  1         1  
423              
424             =head1 EXPORTABLE FUNCTIONS
425              
426             =head2 Debugging (Tag :debug)
427              
428             =cut
429              
430             #### debug subs ####
431              
432             =over
433              
434             =item mysql_debug_packet \%packet
435              
436             =item mysql_debug_packet \%packet, $file_handle
437              
438             Dumps a textual representation of the packet to STDERR or the given handle.
439              
440             =cut
441              
442             sub mysql_debug_packet {
443 0     0 1   my $packet = $_[0];
444 0   0       my $stream = $_[1] || \*STDERR;
445 0           while (my ($k, $v) = each %$packet) {
446 0 0         if ($k eq 'row') {
    0          
    0          
447 0           $v = "@$v";
448             }
449             elsif ($k eq 'server_capa') {
450 0           $v = sprintf('%16b', $v) . ' = ' . join ' | ', grep { $v & eval } @{ $EXPORT_TAGS{CLIENT} };
  0            
  0            
451             }
452             elsif ($k eq 'crypt_seed') {
453 0           $v = unpack 'H*', $v;
454             }
455 0           print $stream "$k\t=>\t$v\n";
456             }
457 0           return;
458             }
459              
460             =back
461              
462             =head2 Packet Type Tests (Tag :test)
463              
464             These functions operate on $_ if no data argument is given. They must be used
465             only after L has succeeded.
466              
467             =cut
468              
469             #### packet testing subs ####
470              
471             =over
472              
473             =item $bool = mysql_test_var \%packet;
474              
475             =item $bool = mysql_test_var \%packet, $data
476              
477             Returns true if the data encodes a variable-length binary number or string.
478              
479             =cut
480              
481             sub mysql_test_var {
482 0 0   0 1   local $_ = $_[1] if exists $_[1];
483 0 0         length && do {
484 0           my $x = ord;
485 0 0 0       $x <= 0xfd ||
486             $x == 0xfe && $_[0]{packet_size} >= 9
487             };
488             }
489              
490             =item $bool = mysql_test_end \%packet
491              
492             =item $bool = mysql_test_end \%packet, $data
493              
494             Returns true if the data is an end packet (often called EOF packet).
495              
496             =cut
497              
498             sub mysql_test_end {
499 0 0   0 1   local $_ = $_[1] if exists $_[1];
500 0 0 0       length && ord == 0xfe && $_[0]{packet_size} < 9;
501             }
502              
503             =item $bool = mysql_test_error \%packet
504              
505             =item $bool = mysql_test_error \%packet, $data
506              
507             Returns true if the data is an error packet.
508              
509             =cut
510              
511             sub mysql_test_error {
512 0 0   0 1   local $_ = $_[1] if exists $_[1];
513 0 0         length && ord == 0xff;
514             }
515              
516             =back
517              
518             =head2 Decoding Packets (Tag :decode)
519              
520             These functions take either a hash reference (to be populated with packet
521             information), or a scalar (to receive a number or string). The optional
522             second argument is always the data to decode. If omitted, $_ is used instead,
523             and bytes are consumed from the beginning of $_ as it is processed.
524              
525             All except L return the number of bytes consumed, -1 if the
526             data is invalid, or 0 if processing cannot continue until there is more data
527             available. If the return is -1 there is no way to continue, and an unknown
528             number of bytes may have been consumed.
529              
530             =cut
531              
532             #### packet decoding subs ####
533              
534             =over
535              
536             =item $rc = mysql_decode_header \%packet
537              
538             =item $rc = mysql_decode_header \%packet, $data
539              
540             Populates %packet with header information. This always has to be done before
541             any other decoding subs, or any testing subs, are used.
542              
543             packet_size => size of packet body
544             packet_serial => packet serial number from 0 to 255
545              
546             =cut
547              
548             sub mysql_decode_header {
549 0 0   0 1   local $_ = $_[1] if exists $_[1];
550 0 0         return 0 unless 4 <= length;
551 0           my $header = unpack 'V', substr $_, 0, 4, '';
552 0           $_[0]{packet_size} = $header & 0xffffff;
553 0           $_[0]{packet_serial} = $header >> 24;
554 0           return 4;
555             }
556              
557             =item $rc = mysql_decode_skip \%packet
558              
559             =item $rc = mysql_decode_skip \%packet, $data
560              
561             If the number of available bytes is equal to or greater than the packet size,
562             consumes that many bytes and returns them. Otherwise, returns undef.
563              
564             =cut
565              
566             sub mysql_decode_skip {
567 0 0   0 1   local $_ = $_[1] if exists $_[1];
568 0 0         return undef unless $_[0]{packet_size} <= length;
569 0           substr $_, 0, $_[0]{packet_size}, '';
570             }
571              
572             =item $rc = mysql_decode_varnum $number
573              
574             =item $rc = mysql_decode_varnum $number, $data
575              
576             Consumes a variable-length binary number and stores it in $number. Note that
577             $number is NOT passed as a reference.
578              
579             =cut
580              
581             sub mysql_decode_varnum {
582 0 0   0 1   local $_ = $_[1] if exists $_[1];
583 0 0         return 0 unless length;
584 0           my $first = ord;
585 0 0         if ($first < 251) {
    0          
    0          
    0          
    0          
586 0           $_[0] = $first;
587 0           substr $_, 0, 1, '';
588 0           return 1;
589             }
590             elsif ($first == 251) {
591 0           $_[0] = undef;
592 0           substr $_, 0, 1, '';
593 0           return 1;
594             }
595             elsif ($first == 252) {
596 0 0         return 0 unless 3 <= length;
597 0           substr $_, 0, 1, '';
598 0           $_[0] = unpack 'v', substr $_, 0, 2, '';
599 0           return 3;
600             }
601             elsif ($first == 253) {
602 0 0         return 0 unless 5 <= length;
603 0           substr $_, 0, 1, '';
604 0           $_[0] = unpack 'V', substr $_, 0, 4, '';
605 0           return 5;
606             }
607             elsif ($first == 254) {
608 0 0         return 0 unless 9 <= length;
609 0           substr $_, 0, 1, '';
610 0           $_[0] = (unpack 'V', substr $_, 0, 4, '')
611             | (unpack 'V', substr $_, 0, 4, '') << 32;
612 0           return 9;
613             }
614             else {
615 0           return -1;
616             }
617             }
618              
619             =item $rc = mysql_decode_varstr $string
620              
621             =item $rc = mysql_decode_varstr $string, $data
622              
623             Consumes a variable-length string and stores it in $string. Note that $string
624             is NOT passed as a reference.
625              
626             =cut
627              
628             sub mysql_decode_varstr {
629 0 0   0 1   local $_ = $_[1] if exists $_[1];
630 0           my $length;
631 0           my $i = mysql_decode_varnum $length, $_;
632 0 0         if ($i <= 0) {
    0          
    0          
633 0           $i;
634             }
635             elsif (not defined $length) {
636 0           substr $_, 0, $i, '';
637 0           $_[0] = undef;
638 0           $i;
639             }
640             elsif ($i + $length <= length) {
641 0           substr $_, 0, $i, '';
642 0           $_[0] = substr $_, 0, $length, '';
643 0           $i + $length;
644             }
645             else {
646 0           0;
647             }
648             }
649              
650             =item $rc = mysql_decode_greeting \%packet
651              
652             =item $rc = mysql_decode_greeting \%packet, $data
653              
654             Consumes the greeting packet (also called handshake initialization packet)
655             sent by the server upon connection, and populates %packet. After this the
656             client authentication may be encoded and sent.
657              
658             protocol_version => equal to 10 for modern MySQL servers
659             server_version => e.g. "5.0.26-log"
660             thread_id => unique to each active client connection
661             crypt_seed => some random bytes for challenge/response auth
662             server_capa => flags the client may specify during auth
663             server_lang => server's charset number
664             server_status => server status flags
665              
666             =cut
667              
668             sub mysql_decode_greeting {
669 0 0   0 1   local $_ = $_[1] if exists $_[1];
670 0 0         return 0 unless $_[0]{packet_size} <= length;
671             # todo: older protocol doesn't include 2nd crypt_seed fragment..
672             (
673             $_[0]{protocol_version},
674             $_[0]{server_version},
675             $_[0]{thread_id},
676             my $crypt_seed1,
677             $_[0]{server_capa},
678             $_[0]{server_lang},
679             $_[0]{server_status},
680             my $crypt_seed2,
681 0           ) = eval {
682 0           unpack 'CZ*Va8xvCvx13a12x', substr $_, 0, $_[0]{packet_size}, ''
683             };
684 0 0         return -1 if $@; # believe it or not, unpack can be fatal..
685             # todo: investigate performance penalty of eval;
686             # this could be replaced with cautious logic
687 0           $_[0]{crypt_seed} = $crypt_seed1 . $crypt_seed2;
688 0           return $_[0]{packet_size};
689             }
690              
691             =item $rc = mysql_decode_result \%packet
692              
693             =item $rc = mysql_decode_result \%packet, $data
694              
695             Consumes a result packet and populates %packet. Handles OK packets, error
696             packets, end packets, and result-set header packets.
697              
698             Error Packet:
699              
700             error => 1
701             errno => MySQL's errno
702             message => description of error
703             sqlstate => some sort of official 5-digit code
704              
705             End Packet:
706              
707             end => 1
708             warning_count => a number
709             server_status => bitwise flags
710              
711             OK Packet:
712              
713             field_count => 0
714             affected_rows => a number
715             last_insert_id => a number
716             server_status => bitwise flags
717             warning_count => a number
718             message => some text
719              
720             Result Header Packet:
721              
722             field_count => a number greater than zero
723              
724             =cut
725              
726             sub mysql_decode_result {
727 0 0   0 1   local $_ = $_[1] if exists $_[1];
728 0           my $n = $_[0]{packet_size};
729 0 0         return 0 unless $n <= length;
730 0 0         return do {
731 0           my $type = ord;
732 0 0 0       if ($type == 0xff) {
    0          
    0          
733 0 0         return -1 unless 3 <= $n;
734 0           substr $_, 0, 1, '';
735 0           $_[0]{error} = 1;
736 0           $_[0]{errno} = unpack 'v', substr $_, 0, 2, '';
737 0           $_[0]{message} = substr $_, 0, $n - 3, '';
738 0 0         $_[0]{sqlstate} = ($_[0]{message} =~ s/^#// ? substr $_[0]{message}, 0, 5, '' : '');
739 0           $n;
740             }
741             elsif ($type == 0xfe && $n < 9) {
742 0 0         if ($n == 1) {
    0          
743 0           substr $_, 0, 1, '';
744 0           $_[0]{end} = 1;
745 0           $_[0]{warning_count} = 0;
746 0           $_[0]{server_status} = 0;
747 0           1;
748             }
749             elsif ($n == 5) {
750 0           substr $_, 0, 1, '';
751 0           $_[0]{end} = 1;
752 0           $_[0]{warning_count} = unpack 'v', substr $_, 0, 2, '';
753 0           $_[0]{server_status} = unpack 'v', substr $_, 0, 2, '';
754 0           5;
755             }
756             else {
757 0           return -1;
758             }
759             }
760             elsif ($type > 0) {
761 0           my $i = 0;
762 0           my $j;
763 0 0         0 >= ($j = mysql_decode_varnum $_[0]{field_count}) ? (return -1) : ($i += $j);
764 0 0         0 >= ($j = mysql_decode_varnum $_[0]{extra}) ? (return -1) : ($i += $j) if $i < $n;
    0          
765 0           $i;
766             }
767             else {
768 0           substr $_, 0, 1, '';
769 0           my $i = 1;
770 0           my $j;
771 0           $_[0]{field_count} = 0;
772 0 0         0 >= ($j = mysql_decode_varnum $_[0]{affected_rows}) ? (return -1) : ($i += $j);
773 0 0         0 >= ($j = mysql_decode_varnum $_[0]{last_insert_id}) ? (return -1) : ($i += $j);
774 0           $_[0]{server_status} = unpack 'v', substr $_, 0, 2, ''; $i += 2;
  0            
775             # todo: older protocol has no warning_count here
776 0           $_[0]{warning_count} = unpack 'v', substr $_, 0, 2, ''; $i += 2;
  0            
777 0 0         0 >= ($j = mysql_decode_varstr $_[0]{message}) ? (return -1) : ($i += $j) if $i < $n;
    0          
778 0           $i;
779             }
780             } == $n ? $n : -1;
781             }
782              
783             =item $rc = mysql_decode_field \%packet
784              
785             =item $rc = mysql_decode_field \%packet, $data
786              
787             Consumes a field packet and populates %packet.
788              
789             catalog => catalog name
790             db => database name
791             table => table name after aliasing
792             org_table => original table name
793             name => field name after aliasing
794             org_name => original field name
795             charset_no => field character set number
796             display_length => suggested field display width
797             field_type => a number from 0 to 255
798             flags => bitwise flags
799             scale => number of digits after decimal point
800              
801             =cut
802              
803             sub mysql_decode_field {
804 0 0   0 1   local $_ = $_[1] if exists $_[1];
805 0 0         return 0 unless $_[0]{packet_size} <= length;
806 0           my $i = 0;
807 0           my $j;
808             # todo: this is different in older protocol..
809 0 0         0 >= ($j = mysql_decode_varstr $_[0]{catalog}) ? (return -1) : ($i += $j);
810 0 0         0 >= ($j = mysql_decode_varstr $_[0]{db}) ? (return -1) : ($i += $j);
811 0 0         0 >= ($j = mysql_decode_varstr $_[0]{table}) ? (return -1) : ($i += $j);
812 0 0         0 >= ($j = mysql_decode_varstr $_[0]{org_table}) ? (return -1) : ($i += $j);
813 0 0         0 >= ($j = mysql_decode_varstr $_[0]{name}) ? (return -1) : ($i += $j);
814 0 0         0 >= ($j = mysql_decode_varstr $_[0]{org_name}) ? (return -1) : ($i += $j);
815 0           substr $_, 0, 1, ''; $i += 1;
  0            
816 0           $_[0]{charset_no} = unpack 'v', substr $_, 0, 2, ''; $i += 2;
  0            
817 0           $_[0]{display_length} = unpack 'V', substr $_, 0, 4, ''; $i += 4;
  0            
818 0           $_[0]{field_type} = ord substr $_, 0, 1, ''; $i += 1;
  0            
819 0           $_[0]{flags} = unpack 'v', substr $_, 0, 2, ''; $i += 2;
  0            
820 0           $_[0]{scale} = ord substr $_, 0, 1, ''; $i += 1;
  0            
821 0           substr $_, 0, 2, ''; $i += 2;
  0            
822             # when do we read "default" ?
823 0 0         $i == $_[0]{packet_size} ? $i : -1;
824             }
825              
826             =item $rc = mysql_decode_row \%packet
827              
828             =item $rc = mysql_decode_row \%packet, $data
829              
830             Consumes a row packet and populates %packet.
831              
832             row => ref to array of (stringified) values
833              
834             =cut
835              
836             sub mysql_decode_row {
837 0 0   0 1   local $_ = $_[1] if exists $_[1];
838 0 0         return 0 unless $_[0]{packet_size} <= length;
839 0           my ($n, $i, $j);
840 0           for ($n = 0, $i = 0; $i < $_[0]{packet_size}; $i += $j) {
841 0 0         return -1 if 0 >= ($j = mysql_decode_varstr $_[0]{row}[$n++]);
842             }
843 0 0         $i == $_[0]{packet_size} ? $i : -1;
844             }
845              
846             =back
847              
848             =head2 Encoding Packets (Tag :encode)
849              
850             These functions all return the encoded binary data.
851              
852             =cut
853              
854             #### packet encoding subs ####
855              
856             =over
857              
858             =item $header_data = mysql_encode_header $packet_data
859              
860             =item $header_data = mysql_encode_header $packet_data, $packet_serial
861              
862             Returns the header for the already encoded packet. The serial number defaults
863             to 0 which is fine except for the authentication packet, for which it must be
864             1.
865              
866             =cut
867              
868             sub mysql_encode_header {
869 0 0   0 1   pack 'V', length($_[0]) | (exists $_[1] ? $_[1] << 24 : 0);
870             }
871              
872             =item $data = mysql_encode_varnum $number
873              
874             Returns the variable-length binary encoding for $number.
875              
876             =cut
877              
878             sub mysql_encode_varnum {
879 0     0 1   my $num = $_[0];
880 0 0         $num <= 250 ? chr($num) :
    0          
    0          
881             $num <= 0xffff ? chr(252) . pack('v', $num) :
882             $num <= 0xffffffff ? chr(253) . pack('V', $num) :
883             chr(254) . pack('V', $num & 0xffffffff) . pack('V', $num >> 32);
884             }
885              
886             =item $data = mysql_encode_varnum $string
887              
888             Returns the variable-length binary encoding for $string.
889              
890             =cut
891              
892             sub mysql_encode_varstr {
893 0     0 0   map { mysql_encode_varnum(length) . $_ } @_;
  0            
894             }
895              
896             =item $data = mysql_encode_client_auth @args
897              
898             Returns the payload for an authentication packet where @args =
899             ($flags, $max_packet_size, $charset_no, $username, $crypt_pw, $database).
900             The $database is optional.
901              
902             =cut
903              
904             sub mysql_encode_client_auth {
905             my (
906 0     0 1   $flags,
907             $max_packet_size,
908             $charset_no,
909             $username,
910             $crypt_pw,
911             $database,
912             ) = @_;
913 0           my $packet = pack 'VVCx23Z*a*', (
914             $flags,
915             $max_packet_size,
916             $charset_no,
917             $username,
918             mysql_encode_varstr($crypt_pw),
919             );
920 0 0         $packet .= pack 'Z*', $database if $flags & CLIENT_CONNECT_WITH_DB;
921 0           return $packet;
922             }
923              
924             =item $data = mysql_encode_com_quit
925              
926             Encodes the QUIT command. Takes no arguments.
927              
928             =cut
929              
930             sub mysql_encode_com_quit {
931 0     0 1   chr(COM_QUIT);
932             }
933              
934             =item $data = mysql_encode_com_query @sql
935              
936             Encodes the QUERY command, using the concatenation of the arguments as the SQL
937             string.
938              
939             =cut
940              
941             sub mysql_encode_com_query {
942 0     0 1   chr(COM_QUERY) . join '', @_;
943             }
944              
945             =back
946              
947             =head2 Password Cryption (Tag :crypt)
948              
949             =cut
950              
951             #### mysql challenge/response crypt ####
952              
953             =over
954              
955             =item $crypt_pw = mysql_crypt $password, $crypt_seed
956              
957             Implements MySQL's crypt algorithm to crypt a plaintext $password using the
958             $crypt_seed from the greeting packet. Returns a binary string suitable for
959             passing to L. Requires either Digest::SHA
960             or Digest::SHA1.
961              
962             =cut
963              
964             sub mysql_crypt {
965 0     0 1   my ($pass, $salt) = @_;
966 0           my $crypt1 = sha1 $pass;
967 0           my $crypt2 = sha1 ($salt . sha1 $crypt1);
968 0           return $crypt1 ^ $crypt2;
969             }
970              
971             =back
972              
973             =cut
974              
975             1;
976              
977             __END__