File Coverage

lib/DBIx/Struct.pm
Criterion Covered Total %
statement 692 1499 46.1
branch 186 682 27.2
condition 60 174 34.4
subroutine 103 162 63.5
pod 5 18 27.7
total 1046 2535 41.2


line stmt bran cond sub pod time code
1             package DBIx::Struct::Connector;
2 1     1   4302 use strict;
  1         3  
  1         24  
3 1     1   4 use warnings;
  1         2  
  1         28  
4 1     1   4 use base 'DBIx::Connector';
  1         5  
  1         978  
5              
6             our $db_reconnect_timeout = 30;
7              
8             sub _connect {
9 0     0   0 my ($self, @args) = @_;
10 0         0 for my $try (1 .. $db_reconnect_timeout) {
11 0         0 my $dbh = eval { $self->SUPER::_connect(@args) };
  0         0  
12 0 0       0 return $dbh if $dbh;
13 0 0       0 sleep 1 if $try != $db_reconnect_timeout;
14             }
15 0 0       0 die $@ if $@;
16 0         0 die "DB connect error";
17             }
18              
19             package DBIx::Struct::Error::String;
20 1     1   28167 use strict;
  1         2  
  1         21  
21 1     1   5 use warnings;
  1         3  
  1         27  
22 1     1   4 use Carp;
  1         2  
  1         223  
23              
24             sub error_message (+%) {
25 0     0   0 my $msg = $_[0];
26 0         0 delete $msg->{result};
27 0         0 my $message = delete $msg->{message};
28 0         0 croak join "; ", $message, map { "$_: $msg->{$_}" } keys %$msg;
  0         0  
29             }
30              
31             package DBIx::Struct::Error::Hash;
32 1     1   5 use strict;
  1         1  
  1         26  
33 1     1   5 use warnings;
  1         1  
  1         52  
34              
35             sub error_message (+%) {
36 0     0   0 die $_[0];
37             }
38              
39             package DBIx::Struct;
40 1     1   4 use strict;
  1         1  
  1         21  
41 1     1   4 use warnings;
  1         1  
  1         26  
42 1     1   1201 use SQL::Abstract;
  1         12348  
  1         76  
43 1     1   11 use Digest::MD5;
  1         2  
  1         33  
44 1     1   1069 use Data::Dumper;
  1         7419  
  1         88  
45 1     1   20 use base 'Exporter';
  1         2  
  1         94  
46 1     1   14 use v5.10;
  1         3  
