os/graphics/graphicstest/uibench/scripts/mysql.pm
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
# Copyright (C) 2002 Hiroyuki OYAMA. Japan. All rights reserved.
sl@0
     2
# This program is free software: you can redistribute it and/or modify
sl@0
     3
# it under the terms of the GNU General Public License as published by
sl@0
     4
# the Free Software Foundation, either version 2 of the License, or
sl@0
     5
# (at your option) any later version.
sl@0
     6
#
sl@0
     7
# This program is distributed in the hope that it will be useful,
sl@0
     8
# but WITHOUT ANY WARRANTY; without even the implied warranty of
sl@0
     9
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
sl@0
    10
# GNU General Public License for more details.
sl@0
    11
#
sl@0
    12
# You should have received a copy of the GNU General Public License
sl@0
    13
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
sl@0
    14
sl@0
    15
package Net::MySQL;
sl@0
    16
sl@0
    17
use 5.004;
sl@0
    18
use IO::Socket;
sl@0
    19
use Carp;
sl@0
    20
use vars qw($VERSION $DEBUG);
sl@0
    21
use strict;
sl@0
    22
$VERSION = '0.09';
sl@0
    23
sl@0
    24
use constant COMMAND_SLEEP          => "\x00";
sl@0
    25
use constant COMMAND_QUIT           => "\x01";
sl@0
    26
use constant COMMAND_INIT_DB        => "\x02";
sl@0
    27
use constant COMMAND_QUERY          => "\x03";
sl@0
    28
use constant COMMAND_FIELD_LIST     => "\x04";
sl@0
    29
use constant COMMAND_CREATE_DB      => "\x05";
sl@0
    30
use constant COMMAND_DROP_DB        => "\x06";
sl@0
    31
use constant COMMAND_REFRESH        => "\x07";
sl@0
    32
use constant COMMAND_SHUTDOWN       => "\x08";
sl@0
    33
use constant COMMAND_STATISTICS     => "\x09";
sl@0
    34
use constant COMMAND_PROCESS_INFO   => "\x0A";
sl@0
    35
use constant COMMAND_CONNECT        => "\x0B";
sl@0
    36
use constant COMMAND_PROCESS_KILL   => "\x0C";
sl@0
    37
use constant COMMAND_DEBUG          => "\x0D";
sl@0
    38
use constant COMMAND_PING           => "\x0E";
sl@0
    39
use constant COMMAND_TIME           => "\x0F";
sl@0
    40
use constant COMMAND_DELAYED_INSERT => "\x10";
sl@0
    41
use constant COMMAND_CHANGE_USER    => "\x11";
sl@0
    42
use constant COMMAND_BINLOG_DUMP    => "\x12";
sl@0
    43
use constant COMMAND_TABLE_DUMP     => "\x13";
sl@0
    44
use constant COMMAND_CONNECT_OUT    => "\x14";
sl@0
    45
sl@0
    46
use constant DEFAULT_PORT_NUMBER => 3306;
sl@0
    47
use constant BUFFER_LENGTH       => 1460;
sl@0
    48
use constant DEFAULT_UNIX_SOCKET => '/tmp/mysql.sock';
sl@0
    49
sl@0
    50
sl@0
    51
sub new
sl@0
    52
{
sl@0
    53
	my $class = shift;
sl@0
    54
	my %args = @_;
sl@0
    55
sl@0
    56
	my $self = bless {
sl@0
    57
		hostname   => $args{hostname},
sl@0
    58
		unixsocket => $args{unixsocket} || DEFAULT_UNIX_SOCKET,
sl@0
    59
		port       => $args{port}       || DEFAULT_PORT_NUMBER,
sl@0
    60
		database   => $args{database},
sl@0
    61
		user       => $args{user},
sl@0
    62
		password   => $args{password},
sl@0
    63
		timeout    => $args{timeout}  || 60,
sl@0
    64
		'socket'   => undef,
sl@0
    65
		salt                 => '',
sl@0
    66
		protocol_version     => undef,
sl@0
    67
		client_capabilities  => 0,
sl@0
    68
		affected_rows_length => 0,
sl@0
    69
	}, $class;
sl@0
    70
	$self->debug($args{debug});
sl@0
    71
	$self->_initialize;
sl@0
    72
	return $self;
sl@0
    73
}
sl@0
    74
sl@0
    75
sl@0
    76
sub query
sl@0
    77
{
sl@0
    78
	my $self = shift;
sl@0
    79
	my $sql = join '', @_;
sl@0
    80
	my $mysql = $self->{socket};
sl@0
    81
sl@0
    82
	return $self->_execute_command(COMMAND_QUERY, $sql);
sl@0
    83
}
sl@0
    84
sl@0
    85
sl@0
    86
sub create_database
sl@0
    87
{
sl@0
    88
	my $self = shift;
sl@0
    89
	my $db_name = shift;
sl@0
    90
	my $mysql = $self->{socket};
sl@0
    91
sl@0
    92
	return $self->_execute_command(COMMAND_CREATE_DB, $db_name);
sl@0
    93
}
sl@0
    94
sl@0
    95
sl@0
    96
sub drop_database
sl@0
    97
{
sl@0
    98
	my $self = shift;
sl@0
    99
	my $db_name = shift;
sl@0
   100
	my $mysql = $self->{socket};
sl@0
   101
sl@0
   102
	return $self->_execute_command(COMMAND_DROP_DB, $db_name);
sl@0
   103
}
sl@0
   104
sl@0
   105
sl@0
   106
sub close
sl@0
   107
{
sl@0
   108
	my $self = shift;
sl@0
   109
	my $mysql = $self->{socket};
sl@0
   110
	return unless $mysql->can('send');
sl@0
   111
sl@0
   112
	my $quit_message =
sl@0
   113
		chr(length(COMMAND_QUIT)). "\x00\x00\x00". COMMAND_QUIT;
sl@0
   114
	$mysql->send($quit_message, 0);
sl@0
   115
	$self->_dump_packet($quit_message) if Net::MySQL->debug;
sl@0
   116
	$mysql->close;
sl@0
   117
}
sl@0
   118
sl@0
   119
sl@0
   120
sub get_affected_rows_length
sl@0
   121
{
sl@0
   122
	my $self = shift;
sl@0
   123
	$self->{affected_rows_length};
sl@0
   124
}
sl@0
   125
sl@0
   126
sl@0
   127
sub get_insert_id
sl@0
   128
{
sl@0
   129
	my $self = shift;
sl@0
   130
	$self->{insert_id};
sl@0
   131
}
sl@0
   132
