mysql support for extended columns_info_for() abandoned/mysql_columns_info_for
Carl Franks [Thu, 11 May 2006 11:15:07 +0000 (11:15 +0000)]
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Storage/DBI/mysql.pm

index 6a85b2c..269683b 100644 (file)
@@ -2162,6 +2162,63 @@ sub sth {
   $self->dbh_do('_dbh_sth', $sql);  # retry over disconnects
 }
 
+=head1 columns_info_for
+
+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 _dbh_columns_info_for {
   my ($self, $dbh, $table) = @_;
 
index 2c49691..745ae66 100644 (file)
@@ -102,6 +102,128 @@ sub _subq_update_delete {
   return shift->_per_row_update_delete (@_);
 }
 
+
+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 ($self->dbh->{mysql_serverinfo} < version->new('4.1')) {
+      $column_info{length_in_bytes} = 1;
+    }
+    $column_info{ignore_trailing_spaces} = 1;
+  }
+  elsif ($basetype eq 'varchar') {
+    if ($self->dbh->{mysql_serverinfo} <= version->new('4.1')) {
+      $column_info{ignore_trailing_spaces} = 1;
+    }
+    if ($self->dbh->{mysql_serverinfo} < version->new('4.1')) {
+      $column_info{length_in_bytes} = 1;
+    }
+  }
+  elsif ($basetype =~ /text$/) {
+    if ($basetype =~ /blob$/) {
+      $column_info{length_in_bytes} = 1;
+    }
+    elsif ($self->dbh->{mysql_serverinfo} < version->new('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 ($self->dbh->{mysql_serverinfo} <= version->new('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 ($self->dbh->{mysql_serverinfo} <= version->new('4.1')) {
+      $column_info{decimal_high_positive} = 1;
+    }
+    if ($self->dbh->{mysql_serverinfo} < version->new('3.23')) {
+      $column_info{decimal_literal_range} = 1;
+    }
+    $column_info{decimal_digits} = $info->{DECIMAL_DIGITS};
+  }
+
+  return %column_info;
+}
+
 1;
 
 =head1 NAME