/me predicts mst will be annoyed with the number of revisions when he wakes up

Here's the latest patch!

I'm going to try and get some live db tests working, and so hopefully
stop with the buggy nonsense.
Index: lib/DBIx/Class/Storage/DBI.pm
===================================================================
--- lib/DBIx/Class/Storage/DBI.pm	(revision 1369)
+++ lib/DBIx/Class/Storage/DBI.pm	(working copy)
@@ -529,6 +529,59 @@
 
 Returns database type info for a given table columns.
 
+Keys which may be set include:
+
+=over
+
+=item C<data_type>
+
+=item C<size>
+
+=item C<default_value>
+
+=item C<is_nullable>
+
+=item C<is_unsigned> For numeric types.
+
+=item C<decimal_digits>
+
+=item C<data_set> For list types such as C<enum> and C<set>, contains
+an arrayref of valid values.
+
+=item C<range_min> For numeric types, the minimum valid value.
+
+=item C<range_max> For numeric types, the maximum valid value.
+
+=back
+
+Keys which may be set for various database's features/bugs.
+
+=over
+
+=item C<length_in_bytes> When the C<size> is a length, such as the length of
+a C<text> field, if this value is true, the length should be measured in
+bytes rather than characters.
+
+=item C<ignore_trailing_spaces> When the size is a length, such as the
+length of a C<text> field, if this value is true, trailing spaced should be
+counted.
+
+=item C<decimal_high_positive> For decimal types which, when positive,
+use the byte reserved for the sign to increase the precision by 1.
+
+For Example: The normal range of C<DECIMAL(5,2)> should be C<-999.99> to
+C<999.99>. C<decimal_high_positive> indicates that the valid range of values
+is actually C<-999.99> to C<9999.99>.
+
+=item C<decimal_literal_range> For decimal types which include the sign and
+decimal point in the precision length.
+
+For Example: The normal range of C<DECIMAL(5,2)> should be C<-999.99> to
+C<999.99>. C<decimal_literal_range> indicates that the valid range of values
+is actually C<-9.99> to C<99.99>.
+
+=back
+
 =cut
 
 sub columns_info_for {
Index: lib/DBIx/Class/Storage/DBI/mysql.pm
===================================================================
--- lib/DBIx/Class/Storage/DBI/mysql.pm	(revision 1369)
+++ lib/DBIx/Class/Storage/DBI/mysql.pm	(working copy)
@@ -2,21 +2,12 @@
 
 use strict;
 use warnings;
+use Carp::Clan qw/^DBIx::Class/;
 
 use base qw/DBIx::Class::Storage::DBI/;
 
 # __PACKAGE__->load_components(qw/PK::Auto/);
 
-sub last_insert_id {
-  return $_[0]->_dbh->{mysql_insertid};
-}
-
-sub sqlt_type {
-  return 'MySQL';
-}
-
-1;
-
 =head1 NAME
 
 DBIx::Class::Storage::DBI::mysql - Automatic primary key class for MySQL
@@ -31,6 +22,206 @@
 
 This class implements autoincrements for MySQL.
 
+=head1 METHODS
+
+=head2 columns_info_for
+
+Extends L<DBIx::Class::Storage::DBI/columns_info_for>.
+
+=cut
+
+sub columns_info_for {
+  my ($self, $table) = @_;
+
+  my $result;
+  
+  if ($self->dbh->can('column_info')) {
+    my $old_raise_err = $self->dbh->{RaiseError};
+    my $old_print_err = $self->dbh->{PrintError};
+    $self->dbh->{RaiseError} = 1;
+    $self->dbh->{PrintError} = 0;
+    eval {
+      my $sth = $self->dbh->column_info( undef, undef, $table, '%' );
+      $sth->execute();
+      while ( my $info = $sth->fetchrow_hashref() ){
+        my %column_info;
+        $column_info{data_type}     = $info->{TYPE_NAME};
+        $column_info{size}          = $info->{COLUMN_SIZE};
+        $column_info{is_nullable}   = $info->{NULLABLE} ? 1 : 0;
+        $column_info{default_value} = $info->{COLUMN_DEF};
+
+        my %info = $self->_extract_mysql_specs($info);
+        $column_info{$_} = $info{$_} for keys %info;
+
+        $result->{$info->{COLUMN_NAME}} = \%column_info;
+      }
+    };
+    $self->dbh->{RaiseError} = $old_raise_err;
+    $self->dbh->{PrintError} = $old_print_err;
+    return {} if $@;
+  }
+
+  return $result;
+}
+
+sub _extract_mysql_specs {
+  my ($self, $info) = @_;
+  
+  my $basetype   = lc($info->{TYPE_NAME});
+  my $mysql_type = lc($info->{mysql_type_name});
+  my %column_info;
+  
+  if ($basetype eq 'char') {
+    if (_version_lt($self->dbh->{mysql_serverinfo}, '4.1')) {
+      $column_info{length_in_bytes} = 1;
+    }
+    $column_info{ignore_trailing_spaces} = 1;
+  }
+  elsif ($basetype eq 'varchar') {
+    if (_version_le($self->dbh->{mysql_serverinfo}, '4.1')) {
+      $column_info{ignore_trailing_spaces} = 1;
+    }
+    if (_version_lt($self->dbh->{mysql_serverinfo}, '4.1')) {
+      $column_info{length_in_bytes} = 1;
+    }
+  }
+  elsif ($basetype =~ /text$/) {
+    if ($basetype =~ /blob$/) {
+      $column_info{length_in_bytes} = 1;
+    }
+    elsif (_version_lt($self->dbh->{mysql_serverinfo}, '4.1')) {
+      $column_info{length_in_bytes} = 1;
+    }
+  }
+  elsif ($basetype eq 'binary') {
+    $column_info{ignore_trailing_spaces} = 1;
+    $column_info{length_in_bytes}        = 1;
+  }
+  elsif ($basetype eq 'varbinary') {
+    if (_version_le($self->dbh->{mysql_serverinfo}, '4.1')) {
+      $column_info{ignore_trailing_spaces} = 1;
+    }
+    $column_info{length_in_bytes} = 1;
+  }
+  elsif ($basetype =~ /^(enum|set)/) {
+    $column_info{data_set} = $info->{mysql_values};
+  }
+  elsif ($basetype =~ /int$/) {
+    if ($mysql_type =~ /unsigned /) {
+      my %max = (
+        tinyint   => 2**8 - 1,
+        smallint  => 2**16 - 1,
+        mediumint => 2**24 - 1,
+        int       => 2**32 - 1,
+        bigint    => 2**64 - 1,
+      );
+      $column_info{is_unsigned} = 1;
+      $column_info{range_min}   = 0;
+      $column_info{range_max}   = $max{$basetype};
+    }
+    else { # not unsigned
+      my %min = (
+        tinyint   => - 2**7,
+        smallint  => - 2**15,
+        mediumint => - 2**23,
+        int       => - 2**31,
+        bigint    => - 2**63,
+      );
+      my %max = (
+        tinyint   => 2**7 - 1,
+        smallint  => 2**15 - 1,
+        mediumint => 2**23 - 1,
+        int       => 2**31 - 1,
+        bigint    => 2**63 - 1,
+      );
+      $column_info{range_min} = $min{$basetype};
+      $column_info{range_max} = $max{$basetype};
+    }
+  }
+  elsif ($basetype =~ /^decimal/) {
+    if (_version_le($self->dbh->{mysql_serverinfo}, '4.1')) {
+      $column_info{decimal_high_positive} = 1;
+    }
+    if (_version_lt($self->dbh->{mysql_serverinfo}, '3.23')) {
+      $column_info{decimal_literal_range} = 1;
+    }
+    $column_info{decimal_digits} = $info->{DECIMAL_DIGITS};
+  }
+    
+  return %column_info;
+}
+
+sub _version_eq {
+  my ($x, $y) = @_;
+  
+  ($x, $y) = _version_normalize( @_ );
+  
+  return 1 if defined $x && $x eq $y;
+  return;
+}
+
+sub _version_lt {
+  my ($x, $y) = @_;
+  
+  ($x, $y) = _version_normalize( @_ );
+  
+  return 1 if defined $x && $x lt $y;
+  return;
+}
+
+sub _version_gt {
+  my ($x, $y) = @_;
+  
+  ($x, $y) = _version_normalize( @_ );
+  
+  return 1 if defined $x && $x gt $y;
+  return;
+}
+
+sub _version_le {
+  my ($x, $y) = @_;
+  
+  return 1 if _version_lt($x, $y) || _version_eq($x, $y);
+  return;
+}
+
+sub _version_ge {
+  my ($x, $y) = @_;
+  
+  return 1 if _version_gt($x, $y) || _version_eq($x, $y);
+  return;
+}
+
+sub _version_normalize {
+  my ($x, $y) = @_;
+  
+  my ($x1, $x2, $x3) = $x =~ /(^\d+)(?:\.(\d+))?(?:\.(\d+))?/
+    or return;
+  
+  $x2 = 0 if not defined $x2;
+  $x3 = 0 if not defined $x3;
+  
+  $x = sprintf "%03d%03d%03d", $x1, $x2, $x3;
+  
+  my ($y1, $y2, $y3) = $y =~ /(^\d+)(?:\.(\d+))?(?:\.(\d+))?/
+    or return;
+  
+  $y2 = 0 if not defined $y2;
+  $y3 = 0 if not defined $y3;
+  
+  $y = sprintf "%03d%03d%03d", $y1, $y2, $y3;
+  
+  return ($x, $y);
+}
+
+sub last_insert_id {
+  return $_[0]->_dbh->{mysql_insertid};
+}
+
+sub sqlt_type {
+  return 'MySQL';
+}
+
 =head1 AUTHORS
 
 Matt S. Trout <[EMAIL PROTECTED]>
@@ -40,3 +231,5 @@
 You may distribute this code under the same terms as Perl itself.
 
 =cut
+
+1;

_______________________________________________
List: http://lists.rawmode.org/cgi-bin/mailman/listinfo/dbix-class
Wiki: http://dbix-class.shadowcatsystems.co.uk/
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/trunk/DBIx-Class/

Reply via email to