2008-10-23 13 views
5

Ich habe etwas Code, der sicherstellen muss, dass einige Daten in einer Mysql-Enumeration vor dem Einfügen in der Datenbank sind. Die sauberste Art, wie ich dies zu tun gefunden habe, ist der folgende Code:Wie extrahiere ich sauber MySQL Enum-Werte in 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; 
} 

Wir DBIx::Class verwenden. Sicherlich gibt es einen besseren Weg, dies zu erreichen? (Beachten Sie, dass die $ table-Variable von unserem Code kommt, nicht von einer externen Quelle. Daher kein Sicherheitsproblem).

Antwort

13

Keine Notwendigkeit, so heroisch zu sein. Mit einer halbwegs modernen Version von DBD::mysql, die Hash zurück von DBI ‚s column info Methode enthält eine Pre-Split-Version der gültigen Enum-Werte in dem Schlüssel 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

Schön :-). Jemand sollte das an einem offensichtlichen Ort dokumentieren. –

+0

Ausgezeichnet. Vielen Dank! – Ovid

+0

FWIW, ich habe diese Antwort aus Rose :: DB :: Object herausgezogen, die MySQL-Enums, Postgres-Array-Spalten und viele andere solche Typen untersucht und automatisch konfiguriert. Sein Code ist eine gute Quelle für Antworten, wenn die DBD :: * -Dokumente nicht ausreichen. –

3

Ich würde sagen, mit Text :: CSV_XS kann ein Overkill sein, es sei denn, Sie haben seltsame Dinge wie Kommas in enums (eine schlechte Idee, wenn Sie mich fragen). Ich würde das wahrscheinlich stattdessen verwenden.

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

Ansonsten denke ich nicht, dass es Abkürzungen gibt.

+0

Wir haben ziemlich strenge Einschränkungen, denen wir zu folgen Namenskonventionen folgen, so dass es scheint, wie eine nette Vereinfachung. Vielen Dank! – Ovid

+0

Eine kleine Korrektur allerdings: Es wird mit einem Komma in der Enum umgehen, es wird aber nicht mit einem Apostroph umgehen. –

0

ich Teil des Tages verbrachte den #dbix fragen -Klasse-Kanal über MagNet die gleiche Frage und stieß auf diesen Mangel an Antwort. Da ich die Antwort und sonst niemand scheint gefunden, so noch zu tun zu haben, werde ich das Protokoll unter dem TL Paste; DR hier:

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}; 

Und nun das IRC Protokoll, mich zu schlagen meinen Kopf gegen eine Wand:

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 :) 
Verwandte Themen