File Coverage

blib/lib/Eve/PgSqlFunction.pm
Criterion Covered Total %
statement 15 84 17.8
branch 0 14 0.0
condition n/a
subroutine 5 8 62.5
pod 2 2 100.0
total 22 108 20.3


line stmt bran cond sub pod time code
1             package Eve::PgSqlFunction;
2              
3 8     8   54 use parent qw(Eve::Class);
  8         20  
  8         76  
4              
5 8     8   527 use strict;
  8         21  
  8         261  
6 8     8   47 use warnings;
  8         25  
  8         262  
7              
8 8     8   45 use DBI;
  8         17  
  8         267  
9              
10 8     8   47 use Eve::Exception;
  8         16  
  8         7373  
11              
12             =head1 NAME
13              
14             B - a PostgreSQL stored function class.
15              
16             =head1 SYNOPSIS
17              
18             my $foo = Eve::PgSqlFunction->new(
19             connection => $pgsql_connection,
20             name => 'foo',
21             input_list => [
22             {'bar' => $pgsql_bigint}],
23             output_list => [
24             {'bar' => $pgsql_bigint},
25             {'baz' => $pgsql_text}]);
26              
27             my $result_list = $foo->execute(value_hash => {'bar' => 123});
28              
29             =head1 DESCRIPTION
30              
31             B is an adapter class for PostgreSQL stored
32             function. It adapts B B statement handle and
33             encapsulates statement preparation and execution mechanisms.
34              
35             =head3 Attributes
36              
37             =over 4
38              
39             =item C
40              
41             a service attribute containing a statement handle (not for regular
42             use).
43              
44             =back
45              
46             =head3 Constructor arguments
47              
48             =over 4
49              
50             =item C
51              
52             a PostgreSQL connection (B) object
53              
54             =item C
55              
56             a stored function name
57              
58             =item C
59              
60             an optional list of input parameters, each of which is specified as a
61             structure like
62              
63             {'parameter_name' => $parameter_type}
64              
65             where the C<$parameter_type> is a B derivative.
66              
67             =item C
68              
69             an optional list of output parameters specified, just like the
70             C argument
71              
72             =item C
73              
74             A boolean value depending of what do we expect from the function - a
75             set or one row.
76              
77             =back
78              
79             =head3 Throws
80              
81             =over 4
82              
83             =item C
84              
85             when input or output parameter definitions does not match the required
86             definition format.
87              
88             =back
89              
90             =head1 METHODS
91              
92             =head2 B
93              
94             =cut
95              
96             sub init {
97 0     0 1   my ($self, %arg_hash) = @_;
98 0           Eve::Support::arguments(
99             \%arg_hash,
100             my ($connection, $name),
101             my ($input_list, $output_list, $is_set_returning) = ([], [], 0));
102              
103 0           $self->{'_input_list'} = $self->_transform_parameter_list(
104             raw_list => $input_list);
105 0           $self->{'_output_list'} = $self->_transform_parameter_list(
106             raw_list => $output_list);
107 0           $self->{'_is_set_returning'} = $is_set_returning;
108              
109 0           $self->{'sth'} = $connection->dbh->prepare(
110             'SELECT * FROM '.$name.'('.
111             join(
112             ', ',
113             map(
114             $_->{'type'}->wrap(expression => '?'),
115 0           @{$self->_input_list})).
116             ')');
117              
118 0           my $i = 1;
119 0           for my $param (@{$self->{'_input_list'}}) {
  0            
120 0           $self->sth->bind_param(
121             $i++, undef, { 'pg_type' => $param->{'type'}->get_type() });
122             }
123              
124 0           return;
125             }
126              
127             =head2 B<_transform_parameter_list()>
128              
129             =cut
130              
131             sub _transform_parameter_list {
132 0     0     my ($self, %arg_hash) = @_;
133 0           Eve::Support::arguments(\%arg_hash, my $raw_list);
134              
135 0           my $result_list = [];
136 0           for my $hash (@{$raw_list}) {
  0            
137 0 0         if (scalar keys %{$hash} > 1) {
  0            
138 0           Eve::Error::Value->throw(
139             message => 'Wrong parameter definition');
140             }
141 0           for my $key (keys %{$hash}) {
  0            
142 0           push(
143 0           @{$result_list},
144             {'name' => $key, 'type' => $hash->{$key}});
145             }
146             }
147              
148 0           return $result_list;
149             }
150              
151             =head2 B
152              
153             Executes the stored function.
154              
155             =head3 Arguments
156              
157             =over 4
158              
159             =item C
160              
161             an optional hash of the input parameters substitutions where keys are
162             parameter names and values are values to substitute.
163              
164             =back
165              
166             =head3 Returns
167              
168             A list of hashes corresponding to the rows returning from the stored
169             function if C is true or a hash of a single row
170             otherwise.
171              
172             =head3 Throws
173              
174             =over 4
175              
176             =item C
177              
178             in case when input values does not meet the signature of the stored
179             function, when resulting columns set does not meet required output
180             parameters or when 0 or more than 1 row returned in case of not set
181             returning function.
182              
183             =back
184              
185             =cut
186              
187             sub execute {
188 0     0 1   my ($self, %arg_hash) = @_;
189 0           Eve::Support::arguments(\%arg_hash, my $value_hash = {});
190              
191 0           my $value_list = [];
192 0           for my $input (@{$self->_input_list}) {
  0            
193 0 0         if (not exists $value_hash->{$input->{'name'}}) {
194 0           Eve::Error::Value->throw(
195             message => 'Required input parameter: '.$input->{'name'});
196             }
197              
198             push(
199 0           @{$value_list},
  0            
200             $input->{'type'}->serialize(
201             value => $value_hash->{$input->{'name'}}));
202 0           delete($value_hash->{$input->{'name'}});
203             }
204              
205 0           my $key_list = [keys %{$value_hash}];
  0            
206 0 0         if (@{$key_list}) {
  0            
207 0           Eve::Error::Value->throw(
208             message =>
209             'Redundant input parameter(s): '.
210 0           join(', ', sort(@{$key_list})));
211             }
212              
213 0           $self->sth->execute(@{$value_list});
  0            
214              
215 0           my $row_hash_list = $self->sth->fetchall_arrayref({});
216 0           for my $row_hash (@{$row_hash_list}) {
  0            
217 0           my $key_hash = {%{$row_hash}};
  0            
218 0           for my $output (@{$self->_output_list}) {
  0            
219 0 0         if (not exists $row_hash->{$output->{'name'}}) {
220 0           Eve::Error::Value->throw(
221             message =>
222             'Column has not been returned: '.
223             $output->{'name'});
224             }
225              
226 0           $row_hash->{$output->{'name'}} = $output->{'type'}->deserialize(
227             value => $row_hash->{$output->{'name'}});
228 0           delete($key_hash->{$output->{'name'}});
229             }
230              
231 0           $key_list = [keys %{$key_hash}];
  0            
232 0 0         if (@{$key_list}) {
  0            
233 0           Eve::Error::Value->throw(
234             message =>
235             'Redundant column(s) returned: '.
236 0           join(', ', sort(@{$key_list})));
237             }
238             }
239              
240 0           my $result;
241 0           my $row_count = @{$row_hash_list};
  0            
242 0 0         if ($self->_is_set_returning) {
243 0           $result = $row_hash_list;
244             } else {
245 0 0         if (not $row_count == 1) {
246 0           Eve::Error::Value->throw(
247             message =>
248             'Expected 1 row from database but returned '.
249             $row_count);
250             }
251 0           $result = $row_hash_list->[0];
252             }
253              
254 0           return $result;
255             }
256              
257             =head1 SEE ALSO
258              
259             =over 4
260              
261             =item L
262              
263             =item L
264              
265             =item L
266              
267             =item L
268              
269             =item L
270              
271             =back
272              
273             =head1 LICENSE AND COPYRIGHT
274              
275             Copyright 2012 Igor Zinovyev.
276              
277             This program is free software; you can redistribute it and/or modify it
278             under the terms of either: the GNU General Public License as published
279             by the Free Software Foundation; or the Artistic License.
280              
281             See http://dev.perl.org/licenses/ for more information.
282              
283              
284             =head1 AUTHOR
285              
286             =over 4
287              
288             =item L
289              
290             =back
291              
292             =cut
293              
294             1;