Improve documention on loading large data sets into plperl.
authorBruce Momjian
Fri, 12 Aug 2005 21:42:53 +0000 (21:42 +0000)
committerBruce Momjian
Fri, 12 Aug 2005 21:42:53 +0000 (21:42 +0000)
David Fetter

doc/src/sgml/plperl.sgml
src/backend/storage/buffer/bufmgr.c

index 03a2064d3cda519f6ebc42fe5ff0a4cd4b16f855..2702508880e81088bb2fc58a7d46b59e1277580c 100644 (file)
@@ -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.
      
+
     
    
   
index 6243f2481e4a3acae2257538f9d750feae161f98..9cd9ed2112cb0d68e805f28cec6ff670c51c9acc 100644 (file)
@@ -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++;
    }