On Thu, Feb 20, 2003 at 06:57:37PM +0000, Tim Bunce wrote:
> Does anyone have a reasonably working implementation of column_info() for mysql?

I guess not, so I've written my own basic one and appended it below for feedback.

Tim.


sub DBD::mysql::db::column_info {
    my ($dbh, $catalog, $schema, $table, $column) = @_;
    return $dbh->set_err(1, "column_info doesn't support catalog or schema")
        if defined $catalog or defined $schema;
    return $dbh->set_err(1, "column_info doesn't support table wildcard")
        if $table !~ /^\w+$/;
    return $dbh->set_err(1, "column_info doesn't support column selection")
        if $column ne "%";

    my @names = qw(
        TABLE_CAT TABLE_SCHEM TABLE_NAME COLUMN_NAME
        DATA_TYPE TYPE_NAME COLUMN_SIZE BUFFER_LENGTH DECIMAL_DIGITS
        NUM_PREC_RADIX NULLABLE REMARKS COLUMN_DEF
        SQL_DATA_TYPE SQL_DATETIME_SUB CHAR_OCTET_LENGTH
        ORDINAL_POSITION IS_NULLABLE CHAR_SET_CAT
        CHAR_SET_SCHEM CHAR_SET_NAME COLLATION_CAT COLLATION_SCHEM COLLATION_NAME
        UDT_CAT UDT_SCHEM UDT_NAME DOMAIN_CAT DOMAIN_SCHEM DOMAIN_NAME
        SCOPE_CAT SCOPE_SCHEM SCOPE_NAME MAX_CARDINALITY
        DTD_IDENTIFIER IS_SELF_REF
    );
    my %col_info;

    my $desc = $dbh->selectall_arrayref("DESCRIBE $table", { Columns=>{} });
    my $ordinal_pos = 0;
    foreach my $row (@$desc) {
        my $info = $col_info{ $row->{field} } = {
            TABLE_NAME => $table,
            COLUMN_NAME => $row->{field},
            ORDINAL_POSITION => ++$ordinal_pos,
            NULLABLE    => ($row->{null} eq 'YES') ? 1 : 0,
            IS_NULLABLE => ($row->{null} eq 'YES') ? "YES" : "NO",
            TYPE_NAME => $row->{type},
            COLUMN_DEF => $row->{default},
            mysql_is_pri_key => ($row->{key}  eq 'PRI'),
        };
        my $type = $row->{type};
        $type =~ m/^(\w+)(?:\((.*?)\))?\s*(.*)/;
        my $basetype = $1;
        my @type_params = map { s/^'(.*)'$/$1/; $_ } split /,/, $2||'';
        my @type_attr = split / /, $3||'';
        #warn "$type: $basetype [@type_params] [@type_attr]\n";

        $info->{DATA_TYPE} = SQL_VARCHAR();
        if ($basetype =~ /char|text|long/) {
            $info->{DATA_TYPE} = SQL_CHAR() if $basetype eq 'char';
            $info->{COLUMN_SIZE} = $type_params[0];
        }
        elsif ($basetype =~ /enum|set/) {
            $info->{COLUMN_SIZE} = ($basetype eq 'set') ? length(join 
",",@type_params) : 255; # XXX fix
            $info->{"mysql_${basetype}_values"} = \@type_params;
        }
        elsif ($basetype =~ /int/) {
            $info->{DATA_TYPE} = SQL_INTEGER();
            $info->{NUM_PREC_RADIX} = 10;
            $info->{COLUMN_SIZE} = $type_params[0];
        }
        elsif ($basetype =~ /decimal/) {
            $info->{DATA_TYPE} = SQL_DECIMAL();
            $info->{NUM_PREC_RADIX} = 10;
            $info->{COLUMN_SIZE}    = $type_params[0];
            $info->{DECIMAL_DIGITS} = $type_params[1];
        }
        elsif ($basetype =~ /float|double/) {
            $info->{DATA_TYPE} = ($basetype eq 'float') ? SQL_FLOAT() : SQL_DOUBLE();
            $info->{NUM_PREC_RADIX} = 2;
            $info->{COLUMN_SIZE} = ($basetype eq 'float') ? 32 : 64;
        }
        elsif ($basetype =~ /date|time/) { # date/datetime/time/timestamp
            if ($basetype eq 'time' or $basetype eq 'date') {
                $info->{DATA_TYPE}   = ($basetype eq 'time') ? SQL_TYPE_TIME() : 
SQL_TYPE_DATE();
                $info->{COLUMN_SIZE} = ($basetype eq 'time') ? 8 : 10;
            }
            else { # datetime/timestamp
                $info->{DATA_TYPE}     = SQL_TYPE_TIMESTAMP();
                $info->{SQL_DATA_TYPE} = SQL_DATETIME();
                $info->{SQL_DATETIME_SUB} = $info->{DATA_TYPE} - 
($info->{SQL_DATA_TYPE} * 10);
                $info->{COLUMN_SIZE}   = ($basetype eq 'datetime') ? 19 : 
$type_params[0] || 14;
            }
            $info->{DECIMAL_DIGITS} = 0; # no fractional seconds
        }
        else {
            warn "unsupported column '$row->{field}' type '$basetype' treated as 
varchar";
        }
        $info->{SQL_DATA_TYPE} ||= $info->{DATA_TYPE};
        #warn Dumper($info);
    }

    my $sponge = DBI->connect("DBI:Sponge:", '','')
        or return $dbh->DBI::set_err($DBI::err, "DBI::Sponge: $DBI::errstr");
    my $sth = $sponge->prepare("column_info $table", {
        rows => [ map { [ @{$_}{@names} ] } values %col_info ],
        NUM_OF_FIELDS => scalar @names,
        NAME => \@names,
    }) or return $dbh->DBI::set_err($sponge->err(), $sponge->errstr());

}

1;
__END__

Reply via email to