sl@0
   133
sl@0
   134
sub create_record_iterator
sl@0
   135
{
sl@0
   136
	my $self = shift;
sl@0
   137
	return undef unless $self->has_selected_record;
sl@0
   138
sl@0
   139
	my $record = Net::MySQL::RecordIterator->new(
sl@0
   140
		$self->{selected_record}
sl@0
   141
	);
sl@0
   142
	$self->{selected_record} = undef;
sl@0
   143
	$record->parse;
sl@0
   144
	return $record;
sl@0
   145
}
sl@0
   146
sl@0
   147
sl@0
   148
sub has_selected_record
sl@0
   149
{
sl@0
   150
	my $self = shift;
sl@0
   151
	$self->{selected_record} ? 1 : undef;
sl@0
   152
}
sl@0
   153
sl@0
   154
sl@0
   155
sub is_error
sl@0
   156
{
sl@0
   157
	my $self = shift;
sl@0
   158
	$self->{error_code} ? 1 : undef;
sl@0
   159
}
sl@0
   160
sl@0
   161
sl@0
   162
sub get_error_code
sl@0
   163
{
sl@0
   164
	my $self = shift;
sl@0
   165
	$self->{error_code};
sl@0
   166
}
sl@0
   167
sl@0
   168
sl@0
   169
sub get_error_message
sl@0
   170
{
sl@0
   171
	my $self = shift;
sl@0
   172
	$self->{server_message};
sl@0
   173
}
sl@0
   174
sl@0
   175
sl@0
   176
sub debug
sl@0
   177
{
sl@0
   178
	my $class = shift;
sl@0
   179
	$DEBUG = shift if @_;
sl@0
   180
	$DEBUG;
sl@0
   181
}
sl@0
   182
sl@0
   183
sl@0
   184
sub _connect
sl@0
   185
{
sl@0
   186
	my $self = shift;
sl@0
   187
sl@0
   188
	my $mysql;
sl@0
   189
	if ($self->{hostname}) {
sl@0
   190
		printf "Use INET Socket: %s %d/tcp\n", $self->{hostname}, $self->{port}
sl@0
   191
			if $self->debug;
sl@0
   192
		$mysql = IO::Socket::INET->new(
sl@0
   193
			PeerAddr => $self->{hostname},
sl@0
   194
			PeerPort => $self->{port},
sl@0
   195
			Proto    => 'tcp',
sl@0
   196
			Timeout  => $self->{timeout} || 60,
sl@0
   197
		) or croak "Couldn't connect to $self->{hostname}:$self->{port}/tcp: $@";
sl@0
   198
	}
sl@0
   199
	else {
sl@0
   200
		printf "Use UNIX Socket: %s\n", $self->{unixsocket} if $self->debug;
sl@0
   201
		$mysql = IO::Socket::UNIX->new(
sl@0
   202
			Type => SOCK_STREAM,
sl@0
   203
			Peer => $self->{unixsocket},
sl@0
   204
		) or croak "Couldn't connect to $self->{unixsocket}: $@";
sl@0
   205
	}
sl@0
   206
	$mysql->autoflush(1);
sl@0
   207
	$self->{socket} = $mysql;
sl@0
   208
}
sl@0
   209
sl@0
   210
sl@0
   211
sub _get_server_information
sl@0
   212
{
sl@0
   213
	my $self = shift;
sl@0
   214
	my $mysql = $self->{socket};
sl@0
   215
sl@0
   216
	my $message;
sl@0
   217
	$mysql->recv($message, BUFFER_LENGTH, 0);
sl@0
   218
	$self->_dump_packet($message)
sl@0
   219
		if Net::MySQL->debug;
sl@0
   220
	my $i = 0;
sl@0
   221
	my $packet_length = ord substr $message, $i, 1;
sl@0
   222
	$i += 4;
sl@0
   223
	$self->{protocol_version} = ord substr $message, $i, 1;
sl@0
   224
	printf "Protocol Version: %d\n", $self->{protocol_version}
sl@0
   225
		if Net::MySQL->debug;
sl@0
   226
	if ($self->{protocol_version} == 10) {
sl@0
   227
		$self->{client_capabilities} = 1;
sl@0
   228
	}
sl@0
   229
sl@0
   230
	++$i;
sl@0
   231
	my $string_end = index($message, "\0", $i) - $i;
sl@0
   232
	$self->{server_version} = substr $message, $i, $string_end;
sl@0
   233
	printf "Server Version: %s\n", $self->{server_version}
sl@0
   234
		if Net::MySQL->debug;
sl@0
   235
sl@0
   236
	$i += $string_end + 1;
sl@0
   237
	$self->{server_thread_id} = unpack 'v', substr $message, $i, 2;
sl@0
   238
	$i += 4;
sl@0
   239
	$self->{salt} = substr $message, $i, 8;
sl@0
   240
	#
sl@0
   241
	$i += 8+1;
sl@0
   242
	if (length $message >= $i + 1) {
sl@0
   243
		$i += 1;
sl@0
   244
	}
sl@0
   245
	if (length $message >= $i + 18) {
sl@0
   246
		# get server_language
sl@0
   247
		# get server_status
sl@0
   248
	}
sl@0
   249
	$i += 18 - 1;
sl@0
   250
	if (length $message >= $i + 12 - 1) {
sl@0
   251
		$self->{salt} .= substr $message, $i, 12;
sl@0
   252
	}
sl@0
   253
	printf "Salt: %s\n", $self->{salt} if Net::MySQL->debug;
sl@0
   254
sl@0
   255
}
sl@0
   256
sl@0
   257
sl@0
   258
sub _request_authentication
sl@0
   259
{
sl@0
   260
	my $self = shift;
sl@0
   261
	my $mysql = $self->{socket};
sl@0
   262
	$self->_send_login_message();
sl@0
   263
sl@0
   264
	my $auth_result;
sl@0
   265
	$mysql->recv($auth_result, BUFFER_LENGTH, 0);
sl@0
   266
	$self->_dump_packet($auth_result) if Net::MySQL->debug;
sl@0
   267
	if ($self->_is_error($auth_result)) {
sl@0
   268
		$mysql->close;
sl@0
   269
		if (length $auth_result < 7) {
sl@0
   270
			croak "Timeout of authentication";
sl@0
   271
		}
sl@0
   272
		croak substr $auth_result, 7;
sl@0
   273
	}
sl@0
   274
	print "connect database\n" if Net::MySQL->debug;
sl@0
   275
}
sl@0
   276
