DBIx::ClassとSQLiteで複数のカラムをDISTINCTするとエラーする

DISTINCTで複数のカラムを指定するとエラーした。

以下のようにして、複数のカラムをDISTINCTするコード

$c->stash->{employees} = [$schema->resultset('Employee')->search(
  {'name' => {'LIKE' => '%hoge%'} },
  { select => {distinct =>  [qw(me.name, me.pr, me.background, me.licence)] } } 
)];

実行すると以下のようなSQLが発行された

SELECT DISTINCT( me.name, me.pr, me.background, me.licence ) FROM employee me WHERE ( name LIKE ? ): '%hoge%'

で吐かれた、エラーがコレ

[error] rollbacked: DBI Exception: DBD::SQLite::db prepare_cached failed: near ",": syntax error(1) at dbdimp.c line 271 [for Statement "SELECT DISTINCT( me.name, me.pr, me.background, me.licence ) FROM employee me WHERE ( name LIKE ? )"] at /Library/Perl/5.8.8/DBIx/Class/Schema.pm line 954
	DBIx::Class::Schema::throw_exception('ResoManage::Model::Schema=HASH(0xd26918)', 'DBI Exception: DBD::SQLite::db prepare_cached failed: near ",...') called at /Library/Perl/5.8.8/DBIx/Class/Storage.pm line 122
	DBIx::Class::Storage::throw_exception('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'DBI Exception: DBD::SQLite::db prepare_cached failed: near ",...') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 846
	DBIx::Class::Storage::DBI::__ANON__('DBD::SQLite::db prepare_cached failed: near ",": syntax error...', 'DBI::db=HASH(0xe08044)', 'undef') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 1188
	DBIx::Class::Storage::DBI::_dbh_sth('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'DBI::db=HASH(0xe08044)', 'SELECT DISTINCT( me.name, me.pr, me.background, me.licence ) ...') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 582
	DBIx::Class::Storage::DBI::dbh_do('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'CODE(0xdb50e4)', 'SELECT DISTINCT( me.name, me.pr, me.background, me.licence ) ...') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 1201
	DBIx::Class::Storage::DBI::sth('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'SELECT DISTINCT( me.name, me.pr, me.background, me.licence ) ...', 'select') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 981
	DBIx::Class::Storage::DBI::_dbh_execute('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'DBI::db=HASH(0xe08044)', 'select', 'undef', 'ARRAY(0xe32df0)', 'HASH(0xe33540)', 'ARRAY(0xe32ce8)', 'HASH(0xe32bb0)', 'ARRAY(0xe32cf4)', ...) called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 582
	DBIx::Class::Storage::DBI::dbh_do('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'CODE(0xdb2d5c)', 'select', 'undef', 'ARRAY(0xe32df0)', 'HASH(0xe33540)', 'ARRAY(0xe32ce8)', 'HASH(0xe32bb0)', 'ARRAY(0xe32cf4)', ...) called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 1013
	DBIx::Class::Storage::DBI::_execute('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'select', 'undef', 'ARRAY(0xe32df0)', 'HASH(0xe33540)', 'ARRAY(0xe32ce8)', 'HASH(0xe32bb0)', 'ARRAY(0xe32cf4)', 'undef', ...) called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 1128
	DBIx::Class::Storage::DBI::_select('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'ARRAY(0xe32df0)', 'ARRAY(0xe32ce8)', 'HASH(0xe32bb0)', 'HASH(0xe32e98)') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI/MultiDistinctEmulation.pm line 18
	DBIx::Class::Storage::DBI::MultiDistinctEmulation::_select('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'ARRAY(0xe32df0)', 'ARRAY(0xe32ce8)', 'HASH(0xe32bb0)', 'HASH(0xe32e98)') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI/Cursor.pm line 121
	DBIx::Class::Storage::DBI::Cursor::_dbh_all('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'DBI::db=HASH(0xe08044)', 'DBIx::Class::Storage::DBI::Cursor=HASH(0xe33258)') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 582
	DBIx::Class::Storage::DBI::dbh_do('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'CODE(0xd90d0c)', 'DBIx::Class::Storage::DBI::Cursor=HASH(0xe33258)') called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI/Cursor.pm line 131
	DBIx::Class::Storage::DBI::Cursor::all('DBIx::Class::Storage::DBI::Cursor=HASH(0xe33258)') called at /Library/Perl/5.8.8/DBIx/Class/ResultSet.pm line 1052
	DBIx::Class::ResultSet::all('DBIx::Class::ResultSet=HASH(0xe32d9c)') called at /Library/Perl/5.8.8/DBIx/Class/ResultSet.pm line 151
	DBIx::Class::ResultSet::search('DBIx::Class::ResultSet=HASH(0xe32b2c)', 'HASH(0xe32bb0)', 'HASH(0xe32c40)') called at /Users/wyrd/Development/repos/ResoManage/trunk/script/../lib/ResoManage/Controller/Employee.pm line 48
	ResoManage::Controller::Employee::__ANON__() called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 646
	eval {...} called at /Library/Perl/5.8.8/DBIx/Class/Storage/DBI.pm line 634
	DBIx::Class::Storage::DBI::txn_do('DBIx::Class::Storage::DBI::SQLite=HASH(0xd4df14)', 'CODE(0xdfe0fc)') called at /Library/Perl/5.8.8/DBIx/Class/Schema.pm line 734
	DBIx::Class::Schema::txn_do('ResoManage::Model::Schema=HASH(0xd26918)', 'CODE(0xdfe0fc)') called at /Users/wyrd/Development/repos/ResoManage/trunk/script/../lib/ResoManage/Controller/TextController.pm line 36
	eval {...} called at /Users/wyrd/Development/repos/ResoManage/trunk/script/../lib/ResoManage/Controller/TextController.pm line 36
	ResoManage::Controller::TextController::db_transact('ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)', 'CODE(0xdfe0fc)') called at /Users/wyrd/Development/repos/ResoManage/trunk/script/../lib/ResoManage/Controller/Employee.pm line 100
	ResoManage::Controller::Employee::search('ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 47
	Catalyst::Action::execute('Catalyst::Action=HASH(0xde8c24)', 'ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 32
	Catalyst::Action::__ANON__('ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst.pm line 1219
	eval {...} called at /Library/Perl/5.8.8/Catalyst.pm line 1219
	Catalyst::execute('ResoManage=HASH(0xdf7040)', 'ResoManage::Controller::Employee', 'Catalyst::Action=HASH(0xde8c24)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 42
	Catalyst::Action::dispatch('Catalyst::Action=HASH(0xde8c24)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Controller.pm line 74
	Catalyst::Controller::_ACTION('ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 47
	Catalyst::Action::execute('Catalyst::Action=HASH(0xde8ab0)', 'ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 32
	Catalyst::Action::__ANON__('ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst.pm line 1219
	eval {...} called at /Library/Perl/5.8.8/Catalyst.pm line 1219
	Catalyst::execute('ResoManage=HASH(0xdf7040)', 'ResoManage::Controller::Employee', 'Catalyst::Action=HASH(0xde8ab0)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 42
	Catalyst::Action::dispatch('Catalyst::Action=HASH(0xde8ab0)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Dispatcher.pm line 177
	Catalyst::Dispatcher::forward('Catalyst::Dispatcher=HASH(0xb700f4)', 'ResoManage=HASH(0xdf7040)', '_ACTION') called at /Library/Perl/5.8.8/Catalyst.pm line 315
	Catalyst::forward('ResoManage=HASH(0xdf7040)', '_ACTION') called at /Library/Perl/5.8.8/Catalyst/Controller.pm line 48
	Catalyst::Controller::_DISPATCH('ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 47
	Catalyst::Action::execute('Catalyst::Action=HASH(0xde88f4)', 'ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 32
	Catalyst::Action::__ANON__('ResoManage::Controller::Employee=HASH(0xdc3380)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst.pm line 1219
	eval {...} called at /Library/Perl/5.8.8/Catalyst.pm line 1219
	Catalyst::execute('ResoManage=HASH(0xdf7040)', 'ResoManage::Controller::Employee', 'Catalyst::Action=HASH(0xde88f4)') called at /Library/Perl/5.8.8/Catalyst/Action.pm line 42
	Catalyst::Action::dispatch('Catalyst::Action=HASH(0xde88f4)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Dispatcher.pm line 177
	Catalyst::Dispatcher::forward('Catalyst::Dispatcher=HASH(0xb700f4)', 'ResoManage=HASH(0xdf7040)', '/employee/_DISPATCH') called at /Library/Perl/5.8.8/Catalyst.pm line 315
	Catalyst::forward('ResoManage=HASH(0xdf7040)', '/employee/_DISPATCH') called at /Library/Perl/5.8.8/Catalyst/Dispatcher.pm line 116
	Catalyst::Dispatcher::dispatch('Catalyst::Dispatcher=HASH(0xb700f4)', 'ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst.pm line 1168
	Catalyst::dispatch('ResoManage=HASH(0xdf7040)') called at /System/Library/Perl/5.8.8/NEXT.pm line 75
	NEXT::AUTOLOAD('ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst/Plugin/Static/Simple.pm line 67
	Catalyst::Plugin::Static::Simple::dispatch('ResoManage=HASH(0xdf7040)') called at /Library/Perl/5.8.8/Catalyst.pm line 1510
	eval {...} called at /Library/Perl/5.8.8/Catalyst.pm line 1501
	Catalyst::handle_request('ResoManage') called at /Library/Perl/5.8.8/Catalyst/Engine/HTTP.pm line 380
	Catalyst::Engine::HTTP::_handler('Catalyst::Engine::HTTP::Restarter=HASH(0xb50e38)', 'ResoManage', 3000, 'GET', '/employee/search?k=%E6%AD%8C%E3%80%80%E3%83%89%E3%83%A9%E3%81...', 'HTTP/1.1') called at /Library/Perl/5.8.8/Catalyst/Engine/HTTP.pm line 285
	Catalyst::Engine::HTTP::run('Catalyst::Engine::HTTP::Restarter=HASH(0xb50e38)', 'ResoManage', 3000, 'undef', 'HASH(0x800c9c)') called at /System/Library/Perl/5.8.8/NEXT.pm line 75
	NEXT::AUTOLOAD('Catalyst::Engine::HTTP::Restarter=HASH(0xb50e38)', 'ResoManage', 3000, 'undef', 'HASH(0x800c9c)') called at /Library/Perl/5.8.8/Catalyst/Engine/HTTP/Restarter.pm line 70
	Catalyst::Engine::HTTP::Restarter::run('Catalyst::Engine::HTTP::Restarter=HASH(0xb50e38)', 'ResoManage', 3000, 'undef', 'HASH(0x800c9c)') called at /Library/Perl/5.8.8/Catalyst.pm line 1824
	Catalyst::run('ResoManage', 3000, 'undef', 'HASH(0x800c9c)') called at script/resomanage_server.pl line 57
[info] Request took 0.150941s (6.625/s)

SQLSQLiteで実行してみると

sqlite> SELECT DISTINCT( me.name, me.pr, me.background, me.licence ) FROM employee me WHERE ( name LIKE '%hoge%');
SQL error: near ",": syntax error

DISTINCTの括弧をとって実行すると、すんなり通った。ってことで、ソースを追っていったり調べたりしたところ。

DBIx::Class::Storage::DBIクラスが書かれてるファイルの_recurse_fieldsを書き換えたら動いた。

sub _recurse_fields {
  my ($self, $fields, $params) = @_;
  my $ref = ref $fields;
  return $self->_quote($fields) unless $ref;
  return $$fields if $ref eq 'SCALAR';

  if ($ref eq 'ARRAY') {
    return join(', ', map {
      $self->_recurse_fields($_)
        .(exists $self->{rownum_hack_count} && !($params && $params->{no_rownum_hack})
          ? ' AS col'.$self->{rownum_hack_count}++
          : '')
      } @$fields);
  } elsif ($ref eq 'HASH') {
    foreach my $func (keys %$fields) {
-      return $self->_sqlcase($func)
-        .'( '.$self->_recurse_fields($fields->{$func}).' )';
+      if($func eq 'distinct') {
+        return $self->_sqlcase($func)
+          .' '.$self->_recurse_fields($fields->{$func});
+      } else {
+        return $self->_sqlcase($func)
+          .'( '.$self->_recurse_fields($fields->{$func}).' )';
+      }
    }
  }
}

distinctの時だけ括弧を取っ払った。明らかに力技。この方法は良くないと分かっているので正しい解決方法あったら教えてください。

追記

前に、こんな事をして対策してましたが今はgroup_byを使ってます。