File Coverage

blib/lib/PostgreSQL/PLPerl/Call.pm
Criterion Covered Total %
statement 17 84 20.2
branch 0 40 0.0
condition 0 11 0.0
subroutine 5 13 38.4
pod 0 1 0.0
total 22 149 14.7


line stmt bran cond sub pod time code
1             package PostgreSQL::PLPerl::Call;
2             our $VERSION = '1.006';
3              
4             =head1 NAME
5              
6             PostgreSQL::PLPerl::Call - Simple interface for calling SQL functions from PostgreSQL PL/Perl
7              
8             =head1 VERSION
9              
10             version 1.006
11              
12             =head1 SYNOPSIS
13              
14             use PostgreSQL::PLPerl::Call;
15              
16             Returning single-row single-column values:
17              
18             $pi = call('pi'); # 3.14159265358979
19              
20             $net = call('network(inet)', '192.168.1.5/24'); # '192.168.1.0/24';
21              
22             $seqn = call('nextval(regclass)', $sequence_name);
23              
24             $dims = call('array_dims(text[])', '{a,b,c}'); # '[1:3]'
25              
26             # array arguments can be perl array references:
27             $ary = call('array_cat(int[], int[])', [1,2,3], [2,1]); # '{1,2,3,2,1}'
28              
29             Returning multi-row single-column values:
30              
31             @ary = call('generate_series(int,int)', 10, 15); # (10,11,12,13,14,15)
32              
33             Returning single-row multi-column values:
34              
35             # assuming create function func(int) returns table (r1 text, r2 int) ...
36             $row = call('func(int)', 42); # returns hash ref { r1=>..., r2=>... }
37              
38             Returning multi-row multi-column values:
39              
40             @rows = call('pg_get_keywords'); # ({...}, {...}, ...)
41              
42             Alternative method-call syntax:
43              
44             $pi = PG->pi();
45             $seqn = PG->nextval($sequence_name);
46              
47             Here C simply means PostgreSQL. (C is actually an imported constant whose
48             value is the name of a package containing an AUTOLOAD function that dispatches
49             to C. In case you wanted to know.)
50              
51             =head1 DESCRIPTION
52              
53             The C function provides a simple efficient way to call SQL functions
54             from PostgreSQL PL/Perl code.
55              
56             The first parameter is a I that specifies the name of the function
57             to call and, optionally, the types of the arguments.
58              
59             Any further parameters are used as argument values for the function being called.
60              
61             =head2 Signature
62              
63             The first parameter to C is a I that specifies the name of
64             the function.
65              
66             Immediately after the function name, in parenthesis, a comma separated list of
67             type names can be given. For example:
68              
69             'pi'
70             'generate_series(int,int)'
71             'array_cat(int[], int[])'
72             'myschema.myfunc(date, float8)'
73              
74             The types specify how the I to the call should be interpreted.
75             They don't have to exactly match the types used to declare the function you're
76             calling.
77              
78             You also don't have to specify types for I the arguments, just the
79             left-most arguments that need types.
80              
81             The function name should be given in the same way it would in an SQL statement,
82             so if identifier quoting is needed it should be specified already enclosed in
83             double quotes. For example:
84              
85             call('myschema."Foo Bar"');
86              
87             =head2 Array Arguments
88              
89             The argument value corresponding to a type that contains 'C<[]>' can be a
90             string formated as an array literal, or a reference to a perl array. In the
91             later case the array reference is automatically converted into an array literal
92             using the C function.
93              
94             =head2 Varadic Functions
95              
96             Functions with C arguments can be called with a fixed number of
97             arguments by repeating the type name in the signature the same number of times.
98             For example, given:
99              
100             create function vary(VARIADIC int[]) as ...
101              
102             you can call that function with three arguments using:
103              
104             call('vary(int,int,int)', $int1, $int2, $int3);
105              
106             Alternatively, you can append the string 'C<...>' to the last type in the
107             signature to indicate that the argument is variadic. For example:
108              
109             call('vary(int...)', @ints);
110              
111             Type names must be included in the signature in order to call variadic functions.
112              
113             Functions with a variadic argument must have at least one value for that
114             argument. Otherwise you'll get a "function ... does not exist" error.
115              
116             =head2 Method-call Syntax
117              
118             An alternative syntax can be used for making calls:
119              
120             PG->function_name(@args)
121              
122             For example:
123              
124             $pi = PG->pi();
125             $seqn = PG->nextval($sequence_name);
126              
127             Using this form you can't easily specify a schema name or argument types, and
128             you can't call variadic functions. (For various technical reasons.)
129             In cases where a signature is needed, like variadic or polymorphic functions,
130             you might get a somewhat confusing error message. For example:
131              
132             PG->generate_series(10,20);
133              
134             fails with the error "there is no parameter $1". The underlying problem is that
135             C is a polymorphic function: different versions of the
136             function are executed depending on the type of the arguments.
137              
138             =head2 Wrapping and Currying
139              
140             It's simple to wrap a call into an anonymous subroutine and pass that code
141             reference around. For example:
142              
143             $nextval_fn = sub { PG->nextval(@_) };
144             ...
145             $val = $nextval_fn->($sequence_name);
146              
147             or
148              
149             $some_func = sub { call('some_func(int, date[], int)', @_) };
150             ...
151             $val = $some_func->($foo, \@dates, $debug);
152              
153             You can take this approach further by specifying some of the arguments in the
154             anonymous subroutine so they don't all have to be provided in the call:
155              
156             $some_func = sub { call('some_func(int, date[], int)', $foo, shift, $debug) };
157             ...
158             $val = $some_func->(\@dates);
159              
160              
161             =head2 Results
162              
163             The C function processes return values in one of four ways depending on
164             two criteria: single column vs. multi-column results, and list context vs scalar context.
165              
166             If the results contain a single column with the same name as the function that
167             was called, then those values are extracted and returned directly. This makes
168             simple calls very simple:
169              
170             @ary = call('generate_series(int,int)', 10, 15); # (10,11,12,13,14,15)
171              
172             Otherwise, the rows are returned as references to hashes:
173              
174             @rows = call('pg_get_keywords'); # ({...}, {...}, ...)
175              
176             If the C function was executed in list context then all the values/rows
177             are returned, as shown above.
178              
179             If the function was executed in scalar context then an exception will be thrown
180             if more than one row is returned. For example:
181              
182             $foo = call('generate_series(int,int)', 10, 10); # 10
183             $bar = call('generate_series(int,int)', 10, 11); # dies
184              
185             If you only want the first result you can use list context;
186              
187             ($bar) = call('generate_series(int,int)', 10, 11);
188             $bar = (call('generate_series(int,int)', 10, 11))[0];
189              
190             =head1 ENABLING
191              
192             In order to use this module you need to arrange for it to be loaded when
193             PostgreSQL initializes a Perl interpreter.
194              
195             Create a F file in the same directory as your
196             F file, if it doesn't exist already.
197              
198             In the F file write the code to load this module.
199              
200             =head2 PostgreSQL 8.x
201              
202             Set the C before starting postgres, to something like this:
203              
204             PERL5OPT='-e "require q{plperlinit.pl}"'
205              
206             The code in the F should also include C
207             to avoid any problems with nested invocations of perl, e.g., via a C
208             function.
209              
210             =head2 PostgreSQL 9.0
211              
212             For PostgreSQL 9.0 you can still use the C method described above.
213             Alternatively, and preferably, you can use the C configuration
214             variable in the F file.
215              
216             plperl.on_init='require q{plperlinit.pl};'
217              
218             =head plperl
219              
220             You can use the L module to make the
221             call() function available for use in the C language:
222              
223             use PostgreSQL::PLPerl::Injector;
224             inject_plperl_with_names_from(PostgreSQL::PLPerl::Call => 'call');
225              
226             =head1 OTHER INFORMATION
227              
228             =head2 Performance
229              
230             Internally C uses C to create a plan to execute the
231             function with the typed arguments.
232              
233             The plan is cached using the call 'signature' as the key. Minor variations in
234             the signature will still reuse the same plan.
235              
236             For variadic functions, separate plans are created and cached for each distinct
237             number of arguments the function is called with.
238              
239             =head2 Limitations and Caveats
240              
241             Requires PostgreSQL 9.0 or later.
242              
243             Types that contain a comma can't be used in the call signature. That's not a
244             problem in practice as it only affects 'C' and 'C'
245             and the 'C<,s>' part isn't needed. Typically the 'C<(p,s)>' portion isn't used in
246             signatures.
247              
248             The return value of functions that have a C return type should not be
249             relied upon, naturally.
250              
251             =head2 Author and Copyright
252              
253             Tim Bunce L
254              
255             Copyright (c) Tim Bunce, Ireland, 2010. All rights reserved.
256             You may use and distribute on the same terms as Perl 5.10.1.
257              
258             With thanks to L for sponsoring development.
259              
260             =cut
261              
262 1     1   1193 use strict;
  1         2  
  1         45  