47              
48             our $VERSION = '0.03';
49              
50             our @EXPORT = qw{
51             one_row
52             all_rows
53             new_row
54             };
55              
56             our @EXPORT_OK = qw{
57             connector
58             hash_ref_slice
59             };
60              
61             our $conn;
62             our $update_on_destroy = 1;
63             our $connector_module = 'DBIx::Struct::Connector';
64             our $connector_constructor = 'new';
65             our $connector_args = [];
66             our $connector_driver;
67             our $table_classes_namespace = 'DBC';
68             our $query_classes_namespace = 'DBQ';
69             our $error_message_class = 'DBIx::Struct::Error::String';
70             our %driver_pk_insert;
71              
72             sub error_message (+%);
73              
74             %driver_pk_insert = (
75             _returning => sub {
76             my ($table, $pk_row_data, $pk_returninig) = @_;
77             my $ret;
78             if ($pk_row_data) {
79             $ret = <
80             ($pk_row_data) =
81             \$_->selectrow_array(\$insert . " $pk_returninig", undef, \@bind)
82             INS
83             } else {
84             $ret = <
85             \$_->do(\$insert, undef, \@bind)
86             INS
87             }
88             $ret .= <
89             or DBIx::Struct::error_message {
90             result => 'SQLERR',
91             message => 'error '.\$_->errstr.' inserting into table $table'
92             };
93             INS
94             },
95             _last_id_undef => sub {
96             my ($table, $insert_exp, $pk_row_data) = @_;
97             my $ret;
98             $ret = <
99             \$_->do(\$insert, undef, \@bind)
100             or DBIx::Struct::error_message {
101             result => 'SQLERR',
102             message => 'error '.\$_->errstr.' inserting into table $table'
103             };
104             INS
105             if ($pk_row_data) {
106             $ret .= <
107             $pk_row_data = $_->last_insert_id(undef, undef, undef, undef);
108             INS
109             }
110             },
111             _last_id_empty => sub {
112             my ($table, $insert_exp, $pk_row_data) = @_;
113             my $ret;
114             $ret = <
115             \$_->do(\$insert, undef, \@bind)
116             or DBIx::Struct::error_message {
117             result => 'SQLERR',
118             message => 'error '.\$_->errstr.' inserting into table $table'
119             };
120             INS
121             if ($pk_row_data) {
122             $ret .= <
123             $pk_row_data = $_->last_insert_id("", "", "", "");
124             INS
125             }
126             }
127             );
128              
129             $driver_pk_insert{Pg} = $driver_pk_insert{_returning};
130             $driver_pk_insert{mysql} = $driver_pk_insert{_last_id_undef};
131             $driver_pk_insert{SQLite} = $driver_pk_insert{_last_id_empty};
132              
133             sub hash_ref_slice($@) {
134 0     0 1 0 my ($hashref, @slice) = @_;
135 0 0       0 error_message {
136             message => "first parameter is not hash reference",
137             result => 'INTERR',
138             }
139             if 'HASH' ne ref $hashref;
140 0         0 map { $_ => $hashref->{$_} } @slice;
  0         0  
141             }
142              
143             sub check_package_scalar {
144 1     1 0 2 my ($package, $scalar) = @_;
145 1     1   6 no strict 'refs';
  1         2  
  1         215  
146 1         1 my $pr = \%{$package . '::'};
  1         5  
147 1         14 my $er = $$pr{$scalar};
148 1 50       13 return unless $er;
149 0         0 defined *{$er}{'SCALAR'};
  0         0  
150             }
151              
152             sub import {
153 1     1   7 my ($class, @args) = @_;
154 1         3 state $init_import = 0;
155 1         2 my $defconn = 0;
156 1         6 for (my $i = 0 ; $i < @args ; ++$i) {
157 1 50       5 if ($args[$i] eq 'connector_module') {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
158 1         4 (undef, $connector_module) = splice @args, $i, 2;
159 1         1 --$i;
160 1 50 33     7 if (not $defconn and check_package_scalar($connector_module, 'conn')) {
161 1     1   5 no strict 'refs';
  1         2  
  1         617  
162 0         0 *conn = \${$connector_module . '::conn'};
  0         0  
163             }
164             } elsif ($args[$i] eq 'connector_constructor') {
165 0         0 (undef, $connector_constructor) = splice @args, $i, 2;
166 0         0 --$i;
167             } elsif ($args[$i] eq 'table_classes_namespace') {
168 0         0 (undef, $table_classes_namespace) = splice @args, $i, 2;
169 0         0 --$i;
170             } elsif ($args[$i] eq 'query_classes_namespace') {
171 0         0 (undef, $query_classes_namespace) = splice @args, $i, 2;
172 0         0 --$i;
173             } elsif ($args[$i] eq 'connect_timeout') {
174 0         0 (undef, $db_reconnect_timeout) = splice @args, $i, 2;
175 0         0 --$i;
176             } elsif ($args[$i] eq 'error_class') {
177 0         0 my (undef, $emc) = splice @args, $i, 2;
178 0 0       0 $error_message_class = $emc if !$init_import;
179 0         0 --$i;
180             } elsif ($args[$i] eq 'connector_args') {
181 0         0 (undef, $connector_args) = splice @args, $i, 2;
182 0         0 --$i;
183             } elsif ($args[$i] eq 'connector_object') {
184 0         0 $defconn = 1;
185 0         0 my (undef, $connector_object) = splice @args, $i, 2;
186 0         0 --$i;
187 0         0 *conn = \${$connector_object};
  0         0  
188             }
189             }
190 1 50       3 if (!$init_import) {
191 1         3 my $eval = "*error_message = \\&$error_message_class" . "::error_message";
192 1         79 eval $eval;
193             }
194 1         4 my %imps = map { $_ => undef } @args, @EXPORT;
  3         9  
195 1         149 $class->export_to_level(1, $class, keys %imps);
196 1         26 $init_import = 1;
197             }
198              
199             sub connector {
200 51     51 0 194 $conn;
201             }
202              
203             sub _not_yet_connected {
204 1 50   1   5 if (not $conn) {
205 1         4 my ($dsn, $user, $password) = @_;
206 1 50 33     6 if ($dsn && $dsn !~ /^dbi:/i) {
207 0         0 $dsn = "dbi:Pg:dbname=$dsn";
208             }
209 1         8 my $connect_attrs = {
210             AutoCommit => 1,
211             PrintError => 0,
212             AutoInactiveDestroy => 1,
213             RaiseError => 0,
214             };
215 1 50       3 if ($dsn) {
216 0         0 my ($driver) = $dsn =~ /^dbi:(\w*?)(?:\((.*?)\))?:/i;
217 0 0       0 if ($driver) {
218 0 0       0 if ($driver eq 'Pg') {
    0          
219 0         0 $connect_attrs->{pg_enable_utf8} = 1;
220             } elsif ($driver eq 'mysql') {
221 0         0 $connect_attrs->{mysql_enable_utf8} = 1;
222             }
223             }
224             }
225 1 50       6 if (!@$connector_args) {
226 1         4 @$connector_args = ($dsn, $user, $password, $connect_attrs);
227             }
228 1 50       14 $conn = $connector_module->$connector_constructor(@$connector_args)
229             or error_message {
230             message => "DB connect error",
231             result => 'SQLERR',
232             };
233 1         12 $conn->mode('fixup');
234             }
235 1         7 '' =~ /()/;
236 1         7 $connector_driver = $conn->driver->{driver};
237 1     1   5 no warnings 'redefine';
  1         2  
  1         1393  
238 1         10 *connect = \&connector;
239 1         4 populate();
240 1         12 $conn;
241             }
242              
243             sub connect {
244 1     1 1 14 goto &_not_yet_connected;
245             }
246              
247             {
248             my $md5 = Digest::MD5->new;
249              
250             sub make_name {
251 33     33 0 43 my ($table) = @_;
252 33         59 my $simple_table = (index ($table, " ") == -1);
253 33         34 my $ncn;
254 33 100       54 if ($simple_table) {
255 30         141 $ncn = $table_classes_namespace . "::" . join ('', map { ucfirst ($_) } split (/[^a-zA-Z0-9]/, $table));
  39         118  
256             } else {
257 3         28 $md5->add($table);
258 3         18 $ncn = $query_classes_namespace . "::" . "G" . $md5->hexdigest;
259 3         10 $md5->reset;
260             }
261 33         91 $ncn;
262             }
263             }
264              
265             sub populate {
266 1     1 0 3 my @tables;
267             DBIx::Struct::connect->run(
268             sub {
269 1     1   21 my $sth = $_->table_info('', '', '%', "TABLE");
270 1 50       61 return if not $sth;
271 1         6 my $tables = $sth->fetchall_arrayref;
272             @tables =
273 1 50       9 map { $_->[2] } grep { $_->[3] eq 'TABLE' and $_->[2] !~ /^sql_/ } @$tables;
  3         31  
  3         21  
274             }
275 1         3 );
276 1         11 setup_row($_) for @tables;
277             }
278              
279             sub _row_data () { 0 }
280             sub _row_updates () { 1 }
281              
282             sub make_object_new {
283 6     6 0 13 my ($table, $required, $pk_row_data, $pk_returninig) = @_;
284 6         7 my $new = <
285             sub new {
286             my \$class = \$_[0];
287             my \$self = [ [] ];
288             if(CORE::defined(\$_[1]) && CORE::ref(\$_[1]) eq 'ARRAY') {
289 6         20 \$self->[@{[_row_data]}] = \$_[1];
290             }
291             NEW
292 6 100       15 if (not ref $table) {
293 3         4 $new .= <
294             elsif(CORE::defined \$_[1]) {
295             my \%insert;
296             for(my \$i = 1; \$i < \@_; \$i += 2) {
297             if (CORE::exists \$fields{\$_[\$i]}) {
298 3         27 \$self->[@{[_row_data]}]->[\$fields{\$_[\$i]}] = \$_[\$i + 1];
299             \$insert{\$_[\$i]} = \$_[\$i + 1];
300             } else {
301             DBIx::Struct::error_message {
302             result => 'SQLERR',
303             message => "unknown column \$_[\$i] inserting into table $table"
304             }
305             }
306             }
307             my (\@insert, \@values, \@bind);
308             \@insert =
309             CORE::map {
310             if(CORE::ref(\$insert{\$_}) eq 'ARRAY' and CORE::ref(\$insert{\$_}[0]) eq 'SCALAR') {
311             CORE::push \@bind, \@{\$insert{\$_}}[1..\$#{\$insert{\$_}}];
312             CORE::push \@values, \${\$insert{\$_}[0]};
313             "\$_";
314             } elsif(CORE::ref(\$insert{\$_}) eq 'REF' and CORE::ref(\${\$insert{\$_}}) eq 'ARRAY') {
315             if(CORE::defined \${\$insert{\$_}}->[0]) {
316             CORE::push \@bind, \@{\${\$insert{\$_}}}[1..\$#{\${\$insert{\$_}}}];
317             CORE::push \@values, \${\$insert{\$_}}->[0];
318             "\$_";
319             } else {
320             CORE::push \@values, "null";
321             "\$_"
322             }
323             } elsif(CORE::ref(\$insert{\$_}) eq 'SCALAR') {
324             CORE::push \@values, \${\$insert{\$_}};
325             "\$_";
326             } else {
327             CORE::push \@bind, \$insert{\$_};
328             CORE::push \@values, "?";
329             "\$_"
330             }
331             } CORE::keys \%insert;
332             my \$insert = "insert into $table (" . CORE::join( ", ", \@insert) . ") values ("
333             . CORE::join( ", ", \@values) . ")";
334             NEW
335 3 50       8 if ($required) {
336 0         0 $new .= <
337             for my \$r ($required) {
338             DBIx::Struct::error_message {
339             result => 'SQLERR',
340             message => "required field \$r is absent for table $table"
341             } if not CORE::exists \$insert{\$r};
342             }
343             NEW
344             }
345 3         16 $new .= <
346             DBIx::Struct::connect->run(
347             sub {
348             NEW
349 3         13 $new .= $driver_pk_insert{$connector_driver}->($table, $pk_row_data, $pk_returninig);
350 3         6 $new .= <
351             });
352             }
353             NEW
354             }
355 6         10 $new .= <
356             bless \$self, \$class;
357             }
358             NEW
359 6         36 $new;
360             }
361              
362             sub make_object_filter_timestamp {
363 6     6 0 9 my ($timestamps) = @_;
364 6         12 my $filter_timestamp = <
365             sub filter_timestamp {
366             my \$self = \$_[0];
367             if(\@_ == 1) {
368             for my \$f ($timestamps) {
369 6         15 \$self->[@{[_row_data]}][\$fields{\$f}] =~ s/\\.\\d+\$// if \$self->[@{[_row_data]}][\$fields{\$f}];
  6         16  
370             }
371             } else {
372             for my \$f (\@_[1..\$#_]) {
373 6         15 \$self->[@{[_row_data]}][\$fields{\$f}] =~ s/\\.\\d+\$// if \$self->[@{[_row_data]}][\$fields{\$f}];
  6         17  
374             }
375             }
376             '' =~ /()/;
377             \$self;
378             }
379             FTS
380 6         15 $filter_timestamp;
381             }
382              
383             sub make_object_set {
384 6     6 0 9 my $set = <
385             sub set {
386             my \$self = \$_[0];
387             if(CORE::defined(\$_[1])) {
388             if(CORE::ref(\$_[1]) eq 'ARRAY') {
389 6         16 \$self->[@{[_row_data]}] = \$_[1];
390 6         16 \$self->[@{[_row_updates]}] = {};
391             } elsif(CORE::ref(\$_[1]) eq 'HASH') {
392             for my \$f (CORE::keys \%{\$_[1]}) {
393             if (CORE::exists \$fields{\$_[\$f]}) {
394 6         14 \$self->[@{[_row_data]}]->[\$fields{\$f}] = \$_[1]->{\$f};
395 6         13 \$self->[@{[_row_updates]}]{\$f} = undef;
396             }
397             }
398             } elsif(not CORE::ref(\$_[1])) {
399             for(my \$i = 1; \$i < \@_; \$i += 2) {
400             if (CORE::exists \$fields{\$_[\$i]}) {
401 6         14 \$self->[@{[_row_data]}]->[\$fields{\$_[\$i]}] = \$_[\$i + 1];
402 6         12 \$self->[@{[_row_updates]}]{\$_[\$i]} = undef;
403             }
404             }
405             }
406             }
407             \$self;
408             }
409             SET
410 6         15 $set;
411             }
412              
413             sub make_object_data {
414 6     6 0 9 my $data = <
415             sub data {
416             my \$self = \$_[0];
417             my \@ret_keys;
418             my \$ret;
419             if(CORE::defined(\$_[1])) {
420             if(CORE::ref(\$_[1]) eq 'ARRAY') {
421             if(!\@{\$_[1]}) {
422 6         17 \$ret = \$self->[@{[_row_data]}];
423             } else {
424 6         14 \$ret = [CORE::map {\$self->[@{[_row_data]}]->[\$fields{\$_}] } CORE::grep {CORE::exists \$fields{\$_}} \@{\$_[1]}];
425             }
426             } else {
427             for my \$k (\@_[1..\$#_]) {
428             CORE::push \@ret_keys, \$k if CORE::exists \$fields{\$k};
429             }
430             }
431             } else {
432             \@ret_keys = keys \%fields;
433             }
434 6         13 \$ret = { CORE::map {\$_ => \$self->[@{[_row_data]}]->[\$fields{\$_}] } \@ret_keys} if not CORE::defined \$ret;
435             \$ret;
436             }
437             DATA
438 6         12 $data;
439             }
440              
441             sub make_object_update {
442 6     6 0 7 my ($table, $pk_where, $pk_row_data) = @_;
443 6         21 my $update;
444 6 100       14 if (not ref $table) {
445             # means this is just one simple table
446 3         11 $update = <
447             sub update {
448             my \$self = \$_[0];
449             if(\@_ > 1 && CORE::ref(\$_[1]) eq 'HASH') {
450             my (\$set, \$where, \@bind, \@bind_where);
451             {
452             no strict 'vars';
453             local *set_hash = \$_[1];
454             my \@unknown_columns = CORE::grep {not CORE::exists \$fields{\$_}} CORE::keys %set_hash;
455             DBIx::Struct::error_message {
456             result => 'SQLERR',
457             message => 'unknown columns '.CORE::join(", ", \@unknown_columns).' updating table $table'
458             } if \@unknown_columns;
459             \$set =
460             CORE::join ", ",
461             CORE::map {
462             if(CORE::ref(\$set_hash{\$_}) eq 'ARRAY' and CORE::ref(\$set_hash{\$_}[0]) eq 'SCALAR') {
463             CORE::push \@bind, \@{\$set_hash{\$_}}[1..\$#{\$set_hash{\$_}}];
464             "\$_ = " . \${\$set_hash{\$_}[0]};
465             } elsif(CORE::ref(\$set_hash{\$_}) eq 'REF' and CORE::ref(\${\$set_hash{\$_}}) eq 'ARRAY') {
466             if(CORE::defined \${\$set_hash{\$_}}->[0]) {
467             CORE::push \@bind, \@{\${\$set_hash{\$_}}}[1..\$#{\${\$set_hash{\$_}}}];
468             CORE::push \@values, \${\$set_hash{\$_}}->[0];
469             "\$_ = " . \${\$set_hash{\$_}}->[0];
470             } else {
471             "\$_ = null"
472             }
473             } elsif(CORE::ref(\$set_hash{\$_}) eq 'SCALAR') {
474             "\$_ = " . \${\$set_hash{\$_}};
475             } else {
476             CORE::push \@bind, \$set_hash{\$_};
477             "\$_ = ?"
478             }
479             } CORE::keys \%set_hash;
480             }
481             if(\@_ > 2) {
482             my \$cond = \$_[2];
483             if(not CORE::ref(\$cond)) {
484             \$cond = {(selectKeys)[0] => \$_[2]};
485             }
486             (\$where, \@bind_where) = SQL::Abstract->new->where(\$cond);
487             }
488             DBIx::Struct::connect->run(sub {
489             \$_->do(qq{update $table set \$set \$where}, undef, \@bind, \@bind_where)
490             or DBIx::Struct::error_message {
491             result => 'SQLERR',
492             message => 'error '.\$_->errstr.' updating table $table'
493             }
494             });
495 3         28 } elsif (CORE::ref(\$self) && \@\$self > 1 && \%{\$self->[@{[_row_updates]}]}) {
496             my (\$set, \@bind);
497             {
498             no strict 'vars';
499             \$set =
500             CORE::join ", ",
501             CORE::map {
502 3         11 local *column_value = \\\$self->[@{[_row_data]}][\$fields{\$_}];
503             if(CORE::ref(\$column_value) eq 'ARRAY' and CORE::ref(\$column_value->[0]) eq 'SCALAR') {
504             CORE::push \@bind, \@{\$column_value}[1..\$#\$column_value];
505             "\$_ = " . \${\$column_value->[0]};
506             } elsif(CORE::ref(\$column_value) eq 'REF' and CORE::ref(\${\$column_value}) eq 'ARRAY') {
507             if(CORE::defined \${\$column_value}->[0]) {
508             CORE::push \@bind, \@{\${\$column_value}}[1..\$#{\${\$column_value}}];
509             "\$_ = " . \${\$column_value}->[0];
510             } else {
511             "\$_ = null"
512             }
513             } elsif(CORE::ref(\$column_value) eq 'SCALAR') {
514             "\$_ = " . \$\$column_value;
515             } else {
516             CORE::push \@bind, \$column_value;
517             "\$_ = ?"
518             }
519 3         24 } CORE::keys \%{\$self->[@{[_row_updates]}]};
520             }
521             my \$update_query = qq{update $table set \$set where $pk_where};
522             DBIx::Struct::connect->run(
523             sub {
524             \$_->do(\$update_query, undef, \@bind, $pk_row_data)
525             or DBIx::Struct::error_message {
526             result => 'SQLERR',
527             message => 'error '.\$_->errstr.' updating table $table',
528             query => \$update_query,
529             bind => \\\@bind,
530             }
531             }
532             );
533             pop \@{\$self};
534             }
535             \$self;
536             }
537             UPD
538             } else {
539 3         6 $update = <
540             sub update {}
541             UPD
542             }
543 6         17 $update;
544             }
545              
546             sub make_object_delete {
547 6     6 0 10 my ($table, $pk_where, $pk_row_data) = @_;
548 6         7 my $delete;
549 6 100       12 if (not ref $table) {
550 3         13 $delete = <
551             sub delete {
552             my \$self = \$_[0];
553             if(\@_ > 1) {
554             my (\$where, \@bind);
555             my \$cond = \$_[1];
556             if(not CORE::ref(\$cond)) {
557             \$cond = {(selectKeys)[0] => \$_[1]};
558             }
559             (\$where, \@bind) = SQL::Abstract->new->where(\$cond);
560             DBIx::Struct::connect->run(sub {
561             \$_->do(qq{delete from $table \$where}, undef, \@bind)
562             or DBIx::Struct::error_message {
563             result => 'SQLERR',
564             message => 'error '.\$_->errstr.' updating table $table'
565             }
566             });
567             } else {
568             DBIx::Struct::connect->run(
569             sub {
570             \$_->do(qq{delete from $table where $pk_where}, undef, $pk_row_data)
571             or DBIx::Struct::error_message {
572             result => 'SQLERR',
573             message => 'error '.\$_->errstr.' updating table $table'
574             }
575             });
576             }
577             \$self;
578             }
579             DEL
580             } else {
581 3         5 $delete = <
582             sub delete {}
583             DEL
584             }
585 6         13 $delete;
586             }
587              
588             sub make_object_fetch {
589 6     6 0 12 my ($table, $pk_where, $pk_row_data) = @_;
590 6         8 my $fetch;
591 6 100       18 if (not ref $table) {
592 3         8 $fetch = <
593             sub fetch {
594             my \$self = \$_[0];
595             if(\@_ > 1) {
596             my (\$where, \@bind);
597             my \$cond = \$_[1];
598             if(not CORE::ref(\$cond)) {
599             \$cond = {(selectKeys)[0] => \$_[1]};
600             }
601             (\$where, \@bind) = SQL::Abstract->new->where(\$cond);
602             DBIx::Struct::connect->run(sub {
603             my \$rowref = \$_->selectrow_arrayref(qq{select * from $table \$where}, undef, \@bind)
604             or DBIx::Struct::error_message {
605             result => 'SQLERR',
606             message => 'error '.\$_->errstr.' fetching table $table'
607             };
608 3         11 \$self->[@{[_row_data]}] = [\@\$rowref];
609             });
610             } else {
611             DBIx::Struct::connect->run(
612             sub {
613             my \$rowref = \$_->selectrow_arrayref(qq{select * from $table where $pk_where}, undef, $pk_row_data)
614             or DBIx::Struct::error_message {
615             result => 'SQLERR',
616             message => 'error '.\$_->errstr.' fetching table $table'
617             };
618 3         17 \$self->[@{[_row_data]}] = [\@\$rowref];
619             });
620             }
621             \$self;
622             }
623             FETCH
624             } else {
625 3         7 $fetch = <
626             sub fetch { \$_[0] }
627             FETCH
628             }
629 6         16 $fetch;
630             }
631              
632             sub _exists_row ($) {
633 32     32   47 my $ncn = $_[0];
634 1     1   7 no strict "refs";
  1         1  
  1         165  
635 32 100       41 if (grep { !/::$/ } keys %{"${ncn}::"}) {
  698         1104  
  32         209  
636 26         77 return 1;
637             }
638 6         15 return;
639             }
640              
641             sub _parse_interface ($) {
642 0     0   0 my $interface = $_[0];
643 0         0 my %ret;
644 0 0       0 $interface = [$interface] if not ref $interface;
645 0 0       0 if ('ARRAY' eq ref $interface) {
    0          
646 0         0 for my $i (@$interface) {
647 0         0 my $dbc_name = make_name($i);
648 0 0       0 error_message {
649             result => 'SQLERR',
650             message => "Unknown base interface table $i",
651             }
652             unless _exists_row $dbc_name;
653 1     1   5 no strict 'refs';
  1         1  
  1         126  
654 0         0 my $href = \%{"${dbc_name}::fkfuncs"};
  0         0  
655 0 0 0     0 if ($href && %$href) {
656 0         0 my @i = keys %$href;
657 0         0 @ret{@i} = @{$href}{@i};
  0         0  
658             }
659             }
660             } elsif ('HASH' eq ref $interface) {
661 0         0 for my $i (keys %$interface) {
662 0         0 my $dbc_name = make_name($i);
663 0 0       0 error_message {
664             result => 'SQLERR',
665             message => "Unknown base interface table $i",
666             }
667             unless _exists_row $dbc_name;
668 1     1   5 no strict 'refs';
  1         1  
  1         2544  
669 0         0 my $href = \%{"${dbc_name}::fkfuncs"};
  0         0  
670 0 0 0     0 next if not $href or not %$href;
671 0         0 my $fl = $interface->{$i};
672 0 0       0 $fl = [$fl] if not ref $fl;
673 0 0       0 if ('ARRAY' eq ref $fl) {
674 0         0 for my $m (@$fl) {
675 0 0       0 $ret{$m} = $href->{$m} if exists $href->{$m};
676             }
677             } else {
678 0         0 error_message {
679             result => 'SQLERR',
680             message => "Usupported interface structure",
681             };
682             }
683             }
684             } else {
685 0         0 error_message {
686             result => 'SQLERR',
687             message => "Unknown interface structure: " . ref ($interface),
688             };
689             }
690 0         0 \%ret;
691             }
692              
693             sub setup_row {
694 29     29 0 44 my ($table, $ncn, $interface) = @_;
695 29         58 my $conn = DBIx::Struct::connect;
696             error_message {
697             result => 'SQLERR',
698             message => "Unsupported driver $connector_driver",
699             }
700 29 50       84 unless exists $driver_pk_insert{$connector_driver};
701 29   66     69 $ncn ||= make_name($table);
702 29 100       61 return $ncn if _exists_row $ncn ;
703 6         10 my %fields;
704             my @fields;
705 0         0 my @timestamp_fields;
706 0         0 my @required;
707 0         0 my @pkeys;
708 0         0 my @fkeys;
709 0         0 my @refkeys;
710 6 100       15 if (not ref $table) {
711             # means this is just one simple table
712             $conn->run(
713             sub {
714 3     3   33 my $cih = $_->column_info(undef, undef, $table, undef);
715 3 50       208 error_message {
716             result => 'SQLERR',
717             message => "Unknown table $table",
718             }
719             if not $cih;
720 3         4 my $i = 0;
721 3         13 while (my $chr = $cih->fetchrow_hashref) {
722 6         497 $chr->{COLUMN_NAME} =~ s/"//g;
723 6         10 push @fields, $chr->{COLUMN_NAME};
724 6 50       18 if ($chr->{TYPE_NAME} =~ /^time/) {
725 0         0 push @timestamp_fields, $chr->{COLUMN_NAME};
726             }
727 6 50 66     21 if ($chr->{NULLABLE} == 0 && !defined ($chr->{COLUMN_DEF})) {
728 0         0 push @required, $chr->{COLUMN_NAME};
729             }
730 6         78 $fields{$chr->{COLUMN_NAME}} = $i++;
731             }
732 3         31 @pkeys = $_->primary_key(undef, undef, $table);
733 3 50 66     38 if (!@pkeys && @required) {
734 0         0 my $ukh = $_->statistics_info(undef, undef, $table, 1, 1);
735 0         0 my %req = map { $_ => undef } @required;
  0         0  
736 0         0 my %pkeys;
737 0         0 while (my $ukr = $ukh->fetchrow_hashref) {
738 0 0 0     0 if (not exists $req{$ukr->{COLUMN_NAME}} or defined $ukr->{FILTER_CONDITION}) {
739 0         0 $pkeys{$ukr->{INDEX_NAME}}{drop} = 1;
740             } else {
741 0         0 $pkeys{$ukr->{INDEX_NAME}}{fields}{$ukr->{COLUMN_NAME}} = undef;
742             }
743             }
744 0         0 my @d = grep { exists $pkeys{$_}{drop} } keys %pkeys;
  0         0  
745 0         0 delete $pkeys{$_} for @d;
746 0 0       0 if (%pkeys) {
747 0         0 my @spk = sort { scalar (keys %{$pkeys{$a}{fields}}) <=> scalar (keys %{$pkeys{$b}{fields}}) }
  0         0  
  0         0  
  0         0  
748             keys %pkeys;
749 0         0 @pkeys = keys %{$pkeys{$spk[0]}{fields}};
  0         0  
750             }
751             }
752 3         12 my $sth = $_->foreign_key_info(undef, undef, undef, undef, undef, $table);
753 3 50       141 if ($sth) {
754             @fkeys =
755 3         5 grep { $_->{FK_COLUMN_NAME} !~ /[^a-z_0-9]/ } @{$sth->fetchall_arrayref({})};
  2         116  
  3         9  
756             }
757 3         26 $sth = $_->foreign_key_info(undef, undef, $table, undef, undef, undef);
758 3 50       124 if ($sth) {
759             @refkeys =
760 3         6 grep { $_->{FK_COLUMN_NAME} !~ /[^a-z_0-9]/ } @{$sth->fetchall_arrayref({})};
  2         155  
  3         8  
761             }
762             }
763 3         32 );
764             } else {
765             # means this is a query
766 3         6 %fields = %{$table->{NAME_hash}};
  3         11  
767             $conn->run(
768             sub {
769 3     3   18 for (my $cn = 0 ; $cn < @{$table->{NAME}} ; ++$cn) {
  9         29  
770 6         25 my $ti = $_->type_info($table->{TYPE}->[$cn]);
771             push @timestamp_fields, $table->{NAME}->[$cn]
772 6 50 33     79 if $ti && $ti->{TYPE_NAME} =~ /^time/;
773             }
774             }
775 3         19 );
776             }
777 6         93 my $fields = join ", ", map { qq|"$_" => $fields{$_}| } keys %fields;
  12         39  
778 6         13 my $required = '';
779 6 50       17 if (@required) {
780 0         0 $required = join (", ", map { qq|"$_"| } @required);
  0         0  
781             }
782 6         8 my $timestamps = '';
783 6 50       15 if (@timestamp_fields) {
784 0         0 $timestamps = join (", ", map { qq|"$_"| } @timestamp_fields);
  0         0  
785             } else {
786 6         12 $timestamps = "()";
787             }
788 6         31 my %keywords = (
789             new => undef,
790             set => undef,
791             data => undef,
792             delete => undef,
793             fetch => undef,
794             update => undef,
795             DESTROY => undef,
796             filter_timestamp => undef,
797             );
798 6         9 my $pk_row_data = '';
799 6         10 my $pk_returninig = '';
800 6         7 my $pk_where = '';
801 6         8 my $select_keys = '';
802 6         8 my %pk_fields;
803 6 100       21 if (@pkeys) {
804 2         4 @pk_fields{@pkeys} = undef;
805 2         5 $pk_row_data = join (", ", map { qq|\$self->[@{[_row_data]}]->[$fields{"$_"}]| } @pkeys);
  2         4  
  2         13  
806 2         5 $pk_returninig = 'returning ' . join (", ", @pkeys);
807 2         5 $pk_where = join (" and ", map { "$_ = ?" } @pkeys);
  2         7  
808 2         5 my $sk_list = join (", ", map { qq|"$_"| } @pkeys);
  2         49  
809 2         6 $select_keys = <
810             sub selectKeys () {
811             ($sk_list)
812             }
813             SK
814             } else {
815 4 100       12 if (@fields) {
816 1         3 my $sk_list = join (", ", map { qq|"$_"| } @fields);
  2         5  
817 1         4 $select_keys = <
818             sub selectKeys () {
819             ($sk_list)
820             }
821             SK
822             } else {
823 3         6 $select_keys = <
824             sub selectKeys () { () }
825             SK
826             }
827             }
828 6         12 my $foreign_tables = '';
829 6         8 my %foreign_tables;
830             my %fkfuncs;
831 6         14 for my $fk (@fkeys) {
832 2         5 $fk->{FK_COLUMN_NAME} =~ s/"//g;
833 2         4 my $fn = $fk->{FK_COLUMN_NAME};
834 2 50       9 $fn =~ s/^id_// or $fn =~ s/_id(?=[^a-z]|$)//;
835 2         7 $fn =~ tr/_/-/;
836 2         14 $fn =~ s/\b(\w)/\u$1/g;
837 2         5 $fn =~ tr/-//d;
838 2   33     9 (my $pt = $fk->{PKTABLE_NAME} || $fk->{UK_TABLE_NAME}) =~ s/"//g;
839 2   33     9 (my $pk = $fk->{PKCOLUMN_NAME} || $fk->{UK_COLUMN_NAME}) =~ s/"//g;
840 2         6 $fkfuncs{$fn} = undef;
841 2         8 $foreign_tables .= <
842             sub $fn {
843             if(CORE::defined(\$_[0]->$fk->{FK_COLUMN_NAME})) {
844             return DBIx::Struct::one_row("$pt", {$pk => \$_[0]->$fk->{FK_COLUMN_NAME}});
845             } else {
846             return
847             }
848             }
849             FKT
850 2         9 $foreign_tables{$pt} = [$fk->{FK_COLUMN_NAME} => $pk];
851             }
852 6         10 for my $ft (keys %foreign_tables) {
853 2         5 my $ucft = ucfirst $ft;
854 2         7 $fkfuncs{"foreignKey$ucft"} = undef;
855 2         8 $foreign_tables .= <
856             sub foreignKey$ucft () {("$foreign_tables{$ft}[0]" => "$foreign_tables{$ft}[1]")}
857             FKT
858             }
859 6         14 my $references_tables = '';
860 6         13 for my $rk (@refkeys) {
861 2         9 $rk->{FK_TABLE_NAME} =~ s/"//g;
862 2         4 my $ft = $rk->{FK_TABLE_NAME};
863 2         5 (my $fk = $rk->{FK_COLUMN_NAME}) =~ s/"//g;
864 2   33     10 (my $pt = $rk->{PKTABLE_NAME} || $rk->{UK_TABLE_NAME}) =~ s/"//g;
865 2   33     9 (my $pk = $rk->{PKCOLUMN_NAME} || $rk->{UK_COLUMN_NAME}) =~ s/"//g;
866 2 50       6 if ($pk ne $fk) {
867 2         3 my $fn = $fk;
868 2 50       13 $fn =~ s/^id_// or $fn =~ s/_id(?=[^a-z]|$)//;
869 2         18 $fn =~ s/$ft//;
870 2 50       8 $ft .= "_$fn" if $fn;
871             }
872 2         7 $ft =~ tr/_/-/;
873 2         25 $ft =~ s/\b(\w)/\u$1/g;
874 2         5 $ft =~ tr/-//d;
875 2         8 $fkfuncs{"ref${ft}s"} = undef;
876 2         5 $fkfuncs{"ref${ft}"} = undef;
877 2         18 $references_tables .= <
878             sub ref${ft}s {
879             my (\$self, \@cond) = \@_;
880             my \%cond;
881             if(\@cond) {
882             if(not CORE::ref \$cond[0]) {
883             \%cond = \@cond;
884             } else {
885             \%cond = \%{\$cond[0]};
886             }
887             }
888             \$cond{$fk} = \$self->$pk;
889             return DBIx::Struct::all_rows("$rk->{FK_TABLE_NAME}", \\\%cond);
890             }
891             sub ref${ft} {
892             my (\$self, \@cond) = \@_;
893             my \%cond;
894             if(\@cond) {
895             if(not CORE::ref \$cond[0]) {
896             \%cond = \@cond;
897             } else {
898             \%cond = \%{\$cond[0]};
899             }
900             }
901             \$cond{$fk} = \$self->$pk;
902             return DBIx::Struct::one_row("$rk->{FK_TABLE_NAME}", \\\%cond);
903             }
904             RT
905             }
906 6         12 my $accessors = '';
907 6         16 for my $k (keys %fields) {
908 12 50       27 next if exists $keywords{$k};
909 12 50       35 next if $k =~ /^\d/;
910 12         17 $k =~ s/[^\w\d]/_/g;
911 12 100 100     57 if (!exists ($pk_fields{$k}) && (not ref $table)) {
912 4         8 $accessors .= <
913             sub $k {
914             if(\@_ > 1) {
915 4         14 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = \$_[1];
916 4         10 \$_[0]->[@{[_row_updates]}]{"$k"} = undef;
917             }
918 4         21 \$_[0]->[@{[_row_data]}]->[$fields{$k}];
919             }
920             ACC
921             } else {
922 8         16 $accessors .= <
923             sub $k {
924 8         33 \$_[0]->[@{[_row_data]}]->[$fields{$k}];
925             }
926             ACC
927             }
928              
929             }
930 6         18 my $package_header = <
931             package ${ncn};
932             use strict;
933             use warnings;
934             use Carp;
935             use SQL::Abstract;
936             our \%fields = ($fields);
937             PHD
938 6 100       15 if (not ref $table) {
939 3 50       6 if (%fkfuncs) {
940 3         8 my $fkfuncs = join ",", map { qq{"$_" => \\&${ncn}::$_} } keys %fkfuncs;
  8         28  
941 3         8 $package_header .= <
942             our \%fkfuncs = ($fkfuncs);
943             PHD
944             } else {
945 0         0 $package_header .= <
946             our \%fkfuncs = ();
947             PHD
948             }
949 3         7 $package_header .= <
950             our \$table_name = "$table";
951             PHD
952             }
953 6         15 my $new = make_object_new($table, $required, $pk_row_data, $pk_returninig);
954 6         18 my $filter_timestamp = make_object_filter_timestamp($timestamps);
955 6         18 my $set = make_object_set();
956 6         23 my $data = make_object_data();
957 6         16 my $update = make_object_update($table, $pk_where, $pk_row_data);
958 6         18 my $delete = make_object_delete($table, $pk_where, $pk_row_data);
959 6         14 my $fetch = make_object_fetch($table, $pk_where, $pk_row_data);
960 6         8 my $destroy;
961 6 100       17 if (not ref $table) {
962 3         4 $destroy = <
963             sub DESTROY {
964             no warnings 'once';
965             \$_[0]->update if \$DBIx::Struct::update_on_destroy;
966             }
967             DESTROY
968             } else {
969 3         4 $destroy = '';
970             }
971 6         74 my $eval_code = join "", $package_header, $select_keys, $new,
972             $filter_timestamp, $set, $data, $fetch,
973             $update, $delete, $destroy, $accessors, $foreign_tables,
974             $references_tables;
975             #print $eval_code;
976 1 50 33 1   11 eval $eval_code;
  1 50 33 1   1  
  1 50 33 1   28  
  1 0 0 1   4  
  1 50 0 1   1  
  1 0 33 1   127  
  1 0 33 1   5  
  1 0 0 1   2  
  1 0 0 1   82  
  1 0 66 1   5  
  1 0 66 1   2  
  1 0 33 1   1798  
  1 0 66 1   6  
  1 0 33 1   2  
  1 0 33 1   525  
  1 50 66 1   4  
  1 50 66 1   1  
  1 0 66 1   710  
  1 0 33 1   5  
  1 0 33 1   1  
  1 50 0 1   344  
  1 0 0 1   7  
  1 50 33 1   1  
  1 0 33 1   20  
  1 0 0 1   5  
  1 0 0 1   1  
  1 0 33 1   27  
  1 0 0 1   4  
  1 0 0 1   1  
  1 0 33 1   56  
  1 0 33 1   13  
  1 0 0 1   1  
  1 0 0 1   1643  
  1 0   6   5  
  1 0   3   1  
  1 0   3   526  
  1 0   0   4  
  1 0   2   3  
  1 0   0   678  
  1 0   0   5  
  1 0   0   1  
  1 0   0   361  
  1 0   0   5  
  1 0   0   3  
  1 0   0   19  
  1 0   1   4  
  1 0   0   2  
  1 0   0   32  
  1 0   0   4  
  1 0   0   1  
  1 0   0   50  
  1 0   0   4  
  1 0   0   1  
  1 0   0   1793  
  1 0   0   6  
  1 0   0   2  
  1 50   0   531  
  1 50   0   5  
  1 0   0   2  
  1 0   0   799  
  1 0   0   7  
  1 0   0   3  
  1 0   0   508  
  1 0   1   13  
  1 0   0   2  
  1 0   0   34  
  1 0   0   6  
  1 0   3   1  
  1 0   0   41  
  1 0   0   5  
  1 0   0   1  
  1 0   4   101  
  1 0   1   6  
  1 0   1   2  
  1 0   3   1158  
  1 0   1   6  
  1 0   3   2  
  1 0   6   26  
  1 0   1   4  
  1 0   4   1  
  1 0   0   32  
  1 0   0   5  
  1 0   0   2  
  1 0   3   49  
  1 0   0   5  
  1 0   0   2  
  1 0   0   855  
  1 50   0   5  
  1 50   0   3  
  1 50   0   29  
  1 0   0   5  
  1 0   0   1  
  1 0   0   30  
  1 0   0   4  
  1 0   0   1  
  1 50   0   88  
  1 0   0   5  
  1 50   10   2  
  1 0   0   856  
  6 0   0   482  
  6 0   3   804  
  3 0   0   21  
  3 0   3   18  
  0 50       0  
  0 0       0  
  0 50       0  
  2 0       7  
  2 100       7  
  0 50       0  
  0 100       0  
  0 100       0  
  0 50       0  
  0 50       0  
  0 100       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  1 0       26  
  3 0       70  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  1 0       21  
  0 0       0  
  3 0       57  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 100       0  
  0 50       0  
  0 100       0  
  0 100       0  
  0 100       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         439  
  1         4  
  1         3  
  1         2  
  1         5  
  0         0  
  1         6  
  1         149  
  0         0  
  1         23  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  0         0  
  0         0  
  0         0  
  3         18  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         11  
  0         0  
  0         0  
  4         16  
  1         2  
  1         3  
  1         7  
  1         4  
  1         10  
  1         2  
  1         3  
  1         8  
  1         2  
  1         10  
  3         7  
  3         8  
  3         29  
  3         7  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         29  
  1         3  
  1         3  
  1         12  
  1         2  
  1         8  
  3         4  
  3         6  
  3         19  
  3         5  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         22  
  6         10  
  6         12  
  6         38  
  3         5  
  3         3  
  3         17  
  3         9  
  3         8  
  3         12  
  0         0  
  3         4  
  3         6  
  3         22  
  1         2  
  1         8  
  1         3  
  1         3  
  1         2  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         2  
  1         2  
  1         4  
  1         2  
  1         3  
  1         3  
  3         11  
  3         8  
  6         101  
  1         6  
  0         0  
  0         0  
  1         6  
  4         1104  
  3         8  
  3         7  
  4         13  
  0         0  
  0         0  
  0         0  
  3         31  
  3         5  
  3         8  
  0         0  
  0         0  
  0         0  
  0         0  
  3         10  
  3         9  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  10         52  
  10         85  
  1         4  
  1         3  
  1         4  
  1         4  
  1         6  
  1         5  
  1         3  
  1         15  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  1         7  
  1         6  
  1         2  
  1         5  
  0         0  
  1         17  
  1         197  
  3         23  
  3         6  
  3         4  
  3         4  
  3         8  
  3         47  
  1         5  
  1         3  
  1         3  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         5  
  1         2  
  1         5  
  3         8  
  3         9  
  3         9  
  3         73  
  3         7  
  10         105  
  3         6  
  3         32  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         47  
  3         4  
  3         32  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         55  
977 6 50       23 error_message {
978             result => 'SQLERR',
979             message => "Unknown error: $@",
980             } if $@;
981 6 50       16 if ($interface) {
982 0         0 my $ifuncs = _parse_interface $interface;
983 1     1   6 no strict 'refs';
  1         2  
  1         2429  
984 0         0 for my $f (keys %$ifuncs) {
985 0         0 *{"${ncn}::$f"} = $ifuncs->{$f};
  0         0  
986             }
987             }
988 6         27 '' =~ /()/;
989 6         67 return $ncn;
990             }
991              
992             sub _table_name() { 0 }
993             sub _table_alias() { 1 }
994             sub _table_join() { 2 }
995             sub _table_join_on() { 3 }
996              
997             my $sql_abstract = SQL::Abstract->new;
998             my $tblnum;
999              
1000             sub _build_complex_query {
1001 2     2   3 my ($table, $query_bind, $where) = @_;
1002 2 50       7 return $table if not ref $table;
1003 2         3 my @from;
1004             my @columns;
1005 2 50       10 my @linked_list = (
1006             ref ($table) eq 'ARRAY'
1007             ? @$table
1008             : error_message {
1009             result => 'SQLERR',
1010             message => "Unsupported type of query: " . ref ($table)
1011             }
1012             );
1013 2         3 my ($conditions, $groupby, $having, $limit, $offset);
1014 2         7 for (my $i = 0 ; $i < @linked_list ; ++$i) {
1015 6         19 my $le = $linked_list[$i];
1016 6 50       14 if ('ARRAY' eq ref $le) {
1017 0         0 my $subfrom = _build_complex_query($le, $query_bind);
1018 0         0 my $ta = "t$tblnum";
1019 0         0 ++$tblnum;
1020 0         0 push @from, ["($subfrom)", $ta];
1021 0         0 next;
1022             }
1023 6 100       14 if (substr ($le, 0, 1) ne '-') {
1024 3         9 my ($tn, $ta) = split ' ', $le;
1025 3 50       7 $ta = $tn if not $ta;
1026 3 50       9 error_message {
1027             result => 'SQLERR',
1028             message => "Unknown table $tn"
1029             }
1030             unless _exists_row(make_name($tn));
1031 3         21 push @from, [$tn, $ta];
1032             } else {
1033 3         6 my $cmd = substr ($le, 1);
1034 3 50       41 if ($cmd eq 'left') {
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1035 0         0 $from[-1][_table_join] = 'left join';
1036             } elsif ($cmd eq 'right') {
1037 0         0 $from[-1][_table_join] = 'right join';
1038             } elsif ($cmd eq 'join') {
1039 1         4 $from[-1][_table_join] = 'join';
1040             } elsif ($cmd eq 'on') {
1041 0         0 $from[-1][_table_join_on] = ["on", $linked_list[++$i]];
1042             } elsif ($cmd eq 'using') {
1043 0         0 $from[-1][_table_join_on] = ["using", $linked_list[++$i]];
1044             } elsif ($cmd eq 'as') {
1045 0         0 $from[-1][_table_alias] = $linked_list[++$i];
1046             } elsif ($cmd eq 'where') {
1047 0         0 $conditions = $linked_list[++$i];
1048             } elsif ($cmd eq 'group_by') {
1049 0         0 $groupby = $linked_list[++$i];
1050             } elsif ($cmd eq 'having') {
1051 0         0 $having = $linked_list[++$i];
1052             } elsif ($cmd eq 'limit') {
1053 0         0 $limit = 0 + $linked_list[++$i];
1054             } elsif ($cmd eq 'offset') {
1055 0         0 $offset = 0 + $linked_list[++$i];
1056             } elsif ($cmd eq 'columns') {
1057 2         5 my $cols = $linked_list[++$i];
1058 2 50       5 if (ref ($cols)) {
1059 0         0 push @columns, @$cols;
1060             } else {
1061 2         7 push @columns, $cols;
1062             }
1063             }
1064             }
1065             }
1066 2 50       6 error_message {
1067             result => 'SQLERR',
1068             message => "No table to build query on"
1069             } if !@from;
1070 2         18 for (my $idx = 1 ; $idx < @from ; ++$idx) {
1071 1 50 33     9 next if $from[$idx][_table_join_on] or not $from[$idx - 1][_table_join];
1072 1 50       4 next if substr ($from[$idx][_table_name], 0, 1) eq "(";
1073 1         3 my $cta = $from[$idx][_table_alias];
1074 1         3 my $cto = make_name($from[$idx][_table_name]);
1075 1         3 my $ucct = ucfirst $from[$idx][_table_name];
1076 1         2 my @join;
1077 1         5 for (my $i = $idx - 1 ; $i >= 0 ; --$i) {
1078 1 50       7 next if not $from[$i][_table_join];
1079 1         3 my $ptn = $from[$i][_table_name];
1080 1 50       4 next if substr ($ptn, 0, 1) eq "(";
1081 1         2 my $ucfptn = ucfirst $ptn;
1082 1 50       8 if ($cto->can("foreignKey$ucfptn")) {
1083 1         9 my $fkfn = "foreignKey$ucfptn";
1084 1         20 my ($ctf, $ptk) = $cto->$fkfn;
1085 1         8 push @join, "$cta.$ctf = " . $from[$i][_table_alias] . ".$ptk";
1086             } else {
1087 0         0 my $ptno = make_name($ptn);
1088 0 0       0 if ($ptno->can("foreignKey$ucct")) {
1089 0         0 my $fkfn = "foreignKey$ucct";
1090 0         0 my ($ptf, $ctk) = $ptno->$fkfn;
1091 0         0 push @join, "$cta.$ctk = " . $from[$i][_table_alias] . ".$ptf";
1092             }
1093             }
1094             }
1095 1         6 $from[$idx][_table_join_on] = ["on", join (" and ", @join)];
1096             }
1097 2         4 my $from = '';
1098 2 50       5 @columns = ('*') if not @columns;
1099 2         3 my $joined = 0;
1100 2         7 for (my $idx = 0 ; $idx < @from ; ++$idx) {
1101 3 100       7 if (not $joined) {
1102 2         6 $from .= " " . $from[$idx][_table_name];
1103 2 50       8 $from .= " " . $from[$idx][_table_alias] if $from[$idx][_table_alias] ne $from[$idx][_table_name];
1104             }
1105 3 100       8 if ($from[$idx][_table_join]) {
1106 1         3 my $nt = $from[$idx + 1];
1107 1         3 $from .= " " . $from[$idx][_table_join];
1108 1         2 $from .= " " . $nt->[_table_name];
1109 1 50       4 $from .= " " . $nt->[_table_alias] if $nt->[_table_alias] ne $nt->[_table_name];
1110 1         2 my $using_on = $nt->[_table_join_on][0];
1111 1 50 33     8 if ($using_on eq 'on' and ref $nt->[_table_join_on][1]) {
1112 0         0 my ($on_where, @on_bind) = $sql_abstract->where($nt->[_table_join_on][1]);
1113 0         0 $on_where =~ s/WHERE //;
1114 0         0 push @$query_bind, @on_bind;
1115 0         0 $from .= " $using_on(" . $on_where . ")";
1116             } else {
1117 1         3 $from .= " $using_on(" . $nt->[_table_join_on][1] . ")";
1118             }
1119 1         4 $joined = 1;
1120             } else {
1121 2 50       4 $from .= "," if $idx != $#from;
1122 2         7 $joined = 0;
1123             }
1124             }
1125 2         6 my $ret = "select " . join (", ", @columns) . " from" . $from;
1126 2 50       10 if (not defined $where) {
1127 0         0 my $sql_grp = _parse_groupby($groupby);
1128 0         0 my $having_bind = [];
1129 0 0 0     0 if ($sql_grp && defined $having) {
1130 0         0 my $sql_having;
1131 0         0 ($sql_having, $having_bind) = _parse_having($having);
1132 0         0 $sql_grp .= " $sql_having";
1133             }
1134 0 0       0 if ($conditions) {
1135 0         0 my @where_bind;
1136 0         0 ($where, @where_bind) = $sql_abstract->where($conditions);
1137 0         0 push @$query_bind, @where_bind;
1138             } else {
1139 0         0 $where = '';
1140             }
1141 0 0       0 if (defined $sql_grp) {
1142 0         0 $where .= " $sql_grp";
1143 0         0 push @$query_bind, @$having_bind;
1144             }
1145 0 0       0 $where .= " limit $limit" if defined $limit;
1146 0 0       0 $where .= " offset $offset" if $offset;
1147             }
1148 2 50       5 $ret .= " $where" if $where;
1149 2         8 $ret;
1150             }
1151              
1152             sub _parse_groupby {
1153 13     13   21 my $groupby = $_[0];
1154 13         14 my $sql_grp;
1155 13 100       24 if (defined $groupby) {
1156 2         4 $sql_grp = "GROUP BY ";
1157 2 50       6 my @groupby = map { /^\d+$/ ? $_ : qq{"$_"} } (ref ($groupby) ? @$groupby : ($groupby));
  2 50       12  
1158 2         5 $sql_grp .= join (", ", @groupby);
1159             }
1160 13         27 $sql_grp;
1161             }
1162              
1163             sub _parse_having {
1164 2     2   9 my $having = $_[0];
1165 2         2 my $sql_having;
1166             my @having_bind;
1167 2 50       6 if (defined $having) {
1168 2         8 ($sql_having, @having_bind) = $sql_abstract->where($having);
1169 2         529 $sql_having =~ s/\bWHERE\b/HAVING/;
1170             }
1171 2         7 ($sql_having, \@having_bind);
1172             }
1173              
1174             sub execute {
1175 13     13 0 21 my ($groupby, $having, $up_conditions, $up_order, $up_limit, $up_offset, $up_interface);
1176 13         42 for (my $i = 2 ; $i < @_ ; ++$i) {
1177 13 100 66     77 next unless defined $_[$i] and not ref $_[$i];
1178 7 100       53 if ($_[$i] eq '-group_by') {
    100          
    50          
    50          
    50          
    50          
    50          
1179 2         10 (undef, $groupby) = splice @_, $i, 2;
1180 2         7 --$i;
1181             } elsif ($_[$i] eq '-having') {
1182 2         5 (undef, $having) = splice @_, $i, 2;
1183 2         5 --$i;
1184             } elsif ($_[$i] eq '-order_by') {
1185 0         0 (undef, $up_order) = splice @_, $i, 2;
1186 0         0 --$i;
1187             } elsif ($_[$i] eq '-where') {
1188 0         0 (undef, $up_conditions) = splice @_, $i, 2;
1189 0         0 --$i;
1190             } elsif ($_[$i] eq '-limit') {
1191 0         0 (undef, $up_limit) = splice @_, $i, 2;
1192 0         0 --$i;
1193             } elsif ($_[$i] eq '-interface') {
1194 0         0 (undef, $up_interface) = splice @_, $i, 2;
1195 0         0 --$i;
1196             } elsif ($_[$i] eq '-offset') {
1197 0         0 (undef, $up_offset) = splice @_, $i, 2;
1198 0         0 --$i;
1199             }
1200             }
1201 13         26 $tblnum = 1;
1202 13         32 my $sql_grp = _parse_groupby($groupby);
1203 13         23 my $having_bind = [];
1204 13 100 66     652 if ($sql_grp && defined $having) {
1205 2         3 my $sql_having;
1206 2         5 ($sql_having, $having_bind) = _parse_having($having);
1207 2         5 $sql_grp .= " $sql_having";
1208             }
1209 13         27 my ($code, $table, $conditions, $order, $limit, $offset) = @_;
1210 13         23 my $have_conditions = @_ > 2;
1211 13   66     33 $conditions //= $up_conditions;
1212 13   33     38 $order //= $up_order;
1213 13   33     37 $limit //= $up_limit;
1214 13   33     36 $offset //= $up_offset;
1215 13         14 my $where;
1216 13         16 my $need_where = 0;
1217 13         14 my @where_bind;
1218 13   100     54 my $simple_table = (not ref $table and index ($table, " ") == -1);
1219 13         14 my $ncn;
1220 13 100       27 if ($simple_table) {
1221 10         20 $ncn = make_name($table);
1222 10         25 setup_row($table);
1223 10 100 100     66 if ($have_conditions and not ref $conditions) {
1224 3 50       15 my $id = ($ncn->selectKeys())[0]
1225             or error_message {
1226             result => 'SQLERR',
1227             message => "unknown primary key",
1228             query => "select * from $table",
1229             };
1230 3 50       7 if (defined $conditions) {
1231 3         6 $where = "where $id = ?";
1232 3         9 @where_bind = ($conditions);
1233             } else {
1234 0         0 $where = "where $id is null";
1235             }
1236             } else {
1237 7         11 $need_where = 1;
1238             }
1239             } else {
1240 3         5 $need_where = 1;
1241             }
1242 13 100       29 if ($need_where) {
1243 10         42 ($where, @where_bind) = $sql_abstract->where($conditions);
1244             }
1245 13 100       895 if (defined $sql_grp) {
1246 2         5 $where .= " $sql_grp";
1247 2         4 push @where_bind, @$having_bind;
1248             }
1249 13 50       25 if ($order) {
1250 0         0 my ($order_sql, @order_bind) = $sql_abstract->where(undef, $order);
1251 0         0 $where .= " $order_sql";
1252 0         0 push @where_bind, @order_bind;
1253             }
1254 13 50       27 if (defined ($limit)) {
1255 0         0 $limit += 0;
1256 0         0 $where .= " limit $limit";
1257             }
1258 13 50       30 if (defined ($offset)) {
1259 0         0 $offset += 0;
1260 0 0       0 $where .= " offset $offset" if $offset;
1261             }
1262 13         15 my $query;
1263             my @query_bind;
1264 13 100       22 if ($simple_table) {
1265 10         25 $query = qq{select * from $table $where};
1266             } else {
1267 3 100       15 $query = (not ref $table) ? qq{$table $where} : _build_complex_query($table, \@query_bind, $where);
1268 3         8 $ncn = make_name($query);
1269             }
1270 13         27 '' =~ /()/;
1271 13         14 my $sth;
1272             return DBIx::Struct::connect->run(
1273             sub {
1274 13 50   13   101 $sth = $_->prepare($query)
1275             or error_message {
1276             result => 'SQLERR',
1277             message => $_->errstr,
1278             query => $query,
1279             };
1280 13 50       439 $sth->execute(@query_bind, @where_bind)
1281             or error_message {
1282             result => 'SQLERR',
1283             message => $_->errstr,
1284             query => $query,
1285             where_bind => Dumper(\@where_bind),
1286             query_bind => Dumper(\@query_bind),
1287             conditions => Dumper($conditions),
1288             };
1289 13         146 setup_row($sth, $ncn, $up_interface);
1290 1     1   5 no strict 'refs';
  1         2  
  1         562  
1291 13 100       120 if (!"$ncn"->can("dumpSQL")) {
1292 6     0   21 *{$ncn . "::dumpSQL"} = sub { $query };
  6         24  
  0         0  
1293 6 50       14 if (@query_bind) {
1294 0         0 *{$ncn . "::needQueryBind"} = sub { 1 };
  0         0  
  0         0  
1295             } else {
1296 6     0   14 *{$ncn . "::needQueryBind"} = sub { 0 };
  6         28  
  0         0  
1297             }
1298             }
1299 13         32 return $code->($sth, $ncn);
1300             }
1301 13         25 );
1302             }
1303              
1304             sub one_row {
1305             return execute(
1306             sub {
1307 13     13   19 my ($sth, $ncn) = @_;
1308 13         39 my $data = $sth->fetchrow_arrayref;
1309 13         158 $sth->finish;
1310 13 100       40 return if not $data;
1311 12         239 return $ncn->new([@$data]);
1312             },
1313             @_
1314 13     13 1 2286 );
1315             }
1316              
1317             sub all_rows {
1318 0     0 1 0 my $mapfunc;
1319 0         0 for (my $i = 0 ; $i < @_ ; ++$i) {
1320 0 0       0 if (ref ($_[$i]) eq 'CODE') {
1321 0         0 $mapfunc = splice @_, $i, 1;
1322 0         0 last;
1323             }
1324             }
1325             return execute(
1326             sub {
1327 0     0   0 my ($sth, $ncn) = @_;
1328 0         0 my @rows;
1329             my $row;
1330 0 0       0 if ($mapfunc) {
1331 0         0 while ($row = $sth->fetch) {
1332 0         0 local $_ = $ncn->new([@$row]);
1333 0         0 push @rows, $mapfunc->();
1334             }
1335             } else {
1336 0         0 push @rows, $ncn->new([@$row]) while ($row = $sth->fetch);
1337             }
1338 0         0 return \@rows;
1339             },
1340             @_
1341 0         0 );
1342             }
1343              
1344             sub new_row {
1345 3     3 1 1098 my ($table, @data) = @_;
1346 3         7 my $simple_table = (index ($table, " ") == -1);
1347 3 50       9 error_message {
1348             result => 'SQLERR',
1349             message => "insert row can't work for queries"
1350             }
1351             unless $simple_table;
1352 3         8 my $ncn = setup_row($table);
1353 3         57 return $ncn->new(@data);
1354             }
1355              
1356             1;