line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::Repgen; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
11680
|
use 5.006; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
53
|
|
4
|
1
|
|
|
1
|
|
7
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
44
|
|
5
|
1
|
|
|
1
|
|
7
|
use Carp; |
|
1
|
|
|
|
|
17
|
|
|
1
|
|
|
|
|
518
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
############################################################################################## |
10
|
|
|
|
|
|
|
############################################################################################## |
11
|
|
|
|
|
|
|
############################################################################################## |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 NAME |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
DBIx::Repgen - simple report generator from DB-selected data |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 SYNOPSIS |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
use Repgen; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
$r = DBIx::Repgen->new( |
22
|
|
|
|
|
|
|
dbh => DBI->connect(...), |
23
|
|
|
|
|
|
|
query => 'select ... from ...', |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
repdata => { |
26
|
|
|
|
|
|
|
today => `date` |
27
|
|
|
|
|
|
|
}, |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
group => ['id'], |
30
|
|
|
|
|
|
|
header => "========\n", |
31
|
|
|
|
|
|
|
footer => sub {my ($r, $data) = @_; return "$data->{NAME} : $data->{VALUE}"}, |
32
|
|
|
|
|
|
|
item => ["%20s %s", qw/NAME VALUE/], |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
output => \$out; |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$r->run(cust => 'tolik'); |
38
|
|
|
|
|
|
|
print $out; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 DESCRIPTION |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
This package implements class C, which is simple report generator from data |
43
|
|
|
|
|
|
|
received from relational database by some select-statement. Such a report can contain |
44
|
|
|
|
|
|
|
hyerarchical grouping by field values, record counters and cumulative totals (sums) of numeric |
45
|
|
|
|
|
|
|
fields for each group as well as for whole report. Each rerort part formatting may be set |
46
|
|
|
|
|
|
|
as literal string, arguments of C function or be code reference. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head2 new, class constructor |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
Constructor has one argument, hashe. Elements of this hashe define the report and are |
51
|
|
|
|
|
|
|
descriebed below. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=over |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=item sth, dbh, query - data source setting |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
The report data are got by executing some select statement against relational database |
58
|
|
|
|
|
|
|
environment. There are following wais for defining this statement. |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=over |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=item 1. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
Constructor receives in C element prepared (C<$dbh->prepare>) but not executed |
65
|
|
|
|
|
|
|
(C<$sth->execute>) statement handle. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=item 2. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Constructor receives database connection handle (from Cconnect(...)>) and full text |
70
|
|
|
|
|
|
|
of select statement to be executed. Needed C and C calls will perform |
71
|
|
|
|
|
|
|
by the report run. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item 3. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Constructor receives already prepared and executed statement handle. In this case C |
76
|
|
|
|
|
|
|
constructor parameter must be set to true. This feature may be useful by dynamic-made select |
77
|
|
|
|
|
|
|
queryes in calling programm. No prepare nor execute action will be performed by report run. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
I: you have to reset (by C method) this statemeny handle before each next |
80
|
|
|
|
|
|
|
report run. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=back |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
Samples: |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
$dbh = DBI->connect('dbi:Oracle:SID', 'user', 'password'); |
87
|
|
|
|
|
|
|
$sth1 = $dbh->prepare('select name, value from tab where value between ? and ?'); |
88
|
|
|
|
|
|
|
$rep1 = DBIx::Repgen->new(sth => $sth); |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
$rep2 = DBIx::Repgen->new(dbh => $dbh, query => "select ... "); |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
$sth3 = $dbh->prepare('select ...'); |
93
|
|
|
|
|
|
|
$sth3->execute(@param); |
94
|
|
|
|
|
|
|
$rep3 = DBIx::Repgen->new(sth => $sth3, noexec => 1); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
Using first two methods you may parametrize the report. This means sql-query can contain |
97
|
|
|
|
|
|
|
placeholders, for substituting values in report run time. See below about report parameters. |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=item param - report parameters |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
The report may have set of named parameters. Single parameter definition contain its name, |
102
|
|
|
|
|
|
|
number (or some numbers) of placeholders in source select query and optional default value. |
103
|
|
|
|
|
|
|
Parametrs definition is a hash reference, value of C element of constructor. Keys in this |
104
|
|
|
|
|
|
|
hash are parameter names and values contain placeholder numbers and default values. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
In the simpliest case parameter definition can be just zero-based number of the only placeholder |
107
|
|
|
|
|
|
|
corresponding to this parameter. In more complex cases is is hash reference. This hash I |
108
|
|
|
|
|
|
|
have C key with value of integer or list of integers and I have C key, which |
109
|
|
|
|
|
|
|
value must be scalar, code reference or array reference (where first element is code reference). |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
The C key defines zero based number (or numbers) of placeholdes in source select query |
112
|
|
|
|
|
|
|
corresponding to this parameter. The C key defines default value for optional |
113
|
|
|
|
|
|
|
parameters. If value of C is code reference then default value is result of this code call (without |
114
|
|
|
|
|
|
|
arguments). If value of C is array reference then first element of this array must |
115
|
|
|
|
|
|
|
be code reference. Default value of parameter in this case is result of call this code with arguments - |
116
|
|
|
|
|
|
|
the rest of array. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Sample of parameter definition. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
$rep = DBIx::Repgen->new( |
121
|
|
|
|
|
|
|
... |
122
|
|
|
|
|
|
|
param => { |
123
|
|
|
|
|
|
|
name => 0, |
124
|
|
|
|
|
|
|
dep => {n => 1}, |
125
|
|
|
|
|
|
|
startdate => {n => [2, 4], dflt => '2000/01/01'}, |
126
|
|
|
|
|
|
|
enddate => {n => 3, dflt => \&DefEndDate}, |
127
|
|
|
|
|
|
|
salary => {n => 5, dflt => [sub {...}, 1000, 2000]} |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
In the example C and C are required parameters corresponding to zero and first placeholders. |
132
|
|
|
|
|
|
|
C has explicit default value and substituted to second and fouth placeholders. |
133
|
|
|
|
|
|
|
C and C have defaults defining by code call in report run time, without and |
134
|
|
|
|
|
|
|
with arguments in correspondence. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=item output - the way of report output |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
The C |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=over |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=item undef or not present |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
The whole output data are the result of C method call. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item string reference |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
The output data are put into this string. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=item code reference |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
This code will be called with two arguments: the report object and string to be out. |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=back |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Output samples. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
$r = DBIx::Repgen(...); |
160
|
|
|
|
|
|
|
print $r->run(); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
$s = ''; |
163
|
|
|
|
|
|
|
$r = DBIx::Repgen(..., output => \$s,); |
164
|
|
|
|
|
|
|
$r->run(); |
165
|
|
|
|
|
|
|
print $s; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub myprint { |
168
|
|
|
|
|
|
|
my ($r, $s) = @_; |
169
|
|
|
|
|
|
|
print "*** $s ***"; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
$r = DBIx::Repgen(..., output => \&myprint,); |
172
|
|
|
|
|
|
|
$r->run(); |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item group - repport groupping |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
The report may be I. The group is sequence of records having the constant value of some |
177
|
|
|
|
|
|
|
field. This field called I. The report may have several includded groups. For |
178
|
|
|
|
|
|
|
group setting you have to define C parameter of report constructor as a reference to |
179
|
|
|
|
|
|
|
an array of group fields. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Note that the right record's sequence must be provided by C part in used select query, not |
182
|
|
|
|
|
|
|
by report itself. Sample of grouping by countries and cities. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
$r = DBIx::Repgen->new( |
185
|
|
|
|
|
|
|
..., |
186
|
|
|
|
|
|
|
query => "select country, city, population from cities |
187
|
|
|
|
|
|
|
order by country, city", |
188
|
|
|
|
|
|
|
group => [qw/COUNTRY CITY/], |
189
|
|
|
|
|
|
|
... |
190
|
|
|
|
|
|
|
); |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Note I field names are in uppercase, regardless used database server. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=item total - cumulative totals |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Value of this argument of constructor is reference to array with report fields to compute |
197
|
|
|
|
|
|
|
totals. Each field summation executed for all the report as well as for each group. See |
198
|
|
|
|
|
|
|
below about access to totals values. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=item header, footer, item etc. - definition of report parts |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
There are following I generated during report output. |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=over |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=item item |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Outputs for each record of the report. |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=item header |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Begin of whole report. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item footer |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Outputs after all, in the very end of report. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=item header_GROUPFIELD |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
Outputs in the begin of record group by GROUPFIELD field. |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=item footer_GROUPFIELD |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Outputs after record group by GROUPFIELD field. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=back |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Each of these report pats may be defined by several ways. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=over |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=item string |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
The string will be printed "as is", without any processing. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
$r = DBIx::Repgen->new( |
237
|
|
|
|
|
|
|
header => "\t\tReport about countries and cities\n", |
238
|
|
|
|
|
|
|
... |
239
|
|
|
|
|
|
|
); |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=item reference to array of strings |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
First element of this array have to be in form of C function format. The rest |
245
|
|
|
|
|
|
|
of values in the array are I (not values!) of current report data. See below |
246
|
|
|
|
|
|
|
about current report data. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
$r = DBIx::Repgen->new( |
249
|
|
|
|
|
|
|
footer => ["Total %d countries, %d cities, population %d people\n", |
250
|
|
|
|
|
|
|
qw/num_COUNTRY num_CITY sum_POPULATION/], |
251
|
|
|
|
|
|
|
... |
252
|
|
|
|
|
|
|
); |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=item code reference |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
The code is called with two arguments: report object and hash reference storing |
257
|
|
|
|
|
|
|
current report data. Subroutine may use C |
258
|
|
|
|
|
|
|
information or just return output string as its result. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
$r = DBIx::Repgen->new( |
261
|
|
|
|
|
|
|
item => sub { |
262
|
|
|
|
|
|
|
my ($r, $d) = @_; |
263
|
|
|
|
|
|
|
$r->Output("%d %s", |
264
|
|
|
|
|
|
|
$d->{POPULATION}, |
265
|
|
|
|
|
|
|
$d->{POPULATION} > 1_000_000 ? '*' : ' '); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
footer => sub {return "Report ended at " . `date`} |
269
|
|
|
|
|
|
|
... |
270
|
|
|
|
|
|
|
); |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=item reference to array where first element is code reference |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
The code is called with following arguments: report object, current report data, the rest of |
275
|
|
|
|
|
|
|
array elements. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
$r = DBIx::Repgen->new( |
278
|
|
|
|
|
|
|
header_COUNTRY => [\&hfcountry, 'header'], |
279
|
|
|
|
|
|
|
header_COUNTRY => [\&hfcountry, 'footer'], |
280
|
|
|
|
|
|
|
... |
281
|
|
|
|
|
|
|
); |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
sub hfcountry { |
284
|
|
|
|
|
|
|
my ($r, $d, $header_or_footer) = @_; |
285
|
|
|
|
|
|
|
if ($header_or_footer eq 'header') {...} else {...}; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=item max_items - max record number limit |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
If this parameter (integer number) is present then no more than C records will be |
291
|
|
|
|
|
|
|
output. It is possible to know via C method call if not all records were output. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=back |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=head3 Current report data |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
All report state data are stored in internal report variables. Access to these data from |
298
|
|
|
|
|
|
|
report parts is possible by data names. There are following fields in current |
299
|
|
|
|
|
|
|
report data. |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=over |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=item FIELDNAME |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
Fields of current report's record. Name is in I. |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=item prev_FIELDNAME |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
Value of FIELDNAME in previous record. When group boundary is detected group field has new value, |
310
|
|
|
|
|
|
|
but its previous value is still stored. This value can be used in group footers. |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=item num_report |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Number (one-based) of current output record for the whole report. This counter never resets. |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=item num_item |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
Number of record in the innermost group. |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item num_GROUPNAME |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Number of group GROUPNAME in including group. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=item total_FIELDNAME |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
Cumulative total of FIELDNAME field for the whole report. Remember FIELDNAME must be listed |
327
|
|
|
|
|
|
|
in C constructor's parameter. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item total_GROUPNAME_FIELDNAME |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Cumulative total by FIELDNAME field into GROUPNAME. These summators are reset each time |
332
|
|
|
|
|
|
|
the group boundary is reached. |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
=back |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=back |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=cut |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
|
343
|
1
|
|
|
1
|
|
8
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3292
|
|
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub new { |
346
|
1
|
|
|
1
|
1
|
3425
|
my ($class, %par) = @_; |
347
|
|
|
|
|
|
|
|
348
|
1
|
|
33
|
|
|
7
|
return bless \%par, ($class || ref $class); |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=head2 run, report execution |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
$r->run(%param); |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
The report is run and output. Input parameters are substituted as values for select query |
356
|
|
|
|
|
|
|
placeholders (see above about report's parameters). If there was no C |
357
|
|
|
|
|
|
|
then the text of report returned as a result of this method. |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
=cut |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub run { |
362
|
1
|
|
|
1
|
1
|
6
|
my ($rep, %param) = @_; |
363
|
|
|
|
|
|
|
|
364
|
1
|
|
|
|
|
4
|
my $warn = $^W; |
365
|
1
|
|
|
|
|
3
|
$^W = 0; |
366
|
|
|
|
|
|
|
|
367
|
1
|
50
|
|
|
|
15
|
unless ($rep->{sth}) { |
368
|
0
|
0
|
|
|
|
0
|
croak "Missing 'dbh' arg" unless exists $rep->{dbh}; |
369
|
0
|
0
|
0
|
|
|
0
|
croak "Missing or non-select query" unless $rep->{query} && $rep->{query} =~ /^\s*select\b/si; |
370
|
0
|
|
|
|
|
0
|
$rep->{sth} = $rep->{dbh}->prepare($rep->{query}); |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
1
|
50
|
|
|
|
5
|
unless ($rep->{output}) { |
374
|
1
|
|
|
|
|
4
|
$rep->{outputstr} = ''; |
375
|
1
|
|
|
|
|
3
|
$rep->{output} = \$rep->{outputstr}; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
1
|
|
|
|
|
2
|
delete $rep->{not_first}; |
379
|
|
|
|
|
|
|
|
380
|
1
|
|
|
|
|
5
|
$rep->{data} = {num_report => 0, num_item => 0}; |
381
|
|
|
|
|
|
|
|
382
|
1
|
50
|
|
|
|
6
|
$rep->{param} = {} unless exists $rep->{param}; |
383
|
1
|
|
|
|
|
3
|
my @param = (); |
384
|
1
|
50
|
|
|
|
4
|
goto AFTEREXEC if $rep->{noexec}; |
385
|
1
|
|
|
|
|
4
|
for my $p (keys %{$rep->{param}}) { |
|
1
|
|
|
|
|
9
|
|
386
|
0
|
0
|
|
|
|
0
|
$rep->{param}{$p} = {n => $rep->{param}{$p}} |
387
|
|
|
|
|
|
|
unless ref($rep->{param}{$p}); |
388
|
0
|
0
|
|
|
|
0
|
croak "No positions are given for '$p' parameter" unless exists $rep->{param}{$p}{n}; |
389
|
|
|
|
|
|
|
|
390
|
0
|
|
|
|
|
0
|
my @n; |
391
|
0
|
0
|
|
|
|
0
|
if (ref ($rep->{param}{$p}{n}) eq 'ARRAY') { |
|
|
0
|
|
|
|
|
|
392
|
0
|
|
|
|
|
0
|
@n = @{$rep->{param}{$p}{n}}; |
|
0
|
|
|
|
|
0
|
|
393
|
|
|
|
|
|
|
} elsif (!ref($rep->{param}{$p}{n})) { |
394
|
0
|
|
|
|
|
0
|
@n = ($rep->{param}{$p}{n}); |
395
|
|
|
|
|
|
|
} else { |
396
|
0
|
|
|
|
|
0
|
croak "Non scalar nor array reference positions for '$p' parameter"; |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
0
|
|
|
|
|
0
|
my $val; |
400
|
0
|
0
|
0
|
|
|
0
|
if (defined($param{$p}) && $param{$p} ne '') { |
|
|
0
|
|
|
|
|
|
401
|
0
|
|
|
|
|
0
|
$val = $param{$p}; |
402
|
|
|
|
|
|
|
} elsif (defined $rep->{param}{$p}{dflt}) { |
403
|
0
|
|
|
|
|
0
|
$val = $rep->{param}{$p}{dflt}; |
404
|
0
|
0
|
0
|
|
|
0
|
unless (ref $val) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# nothing |
406
|
|
|
|
|
|
|
} elsif (ref($val) eq 'CODE') { |
407
|
0
|
|
|
|
|
0
|
$val = $val->(); |
408
|
|
|
|
|
|
|
} elsif (ref($val) eq 'ARRAY' && $val->[0] && (ref($val->[0]) eq 'CODE')) { |
409
|
0
|
|
|
|
|
0
|
my ($sub, @pars) = @$val; |
410
|
0
|
|
|
|
|
0
|
$val = $sub->(@pars); |
411
|
|
|
|
|
|
|
} else { |
412
|
0
|
|
|
|
|
0
|
croak "Wrong dflt for '$p' parameter"; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} else { |
415
|
0
|
|
|
|
|
0
|
croak "Cannot determine value for parameter '$p'"; |
416
|
|
|
|
|
|
|
} |
417
|
0
|
|
|
|
|
0
|
$param[$_] = $val for grep {$_ >= 0} @n; |
|
0
|
|
|
|
|
0
|
|
418
|
0
|
|
|
|
|
0
|
$rep->{data}{"param_$p"} = $val; |
419
|
|
|
|
|
|
|
} |
420
|
1
|
|
|
|
|
10
|
$rep->{sth}->execute(@param); |
421
|
1
|
|
|
|
|
20
|
AFTEREXEC: |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# Заголовок отчета |
425
|
|
|
|
|
|
|
$rep->PrintPart('header'); |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# строки отчета |
428
|
1
|
|
|
|
|
23
|
while ($rep->{row} = $rep->{sth}->fetchrow_hashref('NAME_uc')) { |
429
|
9
|
|
|
|
|
193
|
$rep->PrintItem(); |
430
|
9
|
50
|
33
|
|
|
25
|
$rep->Abort() if $rep->{max_items} && $rep->{max_items} <= $rep->{data}{num_report}; |
431
|
9
|
50
|
|
|
|
17
|
last if $rep->Aborted(); |
432
|
|
|
|
|
|
|
} |
433
|
1
|
|
|
|
|
24
|
$rep->{sth}->finish(); |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# Если надо - завершители групп после отчета |
436
|
1
|
50
|
|
|
|
4
|
if (exists $rep->{group}) { |
437
|
|
|
|
|
|
|
# Формируем "пустую" строку |
438
|
1
|
|
|
|
|
3
|
for (keys %{$rep->{data}}) { |
|
1
|
|
|
|
|
5
|
|
439
|
11
|
100
|
|
|
|
34
|
$rep->{row}{$1} = undef if /prev_(.+)/; |
440
|
|
|
|
|
|
|
} |
441
|
1
|
|
|
|
|
4
|
$rep->PrintHeaderFooter(0, 'footer'); |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# Завершитель отчета |
445
|
1
|
|
|
|
|
13
|
$rep->PrintPart('footer'); |
446
|
|
|
|
|
|
|
|
447
|
1
|
|
|
|
|
2
|
$^W = $warn; |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# Закрыть коннекцию если надо |
450
|
1
|
50
|
33
|
|
|
6
|
$rep->{dbh}->disconnect() if $rep->{dbh} && $rep->{autoclose}; |
451
|
|
|
|
|
|
|
|
452
|
1
|
|
|
|
|
11
|
return $rep->{outputstr}; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
sub PrintItem { |
456
|
9
|
|
|
9
|
0
|
10
|
my ($r) = @_; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# Скопировать поля строки в data |
459
|
9
|
|
|
|
|
10
|
$r->{data}{$_} = $r->{row}{$_} for keys %{$r->{row}}; |
|
9
|
|
|
|
|
59
|
|
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
# Продвинуть "сквозные" сумматоры по полям |
462
|
9
|
50
|
|
|
|
25
|
if (exists $r->{total}) { |
463
|
9
|
|
|
|
|
9
|
$r->{data}{'total_' . $_} += $r->{row}{$_} for @{$r->{total}}; |
|
9
|
|
|
|
|
41
|
|
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# Есть ли граница группы? |
467
|
9
|
|
|
|
|
21
|
my $group = $r->GroupGranze(); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# Если это не самый первый раз - вывести завершители групп |
470
|
9
|
100
|
100
|
|
|
41
|
$r->PrintHeaderFooter($group, 'footer') |
471
|
|
|
|
|
|
|
if defined $group && $r->{not_first}; |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
# Установить, что уже - не первый раз |
474
|
9
|
|
|
|
|
13
|
$r->{not_first} = 1; |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# Продвинуть сквозной номер и номер в пределах младшей группы |
477
|
9
|
|
|
|
|
13
|
$r->{data}{num_report} ++; |
478
|
9
|
|
|
|
|
10
|
$r->{data}{num_item} ++; |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# Вывести заголовок группы (при этом сбрасываются сумматоры и нумераторы) |
481
|
9
|
100
|
|
|
|
24
|
$r->PrintHeaderFooter($group, 'header') if defined $group; |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# Просуммировть групповые сумматоры |
484
|
9
|
50
|
33
|
|
|
43
|
if ($r->{group} && $r->{total}) { |
485
|
9
|
|
|
|
|
9
|
for my $grname (@{$r->{group}}) { |
|
9
|
|
|
|
|
17
|
|
486
|
9
|
|
|
|
|
9
|
$r->{data}{'total_' . $grname . '_' . $_} += $r->{row}{$_} for @{$r->{total}}; |
|
9
|
|
|
|
|
47
|
|
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
# Вывести итем |
491
|
9
|
|
|
|
|
23
|
$r->PrintPart('item'); |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# Записать в $data предыдущие значения строки |
494
|
9
|
|
|
|
|
10
|
$r->{data}{'prev_' . $_} = $r->{row}{$_} for keys %{$r->{row}}; |
|
9
|
|
|
|
|
65
|
|
495
|
|
|
|
|
|
|
|
496
|
9
|
|
|
|
|
17
|
1; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
sub PrintHeaderFooter { |
500
|
6
|
|
|
6
|
0
|
10
|
my ($r, $group, $hf) = @_; |
501
|
6
|
|
|
|
|
6
|
my @group = @{$r->{group}}; |
|
6
|
|
|
|
|
34
|
|
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# Если заголовок |
504
|
6
|
100
|
|
|
|
15
|
if ($hf eq 'header') { |
505
|
|
|
|
|
|
|
# Сбросить сумматоры для каждой группы старше указанной |
506
|
3
|
50
|
|
|
|
9
|
if ($r->{total}) { |
507
|
3
|
|
|
|
|
7
|
for my $grname ((@group)[$group .. $#group]) { |
508
|
3
|
|
|
|
|
2
|
$r->{data}{'total_' . $grname . '_' . $_} = 0 for @{$r->{total}}; |
|
3
|
|
|
|
|
19
|
|
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# И нумераторы |
513
|
3
|
|
|
|
|
10
|
$r->{data}{'num_' . $r->{group}[$group]}++; |
514
|
3
|
|
|
|
|
9
|
$r->{data}{'num_' . $_} = 1 for (@group)[$group+1 .. $#group]; |
515
|
3
|
|
|
|
|
6
|
$r->{data}{'num_item'} = 1; |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# Таки напечатать заголовки или завершители |
519
|
6
|
|
|
|
|
24
|
$r->PrintPart($hf . '_' . $_) for (@group)[$group .. $#group]; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
sub GroupGranze { |
523
|
9
|
|
|
9
|
0
|
10
|
my ($r) = @_; |
524
|
|
|
|
|
|
|
|
525
|
9
|
50
|
|
|
|
21
|
return undef unless $r->{group}; |
526
|
|
|
|
|
|
|
|
527
|
9
|
|
|
|
|
10
|
my $i = 0; |
528
|
9
|
|
|
|
|
15
|
for my $fname (@{$r->{group}}) { |
|
9
|
|
|
|
|
17
|
|
529
|
9
|
50
|
|
|
|
22
|
croak "No '$fname' group field in data" unless exists $r->{row}{$fname}; |
530
|
|
|
|
|
|
|
|
531
|
9
|
100
|
66
|
|
|
82
|
return $i if |
|
|
|
66
|
|
|
|
|
532
|
|
|
|
|
|
|
!exists($r->{data}{'prev_' . $fname}) || |
533
|
|
|
|
|
|
|
( |
534
|
|
|
|
|
|
|
(($r->{data}{'prev_' . $fname} ne $r->{row}{$fname})) || |
535
|
|
|
|
|
|
|
(($r->{data}{'prev_' . $fname} != $r->{row}{$fname})) |
536
|
|
|
|
|
|
|
); |
537
|
6
|
|
|
|
|
13
|
$i++; |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
|
540
|
6
|
|
|
|
|
12
|
undef; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
sub PrintPart { |
545
|
17
|
|
|
17
|
0
|
79
|
my ($r, $part) = @_; |
546
|
|
|
|
|
|
|
|
547
|
17
|
50
|
|
|
|
36
|
return unless $r->{$part}; |
548
|
|
|
|
|
|
|
|
549
|
17
|
|
|
|
|
17
|
my ($fmt, @par); |
550
|
17
|
100
|
33
|
|
|
46
|
if (ref($r->{$part}) eq 'ARRAY') { |
|
|
50
|
|
|
|
|
|
551
|
14
|
|
|
|
|
16
|
($fmt, @par) = @{$r->{$part}}; |
|
14
|
|
|
|
|
43
|
|
552
|
|
|
|
|
|
|
} elsif (ref($r->{$part}) eq 'CODE' || !ref($r->{$part})) { |
553
|
3
|
|
|
|
|
5
|
($fmt, @par) = ($r->{$part}); |
554
|
|
|
|
|
|
|
} else { |
555
|
0
|
|
|
|
|
0
|
croak sprintf("Non supported type of format: '%s'", ref($r->{$part})); |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
17
|
|
|
|
|
24
|
my $s; |
559
|
17
|
100
|
|
|
|
28
|
if (ref $fmt) { |
560
|
4
|
|
|
|
|
16
|
$s = $fmt->($r, $r->{data}, @par); |
561
|
|
|
|
|
|
|
} else { |
562
|
13
|
|
|
|
|
18
|
$s = sprintf($fmt, map {$r->{data}{$_}} @par); |
|
30
|
|
|
|
|
92
|
|
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
|
565
|
17
|
|
|
|
|
73
|
$r->Output($s); |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
=head2 Output |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
$r->Output("Any values: %s and %d", 'qazwsx', 654); |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
This method has the same arguments as C function. It adds formatted string to the output |
573
|
|
|
|
|
|
|
stream (set by C |
574
|
|
|
|
|
|
|
report parts. |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=cut |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sub Output { |
579
|
17
|
|
|
17
|
1
|
24
|
my ($r, $s, @par) = @_; |
580
|
|
|
|
|
|
|
|
581
|
17
|
50
|
|
|
|
35
|
$s = sprintf($s, @par) if @par; |
582
|
|
|
|
|
|
|
|
583
|
17
|
50
|
|
|
|
56
|
if (ref($r->{output}) eq 'CODE') { |
|
|
50
|
|
|
|
|
|
584
|
0
|
|
|
|
|
0
|
$r->{output}->($r, $s); |
585
|
|
|
|
|
|
|
} elsif (ref($r->{output}) eq 'SCALAR') { |
586
|
17
|
|
|
|
|
49
|
${$r->{output}} .= $s; |
|
17
|
|
|
|
|
68
|
|
587
|
|
|
|
|
|
|
} else { |
588
|
0
|
|
|
|
|
0
|
croak "Non supported output method"; |
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=head2 Get, querying of report parameters |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
@group = @{$r->Get('group')}; |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Method returns value of named parameter which is set in constructor or via C method. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=cut |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub Get { |
601
|
0
|
|
|
0
|
1
|
0
|
my ($r, $name) = @_; |
602
|
0
|
|
|
|
|
0
|
return $r->{$name}; |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head2 Set, setting report parameters |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
$r->Set( |
608
|
|
|
|
|
|
|
header => "Very new header", |
609
|
|
|
|
|
|
|
item => ["%s %s", qw/NAME VALUE/] |
610
|
|
|
|
|
|
|
); |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
Method redefines report parameters. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=cut |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
sub Set { |
617
|
0
|
|
|
0
|
1
|
0
|
my ($r, %set) = @_; |
618
|
0
|
|
|
|
|
0
|
while (my ($k, $v) = each %set) { |
619
|
0
|
|
|
|
|
0
|
$r->{$k} = $v; |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head2 Abort |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
$r->Abort(); |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
Being called in the code it breaks report running. |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=cut |
630
|
|
|
|
|
|
|
|
631
|
0
|
|
|
0
|
1
|
0
|
sub Abort {$_[0]->{aborted} = 1} |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=head2 Aborted |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
if ($r->Aborted()) {...} |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
Method returns true if report execution was aborted by C method. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=cut |
640
|
|
|
|
|
|
|
|
641
|
9
|
|
|
9
|
1
|
114
|
sub Aborted {$_[0]->{aborted}} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
1; |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
__END__ |