Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

PostgreSQLのclient_encodingをdatabase.ymlのdsnで指定する

先日の続き。id:iakioさんのコメントから。ありがとうございます:-)


実はpg_connect(”options=’-c client_encoding=euc-jp’”);という書き方もできるので、
options: ’-c client_encoding=euc-jp’
でいけるかもしれません。

dsnで書けるんだろうなぁと思いつつ調べてませんでしたが、なるほど「-c」だったのかぁ。で、早速書いて。。。ん?DSNだと、どうやって書くんだ?

しょうがないので、コードを追ってみました。database.ymlの内容をparseしているのは、symfony/addon/propel/database/sfPropelDatabase.class.phpのaddConfigメソッドで、その中でsymfony/vendor/creole/Creole.phpのparseDSNメソッドを使ってました。実装ですが、

<?phppublic static function parseDSN($dsn)
    {
        if (is_array($dsn)) {
            return $dsn;
        }$info = parse_url($dsn);
                  :

URL扱いかっ!結果を見てみると、QUERY_STRINGに相当するのがoptionsになる模様。そこでdatabase.ymlに

dsn: pgsql://dbuser:dbpass@localhost:5432/service_db?options='-c client_encoding%3dutf8'

と書くと、うまくoptionsに「'-c client_encoding=utf8'」と入ります。

でも、sfPropelDatabase.class.phpのaddConfigメソッドでは、

<?phppublic function addConfig()
  {require_once('creole/Creole.php');
      $params = Creole::parseDSN($dsn);

      $options = array('phptype', 'hostspec', 'database', 'username', 'password', 'port', 'protocol', 'encoding', 'persistent', 'socket','compat_assoc_lower','compat_rtrim_string', 'options');
      foreach ($options as $option)
      {
        if (!$this->getParameter($option) && isset($params[$option]))
        {
          $this->setParameter($option, $params[$option]);
        }
      }

と、折角parseしたoptionsを考慮していないので、そもそも無理。
ということで、やっぱりパッチを作りました。

*** symfony/addon/propel/database/sfPropelDatabase.class.php.org        2008-06-28 18:05:42.000000000 +0900
--- symfony/addon/propel/database/sfPropelDatabase.class.php    2008-06-28 18:15:31.000000000 +0900
***************
*** 68,74 ****
        require_once('creole/Creole.php');
        $params = Creole::parseDSN($dsn);

!       $options = array('phptype', 'hostspec', 'database', 'username', 'password', 'port', 'protocol', 'encoding', 'persistent', 'socket','compat_assoc_lower','compat_rtrim_string');
        foreach ($options as $option)
        {
          if (!$this->getParameter($option) && isset($params[$option]))
--- 68,74 ----
        require_once('creole/Creole.php');
        $params = Creole::parseDSN($dsn);

!       $options = array('phptype', 'hostspec', 'database', 'username', 'password', 'port', 'protocol', 'encoding', 'persistent', 'socket','compat_assoc_lower','compat_rtrim_string', 'options');
        foreach ($options as $option)
        {
          if (!$this->getParameter($option) && isset($params[$option]))
***************
*** 95,100 ****
--- 95,101 ----
            'socket'     => $this->getParameter('socket'),
            'compat_assoc_lower' => $this->getParameter('compat_assoc_lower'),
            'compat_rtrim_string' => $this->getParameter('compat_rtrim_string'),
+           'options' => $this->getParameter('options'),
          ),
        );
    }

これで、前のパッチなしにちゃんと接続できました。なお、optionsに使うクオートはシングルクオートじゃないとダメです。って、何だこれは?

ちなみに、database.ymlに

all:
  propel:
    class:          sfPropelDatabase
    param:
      phptype:        pgsql
      hostspec:       localhost
      username:       dbuser
      password:       dbpass
      database:       service_db
      persistent:     true
      options:        -c client_encoding=utf8
      port:           5432

と書くと、最終的にpg_connect/pg_pconnectする際の接続文字列が

host=localhost port=5432 dbname='service_db' user='dbuser' password='dbpass' options=-c client_encoding=utf8

と、なぜかoptionsにクオートが付きません。というか、symfony/vendor/creole/drivers/pgsql/PgSQLConnection.phpのconnectメソッドで、なぜか

  • dbname
  • user
  • password

しかクオートが付いてません。これも、

*** symfony/vendor/creole/drivers/pgsql/PgSQLConnection.php.org 2008-06-28 18:42:39.000000000 +0900
--- symfony/vendor/creole/drivers/pgsql/PgSQLConnection.php     2008-06-28 18:42:39.000000000 +0900
***************
*** 87,93 ****
              $connstr .= ' password=\'' . addslashes($dsninfo['password']) . '\'';
          }
          if (!empty($dsninfo['options'])) {
!             $connstr .= ' options=' . $dsninfo['options'];
          }
          if (!empty($dsninfo['tty'])) {
              $connstr .= ' tty=' . $dsninfo['tty'];
--- 87,93 ----
              $connstr .= ' password=\'' . addslashes($dsninfo['password']) . '\'';
          }
          if (!empty($dsninfo['options'])) {
!             $connstr .= ' options=\'' . $dsninfo['options'] . '\'';
          }
          if (!empty($dsninfo['tty'])) {
              $connstr .= ' tty=' . $dsninfo['tty'];

のようにしてやれば、とりあえず文字化けせずに接続できます。

うーん。やっぱりsymfonyPostgreSQLは相性が悪い。。。というより、あまり考慮されてない感じがするなぁ。バージョンアップとかあとで面倒なことになるので、できるだけ触らないようにしたいけど、前途多難だ。。。orz