diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml index 03a2064d3cd..2702508880e 100644 --- a/doc/src/sgml/plperl.sgml +++ b/doc/src/sgml/plperl.sgml @@ -1,5 +1,5 @@ @@ -46,7 +46,12 @@ $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.42 2005/07/13 02:10:42 neilc Exp To create a function in the PL/Perl language, use the standard - syntax: + syntax. A PL/Perl function must always return a scalar value. You + can return more complex structures (arrays, records, and sets) + in the appropriate context by returning a reference. + Never return a list. Here follows an example of a PL/Perl + function. + CREATE FUNCTION funcname (argument-types) RETURNS return-type AS $$ # PL/Perl function body @@ -282,7 +287,7 @@ SELECT * FROM perl_set(); - PL/Perl provides two additional Perl commands: + PL/Perl provides three additional Perl commands: @@ -293,11 +298,18 @@ SELECT * FROM perl_set(); spi_exec_query(query [, max-rows]) spi_exec_query(command) + spi_query(command) + spi_fetchrow(command) + - Executes an SQL command. Here is an example of a query - (SELECT command) with the optional maximum - number of rows: + spi_exec_query executes an SQL command and +returns the entire rowset as a reference to an array of hash +references. You should only use this command when you know +that the result set will be relatively small. Here is an +example of a query (SELECT command) with the +optional maximum number of rows: + $rv = spi_exec_query('SELECT * FROM my_table', 5); @@ -345,7 +357,7 @@ 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 FUNCTION test_munge() RETURNS SETOF test AS $$ +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}; @@ -360,7 +372,45 @@ $$ LANGUAGE plperl; SELECT * FROM test_munge(); - + + + spi_query and spi_fetchrow + work together as a pair for rowsets which may be large, or for cases + where you wish to return rows as they arrive. + spi_fetchrow works only with + spi_query. The following example illustrates how + you use them together: + + +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); + + + @@ -716,10 +766,20 @@ CREATE TRIGGER test_valid_id_trig - In the current implementation, if you are fetching or returning - very large data sets, you should be aware that these will all go - into memory. + If you are fetching very large data sets using + spi_exec_query, you should be aware that + these will all go into memory. You can avoid this by using + spi_query/spi_fetchrow as + illustrated earlier. + + A similar problem occurs if a set-returning function passes a + large set of rows back to postgres via return. You + can avoid this problem too by instead using + return_next for each row returned, as shown + previously. + + diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 6243f2481e4..9cd9ed2112c 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.193 2005/08/12 05:05:50 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.194 2005/08/12 21:42:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -153,6 +153,8 @@ ReadBuffer(Relation reln, BlockNumber blockNum) * block is not currently in memory. */ bufHdr = BufferAlloc(reln, blockNum, &found); + /* we are guaranted that nobody else has touched this will-be-new block */ + Assert(!(found && isExtend)); if (found) BufferHitCount++; }