sl@0
   277
sl@0
   278
sub _send_login_message
sl@0
   279
{
sl@0
   280
	my $self = shift;
sl@0
   281
	my $mysql = $self->{socket};
sl@0
   282
	my $body = "\0\0\x01\x0d\xa6\03\0\0\0\0\x01".
sl@0
   283
		"\x21\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".
sl@0
   284
		 join "\0",
sl@0
   285
		$self->{user},
sl@0
   286
		"\x14".
sl@0
   287
		Net::MySQL::Password->scramble(
sl@0
   288
			$self->{password}, $self->{salt}, $self->{client_capabilities}
sl@0
   289
		);
sl@0
   290
	$body .= $self->{database};
sl@0
   291
	$body .= "\0";
sl@0
   292
	my $login_message = chr(length($body)-3). $body;
sl@0
   293
	$mysql->send($login_message, 0);
sl@0
   294
	$self->_dump_packet($login_message) if Net::MySQL->debug;
sl@0
   295
}
sl@0
   296
sl@0
   297
sl@0
   298
sl@0
   299
sub _execute_command
sl@0
   300
{
sl@0
   301
	my $self = shift;
sl@0
   302
	my $command = shift;
sl@0
   303
	my $sql = shift;
sl@0
   304
	my $mysql = $self->{socket};
sl@0
   305
sl@0
   306
	my $message = pack('V', length($sql) + 1). $command. $sql;
sl@0
   307
	$mysql->send($message, 0);
sl@0
   308
	$self->_dump_packet($message) if Net::MySQL->debug;
sl@0
   309
sl@0
   310
	my $result;
sl@0
   311
	$mysql->recv($result, BUFFER_LENGTH, 0);
sl@0
   312
	$self->_dump_packet($result) if Net::MySQL->debug;
sl@0
   313
	$self->_reset_status;
sl@0
   314
sl@0
   315
	if ($self->_is_error($result)) {
sl@0
   316
		return $self->_set_error_by_packet($result);
sl@0
   317
	}
sl@0
   318
	elsif ($self->_is_select_query_result($result)) {
sl@0
   319
		return $self->_get_record_by_server($result);
sl@0
   320
	}
sl@0
   321
	elsif ($self->_is_update_query_result($result)){
sl@0
   322
		return $self->_get_affected_rows_information_by_packet($result);
sl@0
   323
	}
sl@0
   324
	else {
sl@0
   325
		croak 'Unknown Result: '. $self->_get_result_length($result). 'byte';
sl@0
   326
	}
sl@0
   327
}
sl@0
   328
sl@0
   329
sl@0
   330
sub _initialize
sl@0
   331
{
sl@0
   332
	my $self = shift;
sl@0
   333
	$self->_connect;
sl@0
   334
	$self->_get_server_information;
sl@0
   335
	$self->_request_authentication;
sl@0
   336
}
sl@0
   337
sl@0
   338
sl@0
   339
sub _set_error_by_packet
sl@0
   340
{
sl@0
   341
	my $self = shift;
sl@0
   342
	my $packet = shift;
sl@0
   343
sl@0
   344
	my $error_message = $self->_get_server_message($packet);
sl@0
   345
	$self->{server_message} = $error_message;
sl@0
   346
	$self->{error_code}     = $self->_get_error_code($packet);
sl@0
   347
	return undef;
sl@0
   348
}
sl@0
   349
sl@0
   350
sl@0
   351
sub _get_record_by_server
sl@0
   352
{
sl@0
   353
	my $self = shift;
sl@0
   354
	my $packet = shift;
sl@0
   355
	my $mysql = $self->{socket};
sl@0
   356
	$self->_get_column_length($packet);
sl@0
   357
	while ($self->_has_next_packet($packet)) {
sl@0
   358
		my $next_result;
sl@0
   359
		$mysql->recv($next_result, BUFFER_LENGTH, 0);
sl@0
   360
		$packet .= $next_result;
sl@0
   361
		$self->_dump_packet($next_result) if Net::MySQL->debug;
sl@0
   362
	}
sl@0
   363
	$self->{selected_record} = $packet;
sl@0
   364
}
sl@0
   365
sl@0
   366
sl@0
   367
sub _get_affected_rows_information_by_packet
sl@0
   368
{
sl@0
   369
	my $self = shift;
sl@0
   370
	my $packet = shift;
sl@0
   371
sl@0
   372
	$self->{affected_rows_length} = $self->_get_affected_rows_length($packet);
sl@0
   373
	$self->{insert_id} = $self->_get_insert_id($packet);
sl@0
   374
	$self->{server_message} = $self->_get_server_message($packet);
sl@0
   375
	return $self->{affected_rows_length};
sl@0
   376
}
sl@0
   377
sl@0
   378
sl@0
   379
sub _is_error
sl@0
   380
{
sl@0
   381
	my $self = shift;
sl@0
   382
	my $packet = shift;
sl@0
   383
	return 1 if length $packet < 4;
sl@0
   384
	ord(substr $packet, 4) == 255;
sl@0
   385
}
sl@0
   386
sl@0
   387
sl@0
   388
sub _is_select_query_result
sl@0
   389
{
sl@0
   390
	my $self = shift;
sl@0
   391
	my $packet = shift;
sl@0
   392
	return undef if $self->_is_error($packet);
sl@0
   393
	ord(substr $packet, 4) >= 1;
sl@0
   394
}
sl@0
   395
sl@0
   396
sl@0
   397
sub _is_update_query_result
sl@0
   398
{
sl@0
   399
	my $self = shift;
sl@0
   400
	my $packet = shift;
sl@0
   401
	return undef if $self->_is_error($packet);
sl@0
   402
	ord(substr $packet, 4) == 0;
sl@0
   403
}
sl@0
   404
sl@0
   405
sl@0
   406
sub _get_result_length
sl@0
   407
{
sl@0
   408
	my $self = shift;
sl@0
   409
	my $packet = shift;
sl@0
   410
	ord(substr $packet, 0, 1)
sl@0
   411
}
sl@0
   412
sl@0
   413
sl@0
   414
sub _get_column_length
sl@0
   415
{
sl@0
   416
	my $self = shift;
sl@0
   417
	my $packet = shift;
sl@0
   418
	ord(substr $packet, 4);
sl@0
   419
}
sl@0
   420
sl@0
   421
sl@0
   422
