39.2. PL/Perl·ÎºÎÅÍÀÇ µ¥ÀÌÅͺ£À̽º Á¢±Ù

Perl ÇÔ¼ö·ÎºÎÅÍÀÇ µ¥ÀÌÅͺ£À̽º Á¢±ÙÀ», ÈļúÀÇ spi_exec_query ȤÀº DBD::PgSPI ( CPAN mirror sites ·ÎºÎÅÍ ÀÔ¼ö °¡´É)¶ó´Â ½ÇÇè ´Ü°èÀÇ ¸ðµâ °æÀ¯·Î ½Ç½ÃÇÒ ¼ö°¡ ÀÖ½À´Ï´Ù. ÀÌ ¸ðµâ¿¡ ÀÇÇØ, DBIȣȯÀÇ $pg_dbh¶ó´Â µ¥ÀÌÅͺ£À̽º ÇÚµéÀ» »ç¿ëÇÒ ¼ö°¡ ÀÖ½À´Ï´Ù. ÀÌ ÇÚµéÀ» »ç¿ëÇØ, Åë»óÀÇ DBI±¸¹®À» »ç¿ëÇÑ Äõ¸®¸¦ ½Ç½ÃÇÒ ¼ö°¡ ÀÖ½À´Ï´Ù.

ÇöÀç, 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, "can't 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¼³Á¤ ¸Å°³º¯¼ö´Â, ƯÁ¤ÀÇ Á߿䵵ÀÇ ¸Þ¼¼Áö¸¦ Ŭ¶óÀÌ¾ðÆ®¿¡ º¸°íÇϰųª, ¼­¹öÀÇ ·Î±×¿¡ ½á³»°Å³ª, ȤÀº ±× ¾çÂÊ ¸ðµÎ¸¦ Á¦¾îÇÕ´Ï´Ù. ÀÚ¼¼ÇÑ °ÍÀº Chapter 17À» ÂüÁ¶ÇØ ÁÖ¼¼¿ä.