MySQLのSET型をDBIx::Classから使ってみる

という疑問がありまして。色々とご意見をいただきました。ありがとうございます!「ケースバイケース」で結論づけてしまっているのだけど、実際どうしようかなぁ。。

とりあえず

MySQL :: MySQL 5.6 リファレンスマニュアル :: 11.4.5 SET 型
MySQLには(他は知らない)"SET"というデータタイプがあるらしい。これをDBICで使おうとするとどんなカンジになるのか試してみた。
まずは下記のようにtableを作成。

CREATE TABLE `fuga` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `piyo` set('foo','bar','baz') DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

これをDBIx::Classで操作するためにSchemaクラスとかなんとかを作成する。
DBIx::Class::Schema::Loaderには"dbicdump"というコマンドが付属していて、これを使って一発でclassファイルを作成してくれるらしい。

$ mkdir hoge
$ cd hoge
$ dbicdump MyDB::Schema dbi:mysql:hoge root
Dumping manual schema for MyDB::Schema to directory . ...
Schema dump completed.
$ ls -R
MyDB

./MyDB:
Schema    Schema.pm
./MyDB/Schema:
Result
./MyDB/Schema/Result:
Fuga.pm


MyDB/Schema.pm

package MyDB::Schema;

# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE

use strict;
use warnings;

use base 'DBIx::Class::Schema';

__PACKAGE__->load_namespaces;


# Created by DBIx::Class::Schema::Loader v0.05003 @ 2010-03-03 20:26:35
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OBak59WuDE/spb1nXCS8GQ


# You can replace this text with custom content, and it will be preserved on regeneration
1;


MyDB/Schema/Fuga.pm

package MyDB::Schema::Result::Fuga;

# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE

use strict;
use warnings;

use base 'DBIx::Class::Core';


=head1 NAME

MyDB::Schema::Result::Fuga

=cut

__PACKAGE__->table("fuga");

=head1 ACCESSORS

=head2 id

  data_type: INT
  default_value: undef
  is_auto_increment: 1
  is_nullable: 0
  size: 11

=head2 piyo

  data_type: SET
  default_value: undef
  extra: HASH(0x1278af4)
  is_nullable: 1
  size: 11

=cut

__PACKAGE__->add_columns(
  "id",
  {
    data_type => "INT",
    default_value => undef,
    is_auto_increment => 1,
    is_nullable => 0,
    size => 11,
  },
  "piyo",
  {
    data_type => "SET",
    default_value => undef,
    extra => { list => ["foo", "bar", "baz"] },
    is_nullable => 1,
    size => 11,
  },
);
__PACKAGE__->set_primary_key("id");


# Created by DBIx::Class::Schema::Loader v0.05003 @ 2010-03-03 20:26:35
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uOUY6V9Jb8YGT288iMIVsg


# You can replace this text with custom content, and it will be preserved on regeneration
1;

の2つが生成された。便利。


これを使ってperlスクリプトから操作して作成や検索してみる。

#!/usr/bin/perl
use strict;
use warnings;

use MyDB::Schema;
use Data::Dumper;

my $fuga = MyDB::Schema->connect(
    'dbi:mysql:hoge', 'root', '',
)->resultset('Fuga');
$fuga->delete;

$fuga->create({});
$fuga->create({ piyo => 1 });              # 整数値で指定
$fuga->create({ piyo => 'bar' });          # 文字列で指定
$fuga->create({ piyo => 5 });              # 整数値で複数指定 1(= 1<<0)と4(= 1<<2)の論理和
$fuga->create({ piyo => 'baz,bar,foo' });  # 文字列で複数指定 カンマ区切りでできるらしい

print "all:\n";                            # 作成されたものを全部出力
for my $obj ($fuga->all) {
    my %inflated_data = $obj->get_inflated_columns;
    print Dumper( \%inflated_data );
}

print "search bar by 'like':\n";           # LIKE検索で"bar"が含まれているものを探す
for my $obj ($fuga->search({ piyo => { like => '%bar%' } })) {
    my %inflated_data = $obj->get_inflated_columns;
    print Dumper( \%inflated_data );
}

print "search bar by '&':\n";              # 論理積で"bar"が含まれているものを探す
for my $obj ($fuga->search({ piyo => { '&' => 2 } })) {
    my %inflated_data = $obj->get_inflated_columns;
    print Dumper( \%inflated_data );
}

print "search bar by 'FIND_IN_SET':\n";    # FIND_IN_SET関数で"bar"が含まれているものを探す
for my $obj ($fuga->search_literal('FIND_IN_SET(?, piyo)', 'bar')) {
    my %inflated_data = $obj->get_inflated_columns;
    print Dumper( \%inflated_data );
}
$ perl hoge.pl
all:
$VAR1 = {
          'piyo' => undef,
          'id' => '6'
        };
$VAR1 = {
          'piyo' => 'foo',
          'id' => '7'
        };
$VAR1 = {
          'piyo' => 'bar',
          'id' => '8'
        };
$VAR1 = {
          'piyo' => 'foo,baz',
          'id' => '9'
        };
$VAR1 = {
          'piyo' => 'foo,bar,baz',
          'id' => '10'
        };
search bar by 'like':
$VAR1 = {
          'piyo' => 'bar',
          'id' => '8'
        };
$VAR1 = {
          'piyo' => 'foo,bar,baz',
          'id' => '10'
        };
search bar by '&':
$VAR1 = {
          'piyo' => 'bar',
          'id' => '8'
        };
$VAR1 = {
          'piyo' => 'foo,bar,baz',
          'id' => '10'
        };
search bar by 'FIND_IN_SET':
$VAR1 = {
          'piyo' => 'bar',
          'id' => '8'
        };
$VAR1 = {
          'piyo' => 'foo,bar,baz',
          'id' => '10'
        };

ちゃんと作成されて、検索もできているっぽい。DBIC_TRACE=1 で生成されたSQL文は

DELETE FROM fuga: 
INSERT INTO fuga () VALUES (): 
INSERT INTO fuga ( piyo) VALUES ( ? ): '1'
INSERT INTO fuga ( piyo) VALUES ( ? ): 'bar'
INSERT INTO fuga ( piyo) VALUES ( ? ): '5'
INSERT INTO fuga ( piyo) VALUES ( ? ): 'baz,bar,foo'
SELECT me.id, me.piyo FROM fuga me: 
SELECT me.id, me.piyo FROM fuga me WHERE ( piyo LIKE ? ): '%bar%'
SELECT me.id, me.piyo FROM fuga me WHERE ( piyo & ? ): '2'
SELECT me.id, me.piyo FROM fuga me WHERE ( FIND_IN_SET(?, piyo) ): 'bar'

となっていた。


intでビットマスクかけての操作をラップしてくれてるだけ、というカンジ?
わざわざこれだけのためにMySQLしか使えないような仕組みにしようとは思えないなー。