263 1     1   7 use warnings;
  1         2  
  1         33  
264 1     1   14 use Exporter;
  1         3  
  1         48  
265 1     1   5 use Carp;
  1         2  
  1         247  
266              
267             our @ISA = qw(Exporter);
268             our @EXPORT = qw(call PG);
269              
270             my %sig_cache;
271             our $debug = 0;
272              
273             # encapsulated package to provide an AUTOLOAD interface to call()
274 1         2 use constant PG => do {
275             package PostgreSQL::PLPerl::Call::PG;
276 1         2 our $VERSION = '1.006';
277              
278             sub AUTOLOAD {
279             #(my $function = our $AUTOLOAD) =~ s/.*:://;
280 0     0     our $AUTOLOAD =~ s/.*:://;
281 0           shift;
282 0           return PostgreSQL::PLPerl::Call::call($AUTOLOAD, @_);
283             }
284              
285 1         1225 __PACKAGE__;
286 1     1   6 };
  1         2  
287              
288              
289             sub call {
290 0     0 0   my $sig = shift;
291              
292 0           my $arity = scalar @_; # argument count to handle variadic subs
293              
294 0   0       my $how = $sig_cache{"$sig.$arity"} ||= do {
295              
296             # get a normalized signature to recheck the cache with
297             # and also extract the SP name and argument types
298 0 0         my ($stdsig, $fullspname, $spname, $arg_types) = _parse_signature($sig, $arity)
299             or croak "Can't parse '$sig'";
300 0 0         warn "parsed call($sig) => $stdsig\n"
301             if $debug;
302              
303             # recheck the cache with with the normalized signature
304 0   0       $sig_cache{"$stdsig.$arity"} ||= [ # else a new entry (for both caches)
305             $spname, # is name of column for single column results
306             scalar _mk_process_args($arg_types),
307             scalar _mk_process_call($fullspname, $arity, $arg_types),
308             $fullspname, # is name used in SQL to make the call
309             $stdsig,
310             ];
311             };
312              
313 0           my ($spname, $prepargs, $callsub) = @$how;
314              
315 0 0         my $rv = $callsub->( $prepargs ? $prepargs->(@_) : @_ );
316              
317 0           my $rows = $rv->{rows};
318 0 0         my $row1 = $rows->[0] # peek at first row
319             or return; # no row: undef in scalar context else empty list
320              
321 0   0       my $is_single_column = (keys %$row1 == 1 and exists $row1->{$spname});
322              
323 0 0         if (wantarray) { # list context - all rows
    0          
324              
325 0 0         return map { $_->{$spname} } @$rows if $is_single_column;
  0            
326 0           return @$rows;
327             }
328             elsif (defined wantarray) { # scalar context - single row
329              
330 0 0         croak "$sig was called in scalar context but returned more than one row"
331             if @$rows > 1;
332              
333 0 0         return $row1->{$spname} if $is_single_column;
334 0           return $row1;
335             }
336             # else void context - nothing to do
337 0           return;
338             }
339              
340              
341             sub _parse_signature {
342 0     0     my ($sig, $arity) = @_;
343              
344             # extract types from signature, if any
345 0           my $arg_types;
346 0 0         if ($sig =~ s/\s*\((.*?)\)\s*$//) {
347 0           $arg_types = [ split(/\s*,\s*/, lc($1), -1) ];
348 0           s/^\s+// for @$arg_types;
349 0           s/\s+$// for @$arg_types;
350              
351             # if variadic, replace '...' marker with the appropriate number
352             # of copies of the preceding type name
353 0 0 0       if (@$arg_types and $arg_types->[-1] =~ s/\s*\.\.\.//) {
354 0           my $variadic_type = pop @$arg_types;
355 0           push @$arg_types, $variadic_type
356             until @$arg_types >= $arity;
357             }
358             }
359              
360             # the full name is what's left in sig
361 0           my $fullspname = $sig;
362              
363             # extract the function name and un-escape it to get the column name
364 0           (my $spname = $fullspname) =~ s/.*\.//; # remove schema, if any
365 0 0         if ($spname =~ s/^"(.*)"$/$1/) { # unescape
366 0           $spname =~ s/""/"/;
367             }
368              
369             # compose a normalized signature
370 0 0         my $stdsig = "$fullspname".
371             ($arg_types ? "(".join(",",@$arg_types).")" : "");
372              
373 0           return ($stdsig, $fullspname, $spname, $arg_types);
374             }
375              
376              
377             sub _mk_process_args {
378 0     0     my ($arg_types) = @_;
379              
380 0 0         return undef unless $arg_types;
381              
382             # return a closure that pre-processes the arguments of the call
383             # else undef if no argument pre-processing is required
384              
385 0           my $hooks;
386 0           my $i = 0;
387 0           for my $type (@$arg_types) {
388 0 0         if ($type =~ /\[/) { # ARRAY
389 0     0     $hooks->{$i} = sub { return ::encode_array_literal(shift) };
  0            
390             }
391 0           ++$i;
392             }
393              
394 0 0         return undef unless $hooks;
395              
396             my $sub = sub {
397 0     0     my @args = @_;
398 0           while ( my ($argidx, $preproc) = each %$hooks ) {
399 0           $args[$argidx] = $preproc->($args[$argidx]);
400             }
401 0           return @args;
402 0           };
403              
404 0           return $sub;
405             }
406              
407              
408             sub _mk_process_call {
409 0     0     my ($fullspname, $arity, $arg_types) = @_;
410              
411             # return a closure that will execute the query and return result ref
412              
413 0           my $placeholders = join ",", map { '$'.$_ } 1..$arity;
  0            
414 0           my $sql = "select * from $fullspname($placeholders)";
415 0 0         my $plan = eval { ::spi_prepare($sql, $arg_types ? @$arg_types : ()) };
  0            
416 0 0         if ($@) { # internal error, should never happen
417 0           chomp $@;
418 0           croak "$@ while preparing $sql";
419             }
420              
421             my $sub = sub {
422             # XXX need to catch exceptions from here and rethrow using croak
423             # to appear to come from the callers location (outside this package)
424 0 0   0     warn "calling $sql(@_) [@{$arg_types||[]}]"
  0 0          
425             if $debug;
426 0           return ::spi_exec_prepared($plan, @_)
427 0           };
428              
429 0           return $sub;
430             }
431              
432             1;
433              
434             =begin Pod::Coverage
435              
436             call
437              
438             =end Pod::Coverage
439              
440             # vim: ts=8:sw=4:sts=4:et