PL/Perlからのデータベースアクセス

PostgreSQL 8.4.1文書
前のページ | 巻戻し | 第 40章PL/Perl - Perl手続き言語 | 早送り | 次のページ

# 40.2. PL/Perlからのデータベースアクセス

Perl関数からデータベースそのものにアクセスするには以下の関数で行います。

``spi_exec_query`(`query` [, `max-rows`])`
``spi_query`(`command`)`
``spi_fetchrow`(`cursor`)`
``spi_prepare`(`command`, `argument types`)`
``spi_exec_prepared`(`plan`)`
``spi_query_prepared`(`plan` [, `attributes`], `arguments`)`
``spi_cursor_close`(`cursor`)`
``spi_freeplan`(`plan`)`

  `spi_exec_query`はSQLコマンドを実行し、行セット全体をハッシュへの参照を要素とする配列への参照として返します。 結果が相対的に小規模であることが分かっている場合にのみこのコマンドを使用してください。 以下に最大行オプションを持った問い合わせ（`SELECT`コマンド）の例を示します。

      $rv = spi_exec_query('SELECT * FROM my_table', 5);

  これは`my_table`テーブルから5行までを返します。 `my_table`に`my_column`列がある場合、結果の第`$i`行の列値を以下のように取り出すことができます。

      $foo = $rv->{rows}[$i]->{my_column};

  `SELECT`問い合わせから返される行の総数は以下のようにアクセスできます。

      $nrows = $rv->{processed};

  以下は他の種類のコマンドを使用する例です。

      $query = "INSERT INTO my_table VALUES (1, 'test')";
      $rv = spi_exec_query($query);

  この後、以下のようにコマンドステータス（例えば`SPI_OK_INSERT`）にアクセスすることができます。

      $
      $res = $rv->{status};

  影響を受けた行数を取り出すには以下を行います。

      $nrows = $rv->{processed};

  以下に複雑な例を示します。

      CREATE TABLE test (
          i int,
          v varchar
      );

      INSERT INTO test (i, v) VALUES (1, 'first line');
      INSERT INTO test (i, v) VALUES (2, 'second line');
      INSERT INTO test (i, v) VALUES (3, 'third line');
      INSERT INTO test (i, v) VALUES (4, 'immortal');

      CREATE OR REPLACE FUNCTION test_munge() RETURNS SETOF test AS $$
          my $rv = spi_exec_query('select i, v from test;');
          my $status = $rv->{status};
          my $nrows = $rv->{processed};
          foreach my $rn (0 .. $nrows - 1) {
              my $row = $rv->{rows}[$rn];
              $row->{i} += 200 if defined($row->{i});
              $row->{v} =~ tr/A-Za-z/a-zA-Z/ if (defined($row->{v}));
              return_next($row);
          }
          return undef;
      $$ LANGUAGE plperl;

      SELECT * FROM test_munge();

  `spi_query`および`spi_fetchrow`は、大規模になる可能性がある行セット用、または、行を順番通りに返したい場合向けに組み合わせて動作します。 `spi_fetchrow`は`spi_query`と一緒でなければ動作しません。 組み合わせて使用する方法について、以下の例で示します。

      CREATE TYPE foo_type AS (the_num INTEGER, the_text TEXT);

      CREATE OR REPLACE FUNCTION lotsa_md5 (INTEGER) RETURNS SETOF foo_type AS $$
          use Digest::MD5 qw(md5_hex);
          my $file = '/usr/share/dict/words';
          my $t = localtime;
          elog(NOTICE, "opening file $file at $t" );
          open my $fh, '<', $file # ooh, it's a file access!
              or elog(ERROR, "cannot open $file for reading: $!");
          my @words = <$fh>;
          close $fh;
          $t = localtime;
          elog(NOTICE, "closed file $file at $t");
          chomp(@words);
          my $row;
          my $sth = spi_query("SELECT * FROM generate_series(1,$_[0]) AS b(a)");
          while (defined ($row = spi_fetchrow($sth))) {
              return_next({
                  the_num => $row->{a},
                  the_text => md5_hex($words[rand @words])
              });
          }
          return;
      $$ LANGUAGE plperlu;

      SELECT * from lotsa_md5(500);

  `spi_prepare`、`spi_query_prepared`、`spi_exec_prepared`、`spi_freeplan`は、準備済み問い合わせ用に同様の機能を実装します。 `spi_prepare`を呼び出して問い合わせ計画の準備が終わると、 `spi_exec_query`により返されるものと同様の結果となる`spi_exec_prepared`や`spi_query`とまったく同じカーソルが返される`spi_query_prepared`を使用して、その計画を文字列問い合わせの代わりに使用することができます。

  準備済み問い合わせの利点は、1つのプリペアド計画を複数回使用して問い合わせを実行することができるという点です。 計画が不要になった後、`spi_freeplan`を使用して、計画を開放することができます。

      CREATE OR REPLACE FUNCTION init() RETURNS INTEGER AS $$
              $_SHARED{my_plan} = spi_prepare( 'SELECT (now() + $1)::date AS now', 'INTERVAL');
      $$ LANGUAGE plperl;

      CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$
              return spi_exec_prepared(
                      $_SHARED{my_plan},
                      $_[0],
              )->{rows}->[0]->{now};
      $$ LANGUAGE plperl;

      CREATE OR REPLACE FUNCTION done() RETURNS INTEGER AS $$
              spi_freeplan( $_SHARED{my_plan});
              undef $_SHARED{my_plan};
      $$ LANGUAGE plperl;

      SELECT init();
      SELECT add_time('1 day'), add_time('2 days'), add_time('3 days');
      SELECT done();

        add_time  |  add_time  |  add_time
      ------------+------------+------------
       2005-12-10 | 2005-12-11 | 2005-12-12

  `spi_prepare`内のパラメータ添字が$1、$2、$3などを介して定義されることに注意してください。 そのため、検出困難な不具合が簡単に発生することになる二重引用符内での問い合わせ文字列宣言はやめてください。

  通常`spi_fetchrow`は、読み取る行がもう存在しないことを意味する`undef`が返されるまで、繰り返されるはずです。 `spi_fetchrow`が`undef`を返す時、カーソルは自動的に開放されます。 返されるすべての行を読み取る必要がなければ、`spi_cursor_close`を呼び出してカーソルを開放してください。 これに失敗するとメモリリークが発生します。

``elog`(`level`, `msg`)`

  ログまたはエラーメッセージを発行します。 使用できるレベルは、`DEBUG`、`LOG`、`INFO`、`NOTICE`、`WARNING`、および`ERROR`です。 `ERROR`はエラー状態を発生します。 その上位のPerlコードでこのエラーを捕捉しない場合、エラーは問い合わせの呼び出し元まで伝播し、その結果、現在のトランザクションもしくは副トランザクションはアボートします。 これは実質Perlの`die`コマンドと同じです。 他のレベルは、異なる重要度のメッセージを生成するだけです。 log_min_messagesとclient_min_messages設定パラメータは、特定の重要度のメッセージをクライアントに報告するか、サーバのログに書き出すか、あるいはその両方かを制御します。 詳細は第18章を参照してください。

前のページ | ホーム | 次のページ
PL/Perl関数と引数 | 上に戻る | PL/Perlにおけるデータ値