2008-10-23 14 views
5

Tengo un código que necesita asegurar que algunos datos estén en una enumeración mysql antes de la inserción en la base de datos. La manera más limpia que he encontrado para hacer esto es el siguiente código:¿Cómo puedo extraer limpiamente los valores enum de MySQL en Perl?

sub enum_values { 
    my ($self, $schema, $table, $column) = @_; 

    # don't eval to let the error bubble up 
    my $columns = $schema->storage->dbh->selectrow_hashref(
     "SHOW COLUMNS FROM `$table` like ?", 
     {}, 
     $column 
    ); 

    unless ($columns) { 
     X::Internal::Database::UnknownColumn->throw(
      column => $column, 
      table => $table, 
     ); 
    } 

    my $type = $columns->{Type} or X::Panic->throw(
     details => "Could not determine type for $table.$column", 
    ); 

    unless ($type =~ /\Aenum\((.*)\)\z/) { 
     X::Internal::Database::IncorrectTypeForColumn->throw(
      type_wanted => 'enum', 
      type_found => $type, 
     ); 
    } 
    $type = $1; 

    require Text::CSV_XS; 
    my $csv = Text::CSV_XS->new; 
    $csv->parse($type) or X::Panic->throw(
     details => "Could not parse enum CSV data: ".$csv->error_input, 
    ); 
    return map { /\A'(.*)'\z/; $1 }$csv->fields; 
} 

Estamos utilizando DBIx::Class. Sin duda, hay una mejor manera de lograr esto? (Tenga en cuenta que la variable $ table proviene de nuestro código, , no desde cualquier fuente externa. Por lo tanto, no es un problema de seguridad).

Respuesta

13

No hay necesidad de ser tan heroico. Utilizando una versión bastante moderna de DBD::mysql, el hash devuelto por DBI 's column info método contiene una versión pre-fractura de los valores de enumeración válidos en la clave mysql_values:

my $sth = $dbh->column_info(undef, undef, 'mytable', '%'); 

foreach my $col_info ($sth->fetchrow_hashref) 
{ 
    if($col_info->{'TYPE_NAME'} eq 'ENUM') 
    { 
    # The mysql_values key contains a reference to an array of valid enum values 
    print "Valid enum values for $col_info->{'COLUMN_NAME'}: ", 
      join(', ', @{$col_info->{'mysql_values'}}), "\n"; 
    } 
    ... 
} 
+0

Agradable :-). Alguien debería documentar eso en un lugar obvio sin embargo. –

+0

Excelente. ¡Muchas gracias! – Ovid

+0

FWIW, extraje esa respuesta de Rose :: DB :: Object, que introspecta y configura automáticamente las enumeraciones de MySQL, las columnas de matriz de Postgres y muchos otros tipos similares. Su código es una buena fuente de respuestas cuando DBD :: * docs se queda corto. –

3

Yo diría que usar Text :: CSV_XS puede ser una exageración, a menos que tengas cosas raras como comas en enumeraciones (una mala idea de todos modos si me preguntas). Probablemente usaría esto en su lugar.

my @fields = $type =~/' ([^']+) ' (?:,|\z) /msgx; 

Aparte de eso, no creo que haya atajos.

+0

Tenemos limitaciones muy estrictas que tratamos de seguir sobre las convenciones de nombres, por lo que parece una buena simplificación. ¡Gracias! – Ovid

+0

Sin embargo, una pequeña corrección: manejará una coma en la enumeración, aunque no manejará un apóstrofo. –

0

pasé parte del día pidiendo al #dbix -campo de clase en MagNet la misma pregunta y se encontró con esta falta de respuesta. Desde que encontré la respuesta y nadie más parece haber hecho todavía, voy a pegar la transcripción por debajo de la TL; DR aquí:

my $cfg = new Config::Simple($rc_file); 
my $mysql = $cfg->get_block('mysql'); 
my $dsn = 
    "DBI:mysql:database=$mysql->{database};". 
    "host=$mysql->{hostname};port=$mysql->{port}"; 

my $schema = 
    DTSS::CDN::Schema->connect($dsn, $mysql->{user}, $mysql->{password}); 

my $valid_enum_values = 
    $schema->source('Cdnurl')->column_info('scheme')->{extra}->{list}; 

Y ahora el registro de IRC a golpearme la cabeza contra una pared:

14:40 < cj> is there a cross-platform way to get the valid values of an 
      enum? 
15:11 < cj> it looks like I could add 'InflateColumn::Object::Enum' to the 
      __PACKAGE__->load_components(...) list for tables with enum 
      columns 
15:12 < cj> and then call values() on the enum column 
15:13 < cj> but how do I get dbic-dump to add 
      'InflateColumn::Object::Enum' to 
      __PACKAGE__->load_components(...) for only tables with enum 
      columns? 
15:20 < cj> I guess I could just add it for all tables, since I'm doing 
      the same for InflateColumn::DateTime 
15:39 < cj> hurm... is there a way to get a column without making a 
      request to the db? 
15:40 < cj> I know that we store in the DTSS::CDN::Schema::Result::Cdnurl 
      class all of the information that I need to know about the 
      scheme column before any request is issued 
15:42 <@ilmari> cj: for Pg and mysql Schema::Loader will add the list of 
       valid values to the ->{extra}->{list} column attribute 
15:43 <@ilmari> cj: if you're using some other database that has enums, 
       patches welcome :) 
15:43 <@ilmari> or even just a link to the documentation on how to extract 
       the values 
15:43 <@ilmari> and a willingness to test if it's not a database I have 
       access to 
15:43 < cj> thanks, but I'm using mysql. if I were using sqlite for this 
      project, I'd probably oblige :-) 
15:44 <@ilmari> cj: to add components to only some tables, use 
       result_components_map 
15:44 < cj> and is there a way to get at those attributes without making a 
      query? 
15:45 < cj> can we do $schema->resultset('Cdnurl') without having it issue 
      a query, for instance? 
15:45 <@ilmari> $result_source->column_info('colname')->{extra}->{list} 
15:45 < cj> and $result_source is $schema->resultset('Cdnurl') ? 
15:45 <@ilmari> dbic never issues a query until you start retrieving the 
       results 
15:45 < cj> oh, nice. 
15:46 <@ilmari> $schema->source('Cdnurl') 
15:46 <@ilmari> the result source is where the result set gets the results 
       from when they are needed 
15:47 <@ilmari> names have meanings :) 
Cuestiones relacionadas