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