|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
The 7.5 release will include the new and improved plperl. The extensions to plperl in this release will include the ability to write trigger functions, make database queries, return sets and rows and sets of rows as well as store data local to the connection. These abilities are already present in plpgsql, pltcl, plpythonu and most recently in plR. Now plperl has full procedure language functionality, too. Perl lovers will love this. In this issue we cover plain perl functions, query executing functions and trigger functions. The other features, returning composites, returning sets (and sets of composites) and connection duration storage will be covered in the next issue of General Bits. In creating these examples, I ran into a number of problems. These bugs were reported and have been reviewed by the plperl folks. We were all busy on #postgresql this long weekend.
Basic Perl FunctionsYou have long been able to create basic plperl functions in PostgreSQL. These can be handy to do the things that Perl is best at, for example formatting data. In plperl, $_[n] is how the function arguments are referenced.create or replace function strip_punc (text) returns text as ' $_[0] =~ s/\\W|\\s//g ; return $_[0]; ' language 'plperl'; Queries in Perl FunctionsThe query function is: $rv = spi_exec_query( $qry ); If the query is a SELECT, you may have an optional integer argument limiting the number of rows returned. The return values are all hashed relative to the return of the function. For SELECT you can access the row array and then the column values by hash. The number of rows is the size of the row array: $column_value = $rv->{rows}[ In these functions I have used elog to show different values at runtime. In a production system these would be removed, of course. This function calculates the minimum, maximum and average for a column in a table in one pass. The column and table names are arguments to the function. The return value is text. create or replace function mmav(text,text ) returns text as ' my $tble = $_[0]; my $col = $_[1]; my $qry = ''select ''.$col.'' from ''.$tble; my $min = 0; my $max = 0; my $sum = 0; my $rv = spi_exec_query( $qry ); elog NOTICE, $rv->{status}; elog NOTICE, @{$rv->{rows}}; if (@{$rv->{rows}} == 0 ) { return ''No Rows Found''}; for ( my $i=0; $i < @{$rv->{rows}} ; $i++ ) { if ( $i == 0 ) { $sum = $max = $min = $rv->{rows}[$i]->{$col}; next; } if ( $max < $rv->{rows}[$i]->{$col} ) { $max = $rv->{rows}[$i]->{$col}; } if ( $min > $rv->{rows}[$i]->{$col} ) { $min = $rv->{rows}[$i]->{$col}; } $sum += $rv->{rows}[$i]->{$col}; } my $result = sprintf(''min=%d max=%d avg=%d'', $min, $max, $sum/@{$rv->{rows}}); return $result; ' language 'plperl'; For INSERT, UPDATE and DELETE, the number of rows affected is in the processed hash and the status of the query is in the status hash. $number_of_rows = $rv->{processed}; $exec_status = $rv->{status};This function updates an email address in the user table but only if the logged in user matches the user in the row. Be careful with the quoting. Use double quotes and other tricks to simplify things as necessary. create or replace function upd_user( text ) returns integer as ' my $email = $_[0]; my $qry = "update users set email=''".$email."'' where who = USER ; "; my $rv = spi_exec_query( $qry ); elog NOTICE, $qry; elog NOTICE, $rv->{status}; elog NOTICE, $rv->{rows}; elog NOTICE, $rv; return $rv->{rows}; ' language 'plperl';
PLPerl Trigger FunctionsTrigger functions must be declared to return type TRIGGER as is usual for trigger functions. The return values possible are:
There are also other valuable parameters available in $_TD.
This obsessive big brother function monitors inserts and updates to the users table by tracking the changes in a separate bigbro table. In this function many of the $_TD variables are used both for values and to determine the change data for logging. create table users ( email text, who text ); create table bigbro ( who text, what text, tab text, wwhen timestamp, change text); create or replace function bigbro () returns TRIGGER as ' my $changes = ""; my $qry = "insert into bigbro (who, what, tab, wwhen, change) values (USER, ''"; $qry .= $_TD->{"event"}."-".$_TD->{"when"}."'', ''".$_TD->{"relname"}."'', now(), ''"; if ( $_TD->{"event"} eq "INSERT" ){ $changes .= "who=".$_TD->{"new"}{"who"}.", new.email=".$_TD->{"new"}{"email"}." "; } if ( $_TD->{"event"} eq "UPDATE" ){ if ( $_TD->{"new"}{"who"} ne $_TD->{"old"}{"who"}) { $changes .= "old.who=".$_TD->{"old"}{"who"}; $changes .= ", new.who=".$_TD->{"new"}{"who"}." "; } if ( $_TD->{"new"}{"email"} ne $_TD->{"old"}{"email"}) { $changes .= "old.email=".$_TD->{"old"}{"email"}; $changes .= ", new.email=".$_TD->{"new"}{"email"}." "; } } if ( $changes eq "" ) { $changes = "No Changes."; }; $qry .= $changes."'');"; my $rv = spi_exec_query( $qry ); return; ' language 'plperl'; create trigger insbb before insert on users for each row execute procedure bigbro(); create trigger updbb before update on users for each row execute procedure bigbro();The code for these examples can be found here.
|
|||||||||||||||||||||||||||||||
Comments and Corrections are welcome. Suggestions and contributions of items are also welcome. Send them in! Copyright A. Elein Mustain 2003, 2004, 2005, 2006, 2007, 2008, 2009 |