Comment extraire proprement les valeurs MySQL enum en Perl?
-
04-07-2019 - |
Question
J'ai du code qui doit s’assurer que certaines données se trouvent dans un enum mysql avant leur insertion dans la base de données. Le moyen le plus propre que j'ai trouvé est le code suivant:
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;
}
Nous utilisons DBIx :: Class . Il y a sûrement un meilleur moyen d'accomplir cela? (Notez que la variable $ table provient de notre code, pas de toute source externe. Donc, pas de problème de sécurité).
La solution
Pas besoin d'être aussi héroïque. Utilisation d'une version relativement moderne de DBD :: mysql , le hachage renvoyé par colonne info cette méthode contient une version pré-scindée des valeurs enum valides dans la clé 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";
}
...
}
Autres conseils
Je dirais que l'utilisation de Text :: CSV_XS peut être une overkill, sauf si vous avez des choses étranges comme des virgules dans les énumérations (une mauvaise idée quand même, si vous me le demandez). J'utiliserais probablement ceci à la place.
my @fields = $type =~ / ' ([^']+) ' (?:,|\z) /msgx;
À part cela, je ne pense pas qu'il existe de raccourcis.
J'ai passé une partie de la journée à poser la même question au canal # dbix de MagNet et je suis tombé sur ce manque de réponse. Puisque j'ai trouvé la réponse et que personne d'autre ne semble l'avoir fait pour l'instant, je vais coller la transcription en dessous du TL; DR ici:
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};
Et maintenant, le journal de bord IRC me frappant la tête contre un mur:
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 :)