diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml index 7d76b4fc032ffc08f2222d599a6d0245ebadece9..3adb8829fea0b2f6b00f99372aff3f2c4bc5a964 100644 --- a/doc/src/sgml/plperl.sgml +++ b/doc/src/sgml/plperl.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.29 2004/10/15 16:51:48 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.30 2004/11/06 14:32:10 petere Exp $ --> <chapter id="plperl"> @@ -37,7 +37,6 @@ $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.29 2004/10/15 16:51:48 momjian E PL/Perl during the installation process. (Refer to <xref linkend="install-short"> for more information.) Users of binary packages might find PL/Perl in a separate subpackage. - </para> </note> @@ -85,22 +84,19 @@ $$ LANGUAGE plperl; </para> <para> - If an SQL <literal>NULL</literal> value<indexterm><primary>null - value</><secondary sortas="PL/Perl">in PL/Perl</></indexterm> is - passed to a function, the argument value will appear as - <quote>undefined</> in Perl. The above function definition will not - behave very nicely with <literal>NULL</literal> inputs (in fact, it - will act as though they are zeroes). We could add <literal>STRICT</> - to the function definition to make - <productname>PostgreSQL</productname> do something more reasonable: if - a <literal>NULL</literal> value is passed, the function will not be - called at all, but will just return a <literal>NULL</literal> result - automatically. Alternatively, we could check for undefined inputs in - the function body. For example, suppose that we wanted - <function>perl_max</function> with one <literal>NULL</literal> and one - non-<literal>NULL</literal> argument to return the - non-<literal>NULL</literal> argument, rather than a - <literal>NULL</literal> value: + If an SQL null value<indexterm><primary>null value</><secondary + sortas="PL/Perl">in PL/Perl</></indexterm> is passed to a function, + the argument value will appear as <quote>undefined</> in Perl. The + above function definition will not behave very nicely with null + inputs (in fact, it will act as though they are zeroes). We could + add <literal>STRICT</> to the function definition to make + <productname>PostgreSQL</productname> do something more reasonable: + if a null value is passed, the function will not be called at all, + but will just return a null result automatically. Alternatively, + we could check for undefined inputs in the function body. For + example, suppose that we wanted <function>perl_max</function> with + one null and one nonnull argument to return the nonnull argument, + rather than a null value: <programlisting> CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$ @@ -114,12 +110,9 @@ CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$ return $b; $$ LANGUAGE plperl; </programlisting> - </para> - - <para> - As shown above, to return an SQL <literal>NULL</literal> value from - a PL/Perl function, return an undefined value. This can be done - whether the function is strict or not. + As shown above, to return an SQL null value from a PL/Perl + function, return an undefined value. This can be done whether the + function is strict or not. </para> <para> @@ -142,26 +135,23 @@ $$ LANGUAGE plperl; SELECT name, empcomp(employee) FROM employee; </programlisting> </para> - - <para> - There is now support for returning a composite-type result value. - </para> - </sect1> <sect1 id="plperl-database"> <title>Database Access from PL/Perl</title> <para> - Access to the database itself from your Perl function can be done via - spi_exec_query, or via an experimental module <ulink + Access to the database itself from your Perl function can be done + via the function <function>spi_exec_query</function> described + below, or via an experimental module <ulink url="http://www.cpan.org/modules/by-module/DBD/APILOS/"><literal>DBD::PgSPI</literal></ulink> - (also available at <ulink url="http://www.cpan.org/SITES.html"><acronym>CPAN</> - mirror sites</ulink>). This module makes available a + (also available at <ulink + url="http://www.cpan.org/SITES.html"><acronym>CPAN</> mirror + sites</ulink>). This module makes available a <acronym>DBI</>-compliant database-handle named - <varname>$pg_dbh</varname> that can be used to perform queries - with normal <acronym>DBI</> syntax.<indexterm><primary>DBI</></indexterm> - + <varname>$pg_dbh</varname> that can be used to perform queries with + normal <acronym>DBI</> + syntax.<indexterm><primary>DBI</></indexterm> </para> <para> @@ -173,59 +163,56 @@ SELECT name, empcomp(employee) FROM employee; <primary>spi_exec_query</primary> <secondary>in PL/Perl</secondary> </indexterm> - <indexterm> - <primary>elog</primary> - <secondary>in PL/Perl</secondary> - </indexterm> - <term><function>spi_exec_query(</> [ <replaceable>SELECT query</replaceable> [, <replaceable>max_rows</replaceable>]] | [<replaceable>non-SELECT query</replaceable>] ) </term> + <term><literal><function>spi_exec_query</>(<replaceable>query</replaceable> [, <replaceable>max-rows</replaceable>])</literal></term> + <term><literal><function>spi_exec_query</>(<replaceable>command</replaceable>)</literal></term> <listitem> - <para> - Here is an example of a SELECT query with the optional maximum -number of rows. + <para> + Executes an SQL command. Here is an example of a query + (<command>SELECT</command> command) with the optional maximum + number of rows: <programlisting> -$rv = spi_exec_query('SELECT * from my_table', 5); +$rv = spi_exec_query('SELECT * FROM my_table', 5); </programlisting> - -This returns up to 5 rows from my_table. - </para> - <para> -If my_table has a column my_column, it would be accessed as + This returns up to 5 rows from the table + <literal>my_table</literal>. If <literal>my_table</literal> + has a column <literal>my_column</literal>, it could be accessed + like this: <programlisting> $foo = $rv->{rows}[$i]->{my_column}; </programlisting> - </para> - <para> -The number of rows actually returned would be: + The total number of rows returned can be accessed like this: <programlisting> $nrows = @{$rv->{rows}}; </programlisting> - </para> - <para> -Here is an example using a non-SELECT statement. + </para> + + <para> + Here is an example using a different command type: <programlisting> $query = "INSERT INTO my_table VALUES (1, 'test')"; $rv = spi_exec_query($query); </programlisting> - -You can then access status (SPI_OK_INSERT, e.g.) like this. + You can then access the command status (e.g., + <literal>SPI_OK_INSERT</literal>) like this: <programlisting> $res = $rv->{status}; </programlisting> - - </para> - <para> -To get the rows affected, do: + To get the number of rows affected, do: <programlisting> $nrows = $rv->{rows}; </programlisting> - </para> - + </para> </listitem> - </varlistentry> + <varlistentry> - <term><function>elog</> <replaceable>level</replaceable>, <replaceable>msg</replaceable></term> + <indexterm> + <primary>elog</primary> + <secondary>in PL/Perl</secondary> + </indexterm> + + <term><literal><function>elog</>(<replaceable>level</replaceable>, <replaceable>msg</replaceable>)</literal></term> <listitem> <para> Emit a log or error message. Possible levels are @@ -255,102 +242,94 @@ $nrows = $rv->{rows}; </para> <para> - PL/Perl can now return rowsets and composite types, and rowsets of -composite types. - </para> - - <para> - Here is an example of a PL/Perl function returning a rowset of a - row type. Note that a composite type is always represented as a - hash reference. + PL/Perl can also return row sets and composite types, and row sets + of composite types. Here is an example of a PL/Perl function + returning a row set of a row type. Note that a composite type is + always represented as a hash reference. <programlisting> CREATE TABLE test ( - i int, - v varchar + 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'); +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 function test_munge() returns setof test language plperl as $$ +CREATE FUNCTION test_munge() RETURNS SETOF test AS $$ my $res = []; - my $rv = spi_exec_query('select i,v from test;'); + my $rv = spi_exec_query('select i, v from test;'); my $status = $rv->{status}; my $rows = @{$rv->{rows}}; my $processed = $rv->{processed}; - foreach my $rn (0..$rows-1) { + foreach my $rn (0 .. $rows - 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})); - push @$res,$row; + push @$res, $row; } return $res; -$$; +$$ LANGUAGE plperl; -select * from test_munge(); +SELECT * FROM test_munge(); </programlisting> </para> <para> - Here is an example of a PL/Perl function returning a composite type: - <programlisting> + Here is an example of a PL/Perl function returning a composite + type: +<programlisting> CREATE TYPE testrowperl AS (f1 integer, f2 text, f3 text); CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$ - - return {f2 => 'hello', f1 => 1, f3 => 'world'}; - + return {f2 => 'hello', f1 => 1, f3 => 'world'}; $$ LANGUAGE plperl; - </programlisting> +</programlisting> </para> <para> - Here is an example of a PL/Perl function returning a rowset of a -composite type. As a rowset is always a reference to an array -and a composite type is always a reference to a hash, a rowset of a -composite type is a reference to an array of hash references. - <programlisting> + Here is an example of a PL/Perl function returning a row set of a + composite type. Since a row set is always a reference to an array + and a composite type is always a reference to a hash, a rowset of a + composite type is a reference to an array of hash references. +<programlisting> CREATE TYPE testsetperl AS (f1 integer, f2 text, f3 text); CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testsetperl AS $$ - return[ - {f1 => 1, f2 => 'hello', f3 => 'world'}, - {f1 => 2, f2 => 'hello', f3 => 'postgres'}, - {f1 => 3, f2 => 'hello', f3 => 'plperl'} - ]; + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; $$ LANGUAGE plperl; </programlisting> </para> </sect1> + <sect1 id="plperl-global"> <title>Global Values in PL/Perl</title> + <para> - You can use the %_SHARED to store data between function calls. - </para> - <para> -For example: + You can use the global hash <varname>%_SHARED</varname> to store + data between function calls. For example: <programlisting> -CREATE OR REPLACE FUNCTION set_var(name TEXT, val TEXT) RETURNS TEXT AS $$ +CREATE OR REPLACE FUNCTION set_var(name text, val text) RETURNS text AS $$ if ($_SHARED{$_[0]} = $_[1]) { return 'ok'; } else { - return "Can't set shared variable $_[0] to $_[1]"; + return "can't set shared variable $_[0] to $_[1]"; } $$ LANGUAGE plperl; -CREATE OR REPLACE FUNCTION get_var(name TEXT) RETURNS text AS $$ +CREATE OR REPLACE FUNCTION get_var(name text) RETURNS text AS $$ return $_SHARED{$_[0]}; $$ LANGUAGE plperl; -SELECT set_var('sample', $q$Hello, PL/Perl! How's tricks?$q$); +SELECT set_var('sample', 'Hello, PL/Perl! How's tricks?'); SELECT get_var('sample'); </programlisting> - </para> - - </sect1> <sect1 id="plperl-trusted"> @@ -413,63 +392,166 @@ $$ LANGUAGE plperl; <literal>plperlu</>, execution would succeed. </para> </sect1> + <sect1 id="plperl-triggers"> <title>PL/Perl Triggers</title> <para> - PL/Perl can now be used to write trigger functions using the -<varname>$_TD</varname> hash reference. - </para> + PL/Perl can be used to write trigger functions. The global hash + reference <varname>$_TD</varname> contains information about the + current trigger event. The parts of <varname>$_TD</varname> hash + reference are: - <para> - Some useful parts of the $_TD hash reference are: + <variablelist> + <varlistentry> + <term><literal>$_TD->{new}{foo}</literal></term> + <listitem> + <para> + <literal>NEW</literal> value of column <literal>foo</literal> + </para> + </listitem> + </varlistentry> -<programlisting> -$_TD->{new}{foo} # NEW value of column foo -$_TD->{old}{bar} # OLD value of column bar -$_TD{name} # Name of the trigger being called -$_TD{event} # INSERT, UPDATE, DELETE or UNKNOWN -$_TD{when} # BEFORE, AFTER or UNKNOWN -$_TD{level} # ROW, STATEMENT or UNKNOWN -$_TD{relid} # Relation ID of the table on which the trigger occurred. -$_TD{relname} # Name of the table on which the trigger occurred. -@{$_TD{argv}} # Array of arguments to the trigger function. May be empty. -$_TD{argc} # Number of arguments to the trigger. Why is this here? -</programlisting> + <varlistentry> + <term><literal>$_TD->{old}{foo}</literal></term> + <listitem> + <para> + <literal>OLD</literal> value of column <literal>foo</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>$_TD{name}</literal></term> + <listitem> + <para> + Name of the trigger being called + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>$_TD{event}</literal></term> + <listitem> + <para> + Trigger event: <literal>INSERT</>, <literal>UPDATE</>, <literal>DELETE</>, or <literal>UNKNOWN</> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>$_TD{when}</literal></term> + <listitem> + <para> + When the trigger was called: <literal>BEFORE</literal>, <literal>AFTER</literal>, or <literal>UNKNOWN</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>$_TD{level}</literal></term> + <listitem> + <para> + The trigger level: <literal>ROW</literal>, <literal>STATEMENT</literal>, or <literal>UNKNOWN</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>$_TD{relid}</literal></term> + <listitem> + <para> + OID of the table on which the trigger fired + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><literal>$_TD{relname}</literal></term> + <listitem> + <para> + Name of the table on which the trigger fired + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>@{$_TD{argv}}</literal></term> + <listitem> + <para> + Arguments of the trigger function + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>$_TD{argc}</literal></term> + <listitem> + <para> + Number of arguments of the trigger functions + </para> + </listitem> + </varlistentry> + </variablelist> </para> <para> Triggers can return one of the following: -<programlisting> -return; -- Executes the statement -SKIP; -- Doesn't execute the statement -MODIFY; -- Says it modified a NEW row -</programlisting> + + <variablelist> + <varlistentry> + <term><literal>return;</literal></term> + <listitem> + <para> + Execute the statement + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>"SKIP"</literal></term> + <listitem> + <para> + Don't execute the statement + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>"MODIFY"</literal></term> + <listitem> + <para> + Indicates that the <literal>NEW</literal> rows was modified by + the trigger function + </para> + </listitem> + </varlistentry> + </variablelist> </para> <para> -Here is an example of a trigger function, illustrating some of the -above. + Here is an example of a trigger function, illustrating some of the + above: <programlisting> CREATE TABLE test ( - i int, - v varchar + i int, + v varchar ); CREATE OR REPLACE FUNCTION valid_id() RETURNS trigger AS $$ - if (($_TD->{new}{i}>=100) || ($_TD->{new}{i}<=0)) { - return "SKIP"; # Skip INSERT/UPDATE command + if (($_TD->{new}{i} >= 100) || ($_TD->{new}{i} <= 0)) { + return "SKIP"; # skip INSERT/UPDATE command } elsif ($_TD->{new}{v} ne "immortal") { $_TD->{new}{v} .= "(modified by trigger)"; - return "MODIFY"; # Modify tuple and proceed INSERT/UPDATE command + return "MODIFY"; # modify row and execute INSERT/UPDATE command } else { - return; # Proceed INSERT/UPDATE command + return; # execute INSERT/UPDATE command } $$ LANGUAGE plperl; -CREATE TRIGGER "test_valid_id_trig" BEFORE INSERT OR UPDATE ON test -FOR EACH ROW EXECUTE PROCEDURE "valid_id"(); +CREATE TRIGGER test_valid_id_trig + BEFORE INSERT OR UPDATE ON test + FOR EACH ROW EXECUTE PROCEDURE valid_id(); </programlisting> </para> </sect1> @@ -491,19 +573,19 @@ FOR EACH ROW EXECUTE PROCEDURE "valid_id"(); <listitem> <para> - <application>Full SPI</application> is not yet implemented. + SPI is not yet fully implemented. </para> </listitem> + <listitem> - <para> - In the current implementation, if you are fetching or - returning very large datasets, you should be aware that these - will all go into memory. Future features will help with this. - In the meantime, we suggest that you not use pl/perl if you - will fetch or return very large result sets. - </para> + <para> + 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. Future features will help with this. In the + meantime, we suggest that you not use PL/Perl if you will fetch + or return very large result sets. + </para> </listitem> - </itemizedlist> </para> </sect1>