我该如何使用Perl干净地提取MySQL枚举值?

6

我有一些代码需要确保在将数据插入数据库之前,该数据已经存在于MySQL枚举中。我发现最干净的方法是使用以下代码:

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

我们正在使用DBIx::Class。 肯定有更好的方法来实现这个吧?(注意,$ table变量来自我们的代码,而不是任何外部来源。因此,没有安全问题)。
3个回答

13

无需如此英勇。使用相当现代的版本DBD :: mysql ,由 DBI column_info 方法返回的哈希值在键 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";
  }
  ...
}

不错 :-). 不过有人应该把它记录在一个明显的地方。 - Leon Timmermans
就此而言,我从Rose::DB::Object中提取了那个答案,它可以内省并自动配置MySQL枚举、Postgres数组列和许多其他类型。 当DBD::*文档不足以回答问题时,它的代码是一个很好的参考来源。 - John Siracusa
升级DBD::mysql 3.006到最新版本后,我们的验收测试运行时间增加了35分钟(从50分钟增加到85分钟),真是让人头疼。 - Ovid

3

我认为使用Text::CSV_XS可能有些过度,除非你的枚举中有奇怪的逗号(如果你问我的话,这本来就是个坏主意)。我会使用这个代替。

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

除此之外,我认为没有捷径。

我们确实有相当严格的命名约定限制,因此这似乎是一个不错的简化。谢谢! - Ovid
不过需要进行一处小修正:它可以处理枚举中的逗号,但无法处理撇号。 - Leon Timmermans

0
我花了一部分时间在MagNet上的#dbix-class频道上问同样的问题,并遇到了这个缺乏答案的情况。由于我找到了答案,而似乎没有其他人这样做,所以我将在此处的TL;DR下面粘贴转录内容:
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};

现在是我在IRC上撞墙的记录:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接