sub _get_affected_rows_length
sl@0
   423
{
sl@0
   424
	my $self = shift;
sl@0
   425
	my $packet = shift;
sl@0
   426
	my $pos = 5;
sl@0
   427
	return Net::MySQL::Util::get_field_length($packet, \$pos);
sl@0
   428
}
sl@0
   429
sl@0
   430
sl@0
   431
sub _get_insert_id
sl@0
   432
{
sl@0
   433
	my $self = shift;
sl@0
   434
	my $packet = shift;
sl@0
   435
	return ord(substr $packet, 6, 1) if ord(substr $packet, 6, 1) != 0xfc;
sl@0
   436
	unpack 'v', substr $packet, 7, 2;
sl@0
   437
}
sl@0
   438
sl@0
   439
sl@0
   440
sub _get_server_message
sl@0
   441
{
sl@0
   442
	my $self = shift;
sl@0
   443
	my $packet = shift;
sl@0
   444
	return '' if length $packet < 7;
sl@0
   445
	substr $packet, 7;
sl@0
   446
}
sl@0
   447
sl@0
   448
sl@0
   449
sub _get_error_code
sl@0
   450
{
sl@0
   451
	my $self = shift;
sl@0
   452
	my $packet = shift;
sl@0
   453
	$self->_is_error($packet)
sl@0
   454
		or croak "_get_error_code(): Is not error packet";
sl@0
   455
	unpack 'v', substr $packet, 5, 2;
sl@0
   456
}
sl@0
   457
sl@0
   458
sl@0
   459
sub _reset_status
sl@0
   460
{
sl@0
   461
	my $self = shift;
sl@0
   462
	$self->{insert_id}       = 0;
sl@0
   463
	$self->{server_message}  = '';
sl@0
   464
	$self->{error_code}      = undef;
sl@0
   465
	$self->{selected_record} = undef;
sl@0
   466
}
sl@0
   467
sl@0
   468
sl@0
   469
sub _has_next_packet
sl@0
   470
{
sl@0
   471
	my $self = shift;
sl@0
   472
	#substr($_[0], -1) ne "\xfe";
sl@0
   473
	return substr($_[0], -5) ne "\xfe\0\0\x22\x00";
sl@0
   474
}
sl@0
   475
sl@0
   476
sl@0
   477
sub _dump_packet {
sl@0
   478
    my $self = shift;
sl@0
   479
    my $packet = shift;
sl@0
   480
    my ($method_name) = (caller(1))[3];
sl@0
   481
    my $str = sprintf "%s():\n", $method_name;
sl@0
   482
    while ($packet =~ /(.{1,16})/sg) {
sl@0
   483
        my $line = $1;
sl@0
   484
        $str .= join ' ', map {sprintf '%02X', ord $_} split //, $line;
sl@0
   485
        $str .= '   ' x (16 - length $line);
sl@0
   486
        $str .= '  ';
sl@0
   487
        $str .= join '', map {
sl@0
   488
            sprintf '%s', (/[\w\d\*\,\?\%\=\'\;\(\)\.-]/) ? $_ : '.'
sl@0
   489
        } split //, $line;
sl@0
   490
        $str .= "\n"; 
sl@0
   491
    }
sl@0
   492
    print $str;
sl@0
   493
}
sl@0
   494
sl@0
   495
sl@0
   496
sl@0
   497
package Net::MySQL::RecordIterator;
sl@0
   498
use strict;
sl@0
   499
sl@0
   500
use constant NULL_COLUMN           => 251;
sl@0
   501
use constant UNSIGNED_CHAR_COLUMN  => 251;
sl@0
   502
use constant UNSIGNED_SHORT_COLUMN => 252;
sl@0
   503
use constant UNSIGNED_INT24_COLUMN => 253;
sl@0
   504
use constant UNSIGNED_INT32_COLUMN => 254;
sl@0
   505
use constant UNSIGNED_CHAR_LENGTH  => 1;
sl@0
   506
use constant UNSIGNED_SHORT_LENGTH => 2;
sl@0
   507
use constant UNSIGNED_INT24_LENGTH => 3;
sl@0
   508
use constant UNSIGNED_INT32_LENGTH => 4;
sl@0
   509
use constant UNSIGNED_INT32_PAD_LENGTH => 4;
sl@0
   510
sl@0
   511
sl@0
   512
sub new
sl@0
   513
{
sl@0
   514
	my $class = shift;
sl@0
   515
	my $packet = shift;
sl@0
   516
	bless {
sl@0
   517
		packet   => $packet,
sl@0
   518
		position => 0,
sl@0
   519
		column   => [],
sl@0
   520
	}, $class;
sl@0
   521
}
sl@0
   522
sl@0
   523
sl@0
   524
sub parse
sl@0
   525
{
sl@0
   526
	my $self = shift;
sl@0
   527
	$self->_get_column_length;
sl@0
   528
	$self->_get_column_name;
sl@0
   529
}
sl@0
   530
sl@0
   531
sl@0
   532
sub each
sl@0
   533
{
sl@0
   534
	my $self = shift;
sl@0
   535
	my @result;
sl@0
   536
	return undef if $self->is_end_of_packet;
sl@0
   537
sl@0
   538
	for (1..$self->{column_length}) {
sl@0
   539
		push @result, $self->_get_string_and_seek_position;
sl@0
   540
	}
sl@0
   541
	$self->{position} += 4;
sl@0
   542
sl@0
   543
	return \@result;
sl@0
   544
}
sl@0
   545
sl@0
   546
sl@0
   547
sub is_end_of_packet
sl@0
   548
{
sl@0
   549
	my $self = shift;
sl@0
   550
	return substr($self->{packet}, $self->{position}, 1) eq "\xFE";
sl@0
   551
}
sl@0
   552
sl@0
   553
sl@0
   554
sub get_field_length
sl@0
   555
{
sl@0
   556
	my $self = shift;
sl@0
   557
	$self->{column_length};
sl@0
   558
}
sl@0
   559
sl@0
   560
sl@0
   561
sub get_field_names
sl@0
   562
{
sl@0
   563
	my $self = shift;
sl@0
   564
	map { $_->{column} } @{$self->{column}};
sl@0
   565
}
sl@0
   566
sl@0
   567
sl@0
   568
sub _get_column_length
sl@0
   569
{
sl@0
   570
	my $self = shift;
sl@0
   571
	$self->{position} += 4;
sl@0
   572
	$self->{column_length} = ord substr $self->{packet}, $self->{position}, 1;
sl@0
   573
	$self->{position} += 5;
sl@0
   574
	printf "Column Length: %d\n", $self->{column_length}
sl@0
   575
		if Net::MySQL->debug;
sl@0
   576
}
sl@0
   577
sl@0
   578
sl@0
   579
sub _get_column_name
sl@0
   580
{
sl@0
   581
	my $self = shift;
sl@0
   582
sl@0
   583
	for my $i (1.. $self->{column_length}) {
sl@0
   584
		$self->_get_string_and_seek_position;
sl@0
   585
		$self->_get_string_and_seek_position;
sl@0
   586
		my $table = $self->_get_string_and_seek_position;
sl@0
   587
		$self->_get_string_and_seek_position;
sl@0
   588
		my $column = $self->_get_string_and_seek_position;
sl@0
   589
		$self->_get_string_and_seek_position;
sl@0
   590
		push @{$self->{column}}, {
sl@0
   591
			table  => $table,
sl@0
   592
			column => $column,
sl@0
   593
		};
sl@0
   594
		$self->_get_string_and_seek_position;
sl@0
   595
		$self->{position} += 4;
sl@0
   596
	}
sl@0
   597
	$self->{position} += 9;
sl@0
   598
	printf "Column name: '%s'\n",
sl@0
   599
		join ", ", map { $_->{column} } @{$self->{column}}
sl@0
   600
			if Net::MySQL->debug;
sl@0
   601
}
sl@0
   602
sl@0
   603
sl@0
   604
sub _get_string_and_seek_position
sl@0
   605
{
sl@0
   606
	my $self = shift;
sl@0
   607
sl@0
   608
	my $length = $self->_get_field_length();
sl@0
   609
sl@0
   610
	return undef unless defined $length;
sl@0
   611
sl@0
   612
	my $string = substr $self->{packet}, $self->{position}, $length;
sl@0
   613
	$self->{position} += $length;
sl@0
   614
	return $string;
sl@0
   615
}
sl@0
   616
sl@0
   617
sl@0
   618
sub _get_field_length
sl@0
   619
{
sl@0
   620
	my $self = shift;
sl@0
   621
	return Net::MySQL::Util::get_field_length($self->{packet}, \$self->{position});
sl@0
   622
}
sl@0
   623
sl@0
   624
sl@0
   625
package Net::MySQL::Util;
sl@0
   626
use strict;
sl@0
   627
sl@0
   628
use constant NULL_COLUMN           => 251;
sl@0
   629
use constant UNSIGNED_CHAR_COLUMN  => 251;
sl@0
   630
use constant UNSIGNED_SHORT_COLUMN => 252;
sl@0
   631
use constant UNSIGNED_INT24_COLUMN => 253;
sl@0
   632
use constant UNSIGNED_INT32_COLUMN => 254;
sl@0
   633
use constant UNSIGNED_CHAR_LENGTH  => 1;
sl@0
   634
use constant UNSIGNED_SHORT_LENGTH => 2;
sl@0
   635
use constant UNSIGNED_INT24_LENGTH => 3;
sl@0
   636
use constant UNSIGNED_INT32_LENGTH => 4;
sl@0
   637
use constant UNSIGNED_INT32_PAD_LENGTH => 4;
sl@0
   638
sl@0
   639
sl@0
   640
sub get_field_length
sl@0
   641
{
sl@0
   642
	my $packet = shift;
sl@0
   643
	my $pos = shift;
sl@0
   644
sl@0
   645
	my $head = ord substr(
sl@0
   646
		$packet,
sl@0
   647
		$$pos,
sl@0
   648
		UNSIGNED_CHAR_LENGTH
sl@0
   649
	);
sl@0
   650
	$$pos += UNSIGNED_CHAR_LENGTH;
sl@0
   651
sl@0
   652
	return undef if $head == NULL_COLUMN;
sl@0
   653
	if ($head < UNSIGNED_CHAR_COLUMN) {
sl@0
   654
		return $head;
sl@0
   655
	}
sl@0
   656
	elsif ($head == UNSIGNED_SHORT_COLUMN) {
sl@0
   657
		my $length = unpack 'v', substr(
sl@0
   658
			$packet,
sl@0
   659
			$$pos,
sl@0
   660
			UNSIGNED_SHORT_LENGTH
sl@0
   661
		);
sl@0
   662
		$$pos += UNSIGNED_SHORT_LENGTH;
sl@0
   663
		return $length;
sl@0
   664
	}
sl@0
   665
	elsif ($head == UNSIGNED_INT24_COLUMN) {
sl@0
   666
		my $int24 = substr(
sl@0
   667
			$packet, $$pos,
sl@0
   668
			UNSIGNED_INT24_LENGTH
sl@0
   669
		);
sl@0
   670
		my $length = unpack('C', substr($int24, 0, 1))
sl@0
   671
		          + (unpack('C', substr($int24, 1, 1)) << 8)
sl@0
   672
			  + (unpack('C', substr($int24, 2, 1)) << 16);
sl@0
   673
		$$pos += UNSIGNED_INT24_LENGTH;
sl@0
   674
		return $length;
sl@0
   675
	}
sl@0
   676
	else {
sl@0
   677
		my $int32 = substr(
sl@0
   678
			$packet, $$pos,
sl@0
   679
			UNSIGNED_INT32_LENGTH
sl@0
   680
		);
sl@0
   681
		my $length = unpack('C', substr($int32, 0, 1))
sl@0
   682
		          + (unpack('C', substr($int32, 1, 1)) << 8)
sl@0
   683
			  + (unpack('C', substr($int32, 2, 1)) << 16)
sl@0
   684
			  + (unpack('C', substr($int32, 3, 1)) << 24);
sl@0
   685
		$$pos += UNSIGNED_INT32_LENGTH;
sl@0
   686
		$$pos += UNSIGNED_INT32_PAD_LENGTH;
sl@0
   687
		return $length;
sl@0
   688
	}
sl@0
   689
}
sl@0
   690
sl@0
   691
sl@0
   692
sl@0
   693
package Net::MySQL::Password;
sl@0
   694
use strict;
sl@0
   695
use Digest::SHA1;
sl@0
   696
sl@0
   697
sub scramble {
sl@0
   698
	my $class = shift;
sl@0
   699
	my $password = shift;
sl@0
   700
	my $hash_seed = shift;
sl@0
   701
	return '' unless $password;
sl@0
   702
	return '' if length $password == 0;
sl@0
   703
	return _make_scrambled_password($hash_seed, $password);
sl@0
   704
}
sl@0
   705
sl@0
   706
sl@0
   707
sub _make_scrambled_password {
sl@0
   708
	my $message = shift;
sl@0
   709
	my $password = shift;
sl@0
   710
sl@0
   711
	my $ctx = Digest::SHA1->new;
sl@0
   712
	$ctx->reset;
sl@0
   713
	$ctx->add($password);
sl@0
   714
	my $stage1 = $ctx->digest;
sl@0
   715
sl@0
   716
	$ctx->reset;
sl@0
   717
	$ctx->add($stage1);
sl@0
   718
	my $stage2 = $ctx->digest;
sl@0
   719
sl@0
   720
	$ctx->reset;
sl@0
   721
	$ctx->add($message);
sl@0
   722
	$ctx->add($stage2);
sl@0
   723
	my $result = $ctx->digest;
sl@0
   724
	return _my_crypt($result, $stage1);
sl@0
   725
}
sl@0
   726
sl@0
   727
sub _my_crypt {
sl@0
   728
	my $s1 = shift;
sl@0
   729
	my $s2 = shift;
sl@0
   730
	my $l = length($s1) - 1;
sl@0
   731
	my $result = '';
sl@0
   732
	for my $i (0..$l) {
sl@0
   733
		$result .= pack 'C', (unpack('C', substr($s1, $i, 1)) ^ unpack('C', substr($s2, $i, 1)));
sl@0
   734
	}
sl@0
   735
	return $result;
sl@0
   736
}
sl@0
   737
sl@0
   738
package Net::MySQL::Password32;
sl@0
   739
use strict;
sl@0
   740
sl@0
   741
sub scramble
sl@0
   742
{
sl@0
   743
	my $class = shift;
sl@0
   744
	my $password = shift;
sl@0
   745
	my $hash_seed = shift;
sl@0
   746
	my $client_capabilities = shift;
sl@0
   747
sl@0
   748
	return '' unless $password;
sl@0
   749
	return '' if length $password == 0;
sl@0
   750
sl@0
   751
	my $hsl = length $hash_seed;
sl@0
   752
	my @out;
sl@0
   753
	my @hash_pass = _get_hash($password);
sl@0
   754
	my @hash_mess = _get_hash($hash_seed);
sl@0
   755
sl@0
   756
	my ($max_value, $seed, $seed2);
sl@0
   757
	my ($dRes, $dSeed, $dMax);
sl@0
   758
	if ($client_capabilities < 1) {
sl@0
   759
		$max_value = 0x01FFFFFF;
sl@0
   760
		$seed = _xor_by_long($hash_pass[0], $hash_mess[0]) % $max_value;
sl@0
   761
		$seed2 = int($seed / 2);
sl@0
   762
	} else {
sl@0
   763
		$max_value= 0x3FFFFFFF;
sl@0
   764
		$seed  = _xor_by_long($hash_pass[0], $hash_mess[0]) % $max_value;
sl@0
   765
		$seed2 = _xor_by_long($hash_pass[1], $hash_mess[1]) % $max_value;
sl@0
   766
	}
sl@0
   767
	$dMax = $max_value;
sl@0
   768
sl@0
   769
	for (my $i=0; $i < $hsl; $i++) {
sl@0
   770
		$seed  = int(($seed * 3 + $seed2) % $max_value);
sl@0
   771
		$seed2 = int(($seed + $seed2 + 33) % $max_value);
sl@0
   772
		$dSeed = $seed;
sl@0
   773
		$dRes = $dSeed / $dMax;
sl@0
   774
		push @out, int($dRes * 31) + 64;
sl@0
   775
	}
sl@0
   776
sl@0
   777
	if ($client_capabilities == 1) {
sl@0
   778
		# Make it harder to break
sl@0
   779
		$seed  = ($seed * 3 + $seed2  ) % $max_value;
sl@0
   780
		$seed2 = ($seed + $seed2 + 33 ) % $max_value;
sl@0
   781
		$dSeed = $seed;
sl@0
   782
sl@0
   783
		$dRes = $dSeed / $dMax;
sl@0
   784
		my $e = int($dRes * 31);
sl@0
   785
		for (my $i=0; $i < $hsl ; $i++) {
sl@0
   786
			$out[$i] ^= $e;
sl@0
   787
		}
sl@0
   788
	}
sl@0
   789
	return join '', map { chr $_ } @out;
sl@0
   790
}
sl@0
   791
sl@0
   792
sl@0
   793
sub _get_hash
sl@0
   794
{
sl@0
   795
	my $password = shift;
sl@0
   796
sl@0
   797
	my $nr = 1345345333;
sl@0
   798
	my $add = 7; 
sl@0
   799
	my $nr2 = 0x12345671;
sl@0
   800
	my $tmp;
sl@0
   801
	my $pwlen = length $password;
sl@0
   802
	my $c;
sl@0
   803
sl@0
   804
	for (my $i=0; $i < $pwlen; $i++) {
sl@0
   805
		my $c = substr $password, $i, 1;
sl@0
   806
		next if $c eq ' ' || $c eq "\t";
sl@0
   807
		my $tmp = ord $c;
sl@0
   808
		my $value = ((_and_by_char($nr, 63) + $add) * $tmp) + $nr * 256;
sl@0
   809
		$nr = _xor_by_long($nr, $value);
sl@0
   810
		$nr2 += _xor_by_long(($nr2 * 256), $nr);
sl@0
   811
		$add += $tmp;
sl@0
   812
	}
sl@0
   813
	return (_and_by_long($nr, 0x7fffffff), _and_by_long($nr2, 0x7fffffff));
sl@0
   814
}
sl@0
   815
sl@0
   816
sl@0
   817
sub _and_by_char
sl@0
   818
{
sl@0
   819
	my $source = shift;
sl@0
   820
	my $mask   = shift;
sl@0
   821
sl@0
   822
	return $source & $mask;
sl@0
   823
}
sl@0
   824
sl@0
   825
sl@0
   826
sub _and_by_long
sl@0
   827
{
sl@0
   828
	my $source = shift;
sl@0
   829
	my $mask = shift || 0xFFFFFFFF;
sl@0
   830
sl@0
   831
	return _cut_off_to_long($source) & _cut_off_to_long($mask);
sl@0
   832
}
sl@0
   833
sl@0
   834
sl@0
   835
sub _xor_by_long
sl@0
   836
{
sl@0
   837
	my $source = shift;
sl@0
   838
	my $mask = shift || 0;
sl@0
   839
sl@0
   840
	return _cut_off_to_long($source) ^ _cut_off_to_long($mask);
sl@0
   841
}
sl@0
   842
sl@0
   843
sl@0
   844
sub _cut_off_to_long
sl@0
   845
{
sl@0
   846
	my $source = shift;
sl@0
   847
sl@0
   848
	if ($] >= 5.006) {
sl@0
   849
		$source = $source % (0xFFFFFFFF + 1) if $source > 0xFFFFFFFF;
sl@0
   850
		return $source;
sl@0
   851
	}
sl@0
   852
	while ($source > 0xFFFFFFFF) {
sl@0
   853
		$source -= 0xFFFFFFFF + 1;
sl@0
   854
	}
sl@0
   855
	return $source;
sl@0
   856
}
sl@0
   857
sl@0
   858
sl@0
   859
1;
sl@0
   860
__END__
sl@0
   861
sl@0
   862
=head1 NAME
sl@0
   863
sl@0
   864
Net::MySQL - Pure Perl MySQL network protocol interface.
sl@0
   865
sl@0
   866
=head1 SYNOPSIS
sl@0
   867
sl@0
   868
  use Net::MySQL;
sl@0
   869
  
sl@0
   870
  my $mysql = Net::MySQL->new(
sl@0
   871
      # hostname => 'mysql.example.jp',   # Default use UNIX socket
sl@0
   872
      database => 'your_database_name',
sl@0
   873
      user     => 'user',
sl@0
   874
      password => 'password'
sl@0
   875
  );
sl@0
   876
sl@0
   877
  # INSERT example
sl@0
   878
  $mysql->query(q{
sl@0
   879
      INSERT INTO tablename (first, next) VALUES ('Hello', 'World')
sl@0
   880
  });
sl@0
   881
  printf "Affected row: %d\n", $mysql->get_affected_rows_length;
sl@0
   882
sl@0
   883
  # SLECT example
sl@0
   884
  $mysql->query(q{SELECT * FROM tablename});
sl@0
   885
  my $record_set = $mysql->create_record_iterator;
sl@0
   886
  while (my $record = $record_set->each) {
sl@0
   887
      printf "First column: %s Next column: %s\n",
sl@0
   888
          $record->[0], $record->[1];
sl@0
   889
  }
sl@0
   890
  $mysql->close;
sl@0
   891
sl@0
   892
=head1 DESCRIPTION
sl@0
   893
sl@0
   894
Net::MySQL is a Pure Perl client interface for the MySQL database. This module implements network protocol between server and client of MySQL, thus you don't need external MySQL client library like libmysqlclient for this module to work. It means this module enables you to connect to MySQL server from some operation systems which MySQL is not ported. How nifty!
sl@0
   895
sl@0
   896
Since this module's final goal is to completely replace DBD::mysql, API is made similar to that of DBI.
sl@0
   897
sl@0
   898
From perl you activate the interface with the statement
sl@0
   899
sl@0
   900
    use Net::MySQL;
sl@0
   901
sl@0
   902
After that you can connect to multiple MySQL daemon and send multiple queries to any of them via a simple object oriented interface.
sl@0
   903
sl@0
   904
There are two classes which have public APIs: Net::MySQL and Net::MySQL::RecordIterator.
sl@0
   905
sl@0
   906
    $mysql = Net::MySQL->new(
sl@0
   907
        hostname => $host,
sl@0
   908
        database => $database,
sl@0
   909
        user     => $user,
sl@0
   910
        password => $password,
sl@0
   911
    );
sl@0
   912
sl@0
   913
Once you have connected to a daemon, you can can execute SQL with:
sl@0
   914
sl@0
   915
    $mysql->query(q{
sl@0
   916
        INSERT INTO foo (id, message) VALUES (1, 'Hello World')
sl@0
   917
    });
sl@0
   918
sl@0
   919
If you want to retrieve results, you need to create a so-called statement handle with:
sl@0
   920
sl@0
   921
    $mysql->query(q{
sl@0
   922
        SELECT id, message FROM foo
sl@0
   923
    });
sl@0
   924
    if ($mysql->has_selected_record) {
sl@0
   925
        my $a_record_iterator = $mysql->create_record_iterator;
sl@0
   926
        # ...
sl@0
   927
    }
sl@0
   928
sl@0
   929
This Net::MySQL::RecordIterator object can be used for multiple purposes. First of all you can retreive a row of data:
sl@0
   930
sl@0
   931
    my $record = $a_record_iterator->each;
sl@0
   932
sl@0
   933
The each() method takes out the reference result of one line at a time, and the return value is ARRAY reference.
sl@0
   934
sl@0
   935
=head2 Net::MySQL API
sl@0
   936
sl@0
   937
=over 4
sl@0
   938
sl@0
   939
=item new(HASH)
sl@0
   940
sl@0
   941
    use Net::MySQL;
sl@0
   942
    use strict;
sl@0
   943
sl@0
   944
    my $mysql = Net::MySQL->new(
sl@0
   945
        unixsocket => $path_to_socket,
sl@0
   946
        hostname   => $host,
sl@0
   947
        database   => $database,
sl@0
   948
        user       => $user,
sl@0
   949
        password   => $password,
sl@0
   950
    );
sl@0
   951
sl@0
   952
The constructor of Net::MySQL. Connection with MySQL daemon is established and the object is returned. Argument hash contains following parameters:
sl@0
   953
sl@0
   954
=over 8
sl@0
   955
sl@0
   956
=item unixsocket
sl@0
   957
sl@0
   958
Path of the UNIX socket where MySQL daemon. default is F</tmp/mysql.sock>.
sl@0
   959
Supposing I<hostname> is omitted, it will connect by I<UNIX Socket>.
sl@0
   960
sl@0
   961
=item hostname
sl@0
   962
sl@0
   963
Name of the host where MySQL daemon runs.
sl@0
   964
Supposing I<hostname> is specified, it will connect by I<INET Socket>.
sl@0
   965
sl@0
   966
=item port
sl@0
   967
sl@0
   968
Port where MySQL daemon listens to. default is 3306.
sl@0
   969
sl@0
   970
=item database
sl@0
   971
sl@0
   972
Name of the database to connect.
sl@0
   973
sl@0
   974
=item user / password
sl@0
   975
sl@0
   976
Username and password for database authentication.
sl@0
   977
sl@0
   978
=item timeout
sl@0
   979
sl@0
   980
The waiting time which carries out a timeout when connection is overdue is specified.
sl@0
   981
sl@0
   982
=item debug
sl@0
   983
sl@0
   984
The exchanged packet will be outputted if a true value is given.
sl@0
   985
sl@0
   986
=back
sl@0
   987
sl@0
   988
sl@0
   989
=item create_database(DB_NAME)
sl@0
   990
sl@0
   991
A create_DATABASE() method creates a database by the specified name.
sl@0
   992
sl@0
   993
    $mysql->create_database('example_db');
sl@0
   994
    die $mysql->get_error_message if $mysql->is_error;
sl@0
   995
sl@0
   996
=item drop_database(DB_NAME)
sl@0
   997
sl@0
   998
A drop_database() method deletes the database of the specified name.
sl@0
   999
sl@0
  1000
    $mysql->drop_database('example_db');
sl@0
  1001
    die $mysql->get_error_message if $mysql->is_error;
sl@0
  1002
sl@0
  1003
=item query(SQL_STRING)
sl@0
  1004
sl@0
  1005
A query() method transmits the specified SQL string to MySQL database, and obtains the response.
sl@0
  1006
sl@0
  1007
=item create_record_iterator()
sl@0
  1008
sl@0
  1009
When SELECT type SQL is specified, Net::MySQL::RecordIterator object which shows the reference result is returned.
sl@0
  1010
sl@0
  1011
    $mysql->query(q{SELECT * FROM table});
sl@0
  1012
    my $a_record_iterator = $mysql->create_recrod_iterator();
sl@0
  1013
sl@0
  1014
Net::MySQL::RecordIterator object is applicable to acquisition of a reference result. See L<"/Net::SQL::RecordIterator API"> for more.
sl@0
  1015
sl@0
  1016
=item get_affected_rows_length()
sl@0
  1017
sl@0
  1018
returns the number of records finally influenced by specified SQL.
sl@0
  1019
sl@0
  1020
    my $affected_rows = $mysql->get_affected_rows_length;
sl@0
  1021
sl@0
  1022
=item get_insert_id()
sl@0
  1023
sl@0
  1024
MySQL has the ability to choose unique key values automatically. If this happened, the new ID will be stored in this attribute. 
sl@0
  1025
sl@0
  1026
=item is_error()
sl@0
  1027
sl@0
  1028
TRUE will be returned if the error has occurred.
sl@0
  1029
sl@0
  1030
=item has_selected_record()
sl@0
  1031
sl@0
  1032
TRUE will be returned if it has a reference result by SELECT.
sl@0
  1033
sl@0
  1034
=item get_field_length()
sl@0
  1035
sl@0
  1036
return the number of column.
sl@0
  1037
sl@0
  1038
=item get_field_names()
sl@0
  1039
sl@0
  1040
return column names by ARRAY.
sl@0
  1041
sl@0
  1042
=item close()
sl@0
  1043
sl@0
  1044
transmits an end message to MySQL daemon, and closes a socket.
sl@0
  1045
sl@0
  1046
=back
sl@0
  1047
sl@0
  1048
=head2 Net::MySQL::RecordIterator API
sl@0
  1049
sl@0
  1050
Net::MySQL::RecordIterator object is generated by the query() method of Net::MySQL object. Thus it has no public constructor method.
sl@0
  1051
sl@0
  1052
=over 4
sl@0
  1053
sl@0
  1054
=item each()
sl@0
  1055
sl@0
  1056
each() method takes out only one line from a result, and returns it as an ARRAY reference. C<undef> is returned when all the lines has been taken out.
sl@0
  1057
sl@0
  1058
    while (my $record = $a_record_iterator->each) {
sl@0
  1059
        printf "Column 1: %s Column 2: %s Collumn 3: %s\n",
sl@0
  1060
            $record->[0], $record->[1], $record->[2];
sl@0
  1061
    }
sl@0
  1062
sl@0
  1063
=back
sl@0
  1064
sl@0
  1065
=head1 SUPPORT OPERATING SYSTEM
sl@0
  1066
sl@0
  1067
This module has been tested on these OSes.
sl@0
  1068
sl@0
  1069
=over 4
sl@0
  1070
sl@0
  1071
=item * MacOS 9.x
sl@0
  1072
sl@0
  1073
with MacPerl5.6.1r.
sl@0
  1074
sl@0
  1075
=item * MacOS X
sl@0
  1076
sl@0
  1077
with perl5.6.0 build for darwin.
sl@0
  1078
sl@0
  1079
=item * Windows2000
sl@0
  1080
sl@0
  1081
with ActivePerl5.6.1 build631.
sl@0
  1082
sl@0
  1083
=item * FreeBSD 3.4 and 4.x
sl@0
  1084
sl@0
  1085
with perl5.6.1 build for i386-freebsd.
sl@0
  1086
sl@0
  1087
with perl5.005_03 build for i386-freebsd.
sl@0
  1088
sl@0
  1089
=item * Linux
sl@0
  1090
sl@0
  1091
with perl 5.005_03 built for ppc-linux.
sl@0
  1092
sl@0
  1093
with perl 5.6.0 bult for i386-linux.
sl@0
  1094
sl@0
  1095
=item * Solaris 2.6 (SPARC)
sl@0
  1096
sl@0
  1097
with perl 5.6.1 built for sun4-solaris.
sl@0
  1098
sl@0
  1099
with perl 5.004_04 built for sun4-solaris.
sl@0
  1100
sl@0
  1101
Can use on Solaris2.6 with perl5.004_04, although I<make test> is failure.
sl@0
  1102
sl@0
  1103
=back
sl@0
  1104
sl@0
  1105
This list is the environment which I can use by the test usually. Net::MySQL will operate  also in much environment which is not in a list.
sl@0
  1106
sl@0
  1107
I believe this module can work with whatever perls which has B<IO::Socket>. I'll be glad if you give me a report of successful installation of this module on I<rare> OSes.
sl@0
  1108
sl@0
  1109
=head1 SEE ALSO
sl@0
  1110
sl@0
  1111
L<libmysql>, L<IO::Socket>
sl@0
  1112
sl@0
  1113
=head1 AUTHOR
sl@0
  1114
sl@0
  1115
Hiroyuki OYAMA E<lt>oyama@module.jpE<gt>
sl@0
  1116
sl@0
  1117
=head1 COPYRIGHT AND LICENCE
sl@0
  1118
sl@0
  1119
Copyright (C) 2002 Hiroyuki OYAMA. Japan. All rights reserved.
sl@0
  1120
sl@0
  1121
This library is free software; you can redistribute it and/or modify
sl@0
  1122
it under the same terms as Perl itself. 
sl@0
  1123
sl@0
  1124
=cut