File Coverage

lib/DBIx/Struct.pm
Criterion Covered Total %
statement 1033 2385 43.3
branch 295 1108 26.6
condition 123 346 35.5
subroutine 160 293 54.6
pod 6 29 20.6
total 1617 4161 38.8


line stmt bran cond sub pod time code
1             package DBIx::Struct::JSON::Array;
2 3     3   13636 use strict;
  3         19  
  3         107  
3 3     3   14 use warnings;
  3         6  
  3         1680  
4              
5             sub TIEARRAY {
6 0     0   0 bless [$_[1], $_[2], $_[3]], $_[0];
7             }
8              
9             sub FETCHSIZE {
10 0     0   0 scalar @{$_[0][0]};
  0         0  
11             }
12              
13             sub STORESIZE {
14 0     0   0 $_[0][1]{$_[0][2]} = undef;
15 0         0 $#{$_[0][0]} = $_[1] - 1;
  0         0  
16             }
17              
18             sub STORE {
19 0     0   0 $_[0][1]{$_[0][2]} = undef;
20 0         0 $_[0][0][$_[1]] = $_[2];
21             }
22 0     0   0 sub FETCH {$_[0][0][$_[1]]}
23              
24             sub CLEAR {
25 0     0   0 $_[0][1]{$_[0][2]} = undef;
26 0         0 @{$_[0][0]} = ();
  0         0  
27             }
28              
29             sub POP {
30 0     0   0 $_[0][1]{$_[0][2]} = undef;
31 0         0 pop(@{$_[0][0]});
  0         0  
32             }
33              
34             sub PUSH {
35 0     0   0 my $o = shift;
36 0         0 $o->[1]{$o->[2]} = undef;
37 0         0 push(@{$o->[0]}, @_);
  0         0  
38             }
39              
40             sub SHIFT {
41 0     0   0 $_[0][1]{$_[0][2]} = undef;
42 0         0 shift(@{$_[0][0]});
  0         0  
43             }
44              
45             sub UNSHIFT {
46 0     0   0 my $o = shift;
47 0         0 $o->[1]{$o->[2]} = undef;
48 0         0 unshift(@$o, @_);
49             }
50 0     0   0 sub EXISTS {exists $_[0][0]->[$_[1]]}
51 0     0   0 sub DELETE {delete $_[0][0]->[$_[1]]}
52              
53             sub SPLICE {
54 0     0   0 my $ob = shift;
55 0         0 my $sz = $ob->FETCHSIZE;
56 0 0       0 my $off = @_ ? shift : 0;
57 0 0       0 $off += $sz if $off < 0;
58 0 0       0 my $len = @_ ? shift : $sz - $off;
59 0         0 $ob->[1]{$ob->[2]} = undef;
60 0         0 return splice(@{$ob->[0]}, $off, $len, @_);
  0         0  
61             }
62              
63             package DBIx::Struct::JSON::Hash;
64 3     3   24 use strict;
  3         6  
  3         68  
65 3     3   13 use warnings;
  3         5  
  3         1091  
66              
67             sub TIEHASH {
68 0     0   0 bless [$_[1], $_[2], $_[3]], $_[0];
69             }
70              
71             sub STORE {
72 0     0   0 $_[0][1]{$_[0][2]} = undef;
73 0         0 $_[0][0]->{$_[1]} = $_[2];
74             }
75              
76             sub FETCH {
77 0     0   0 $_[0][0]->{$_[1]};
78             }
79              
80             sub FIRSTKEY {
81 0     0   0 my $a = scalar keys %{$_[0][0]};
  0         0  
82 0         0 each %{$_[0][0]};
  0         0  
83             }
84              
85             sub NEXTKEY {
86 0     0   0 each %{$_[0][0]};
  0         0  
87             }
88              
89             sub EXISTS {
90 0     0   0 exists $_[0][0]->{$_[1]};
91             }
92              
93             sub DELETE {
94 0     0   0 $_[0][1]{$_[0][2]} = undef;
95 0         0 delete $_[0][0]->{$_[1]};
96             }
97              
98             sub CLEAR {
99 0     0   0 $_[0][1]{$_[0][2]} = undef;
100 0         0 %{$_[0][0]} = ();
  0         0  
101             }
102              
103             sub SCALAR {
104 0     0   0 scalar %{$_[0][0]};
  0         0  
105             }
106              
107             package DBIx::Struct::JSON;
108              
109 3     3   59 use strict;
  3         7  
  3         73  
110 3     3   23 use warnings;
  3         5  
  3         90  
111 3     3   2061 use JSON;
  3         38216  
  3         22  
112              
113             sub factory {
114 0     0   0 my ($class, $value_ref, $update_hash, $hash_key) = @_;
115 0         0 my $self;
116 0 0       0 if (not ref $$value_ref) {
117 0 0       0 my $jv = from_json($$value_ref) if $$value_ref;
118 0 0       0 $$value_ref = $jv if $jv;
119             }
120 0 0       0 if (not defined $$value_ref) {
    0          
    0          
121 0         0 $self = [undef, undef];
122             } elsif ('HASH' eq ref $$value_ref) {
123 0         0 my %h;
124 0         0 tie %h, 'DBIx::Struct::JSON::Hash', $$value_ref, $update_hash, $hash_key;
125 0         0 $self = [\%h, $$value_ref];
126             } elsif ('ARRAY' eq ref $$value_ref) {
127 0         0 my @a;
128 0         0 tie @a, 'DBIx::Struct::JSON::Array', $$value_ref, $update_hash, $hash_key;
129 0         0 $self = [\@a, $$value_ref];
130             }
131 0         0 $$value_ref = bless $self, $class;
132             }
133              
134             sub revert {
135 0 0   0   0 $_[0] = defined($_[0][1]) ? to_json $_[0][1] : undef;
136             }
137              
138             sub data {
139 0     0   0 $_[0][1];
140             }
141              
142             sub accessor {
143 0     0   0 $_[0][0];
144             }
145              
146             package DBIx::Struct::Connector;
147 3     3   1019 use strict;
  3         5  
  3         60  
148 3     3   14 use warnings;
  3         6  
  3         80  
149 3     3   19 use base 'DBIx::Connector';
  3         6  
  3         3193  
150              
151             our $db_reconnect_timeout = 30;
152              
153             sub _connect {
154 0     0   0 my ($self, @args) = @_;
155 0         0 for my $try (1 .. $db_reconnect_timeout) {
156 0         0 my $dbh = eval {$self->SUPER::_connect(@args)};
  0         0  
157 0 0       0 return $dbh if $dbh;
158 0 0       0 sleep 1 if $try != $db_reconnect_timeout;
159             }
160 0 0       0 die $@ if $@;
161 0         0 die "DB connect error";
162             }
163              
164             package DBIx::Struct::Error::String;
165 3     3   72551 use strict;
  3         5  
  3         59  
166 3     3   22 use warnings;
  3         5  
  3         80  
167 3     3   14 use Carp;
  3         10  
  3         635  
168              
169             sub error_message (+%) {
170 0     0   0 my $msg = $_[0];
171 0         0 delete $msg->{result};
172 0         0 my $message = delete $msg->{message};
173 0         0 croak join "; ", $message, map {"$_: $msg->{$_}"} keys %$msg;
  0         0  
174             }
175              
176             package DBIx::Struct::Error::Hash;
177 3     3   21 use strict;
  3         3  
  3         71  
178 3     3   23 use warnings;
  3         6  
  3         214  
179              
180             sub error_message (+%) {
181 0     0   0 die $_[0];
182             }
183              
184             package DBIx::Struct;
185 3     3   18 use strict;
  3         6  
  3         81  
186 3     3   13 use warnings;
  3         5  
  3         76  
187 3     3   2477 use SQL::Abstract;
  3         38743  
  3         158  
188 3     3   25 use Digest::MD5;
  3         5  
  3         91  
189 3     3   2038 use Data::Dumper;
  3         20703  
  3         197  
190 3     3   21 use Scalar::Util 'refaddr';
  3         5  
  3         304  
191 3     3   21 use base 'Exporter';
  3         10  
  3         450  
192 3     3   40 use v5.14;
  3         8  
193              
194             our $VERSION = '0.49';
195              
196             our @EXPORT = qw{
197             one_row
198             all_rows
199             for_rows
200             new_row
201             };
202              
203             our @EXPORT_OK = qw{
204             connector
205             hash_ref_slice
206             };
207              
208             sub ccmap ($) {
209 51     51 0 61 my $name = $_[0];
210 51         247 $name =~ s/([[:upper:]])/_\l$1/g;
211 51         132 $name =~ s/^_//;
212 51         113 return $name;
213             }
214              
215             our $camel_case_map = \&ccmap;
216             our $conn;
217             our $update_on_destroy = 1;
218             our $connector_module = 'DBIx::Struct::Connector';
219             our $connector_constructor = 'new';
220             our $connector_pool;
221             our $connector_pool_method = 'get_connector';
222             our $connector_args = [];
223             our $connector_driver;
224             our $user_schema_namespace;
225             our $table_classes_namespace = 'DBC';
226             our $query_classes_namespace = 'DBQ';
227             our $error_message_class = 'DBIx::Struct::Error::String';
228             our %driver_pk_insert;
229              
230             sub error_message (+%) {
231 0     0 0 0 goto &DBIx::Struct::Error::String::error_message;
232             }
233              
234             %driver_pk_insert = (
235             _returning => sub {
236             my ($table, $pk_row_data, $pk_returninig) = @_;
237             my $ret;
238             if ($pk_row_data) {
239             $ret = <
240             ($pk_row_data) =
241             \$_->selectrow_array(\$insert . " $pk_returninig", undef, \@bind)
242             INS
243             } else {
244             $ret = <
245             \$_->do(\$insert, undef, \@bind)
246             INS
247             }
248             $ret .= <
249             or DBIx::Struct::error_message {
250             result => 'SQLERR',
251             message => 'error '.\$_->errstr.' inserting into table $table'
252             };
253             INS
254             $ret;
255             },
256             _last_id_undef => sub {
257             my ($table, $pk_row_data) = @_;
258             my $ret;
259             $ret = <
260             \$_->do(\$insert, undef, \@bind)
261             or DBIx::Struct::error_message {
262             result => 'SQLERR',
263             message => 'error '.\$_->errstr.' inserting into table $table'
264             };
265             INS
266             if ($pk_row_data) {
267             $ret .= <
268             $pk_row_data = \$_->last_insert_id(undef, undef, undef, undef);
269             INS
270             }
271             $ret;
272             },
273             _last_id_empty => sub {
274             my ($table, $pk_row_data) = @_;
275             my $ret;
276             $ret = <
277             \$_->do(\$insert, undef, \@bind)
278             or DBIx::Struct::error_message {
279             result => 'SQLERR',
280             message => 'error '.\$_->errstr.' inserting into table $table'
281             };
282             INS
283             if ($pk_row_data) {
284             $ret .= <
285             $pk_row_data = \$_->last_insert_id("", "", "", "");
286             INS
287             }
288             $ret;
289             }
290             );
291              
292             $driver_pk_insert{Pg} = $driver_pk_insert{_returning};
293             $driver_pk_insert{mysql} = $driver_pk_insert{_last_id_undef};
294             $driver_pk_insert{SQLite} = $driver_pk_insert{_last_id_empty};
295              
296             sub hash_ref_slice($@) {
297 0     0 1 0 my ($hashref, @slice) = @_;
298 0 0       0 error_message {
299             message => "first parameter is not hash reference",
300             result => 'INTERR',
301             }
302             if 'HASH' ne ref $hashref;
303 0         0 map {$_ => $hashref->{$_}} @slice;
  0         0  
304             }
305              
306             my @already_exported_to;
307              
308             sub connector {
309 51     51 0 268 $conn;
310             }
311              
312             sub connector_from_pool {
313 0     0 0 0 $connector_pool->$connector_pool_method();
314             }
315              
316             sub set_connector_pool {
317 0     0 0 0 $connector_pool = $_[0];
318 0 0       0 if (\&connector != \&connector_from_pool) {
319 3     3   23 no warnings 'redefine';
  3         5  
  3         159  
320 3     3   31 no strict 'refs';
  3         5  
  3         810  
321 0         0 *connector = \&connector_from_pool;
322 0         0 for my $aep (@already_exported_to) {
323 0         0 *{"$aep\::connector"} = \&connector;
  0         0  
324             }
325             }
326             }
327              
328             sub set_user_schema_namespace {
329 0     0 0 0 $user_schema_namespace = $_[0];
330             }
331              
332             sub set_connector_pool_method {
333 0     0 0 0 $connector_pool_method = $_[0];
334             }
335              
336             sub set_connector_object {
337 0     0 0 0 *conn = \$_[0];
338             }
339              
340             sub set_camel_case_map {
341 0 0   0 0 0 error_message {
342             message => "CamelCaseMap must be code reference",
343             result => 'SQLERR',
344             } if 'CODE' ne ref $_[0];
345 0         0 $camel_case_map = $_[0];
346             }
347              
348             sub check_package_scalar {
349 3     3 0 6 my ($package, $scalar) = @_;
350 3     3   21 no strict 'refs';
  3         5  
  3         587  
351 3         4 my $pr = \%{$package . '::'};
  3         13  
352 3         8 my $er = $$pr{$scalar};
353 3 50       23 return unless $er;
354 0         0 defined *{$er}{'SCALAR'};
  0         0  
355             }
356              
357             sub import {
358 3     3   30 my ($class, @args) = @_;
359 3         4 my $defconn = 0;
360 3         5 my $_emc = 0;
361 3         3 my $_cp = 0;
362 3         3 my $_c = 0;
363 3         18 for (my $i = 0; $i < @args; ++$i) {
364 4 100 33     15 if ($args[$i] eq 'connector_module') {
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
365 3         8 (undef, $connector_module) = splice @args, $i, 2;
366 3         4 --$i;
367 3 50 33     18 if (not $defconn and check_package_scalar($connector_module, 'conn')) {
368 3     3   27 no strict 'refs';
  3         10  
  3         1065  
369 0         0 set_connector_object(${$connector_module . '::conn'});
  0         0  
370             }
371             } elsif ($args[$i] eq 'connector_constructor') {
372 0         0 (undef, $connector_constructor) = splice @args, $i, 2;
373 0         0 --$i;
374             } elsif ($args[$i] eq 'camel_case_map' && 'CODE' eq ref $args[$i]) {
375 0         0 (undef, $camel_case_map) = splice @args, $i, 2;
376 0         0 --$i;
377             } elsif ($args[$i] eq 'user_schema_namespace') {
378 1         3 (undef, $user_schema_namespace) = splice @args, $i, 2;
379 1         2 --$i;
380             } elsif ($args[$i] eq 'table_classes_namespace') {
381 0         0 (undef, $table_classes_namespace) = splice @args, $i, 2;
382 0         0 --$i;
383             } elsif ($args[$i] eq 'query_classes_namespace') {
384 0         0 (undef, $query_classes_namespace) = splice @args, $i, 2;
385 0         0 --$i;
386             } elsif ($args[$i] eq 'connect_timeout') {
387 0         0 (undef, $db_reconnect_timeout) = splice @args, $i, 2;
388 0         0 --$i;
389             } elsif ($args[$i] eq 'error_class') {
390 0         0 my (undef, $emc) = splice @args, $i, 2;
391 0         0 $error_message_class = $emc;
392 0         0 $_emc = 1;
393 0         0 --$i;
394             } elsif ($args[$i] eq 'connector_pool') {
395 0         0 (undef, $connector_pool) = splice @args, $i, 2;
396 0         0 $_cp = 1;
397 0         0 --$i;
398             } elsif ($args[$i] eq 'connector_pool_method') {
399 0         0 (undef, $connector_pool_method) = splice @args, $i, 2;
400 0         0 --$i;
401             } elsif ($args[$i] eq 'connector_args') {
402 0         0 (undef, $connector_args) = splice @args, $i, 2;
403 0         0 --$i;
404             } elsif ($args[$i] eq 'connector') {
405 0         0 $_c = 1;
406             } elsif ($args[$i] eq 'connector_object') {
407 0         0 $defconn = 1;
408 0         0 set_connector_object($args[$i + 1]);
409 0         0 splice @args, $i, 2;
410 0         0 --$i;
411             }
412             }
413 3 50       9 if ($_emc) {
414 3     3   22 no warnings 'redefine';
  3         5  
  3         102  
415 3     3   14 no strict 'refs';
  3         5  
  3         254  
416 0         0 *error_message = \&{$error_message_class . "::error_message"};
  0         0  
417             }
418 3 50       6 if ($_cp) {
419 3     3   20 no warnings 'redefine';
  3         4  
  3         117  
420 3     3   16 no strict 'refs';
  3         45  
  3         1267  
421 0         0 *connector = \&connector_from_pool;
422 0         0 for my $aep (@already_exported_to) {
423 0         0 *{"$aep\::connector"} = \&connector;
  0         0  
424             }
425             }
426 3         15 my $callpkg = caller;
427 3 50       6 push @already_exported_to, $callpkg if $_c;
428 3         8 my %imps = map {$_ => undef} @args, @EXPORT;
  12         37  
429 3         542 $class->export_to_level(1, $class, keys %imps);
430             }
431              
432             sub _not_yet_connected {
433 2 50 33 2   18 if (!$connector_pool && !$conn) {
434 2         7 my ($dsn, $user, $password) = @_;
435 2 50 33     9 if ($dsn && $dsn !~ /^dbi:/i) {
436 0         0 $dsn = "dbi:Pg:dbname=$dsn";
437             }
438 2         33 my $connect_attrs = {
439             AutoCommit => 1,
440             PrintError => 0,
441             AutoInactiveDestroy => 1,
442             RaiseError => 0,
443             };
444 2 50       7 if ($dsn) {
445 0         0 my ($driver) = $dsn =~ /^dbi:(\w*?)(?:\((.*?)\))?:/i;
446 0 0       0 if ($driver) {
447 0 0       0 if ($driver eq 'Pg') {
    0          
    0          
448 0         0 $connect_attrs->{pg_enable_utf8} = 1;
449             } elsif ($driver eq 'mysql') {
450 0         0 $connect_attrs->{mysql_enable_utf8} = 1;
451             } elsif ($driver eq 'SQLite') {
452 0         0 $connect_attrs->{sqlite_unicode} = 1;
453             }
454             }
455             }
456 2 50       7 if (!@$connector_args) {
457 2         6 @$connector_args = ($dsn, $user, $password, $connect_attrs);
458             }
459 2 50       15 $conn = $connector_module->$connector_constructor(@$connector_args)
460             or error_message {
461             message => "DB connect error",
462             result => 'SQLERR',
463             };
464 2         21 $conn->mode('fixup');
465             }
466 2         11 '' =~ /()/;
467 2         6 $connector_driver = connector->driver->{driver};
468 3     3   21 no warnings 'redefine';
  3         6  
  3         1698  
469 2         15 *connect = \&connector;
470 2         8 populate();
471 2         7 connector;
472             }
473              
474             sub connect {
475 2     2 1 200 goto &_not_yet_connected;
476             }
477              
478             {
479             my $md5 = Digest::MD5->new;
480              
481             sub make_name {
482 35     35 0 83 my ($table) = @_;
483 35         66 my $simple_table = (index($table, " ") == -1);
484 35         48 my $ncn;
485 35 100       58 if ($simple_table) {
486 30         139 $ncn = $table_classes_namespace . "::" . join('', map {ucfirst($_)} split(/[^a-zA-Z0-9]/, $table));
  39         137  
487             } else {
488 5         24 $md5->add($table);
489 5         22 $ncn = $query_classes_namespace . "::" . "G" . $md5->hexdigest;
490 5         14 $md5->reset;
491             }
492 35         79 $ncn;
493             }
494             }
495              
496             sub populate {
497 2     2 0 4 my @tables;
498             DBIx::Struct::connect->run(
499             sub {
500 2     2   26 my $sth = $_->table_info('', '%', '%', "TABLE");
501 2 50       127 return if not $sth;
502 2         6 my $tables = $sth->fetchall_arrayref;
503             @tables = map {
504 6         12 (my $t = $_->[2]) =~ s/"//g;
505 6         34 $t;
506             } grep {
507 2 50       21 $_->[3] eq 'TABLE' and $_->[2] !~ /^sql_/
  6         32  
508             } @$tables;
509             }
510 2         5 );
511 2         18 for (@tables) {
512 6         19 my $ncn = setup_row($_);
513 6 100       20 if ($user_schema_namespace) {
514 3         17 (my $uncn = $ncn) =~ s/^.*:://;
515 3     1   157 eval "use ${user_schema_namespace}::${uncn}";
  1         268  
  0            
  0            
516 3     3   23 no strict 'refs';
  3         5  
  3         2220  
517 3         10 eval {
518 3 50 33     5 if (
      66        
519 3         28 keys %{"${user_schema_namespace}::${uncn}::"}
520             and ( not ${"${user_schema_namespace}::${uncn}::"}{ISA}
521             or not "${user_schema_namespace}::${uncn}"->isa($ncn))
522             )
523             {
524 0         0 unshift @{"${user_schema_namespace}::${uncn}::ISA"}, $ncn;
  0         0  
525             }
526             };
527             }
528             }
529             }
530              
531             #<<<
532             my @uneq = (
533             qr/LessThanEqual$/, '<=',
534             qr/LessThan$/, '<',
535             qr/GreaterThanEqual$/, '>=',
536             qr/GreaterThan$/, '>',
537             qr/IsNull$/, sub {"'$_[0]' => {'=', undef}"},
538             qr/IsNotNull$/, sub {"'$_[0]' => {'!=', undef}"},
539             qr/IsNot$/, '!=',
540             qr/NotNull$/, sub {"'$_[0]' => {'!=', undef}"},
541             qr/NotEquals$/, '!=',
542             qr/NotIn$/, '-not_in',
543             qr/NotLike$/, '-not_like',
544             qr/IsEqualTo$/, '=',
545             qr/IsTrue$/, sub {"-bool => '$_[0]'"},
546             qr/IsFalse$/, sub {"-not_bool => '$_[0]'"},
547             qr/Equals$/, '=',
548             qr/True$/, sub {"-bool => '$_[0]'"},
549             qr/False$/, sub {"-not_bool => '$_[0]'"},
550             qr/Like$/, '-like',
551             qr/Is$/, '=',
552             qr/Not$/, '!=',
553             qr/In$/, '-in',
554             );
555             #>>>
556              
557             sub _parse_find_by {
558 20     20   10575 my ($table, $find) = @_;
559 20 50 66     161 $find =~ s/^find(?.*?)By(?![[:lower:]])// || $find =~ s/^find(?.*)// or die "bad pattern: $find";
560 3   100 3   1436 my $what = $+{what} || 'All';
  3         1045  
  3         5945  
  20         145  
561 20         62 $what =~ s/(?Distinct)(?![[:lower:]])//;
562 20   100     88 my $distinct = $+{distinct} // 0;
563 20         119 $what =~ s/((?(All|One|First))(?\d+)?)(?![[:lower:]])//;
564 20   100     85 my $type = $+{type} // 'All';
565 20         63 my $limit = $+{limit};
566 20         49 $what =~ s/(?\w+)//;
567 20   100     95 my $column = $camel_case_map->($+{column} // '');
568 20 100       78 $find =~ s/OrderBy(?.*?)(?Asc|Desc)(?=[[:upper:]]|$)// || $find =~ s/OrderBy(?.*?)$//;
569 20         63 my $order = $+{order};
570 20   100     82 my $asc = $+{asc} || 'Asc';
571 20         32 my $where = $find;
572              
573 20 100 100     69 if ($type eq 'First' && !$limit) {
574 2         3 $limit = 1;
575             }
576 20 100 100     91 if ($limit && $limit == 1) {
577 3         5 $type = 'One';
578             }
579 20         27 my $pi = 1;
580             my $pp = sub {
581 26     26   45 my ($param) = @_;
582 26         27 my $found;
583 26         59 for (my $i = 0; $i < @uneq; $i += 2) {
584 475 100       1231 if ($param =~ s/$uneq[$i]//) {
585 4         4 $found = $i + 1;
586 4         7 last;
587             }
588             }
589 26         43 $param = $camel_case_map->($param);
590 26         32 my $ret;
591 26 100       34 if ($found) {
592 4 100       10 if ('CODE' eq ref $uneq[$found]) {
593 1         4 $ret = $uneq[$found]->($param);
594             } else {
595 3         7 $ret = "'$param' => { '$uneq[$found]' => \$_[$pi]}";
596 3         5 ++$pi;
597             }
598             } else {
599 22         45 $ret = "'$param' => \$_[$pi]";
600 22         37 ++$pi;
601             }
602 26         91 $ret;
603 20         98 };
604             #<<<
605             my $conds = join(
606             ", ",
607             map {
608 20         118 /And(?![[:lower:]])/
609 16 100       80 ? '-and => [' . join(", ", map {$pp->($_)} split /And(?![[:lower:]])/x, $_) . ']'
  19         32  
610             : $pp->($_);
611             } split /Or(?![[:lower:]])/, $where
612             );
613             #>>>
614 20 100       43 my $obj = $type eq 'One' ? 'DBIx::Struct::one_row' : 'DBIx::Struct::all_rows';
615 20 100       34 my $flags = $column ? ", -column => '$column'" : '';
616 20 50       37 $flags = $distinct ? $flags ? ", -distinct => '$column'" : ", '-distinct'" : $flags;
    100          
617 20 100       44 $order =
    100          
618             $order
619             ? $asc eq 'Asc'
620             ? ", -order_by => '" . $camel_case_map->($order) . "'"
621             : ", -order_by => {-desc => '" . $camel_case_map->($order) . "'}"
622             : '';
623 20 100       36 $where = $conds ? ", -where => [$conds]" : '';
624 20 100 66     73 $limit = $limit && $limit > 1 && $type ne 'One' ? ", -limit => $limit" : '';
625 20         31 my $tspec = "'$table'" . $flags;
626 20 100       36 $tspec = "[$tspec]" if $column;
627 20         68 $tspec .= $where . $order . $limit;
628 20         184 return "sub { $obj($tspec) }";
629             }
630              
631             sub _row_data () {0}
632             sub _row_updates () {1}
633              
634             sub make_object_new {
635 10     10 0 22 my ($table, $required, $pk_row_data, $pk_returninig) = @_;
636 10         14 my $new = <
637             sub new {
638             my \$class = \$_[0];
639             my \$self = [ [] ];
640             bless \$self, \$class;
641             if(CORE::defined(\$_[1]) && CORE::ref(\$_[1]) eq 'ARRAY') {
642 10         27 \$self->[@{[_row_data]}] = \$_[1];
643             }
644             NEW
645 10 100       46 if (not ref $table) {
646 6         15 $new .= <
647             else {
648             my \%insert;
649             for(my \$i = 1; \$i < \@_; \$i += 2) {
650             if (CORE::exists \$fields{\$_[\$i]}) {
651             my \$f = \$_[\$i];
652 6         44 \$self->[@{[_row_data]}]->[\$fields{\$_[\$i]}] = \$_[\$i + 1];
653             \$insert{\$f} = \$_[\$i + 1];
654             } else {
655             DBIx::Struct::error_message {
656             result => 'SQLERR',
657             message => "unknown column \$_[\$i] inserting into table $table"
658             }
659             }
660             }
661             my (\@insert, \@values, \@bind);
662             \@insert =
663             CORE::map {
664             if(CORE::ref(\$insert{\$_}) eq 'ARRAY' and CORE::ref(\$insert{\$_}[0]) eq 'SCALAR') {
665             CORE::push \@bind, \@{\$insert{\$_}}[1..\$#{\$insert{\$_}}];
666             CORE::push \@values, \${\$insert{\$_}[0]};
667             DBIx::Struct::connect->dbh->quote_identifier(\$_);
668             } elsif(CORE::ref(\$insert{\$_}) eq 'REF' and CORE::ref(\${\$insert{\$_}}) eq 'ARRAY') {
669             if(CORE::defined \${\$insert{\$_}}->[0]) {
670             CORE::push \@bind, \@{\${\$insert{\$_}}}[1..\$#{\${\$insert{\$_}}}];
671             CORE::push \@values, \${\$insert{\$_}}->[0];
672             DBIx::Struct::connect->dbh->quote_identifier(\$_);
673             } else {
674             CORE::push \@values, "null";
675             DBIx::Struct::connect->dbh->quote_identifier(\$_)
676             }
677             } elsif(CORE::ref(\$insert{\$_}) eq 'SCALAR') {
678             CORE::push \@values, \${\$insert{\$_}};
679             DBIx::Struct::connect->dbh->quote_identifier(\$_);
680             } elsif(CORE::exists (\$json_fields{\$_})
681             && (CORE::ref(\$insert{\$_}) eq 'ARRAY' || CORE::ref(\$insert{\$_}) eq 'HASH')) {
682             CORE::push \@bind, JSON::to_json(\$insert{\$_});
683             CORE::push \@values, "?";
684             DBIx::Struct::connect->dbh->quote_identifier(\$_);
685             } else {
686             CORE::push \@bind, \$insert{\$_};
687             CORE::push \@values, "?";
688             DBIx::Struct::connect->dbh->quote_identifier(\$_);
689             }
690             } CORE::keys \%insert;
691             my \$insert;
692             if(\%insert){
693             \$insert = "insert into $table (" . CORE::join( ", ", \@insert) . ") values ("
694             . CORE::join( ", ", \@values) . ")";
695             } else {
696             \$insert = "insert into $table values (default)";
697             }
698             NEW
699 6 50       21 if ($required) {
700 0         0 $new .= <
701             for my \$r ($required) {
702             DBIx::Struct::error_message {
703             result => 'SQLERR',
704             message => "required field \$r is absent for table $table"
705             } if not CORE::exists \$insert{\$r};
706             }
707             NEW
708             }
709 6         14 $new .= <
710             DBIx::Struct::connect->run(
711             sub {
712             NEW
713 6         25 $new .= $driver_pk_insert{$connector_driver}->($table, $pk_row_data, $pk_returninig);
714 6         18 $new .= <
715             });
716             }
717             NEW
718             }
719 10         17 $new .= <
720             \$self;
721             }
722             NEW
723 10         61 $new;
724             }
725              
726             sub make_object_filter_timestamp {
727 10     10 0 18 my ($timestamps) = @_;
728 10         20 my $filter_timestamp = <
729             sub filter_timestamp {
730             my \$self = \$_[0];
731             if(\@_ == 1) {
732             for my \$f ($timestamps) {
733 10         26 if(\$self->[@{[_row_data]}][\$fields{\$f}]) {
734 10         23 \$self->[@{[_row_data]}][\$fields{\$f}] =~ s/\\.\\d+(\$|\\+|\\-)/\$1/;
735 10         22 \$self->[@{[_row_data]}][\$fields{\$f}] =~ s/(\\+|\\-)(\\d{2})\$/\$1\${2}00/;
736             }
737             }
738             } else {
739             for my \$f (\@_[1..\$#_]) {
740 10         20 if(\$self->[@{[_row_data]}][\$fields{\$f}]) {
741 10         20 \$self->[@{[_row_data]}][\$fields{\$f}] =~ s/\\.\\d+(\$|\\+|\\-)/\$1/;
742 10         19 \$self->[@{[_row_data]}][\$fields{\$f}] =~ s/(\\+|\\-)(\\d{2})\$/\$1\${2}00/;
743             }
744             }
745             }
746             '' =~ /()/;
747             \$self;
748             }
749             FTS
750 10         24 $filter_timestamp;
751             }
752              
753             sub make_object_set {
754 10     10 0 11 my $table = $_[0];
755 10         12 my $set = <
756             sub set {
757             my \$self = \$_[0];
758             my \@unknown_columns;
759             if(CORE::defined(\$_[1])) {
760             if(CORE::ref(\$_[1]) eq 'ARRAY') {
761 10         30 \$self->[@{[_row_data]}] = \$_[1];
762 10         57 \$self->[@{[_row_updates]}] = {};
763             } elsif(CORE::ref(\$_[1]) eq 'HASH') {
764             for my \$f (CORE::keys \%{\$_[1]}) {
765             if (CORE::exists \$fields{\$f}) {
766             \$self->\$f(\$_[1]->{\$f});
767             } else {
768             CORE::push \@unknown_columns, \$f;
769             }
770             }
771             } elsif(not CORE::ref(\$_[1])) {
772             for(my \$i = 1; \$i < \@_; \$i += 2) {
773             if (CORE::exists \$fields{\$_[\$i]}) {
774             my \$f = \$_[\$i];
775             \$self->\$f(\$_[\$i + 1]);
776             } else {
777             CORE::push \@unknown_columns, \$_[\$i];
778             }
779             }
780             }
781             }
782             DBIx::Struct::error_message {
783             result => 'SQLERR',
784             message => 'unknown columns '.CORE::join(", ", \@unknown_columns).' for $table->data'
785             } if \@unknown_columns;
786             \$self;
787             }
788             SET
789 10         22 $set;
790             }
791              
792             sub make_object_data {
793 10     10 0 12 my $table = $_[0];
794 10         11 my $data = <
795             sub data {
796             my \$self = \$_[0];
797             my \@ret_keys;
798             my \$ret;
799             if(CORE::defined(\$_[1])) {
800             if(CORE::ref(\$_[1]) eq 'ARRAY') {
801             if(!\@{\$_[1]}) {
802 10         25 \$ret = \$self->[@{[_row_data]}];
803             } else {
804 10         24 \$ret = [CORE::map {\$self->[@{[_row_data]}]->[\$fields{\$_}] } \@{\$_[1]}];
805             }
806             } else {
807             for my \$k (\@_[1..\$#_]) {
808             CORE::push \@ret_keys, \$k if CORE::exists \$fields{\$k};
809             }
810             }
811             } else {
812             \@ret_keys = keys \%fields;
813             }
814             my \@unknown_columns = CORE::grep {not CORE::exists \$fields{\$_}} \@ret_keys;
815             DBIx::Struct::error_message {
816             result => 'SQLERR',
817             message => 'unknown columns '.CORE::join(", ", \@unknown_columns).' for $table->data'
818             } if \@unknown_columns;
819             \$ret = {
820             CORE::map {\$_ => \$self->\$_} \@ret_keys
821             } if not CORE::defined \$ret;
822             \$ret;
823             }
824             DATA
825 10         20 $data;
826             }
827              
828             sub make_object_update {
829 10     10 0 21 my ($table, $pk_where, $pk_row_data) = @_;
830 10         11 my $update;
831 10 100       26 if (not ref $table) {
832              
833             # means this is just one simple table
834 6         25 $update = <
835             sub update {
836             my \$self = \$_[0];
837             if(\@_ > 1 && CORE::ref(\$_[1]) eq 'HASH') {
838             my (\$set, \$where, \@bind, \@bind_where);
839             {
840             no strict 'vars';
841             local *set_hash = \$_[1];
842             my \@unknown_columns = CORE::grep {not CORE::exists \$fields{\$_}} CORE::keys %set_hash;
843             DBIx::Struct::error_message {
844             result => 'SQLERR',
845             message => 'unknown columns '.CORE::join(", ", \@unknown_columns).' updating table $table'
846             } if \@unknown_columns;
847             \$set =
848             CORE::join ", ",
849             CORE::map {
850             if(CORE::ref(\$set_hash{\$_}) eq 'ARRAY' and CORE::ref(\$set_hash{\$_}[0]) eq 'SCALAR') {
851             CORE::push \@bind, \@{\$set_hash{\$_}}[1..\$#{\$set_hash{\$_}}];
852             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = " . \${\$set_hash{\$_}[0]};
853             } elsif(CORE::ref(\$set_hash{\$_}) eq 'REF' and CORE::ref(\${\$set_hash{\$_}}) eq 'ARRAY') {
854             if(CORE::defined \${\$set_hash{\$_}}->[0]) {
855             CORE::push \@bind, \@{\${\$set_hash{\$_}}}[1..\$#{\${\$set_hash{\$_}}}];
856             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = " . \${\$set_hash{\$_}}->[0];
857             } else {
858             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = null"
859             }
860             } elsif(CORE::ref(\$set_hash{\$_}) eq 'SCALAR') {
861             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = " . \${\$set_hash{\$_}};
862             } elsif(CORE::exists(\$json_fields{\$_})
863             && (CORE::ref(\$set_hash{\$_}) eq 'ARRAY' || CORE::ref(\$set_hash{\$_}) eq 'HASH')) {
864             CORE::push \@bind, JSON::to_json(\$set_hash{\$_});
865             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = ?"
866             } else {
867             CORE::push \@bind, \$set_hash{\$_};
868             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = ?"
869             }
870             } CORE::keys \%set_hash;
871             }
872             if(\@_ > 2) {
873             my \$cond = \$_[2];
874             if(not CORE::ref(\$cond)) {
875             \$cond = {(selectKeys)[0] => \$_[2]};
876             }
877             (\$where, \@bind_where) = SQL::Abstract->new->where(\$cond);
878             }
879             return DBIx::Struct::connect->run(sub {
880             \$_->do(qq{update $table set \$set \$where}, undef, \@bind, \@bind_where)
881             or DBIx::Struct::error_message {
882             result => 'SQLERR',
883             message => 'error '.\$_->errstr.' updating table $table'
884             }
885             });
886 6         15 } elsif (CORE::ref(\$self) && \@\$self > 1 && \%{\$self->[@{[_row_updates]}]}) {
887             my (\$set, \@bind);
888             {
889             no strict 'vars';
890             \$set =
891             CORE::join ", ",
892             CORE::map {
893 6         30 local *column_value = \\\$self->[@{[_row_data]}][\$fields{\$_}];
894             if(CORE::ref(\$column_value) eq 'ARRAY' and CORE::ref(\$column_value->[0]) eq 'SCALAR') {
895             CORE::push \@bind, \@{\$column_value}[1..\$#\$column_value];
896             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = " . \${\$column_value->[0]};
897             } elsif(CORE::ref(\$column_value) eq 'REF' and CORE::ref(\${\$column_value}) eq 'ARRAY') {
898             if(CORE::defined \${\$column_value}->[0]) {
899             CORE::push \@bind, \@{\${\$column_value}}[1..\$#{\${\$column_value}}];
900             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = " . \${\$column_value}->[0];
901             } else {
902             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = null"
903             }
904             } elsif(CORE::ref(\$column_value) && CORE::ref(\$column_value) =~ /^DBIx::Struct::JSON/) {
905             \$column_value->revert;
906             CORE::push \@bind, \$column_value;
907             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = ?"
908             } elsif(CORE::ref(\$column_value) eq 'SCALAR') {
909             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = " . \$\$column_value;
910             } else {
911             CORE::push \@bind, \$column_value;
912             DBIx::Struct::connect->dbh->quote_identifier(\$_) . " = ?"
913             }
914 6         25 } CORE::keys \%{\$self->[@{[_row_updates]}]};
915             }
916             my \$update_query = qq{update $table set \$set where $pk_where};
917             DBIx::Struct::connect->run(
918             sub {
919             \$_->do(\$update_query, undef, \@bind, $pk_row_data)
920             or DBIx::Struct::error_message {
921             result => 'SQLERR',
922             message => 'error '.\$_->errstr.' updating table $table',
923             query => \$update_query,
924             bind => \\\@bind,
925             }
926             }
927             );
928 6         59 \$#\$self = @{[_row_data]};
929             }
930             \$self;
931             }
932             UPD
933             } else {
934 4         7 $update = <
935             sub update {}
936             UPD
937             }
938 10         33 $update;
939             }
940              
941             sub make_object_delete {
942 10     10 0 18 my ($table, $pk_where, $pk_row_data) = @_;
943 10         13 my $delete;
944 10 100       18 if (not ref $table) {
945 6         42 $delete = <
946             sub delete {
947             my \$self = \$_[0];
948             if(Scalar::Util::blessed \$self) {
949             DBIx::Struct::connect->run(
950             sub {
951             \$_->do(qq{delete from $table where $pk_where}, undef, $pk_row_data)
952             or DBIx::Struct::error_message {
953             result => 'SQLERR',
954             message => 'error '.\$_->errstr.' updating table $table'
955             }
956             });
957             return \$self;
958             }
959             my \$where = '';
960             my \@bind;
961             my \$cond = \$_[1] if \@_ > 1;
962             if(not CORE::ref(\$cond)) {
963             \$cond = {};
964             my \@keys = selectKeys();
965             for(my \$i = 1; \$i < \@_; ++\$i) {
966             DBIx::Struct::error_message {
967             result => 'SQLERR',
968             message => "Too many keys to delete for $table"
969             } if not CORE::defined \$keys[\$i-1];
970             \$cond->{\$keys[\$i-1]} = \$_[\$i];
971             }
972             }
973             my \@rpar = ();
974             if(\$cond) {
975             (\$where, \@bind) = SQL::Abstract->new->where(\$cond);
976             \@rpar = (undef, \@bind);
977             }
978             return DBIx::Struct::connect->run(sub {
979             \$_->do(qq{delete from $table \$where}, \@rpar)
980             or DBIx::Struct::error_message {
981             result => 'SQLERR',
982             message => 'error '.\$_->errstr.' updating table $table'
983             }
984             });
985             }
986             DEL
987             } else {
988 4         7 $delete = <
989             sub delete {}
990             DEL
991             }
992 10         19 $delete;
993             }
994              
995             sub make_object_autoload_find {
996 10     10 0 17 my ($table, $pk_where, $pk_row_data) = @_;
997 10         14 my $find = '';
998 10 100       34 if (not ref $table) {
999 6         24 $find = <
1000             sub AUTOLOAD {
1001             my \$self = \$_[0];
1002             ( my \$method = \$AUTOLOAD ) =~ s{.*::}{};
1003            
1004             if(Scalar::Util::blessed \$self) {
1005             \$self = CORE::ref \$self;
1006             }
1007             DBIx::Struct::error_message {
1008             result => 'SQLERR',
1009             message => "Unknown method \$method for $table"
1010             } if !\$self || !"\$self"->can("tableName") || \$method !~ /^find/;
1011             my \$func = DBIx::Struct::_parse_find_by('$table', \$method);
1012             my \$ncn = DBIx::Struct::make_name('$table');
1013             {
1014             no strict 'refs';
1015             *{\$ncn."::".\$method} = eval \$func;
1016             DBIx::Struct::error_message {
1017             result => 'SQLERR',
1018             message => "Error creating method \$method for $table: \$\@"
1019             } if \$\@;
1020             }
1021             goto &{\$ncn."::".\$method};
1022             }
1023             AUTOLOAD
1024             }
1025 10         18 $find;
1026             }
1027              
1028             sub make_object_fetch {
1029 10     10 0 15 my ($table, $pk_where, $pk_row_data) = @_;
1030 10         11 my $fetch;
1031 10 100       21 if (not ref $table) {
1032 6         14 $fetch = <
1033             sub fetch {
1034             my \$self = \$_[0];
1035             if(\@_ > 1) {
1036             my (\$where, \@bind);
1037             my \$cond = \$_[1];
1038             if(not CORE::ref(\$cond)) {
1039             \$cond = {(selectKeys)[0] => \$_[1]};
1040             }
1041             (\$where, \@bind) = SQL::Abstract->new->where(\$cond);
1042             DBIx::Struct::connect->run(sub {
1043             my \$rowref = \$_->selectrow_arrayref(qq{select * from $table \$where}, undef, \@bind)
1044             or DBIx::Struct::error_message {
1045             result => 'SQLERR',
1046             message => 'error '.\$_->errstr.' fetching table $table'
1047             };
1048 6         77 \$self->[@{[_row_data]}] = [\@\$rowref];
1049             });
1050             } else {
1051             DBIx::Struct::connect->run(
1052             sub {
1053             my \$rowref = \$_->selectrow_arrayref(qq{select * from $table where $pk_where}, undef, $pk_row_data)
1054             or DBIx::Struct::error_message {
1055             result => 'SQLERR',
1056             message => 'error '.\$_->errstr.' fetching table $table'
1057             };
1058 6         14 \$self->[@{[_row_data]}] = [\@\$rowref];
1059             });
1060             }
1061             \$self;
1062             }
1063             FETCH
1064             } else {
1065 4         13 $fetch = <
1066             sub fetch { \$_[0] }
1067             FETCH
1068             }
1069 10         21 $fetch;
1070             }
1071              
1072             sub _exists_row ($) {
1073 49     49   62 my $ncn = $_[0];
1074 3     3   25 no strict "refs";
  3         5  
  3         598  
1075 49 100       64 if (grep {!/::$/} keys %{"${ncn}::"}) {
  1368         1788  
  49         407  
1076 39         122 return 1;
1077             }
1078 10         28 return;
1079             }
1080              
1081             sub _parse_interface ($) {
1082 0     0   0 my $interface = $_[0];
1083 0         0 my %ret;
1084 0 0       0 $interface = [$interface] if not ref $interface;
1085 0 0       0 if ('ARRAY' eq ref $interface) {
    0          
1086 0         0 for my $i (@$interface) {
1087 0         0 my $dbc_name = make_name($i);
1088 0 0       0 error_message {
1089             result => 'SQLERR',
1090             message => "Unknown base interface table $i",
1091             }
1092             unless _exists_row $dbc_name;
1093 3     3   30 no strict 'refs';
  3         14  
  3         439  
1094 0         0 my $href = \%{"${dbc_name}::fkfuncs"};
  0         0  
1095 0 0 0     0 if ($href && %$href) {
1096 0         0 my @i = keys %$href;
1097 0         0 @ret{@i} = @{$href}{@i};
  0         0  
1098             }
1099             }
1100             } elsif ('HASH' eq ref $interface) {
1101 0         0 for my $i (keys %$interface) {
1102 0         0 my $dbc_name = make_name($i);
1103 0 0       0 error_message {
1104             result => 'SQLERR',
1105             message => "Unknown base interface table $i",
1106             }
1107             unless _exists_row $dbc_name;
1108 3     3   21 no strict 'refs';
  3         5  
  3         2826  
1109 0         0 my $href = \%{"${dbc_name}::fkfuncs"};
  0         0  
1110 0 0 0     0 next if not $href or not %$href;
1111 0         0 my $fl = $interface->{$i};
1112 0 0       0 $fl = [$fl] if not ref $fl;
1113 0 0       0 if ('ARRAY' eq ref $fl) {
1114              
1115 0         0 for my $m (@$fl) {
1116 0 0       0 $ret{$m} = $href->{$m} if exists $href->{$m};
1117             }
1118             } else {
1119 0         0 error_message {
1120             result => 'SQLERR',
1121             message => "Usupported interface structure",
1122             };
1123             }
1124             }
1125             } else {
1126 0         0 error_message {
1127             result => 'SQLERR',
1128             message => "Unknown interface structure: " . ref($interface),
1129             };
1130             }
1131 0         0 \%ret;
1132             }
1133              
1134             sub make_object_to_json {
1135 10     10 0 17 my ($table, $field_types, $fields) = @_;
1136 10         60 my @to_types = map {[
1137             $_,
1138 20         82 qq|!defined(\$self->[@{[_row_data]}][$fields->{$_}])? undef: |
1139             . (
1140 16         101 $field_types->{$_} eq 'number' ? "0+\$self->[@{[_row_data]}][$fields->{$_}]"
1141 0         0 : $field_types->{$_} eq 'boolean' ? "\$self->[@{[_row_data]}][$fields->{$_}]? \\1: \\0"
1142 20 50       29 : $field_types->{$_} eq 'json'
    50          
    100          
1143 0         0 ? "CORE::ref(\$self->[@{[_row_data]}]->[$fields->{$_}])? \$self->[@{[_row_data]}][$fields->{$_}]->data"
  0         0  
1144 0         0 . ": JSON::from_json(\$self->[@{[_row_data]}][$fields->{$_}])"
1145 4         22 : "\"\$self->[@{[_row_data]}][$fields->{$_}]\""
1146             )
1147             ]
1148             } keys %$field_types;
1149 10         19 my $field_to_types = join ",\n\t\t\t\t ", map {qq|"$_->[0]" => $_->[1]|} @to_types;
  20         54  
1150 10         25 my $sub_to_types = '';
1151 10         17 for my $tt (@to_types) {
1152 20         23 my $k = $tt->[0];
1153 20         32 $k =~ s/[^\w\d]/_/g;
1154 20         50 $sub_to_types .= <
1155             sub _to_json_$k { my \$self = \$_[0]; $tt->[1] }
1156             JSTT
1157             }
1158 10         94 my $to_json = <
1159             sub TO_JSON {
1160             my \$self = shift;
1161             my \$ret;
1162             my \@columns = CORE::grep { not ref } \@_;
1163             my \@refs = CORE::grep { 'HASH' eq ref } \@_;
1164             if(\@columns) {
1165             \$ret = +{
1166             map {
1167             DBIx::Struct::error_message {
1168             result => 'SQLERR',
1169             message => "unknown column \$_ in table $table"
1170             } if not CORE::exists \$fields{\$_};
1171             my \$k = \$_;
1172             \$k =~ s/[^\\w\\d]/_/g;
1173             my \$m = "_to_json_\$k";
1174             \$_ => \$self->\$m
1175             } \@columns
1176             };
1177             } else {
1178             \$ret = +{
1179             $field_to_types
1180             };
1181             }
1182             if(\@refs) {
1183             \$ret = +{ %\$ret, map { %\$_ } \@refs };
1184             }
1185             return \$ret;
1186             }
1187             TOJSON
1188 10         57 return $sub_to_types . $to_json;
1189             }
1190              
1191             sub _field_type_from_name {
1192 20     20   24 my $type_name = $_[0];
1193 20 50       49 return 'string' if not defined $type_name;
1194 20 100 66     199 if ( $type_name =~ /int(\d+)?$/i
    50 66        
    50 66        
    50 33        
    50          
    50          
1195             || $type_name =~ /integer/i
1196             || $type_name =~ /bit$/
1197             || $type_name =~ /float|double|real|decimal|numeric/i)
1198             {
1199 16         70 return 'number';
1200             } elsif ($type_name =~ /json/i) {
1201 0         0 return 'json';
1202             } elsif ($type_name =~ /bool/i) {
1203 0         0 return 'boolean';
1204             } elsif ($type_name =~ /date/i && $type_name =~ /time/i) {
1205 0         0 return 'datetime';
1206             } elsif ($type_name =~ /date/i) {
1207 0         0 return 'date';
1208             } elsif ($type_name =~ /time/i) {
1209 0         0 return 'time';
1210             } else {
1211 4         23 return 'string';
1212             }
1213             }
1214              
1215             sub _schema_name {
1216 23     23   26 my $ncn = $_[0];
1217 23 100       46 if ($user_schema_namespace) {
1218 5         24 (my $uncn = $ncn) =~ s/^.*:://;
1219 3     3   42 no strict 'refs';
  3         7  
  3         11063  
1220 5         7 eval {
1221 5 100 66     6 if (${"${user_schema_namespace}::${uncn}::"}{ISA}
  5         34  
1222             && "${user_schema_namespace}::${uncn}"->isa($ncn))
1223             {
1224 2         6 $ncn = "${user_schema_namespace}::${uncn}";
1225             }
1226             };
1227             }
1228 23         35 $ncn;
1229             }
1230              
1231             sub setup_row {
1232 49     49 0 93 my ($table, $ncn, $interface) = @_;
1233             error_message {
1234             result => 'SQLERR',
1235             message => "Unsupported driver $connector_driver",
1236             }
1237 49 50       113 unless exists $driver_pk_insert{$connector_driver};
1238 49   66     91 $ncn ||= make_name($table);
1239 49 100       88 return $ncn if _exists_row $ncn ;
1240 10         52 my %fields;
1241             my @fields;
1242 10         0 my @timestamp_fields;
1243 10         0 my @required;
1244 10         0 my @pkeys;
1245 10         0 my @fkeys;
1246 10         0 my @refkeys;
1247 10         0 my %json_fields;
1248 10         16 my $connector = DBIx::Struct::connect;
1249 10         15 my %field_types;
1250              
1251 10 100       23 if (not ref $table) {
1252             # means this is just one simple table
1253             $connector->run(
1254             sub {
1255 6     6   63 my $ssth = $_->prepare('select * from ' . $_->quote_identifier($table) . ' where 0 = 1');
1256 6 50       400 error_message {
1257             result => 'SQLERR',
1258             message => "Unknown table $table",
1259             }
1260             if not $ssth;
1261 6 50       27 $ssth->execute
1262             or error_message {
1263             result => 'SQLERR',
1264             message => "Probably unknown table $table: " . $_->errstr,
1265              
1266             };
1267 6         70 my $cih = $_->column_info(undef, undef, $table, undef);
1268 6 50       628 error_message {
1269             result => 'SQLERR',
1270             message => "Unknown table $table",
1271             }
1272             if not $cih;
1273 6         11 my $i = 0;
1274 6         16 while (my $chr = $cih->fetchrow_hashref) {
1275 12         1177 $chr->{COLUMN_NAME} =~ s/"//g;
1276 12         25 $chr->{COLUMN_NAME} = lc $chr->{COLUMN_NAME};
1277 12         21 push @fields, $chr->{COLUMN_NAME};
1278 12 50       43 if ($chr->{TYPE_NAME} =~ /^time/i) {
1279 0         0 push @timestamp_fields, $chr->{COLUMN_NAME};
1280             }
1281 12 50       26 if ($chr->{TYPE_NAME} =~ /^json/i) {
1282 0         0 $json_fields{$chr->{COLUMN_NAME}} = undef;
1283             }
1284 12   66     45 $chr->{COLUMN_DEF} //= $chr->{mysql_is_auto_increment};
1285 12 50 66     47 if ($chr->{NULLABLE} == 0 && !defined($chr->{COLUMN_DEF})) {
1286 0         0 push @required, $chr->{COLUMN_NAME};
1287             }
1288 12         26 $fields{$chr->{COLUMN_NAME}} = $i++;
1289 12         26 $field_types{$chr->{COLUMN_NAME}} = _field_type_from_name($chr->{TYPE_NAME});
1290             }
1291 6         72 @pkeys = map {lc} $_->primary_key(undef, undef, $table);
  4         47  
1292 6 50 66     32 if (!@pkeys && @required) {
1293 0         0 my $ukh = $_->statistics_info(undef, undef, $table, 1, 1);
1294 0         0 my %req = map {$_ => undef} @required;
  0         0  
1295 0         0 my %pkeys;
1296 0         0 while (my $ukr = $ukh->fetchrow_hashref) {
1297 0 0 0     0 if (not exists $req{$ukr->{COLUMN_NAME}} or defined $ukr->{FILTER_CONDITION}) {
1298 0         0 $pkeys{lc $ukr->{INDEX_NAME}}{drop} = 1;
1299             } else {
1300 0         0 $pkeys{lc $ukr->{INDEX_NAME}}{fields}{lc $ukr->{COLUMN_NAME}} = undef;
1301             }
1302             }
1303 0         0 my @d = grep {exists $pkeys{$_}{drop}} keys %pkeys;
  0         0  
1304 0         0 delete $pkeys{$_} for @d;
1305 0 0       0 if (%pkeys) {
1306 0         0 my @spk = sort {scalar(keys %{$pkeys{$a}{fields}}) <=> scalar(keys %{$pkeys{$b}{fields}})}
  0         0  
  0         0  
  0         0  
1307             keys %pkeys;
1308 0         0 @pkeys = keys %{$pkeys{$spk[0]}{fields}};
  0         0  
1309             }
1310             }
1311 6         16 my $sth = $_->foreign_key_info(undef, undef, undef, undef, undef, $table);
1312 6 50       489 if ($sth) {
1313             @fkeys =
1314 4 50 33     25 grep {($_->{PKTABLE_NAME} || $_->{UK_TABLE_NAME}) && $_->{FK_COLUMN_NAME} !~ /[^a-z_0-9]/}
1315             map {
1316             $_->{FK_COLUMN_NAME} = $_->{FKCOLUMN_NAME}
1317 4 50       281 if $_->{FKCOLUMN_NAME};
1318 4 50       9 $_->{FK_TABLE_NAME} = $_->{FKTABLE_NAME} if $_->{FKTABLE_NAME};
1319 4         7 $_->{FK_TABLE_NAME} = lc $_->{FK_TABLE_NAME};
1320 4         6 $_->{FK_COLUMN_NAME} = lc $_->{FK_COLUMN_NAME};
1321 4   33     19 $_->{PKTABLE_NAME} ||= $_->{UK_TABLE_NAME};
1322 4   33     63 $_->{PKCOLUMN_NAME} ||= $_->{UK_COLUMN_NAME};
1323 4 50       20 $_->{PKTABLE_NAME} = lc $_->{PKTABLE_NAME} if $_->{PKTABLE_NAME};
1324 4 50       8 $_->{PKCOLUMN_NAME} = lc $_->{PKCOLUMN_NAME} if $_->{PKCOLUMN_NAME};
1325 4         9 $_
1326 6         10 } @{$sth->fetchall_arrayref({})};
  6         17  
1327             }
1328 6         62 $sth = $_->foreign_key_info(undef, undef, $table, undef, undef, undef);
1329 6 50       427 if ($sth) {
1330             @refkeys =
1331 4 50 33     78 grep {($_->{PKTABLE_NAME} || $_->{UK_TABLE_NAME}) && $_->{FK_COLUMN_NAME} !~ /[^a-z_0-9]/}
1332             map {
1333             $_->{FK_COLUMN_NAME} = $_->{FKCOLUMN_NAME}
1334 4 50       297 if $_->{FKCOLUMN_NAME};
1335 4 50       10 $_->{FK_TABLE_NAME} = $_->{FKTABLE_NAME} if $_->{FKTABLE_NAME};
1336 4         8 $_->{FK_TABLE_NAME} = lc $_->{FK_TABLE_NAME};
1337 4         7 $_->{FK_COLUMN_NAME} = lc $_->{FK_COLUMN_NAME};
1338 4   33     19 $_->{PKTABLE_NAME} = lc($_->{PKTABLE_NAME} || $_->{UK_TABLE_NAME});
1339 4   33     37 $_->{PKCOLUMN_NAME} = lc($_->{PKCOLUMN_NAME} || $_->{UK_COLUMN_NAME});
1340 4         14 $_
1341 6         10 } @{$sth->fetchall_arrayref({})};
  6         12  
1342             }
1343             }
1344 6         60 );
1345             } else {
1346              
1347             # means this is a query
1348 4         4 my %tnh = %{$table->{NAME_lc_hash}};
  4         16  
1349 4         11 for my $k (keys %tnh) {
1350 8         10 my $fk = $k;
1351 8         14 $fk =~ s/[^\w ].*$//;
1352 8         15 $fields{$fk} = $tnh{$k};
1353             }
1354             $connector->run(
1355             sub {
1356 4     4   31 for (my $cn = 0; $cn < @{$table->{NAME}}; ++$cn) {
  12         35  
1357 8         20 my $ti = $_->type_info($table->{TYPE}->[$cn]);
1358 8         48 my $field = lc $table->{NAME}->[$cn];
1359 8         11 $field =~ s/[^\w ].*$//;
1360 8         14 $field_types{$field} = _field_type_from_name($ti->{TYPE_NAME});
1361             push @timestamp_fields, $field
1362 8 50 33     29 if $ti->{TYPE_NAME} && $ti->{TYPE_NAME} =~ /^time/;
1363             $json_fields{$field} = undef
1364 8 50 33     30 if $ti->{TYPE_NAME} && $ti->{TYPE_NAME} =~ /^json/;
1365             }
1366             }
1367 4         37 );
1368             }
1369 10         244 my $field_types = join ", ", map {qq|"$_" => '$field_types{$_}'|} keys %field_types;
  20         73  
1370 10         25 my $fields = join ", ", map {qq|"$_" => $fields{$_}|} keys %fields;
  20         54  
1371 10         24 my $json_fields = join ", ", map {qq|"$_" => undef|} keys %json_fields;
  0         0  
1372 10         13 my $required = '';
1373 10 50       23 if (@required) {
1374 0         0 $required = join(", ", map {qq|"$_"|} @required);
  0         0  
1375             }
1376 10         14 my $timestamps = '';
1377 10 50       21 if (@timestamp_fields) {
1378 0         0 $timestamps = join(", ", map {qq|"$_"|} @timestamp_fields);
  0         0  
1379             } else {
1380 10         13 $timestamps = "()";
1381             }
1382 10         47 my %keywords = (
1383             new => undef,
1384             set => undef,
1385             data => undef,
1386             delete => undef,
1387             fetch => undef,
1388             update => undef,
1389             DESTROY => undef,
1390             filter_timestamp => undef,
1391             );
1392 10         14 my $pk_row_data = '';
1393 10         16 my $pk_returninig = '';
1394 10         10 my $pk_where = '';
1395 10         12 my $select_keys = '';
1396 10         10 my %pk_fields;
1397 10 100       16 if (@pkeys) {
1398 4         11 @pk_fields{@pkeys} = undef;
1399 4         9 $pk_row_data = join(", ", map {qq|\$self->[@{[_row_data]}]->[$fields{"$_"}]|} @pkeys);
  4         6  
  4         23  
1400 4         9 $pk_returninig = 'returning ' . join(", ", @pkeys);
1401 4         7 $pk_where = join(" and ", map {"$_ = ?"} @pkeys);
  4         9  
1402 4         9 my $sk_list = join(", ", map {qq|"$_"|} @pkeys);
  4         8  
1403 4         11 $select_keys = <
1404             sub selectKeys () {
1405             ($sk_list)
1406             }
1407             SK
1408             } else {
1409 6 100       15 if (@fields) {
1410 2         4 my $sk_list = join(", ", map {qq|"$_"|} @fields);
  4         11  
1411 2         6 $select_keys = <
1412             sub selectKeys () {
1413             ($sk_list)
1414             }
1415             SK
1416             } else {
1417 4         5 $select_keys = <
1418             sub selectKeys () { () }
1419             SK
1420             }
1421             }
1422 10         15 my $foreign_tables = '';
1423 10         18 my %foreign_tables;
1424             my %fkfuncs;
1425 10         29 for my $fk (@fkeys) {
1426 4   33     16 (my $pt = $fk->{PKTABLE_NAME} || $fk->{UK_TABLE_NAME}) =~ s/"//g;
1427 4   33     10 (my $pk = $fk->{PKCOLUMN_NAME} || $fk->{UK_COLUMN_NAME}) =~ s/"//g;
1428 4         7 my $fn = $pt;
1429 4         6 $fn =~ tr/_/-/;
1430 4         30 $fn =~ s/\b(\w)/\u$1/g;
1431 4         8 $fn =~ tr/-//d;
1432 4         10 $fk->{FK_COLUMN_NAME} =~ s/"//g;
1433 4         7 my $fn_suffix = $fk->{FK_COLUMN_NAME};
1434 4 50 33     38 $fn_suffix =~ s/^${pk}_*//i or $fn_suffix =~ s/_$pk(?=[^a-z]|$)//i or $fn_suffix =~ s/$pk(?=[^a-z]|$)//i;
1435 4         9 $fn_suffix =~ tr/_/-/;
1436 4         17 $fn_suffix =~ s/\b(\w)/\u$1/g;
1437 4         7 $fn_suffix =~ tr/-//d;
1438 4         49 $fn_suffix =~ s/$fn//;
1439 4         10 $fn .= $fn_suffix;
1440 4         8 $fkfuncs{$fn} = undef;
1441 4         19 $foreign_tables .= <
1442             sub $fn {
1443             if(CORE::defined(\$_[0]->$fk->{FK_COLUMN_NAME})) {
1444             return DBIx::Struct::one_row("$pt", {$pk => \$_[0]->$fk->{FK_COLUMN_NAME}});
1445             } else {
1446             return
1447             }
1448             }
1449             FKT
1450 4         15 $foreign_tables{$pt} = [$fk->{FK_COLUMN_NAME} => $pk];
1451             }
1452 10         33 for my $ft (keys %foreign_tables) {
1453 4         8 my $ucft = ucfirst $ft;
1454 4         10 $fkfuncs{"foreignKey$ucft"} = undef;
1455 4         14 $foreign_tables .= <
1456             sub foreignKey$ucft () {("$foreign_tables{$ft}[0]" => "$foreign_tables{$ft}[1]")}
1457             FKT
1458             }
1459 10         16 my $references_tables = '';
1460 10         19 for my $rk (@refkeys) {
1461 4         12 $rk->{FK_TABLE_NAME} =~ s/"//g;
1462 4         5 my $ft = $rk->{FK_TABLE_NAME};
1463 4         10 (my $fk = $rk->{FK_COLUMN_NAME}) =~ s/"//g;
1464 4   33     14 (my $pt = $rk->{PKTABLE_NAME} || $rk->{UK_TABLE_NAME}) =~ s/"//g;
1465 4   33     12 (my $pk = $rk->{PKCOLUMN_NAME} || $rk->{UK_COLUMN_NAME}) =~ s/"//g;
1466 4 50       9 if ($pk ne $fk) {
1467 4         6 my $fn = $fk;
1468 4 50 33     49 $fn =~ s/^${pk}_*//i or $fn =~ s/_$pk(?=[^a-z]|$)//i or $fn =~ s/$pk(?=[^a-z]|$)//i;
1469 4         37 $fn =~ s/$pt//i;
1470 4 50       14 $ft .= "_$fn" if $fn;
1471             }
1472 4         9 $ft =~ tr/_/-/;
1473 4         42 $ft =~ s/\b(\w)/\u$1/g;
1474 4         11 $ft =~ tr/-//d;
1475 4         11 $fkfuncs{"ref${ft}s"} = undef;
1476 4         9 $fkfuncs{"ref${ft}"} = undef;
1477 4         44 $references_tables .= <
1478             sub ref${ft}s {
1479             my (\$self, \@cond) = \@_;
1480             my \%cond;
1481             if(\@cond) {
1482             if(not CORE::ref \$cond[0]) {
1483             \%cond = \@cond;
1484             } else {
1485             \%cond = \%{\$cond[0]};
1486             }
1487             }
1488             \$cond{$fk} = \$self->$pk;
1489             return DBIx::Struct::all_rows("$rk->{FK_TABLE_NAME}", \\\%cond);
1490             }
1491             sub ref${ft} {
1492             my (\$self, \@cond) = \@_;
1493             my \%cond;
1494             if(\@cond) {
1495             if(not CORE::ref \$cond[0]) {
1496             \%cond = \@cond;
1497             } else {
1498             \%cond = \%{\$cond[0]};
1499             }
1500             }
1501             \$cond{$fk} = \$self->$pk;
1502             return DBIx::Struct::one_row("$rk->{FK_TABLE_NAME}", \\\%cond);
1503             }
1504             RT
1505             }
1506 10         14 my $accessors = <
1507             sub markUpdated {
1508 10         31 \$_[0]->[@{[_row_updates]}]{\$_[1]} = undef if CORE::exists \$fields{\$_[1]};
1509             \$_[0];
1510             }
1511             ACC
1512 10         26 for my $k (keys %fields) {
1513 20 50       40 next if exists $keywords{$k};
1514 20 50       46 next if $k =~ /^\d/;
1515 20         33 $k =~ s/[^\w\d]/_/g;
1516 20 50       32 if (!exists $json_fields{$k}) {
1517 20 100 100     73 if (!exists($pk_fields{$k}) && (not ref $table)) {
1518 8         14 $accessors .= <
1519             sub _$k {
1520             if(\@_ > 1) {
1521 8         22 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = \$_[1];
1522             }
1523 8         23 \$_[0]->[@{[_row_data]}]->[$fields{$k}];
1524             }
1525             sub $k {
1526             if(\@_ > 1) {
1527 8         32 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = \$_[1];
1528 8         22 \$_[0]->[@{[_row_updates]}]{"$k"} = undef;
1529             }
1530 8         43 \$_[0]->[@{[_row_data]}]->[$fields{$k}];
1531             }
1532             ACC
1533             } else {
1534 12         24 $accessors .= <
1535             sub $k {
1536 12         39 \$_[0]->[@{[_row_data]}]->[$fields{$k}];
1537             }
1538             ACC
1539             }
1540             } else {
1541 0 0 0     0 if (!exists($pk_fields{$k}) && (not ref $table)) {
1542 0         0 $accessors .= <
1543             sub _$k {
1544             if(\@_ > 1) {
1545             if(not CORE::ref \$_[1]) {
1546 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = \$_[1];
1547             } else {
1548 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = JSON::to_json(\$_[1]);
1549             }
1550             }
1551 0         0 if(not CORE::ref \$_[0]->[@{[_row_data]}]->[$fields{$k}]) {
1552 0         0 \$_[0]->[@{[_row_updates]}] = {} if not \$_[0]->[@{[_row_updates]}];
  0         0  
1553 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}] =
1554 0         0 DBIx::Struct::JSON->factory(\\\$_[0]->[@{[_row_data]}]->[$fields{$k}], \$_[0]->[@{[_row_updates]}], "$k");
  0         0  
1555             }
1556 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}]->accessor;
1557             }
1558             sub $k {
1559             if(\@_ > 1) {
1560             if(not CORE::ref \$_[1]) {
1561 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = \$_[1];
1562             } else {
1563 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = JSON::to_json(\$_[1]);
1564             }
1565 0         0 \$_[0]->[@{[_row_updates]}]{"$k"} = undef;
1566             }
1567 0         0 if(not CORE::ref \$_[0]->[@{[_row_data]}]->[$fields{$k}]) {
1568 0         0 \$_[0]->[@{[_row_updates]}] = {} if not \$_[0]->[@{[_row_updates]}];
  0         0  
1569 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}] =
1570 0         0 DBIx::Struct::JSON->factory(\\\$_[0]->[@{[_row_data]}]->[$fields{$k}], \$_[0]->[@{[_row_updates]}], "$k");
  0         0  
1571             }
1572 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}]->accessor;
1573             }
1574             ACC
1575             } else {
1576 0         0 $accessors .= <
1577             sub $k {
1578 0         0 if(\$_[0]->[@{[_row_data]}]->[$fields{$k}] and not CORE::ref \$_[0]->[@{[_row_data]}]->[$fields{$k}]) {
  0         0  
1579 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}] = JSON::from_json(\$_[0]->[@{[_row_data]}]->[$fields{$k}]);
  0         0  
1580             }
1581 0         0 \$_[0]->[@{[_row_data]}]->[$fields{$k}];
1582             }
1583             ACC
1584             }
1585             }
1586             }
1587 10         33 my $package_header = <
1588             package ${ncn};
1589             use strict;
1590             use warnings;
1591             use Carp;
1592             use SQL::Abstract;
1593             use JSON;
1594             use Scalar::Util 'blessed';
1595             use vars qw(\$AUTOLOAD);
1596             our \%field_types = ($field_types);
1597             our \%fields = ($fields);
1598             our \%json_fields = ($json_fields);
1599             PHD
1600 10 100       21 if (not ref $table) {
1601 6 50       16 if (%fkfuncs) {
1602 6         16 my $fkfuncs = join ",", map {qq{"$_" => \\&${ncn}::$_}} keys %fkfuncs;
  16         49  
1603 6         19 $package_header .= <
1604             our \%fkfuncs = ($fkfuncs);
1605             PHD
1606             } else {
1607 0         0 $package_header .= <
1608             our \%fkfuncs = ();
1609             PHD
1610             }
1611 6         12 $package_header .= <
1612             sub tableName () {"$table"}
1613             PHD
1614             } else {
1615 4         13 $package_header .= <
1616             sub tableName () {"\\\$query\\\$$ncn"}
1617             PHD
1618             }
1619 10         25 my $new = make_object_new($table, $required, $pk_row_data, $pk_returninig);
1620 10         26 my $filter_timestamp = make_object_filter_timestamp($timestamps);
1621 10         26 my $set = make_object_set($table);
1622 10         24 my $data = make_object_data($table);
1623 10         20 my $update = make_object_update($table, $pk_where, $pk_row_data);
1624 10         19 my $delete = make_object_delete($table, $pk_where, $pk_row_data);
1625 10         23 my $fetch = make_object_fetch($table, $pk_where, $pk_row_data);
1626 10         17 my $autoload = make_object_autoload_find($table, $pk_where, $pk_row_data);
1627 10         33 my $to_json = make_object_to_json($table, \%field_types, \%fields);
1628 10         17 my $destroy;
1629              
1630 10 100       21 if (not ref $table) {
1631 6         9 $destroy = <
1632             sub DESTROY {
1633             no warnings 'once';
1634             \$_[0]->update if \$DBIx::Struct::update_on_destroy;
1635             }
1636             DESTROY
1637             } else {
1638 4         7 $destroy = '';
1639             }
1640 10         219 my $eval_code = join "", $package_header, $select_keys, $new,
1641             $set, $data, $fetch, $autoload, $to_json, $filter_timestamp,
1642             $update, $delete, $destroy, $accessors, $foreign_tables, $references_tables;
1643              
1644             # print $eval_code;
1645 2 0 0 2   26 eval $eval_code;
  2 0 0 2   4  
  2 0 0 2   71  
  2 0 0 2   11  
  2 0 0 2   35  
  2 0 0 2   68  
  2 0 33 2   9  
  2 0 33 2   3  
  2 0 66 2   135  
  2 50 66 2   13  
  2 50 33 2   37  
  2 50 0 2   87  
  2 0 33 2   11  
  2 50 33 2   2  
  2 0 0 2   15  
  2 0 0 2   320  
  2 0 0 2   3  
  2 0 0 2   155  
  2 0 33 2   14  
  2 0 0 2   3  
  2 0 0 2   3889  
  2 0 0 2   16  
  2 0 0 3   4  
  2 0 33 2   1955  
  2 0 0 2   16  
  2 0 66 2   5  
  2 0 33 2   1348  
  2 0 33 2   14  
  2 0 0 2   8  
  2 0 33 2   1882  
  2 0 66 2   15  
  2 0 66 2   4  
  2 0 66 2   921  
  2 0 33 2   13  
  2 0 66 1   4  
  2 0 33 1   50  
  2 0 0 1   8  
  2 0 0 1   4  
  2 0 0 1   50  
  2 0 0 1   8  
  2 0 33 1   3  
  2 0 33 1   100  
  2 0 0 1   10  
  2 0 0 1   4  
  2 0 0 1   54  
  2 0 33 1   10  
  2 0 0 1   3  
  2 0 0 1   7  
  2 0 0 1   273  
  2 0 0 1   4  
  2 50 33 1   93  
  2 0 33 1   12  
  2 0 0 1   2  
  2 50 0 1   3777  
  2 50 0 1   17  
  2 0   1   4  
  2 0   1   1800  
  2 0   1   16  
  2 0   1   4  
  2 0   1   1418  
  2 0   1   15  
  2 0   0   2  
  2 0   0   1816  
  2 50   0   23  
  2 0   4   4  
  2 0   8   1074  
  3 0   5   158  
  2 0   0   4  
  2 0   3   52  
  2 0   0   10  
  2 0   0   4  
  2 0   0   57  
  2 0   0   11  
  2 0   0   3  
  2 0   0   130  
  2 0   0   13  
  2 0   0   3  
  2 0   0   70  
  2 0   0   11  
  2 0   0   3  
  2 0   0   10  
  2 0   0   328  
  2 0   0   3  
  2 0   0   113  
  2 0   0   12  
  2 0   0   15  
  2 0   0   3803  
  2 0   0   18  
  2 0   0   4  
  2 0   0   1931  
  2 0   0   16  
  2 0   0   4  
  2 0   0   1339  
  2 0   0   14  
  2 0   0   3  
  2 0   0   1895  
  2 0   0   16  
  2 0   0   4  
  2 0   0   936  
  2 0   0   148  
  1 0   0   2  
  1 0   0   56  
  1 0   0   6  
  1 0   0   2  
  1 0   0   28  
  1 0   1   4  
  1 0   0   2  
  1 0   0   68  
  1 0   0   7  
  1 0   0   1  
  1 0   0   41  
  1 0   0   5  
  1 0   0   2  
  1 0   0   6  
  1 0   0   127  
  1 0   0   2  
  1 0   0   69  
  1 0   0   9  
  1 0   0   1  
  1 0   0   1753  
  1 0   0   8  
  1 0   0   2  
  1 0   0   27  
  1 0   1   4  
  1 0   0   2  
  1 0   3   42  
  1 0   2   6  
  1 0   0   2  
  1 0   0   51  
  1 0   0   5  
  1 50   0   2  
  1 50   0   38  
  1 0   6   5  
  1 50   0   1  
  1 50   0   5  
  1 0   0   142  
  1 0   0   1  
  1 0   0   59  
  1 0   0   6  
  1 0   0   1  
  1 0   3   1636  
  1 0   3   7  
  1 0   7   2  
  1 0   3   33  
  1 0   3   5  
  1 0   1   1  
  1 0   0   40  
  1 0   1   5  
  1 0   0   9  
  1 0   0   61  
  1 0   0   7  
  1 0   4   1  
  1 0   0   40  
  1 0   3   5  
  1 0   2   2  
  1 0   0   8  
  1 0   0   123  
  1 0   0   2  
  1 0   0   56  
  1 0   0   12  
  1 0   0   1  
  1 0   0   1718  
  1 0   0   8  
  1 0   0   2  
  1 0   0   28  
  1 0   0   4  
  1 0   0   3  
  1 0   0   43  
  1 0   0   5  
  1 0   2   2  
  1 0   12   51  
  1 0   1   5  
  1 0   3   2  
  1 50   0   44  
  1 0   0   5  
  1 0   3   2  
  1 0       21  
  1 0       140  
  1 0       2  
  1 0       57  
  1 0       6  
  1 50       2  
  1 50       1747  
  10 50       982  
  0 0       0  
  0 100       0  
  0 50       0  
  0 100       0  
  0 100       0  
  0 50       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 50       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 50       0  
  0 100       0  
  0 0       0  
  0 50       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  
  0 0       0  
  4 0       807  
  8 0       1362  
  5 0       26  
  0 0       0  
  0 0       0  
  0 0       0  
  3 0       9  
  3 0       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 50       0  
  0 0       0  
  0 50       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 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 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 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  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         19  
  0         0  
  0         0  
  0         0  
  3         59  
  3         58  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         22  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  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         333  
  1         5  
  0         0  
  0         0  
  1         1  
  1         2  
  1         3  
  1         5  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         1  
  1         3  
  1         4  
  1         207  
  1         18  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  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         10  
  0         0  
  3         7  
  2         4  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  6         38  
  0         0  
  0         0  
  6         22  
  0         0  
  0         0  
  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         9  
  3         5  
  3         6  
  3         18  
  3         13  
  1         16  
  3         9  
  3         5  
  3         18  
  3         19  
  3         14  
  1         75  
  7         17  
  7         13  
  7         12  
  7         34  
  4         15  
  3         6  
  3         9  
  3         9  
  3         4  
  3         7  
  3         10  
  0         0  
  3         5  
  3         7  
  3         44  
  1         3  
  1         3  
  1         2  
  1         2  
  1         9  
  1         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  
  1         2  
  1         2  
  3         33  
  0         0  
  0         0  
  0         0  
  1         3  
  1         2  
  3         37  
  3         124  
  3         7  
  3         10  
  0         0  
  3         7  
  7         116  
  3         9  
  3         7  
  3         4  
  3         16  
  3         10  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  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         45  
  3         7  
  3         6  
  3         5  
  3         16  
  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  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         42  
  1         12  
  1         3  
  1         2  
  1         10  
  1         6  
  1         8  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         6  
  0         0  
  0         0  
  1         17  
  0         0  
  0         0  
  0         0  
  4         1002  
  3         6  
  3         8  
  4         11  
  0         0  
  3         36  
  3         5  
  3         7  
  0         0  
  0         0  
  0         0  
  0         0  
  3         8  
  3         8  
  2         36  
  2         3  
  2         5  
  0         0  
  0         0  
  0         0  
  0         0  
  2         6  
  2         6  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  14         68  
  14         128  
  1         2  
  1         2  
  1         2  
  1         4  
  1         3  
  1         4  
  1         2  
  1         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  
  1         2  
  1         3  
  1         53  
  1         3  
  1         4  
  0         0  
  1         4  
  1         260  
  3         12  
  3         7  
  3         3  
  3         4  
  3         9  
  3         22  
  1         3  
  1         3  
  1         3  
  1         42  
  0         0  
  0         0  
  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         5  
  1         3  
  1         14  
  3         9  
  3         92  
  3         9  
  3         87  
  13         335  
  4         13  
  4         41  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         51  
  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  
  0         0  
  0         0  
  3         24  
1646 10 50       43 error_message {
1647             result => 'SQLERR',
1648             message => "Unknown error: $@",
1649             } if $@;
1650 10 50       24 if ($interface) {
1651 0         0 my $ifuncs = _parse_interface $interface;
1652 3     3   26 no strict 'refs';
  3         6  
  3         12771  
1653 0         0 for my $f (keys %$ifuncs) {
1654 0         0 *{"${ncn}::$f"} = $ifuncs->{$f};
  0         0  
1655             }
1656             }
1657 10         39 '' =~ /()/;
1658 10         106 return $ncn;
1659             }
1660              
1661             my %cache_complex_query;
1662             my $json_canonical = JSON->new->canonical->convert_blessed;
1663              
1664             sub _cached_complex_query {
1665 4     4   37 my $key = $json_canonical->encode(\@_);
1666 4         7 my ($ret, $is_one_column);
1667 4 50       17 if (exists $cache_complex_query{$key}) {
1668 0         0 ($ret, $is_one_column) = @{$cache_complex_query{$key}};
  0         0  
1669             } else {
1670 4         8 ($ret, $is_one_column) = _build_complex_query(@_);
1671 4         15 $cache_complex_query{$key} = [($ret, $is_one_column)];
1672             }
1673 4 50       9 if (wantarray) {
1674 4         10 return ($ret, $is_one_column);
1675             } else {
1676 0         0 return $ret;
1677             }
1678             }
1679              
1680             sub _table_name() {0}
1681             sub _table_alias() {1}
1682             sub _table_join() {2}
1683             sub _table_join_on() {3}
1684              
1685             my $sql_abstract = SQL::Abstract->new;
1686             my $tblnum;
1687              
1688             sub _build_complex_query {
1689 4     4   8 my ($table, $query_bind, $where) = @_;
1690 4 50       9 return $table if not ref $table;
1691 4         7 my @from;
1692             my @columns;
1693 4 50       15 my @linked_list = (
1694             ref($table) eq 'ARRAY'
1695             ? @$table
1696             : error_message {
1697             result => 'SQLERR',
1698             message => "Unsupported type of query: " . ref($table)
1699             }
1700             );
1701 4         5 my ($conditions, $groupby, $having, $limit, $offset, $orderby);
1702 4         5 my $one_column = 0;
1703 4         6 my $distinct = 0;
1704 4         4 my $count = 0;
1705 4         5 my $all = 0;
1706              
1707 4         9 for (my $i = 0; $i < @linked_list; ++$i) {
1708 11         15 my $le = $linked_list[$i];
1709 11 50       19 if ('ARRAY' eq ref $le) {
1710 0         0 my $subfrom = _build_complex_query($le, $query_bind);
1711 0         0 my $ta = "t$tblnum";
1712 0         0 ++$tblnum;
1713 0         0 push @from, ["($subfrom)", $ta];
1714 0         0 next;
1715             }
1716 11 100       24 if (substr($le, 0, 1) ne '-') {
1717 5         14 my ($tn, $ta) = split ' ', $le;
1718 5 50       11 $ta = $tn if not $ta;
1719 5         8 my $ncn = make_name($tn);
1720 5         9 $ncn = _schema_name($ncn);
1721 5 50       8 error_message {
1722             result => 'SQLERR',
1723             message => "Unknown table $tn"
1724             }
1725             unless setup_row($tn, $ncn);
1726 5         18 push @from, [$tn, $ta];
1727             } else {
1728 6         7 my $cmd = substr($le, 1);
1729 6 50 66     66 if ($cmd eq 'left') {
    50 100        
    100 66        
    50 33        
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1730 0         0 $from[-1][_table_join] = 'left join';
1731             } elsif ($cmd eq 'right') {
1732 0         0 $from[-1][_table_join] = 'right join';
1733             } elsif ($cmd eq 'join') {
1734 1         8 $from[-1][_table_join] = 'join';
1735             } elsif ($cmd eq 'on') {
1736 0         0 $from[-1][_table_join_on] = ["on", $linked_list[++$i]];
1737             } elsif ($cmd eq 'using') {
1738 0         0 $from[-1][_table_join_on] = ["using", $linked_list[++$i]];
1739             } elsif ($cmd eq 'as') {
1740 0         0 $from[-1][_table_alias] = $linked_list[++$i];
1741             } elsif ($cmd eq 'where') {
1742 0         0 $conditions = $linked_list[++$i];
1743             } elsif ($cmd eq 'group_by') {
1744 0         0 $groupby = $linked_list[++$i];
1745             } elsif ($cmd eq 'order_by') {
1746 0         0 $orderby = $linked_list[++$i];
1747             } elsif ($cmd eq 'having') {
1748 0         0 $having = $linked_list[++$i];
1749             } elsif ($cmd eq 'limit') {
1750 0         0 $limit = 0 + $linked_list[++$i];
1751             } elsif ($cmd eq 'offset') {
1752 0         0 $offset = 0 + $linked_list[++$i];
1753             } elsif ($cmd eq 'columns'
1754             || $cmd eq 'column'
1755             || $cmd eq 'distinct'
1756             || $cmd eq 'count'
1757             || $cmd eq 'all')
1758             {
1759 5 50       10 if ($cmd eq 'all') {
1760 0         0 $all = 1;
1761             }
1762 5 100       10 if ($cmd eq 'distinct') {
1763 1         72 $distinct = 1;
1764             }
1765 5 100       13 if ($cmd eq 'count') {
1766 2         4 $count = 1;
1767             }
1768 5 100 66     23 if ($i + 1 < @linked_list && substr($linked_list[$i + 1], 0, 1) ne '-') {
1769 4         6 my $cols = $linked_list[++$i];
1770 4 100 66     38 if ($cols && $cols !~ /^\d|^true$/) {
    50 33        
      33        
1771 2 50       5 if ('ARRAY' eq ref($cols)) {
1772 0         0 push @columns, @$cols;
1773             } else {
1774 2         4 push @columns, $cols;
1775             }
1776             } elsif (($cols =~ /^\d+$/ && $cols == 0) || $cols eq '') {
1777 0 0       0 $distinct = 0 if $cmd eq 'distinct';
1778             }
1779             }
1780 5 50       12 if ($cmd eq 'column') {
1781 0         0 ++$one_column;
1782             } else {
1783 5         12 $one_column += 2;
1784             }
1785              
1786             } else {
1787 0         0 error_message {
1788             result => 'SQLERR',
1789             message => "Unknown directive $le"
1790             };
1791             }
1792             }
1793             }
1794 4 50       8 error_message {
1795             result => 'SQLERR',
1796             message => "No table to build query on"
1797             } if !@from;
1798 4         9 for (my $idx = 1; $idx < @from; ++$idx) {
1799 1 50 33     8 next if $from[$idx][_table_join_on] or not $from[$idx - 1][_table_join];
1800 1 50       4 next if substr($from[$idx][_table_name], 0, 1) eq "(";
1801 1         3 my $cta = $from[$idx][_table_alias];
1802 1         2 my $cto = make_name($from[$idx][_table_name]);
1803 1         3 my $ucct = ucfirst $from[$idx][_table_name];
1804 1         2 my @join;
1805 1         4 for (my $i = $idx - 1; $i >= 0; --$i) {
1806 1 50       3 next if not $from[$i][_table_join];
1807 1         3 my $ptn = $from[$i][_table_name];
1808 1 50       3 next if substr($ptn, 0, 1) eq "(";
1809 1         2 my $ucfptn = ucfirst $ptn;
1810 1 50       10 if ($cto->can("foreignKey$ucfptn")) {
1811 1         3 my $fkfn = "foreignKey$ucfptn";
1812 1         22 my ($ctf, $ptk) = $cto->$fkfn;
1813 1         9 push @join, "$cta.$ctf = " . $from[$i][_table_alias] . ".$ptk";
1814             } else {
1815 0         0 my $ptno = make_name($ptn);
1816 0 0       0 if ($ptno->can("foreignKey$ucct")) {
1817 0         0 my $fkfn = "foreignKey$ucct";
1818 0         0 my ($ptf, $ctk) = $ptno->$fkfn;
1819 0         0 push @join, "$cta.$ctk = " . $from[$i][_table_alias] . ".$ptf";
1820             }
1821             }
1822             }
1823 1         19 $from[$idx][_table_join_on] = ["on", join(" and ", @join)];
1824             }
1825 4         9 my $from = '';
1826 4 100       16 @columns = ('*') if not @columns;
1827 4 50       9 @columns = map {('SCALAR' eq ref) ? DBIx::Struct::connect->dbh->quote_identifier($$_) : $_} @columns;
  4         14  
1828 4         5 my $joined = 0;
1829 4         16 for (my $idx = 0; $idx < @from; ++$idx) {
1830 5 100       15 if (not $joined) {
1831 4         11 $from .= " " . $from[$idx][_table_name];
1832 4 50       9 $from .= " " . $from[$idx][_table_alias] if $from[$idx][_table_alias] ne $from[$idx][_table_name];
1833             }
1834 5 100       11 if ($from[$idx][_table_join]) {
1835 1         13 my $nt = $from[$idx + 1];
1836 1         6 $from .= " " . $from[$idx][_table_join];
1837 1         3 $from .= " " . $nt->[_table_name];
1838 1 50       3 $from .= " " . $nt->[_table_alias] if $nt->[_table_alias] ne $nt->[_table_name];
1839 1         2 my $using_on = $nt->[_table_join_on][0];
1840 1 50 33     7 if ($using_on eq 'on' and ref $nt->[_table_join_on][1]) {
1841 0         0 my ($on_where, @on_bind) = $sql_abstract->where($nt->[_table_join_on][1]);
1842 0         0 $on_where =~ s/WHERE //;
1843 0         0 push @$query_bind, @on_bind;
1844 0         0 $from .= " $using_on(" . $on_where . ")";
1845             } else {
1846 1         6 $from .= " $using_on(" . $nt->[_table_join_on][1] . ")";
1847             }
1848 1         2 $joined = 1;
1849             } else {
1850 4 50       7 $from .= "," if $idx != $#from;
1851 4         9 $joined = 0;
1852             }
1853             }
1854 4         10 my $what = join(", ", @columns);
1855 4 100       5 if ($count) {
1856 2         4 $one_column = 1;
1857 2 100       6 if ($distinct) {
    50          
1858 1 50       7 $what = $from[0][_table_alias] . ".*" if $what eq '*';
1859 1         4 $what = "count(distinct $what)";
1860             } elsif ($all) {
1861 0 0       0 $what = $from[0][_table_alias] . ".*" if $what eq '*';
1862 0         0 $what = "count(all $what)";
1863             } else {
1864 1         2 $what = "count(*)";
1865             }
1866             } else {
1867 2 50       5 if ($distinct) {
1868 0         0 $what = "distinct $what";
1869             }
1870             }
1871 4         11 my $ret = "select $what from" . $from;
1872 4 50       8 if (not defined $where) {
1873 0         0 my $sql_grp = _parse_groupby($groupby);
1874 0         0 my $having_bind = [];
1875 0 0 0     0 if ($sql_grp && defined $having) {
1876 0         0 my $sql_having;
1877 0         0 ($sql_having, $having_bind) = _parse_having($having);
1878 0         0 $sql_grp .= " $sql_having";
1879             }
1880 0 0       0 if ($conditions) {
1881 0         0 my @where_bind;
1882 0         0 ($where, @where_bind) = $sql_abstract->where($conditions);
1883 0         0 push @$query_bind, @where_bind;
1884             } else {
1885 0         0 $where = '';
1886             }
1887 0 0       0 if (defined $sql_grp) {
1888 0         0 $where .= " $sql_grp";
1889 0         0 push @$query_bind, @$having_bind;
1890             }
1891 0 0       0 $where .= " limit $limit" if defined $limit;
1892 0 0       0 $where .= " offset $offset" if $offset;
1893             }
1894 4 50       7 $ret .= " $where" if $where;
1895 4 50       13 if (wantarray) {
1896 4         22 return ($ret, $one_column == 1);
1897             } else {
1898 0         0 return $ret;
1899             }
1900             }
1901              
1902             sub _parse_groupby {
1903 20     20   23 my $groupby = $_[0];
1904 20         25 my $sql_grp;
1905 20 100       34 if (defined $groupby) {
1906 2         3 $sql_grp = "GROUP BY ";
1907             my @groupby =
1908 2 50       6 map {/^\d+$/ ? $_ : /^[a-z][\w ]*$/i ? "\"$_\"" : "$_"} (ref($groupby) ? @$groupby : ($groupby));
  2 50       20  
    50          
1909 2         8 $sql_grp .= join(", ", @groupby);
1910             }
1911 20         40 $sql_grp;
1912             }
1913              
1914             sub _parse_having {
1915 2     2   5 my $having = $_[0];
1916 2         4 my $sql_having;
1917             my @having_bind;
1918 2 50       5 if (defined $having) {
1919 2         7 ($sql_having, @having_bind) = $sql_abstract->where($having);
1920 2         685 $sql_having =~ s/\bWHERE\b/HAVING/;
1921             }
1922 2         7 ($sql_having, \@having_bind);
1923             }
1924              
1925             sub execute {
1926 20     20 0 34 my ($groupby, $having, $up_conditions, $up_order, $up_limit, $up_offset, $up_interface, $sql, $dry_run);
1927 20         30 my $distinct = '';
1928 20         58 for (my $i = 2; $i < @_; ++$i) {
1929 18 100 66     88 next unless defined $_[$i] and not ref $_[$i];
1930 8 100       81 if ($_[$i] eq '-group_by') {
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
1931 2         6 (undef, $groupby) = splice @_, $i, 2;
1932 2         5 --$i;
1933             } elsif ($_[$i] eq '-distinct') {
1934 0         0 $distinct = ' distinct';
1935 0         0 splice @_, $i, 1;
1936 0         0 --$i;
1937             } elsif ($_[$i] eq '-having') {
1938 2         4 (undef, $having) = splice @_, $i, 2;
1939 2         3 --$i;
1940             } elsif ($_[$i] eq '-order_by') {
1941 0         0 (undef, $up_order) = splice @_, $i, 2;
1942 0         0 --$i;
1943             } elsif ($_[$i] eq '-where') {
1944 0         0 (undef, $up_conditions) = splice @_, $i, 2;
1945 0         0 --$i;
1946             } elsif ($_[$i] eq '-limit') {
1947 0         0 (undef, $up_limit) = splice @_, $i, 2;
1948 0         0 --$i;
1949             } elsif ($_[$i] eq '-interface') {
1950 0         0 (undef, $up_interface) = splice @_, $i, 2;
1951 0         0 --$i;
1952             } elsif ($_[$i] eq '-offset') {
1953 0         0 (undef, $up_offset) = splice @_, $i, 2;
1954 0         0 --$i;
1955             } elsif ($_[$i] eq '-sql') {
1956 0         0 (undef, $sql) = splice @_, $i, 2;
1957 0         0 --$i;
1958             } elsif ($_[$i] eq '-dry_run') {
1959 0         0 (undef, $dry_run) = splice @_, $i, 2;
1960 0         0 --$i;
1961             } elsif (substr($_[$i], 0, 1) eq '-') {
1962 0         0 error_message {
1963             result => 'SQLERR',
1964             message => "Unknown directive $_[$i]"
1965             };
1966             }
1967             }
1968 20         31 $tblnum = 1;
1969 20         38 my $sql_grp = _parse_groupby($groupby);
1970 20         30 my $having_bind = [];
1971 20 100 66     48 if ($sql_grp && defined $having) {
1972 2         3 my $sql_having;
1973 2         14 ($sql_having, $having_bind) = _parse_having($having);
1974 2         5 $sql_grp .= " $sql_having";
1975             }
1976 20         57 my ($code, $table, $conditions, $order, $limit, $offset) = @_;
1977 20         31 my $have_conditions = @_ > 2;
1978 20   66     51 $conditions //= $up_conditions;
1979 20   33     63 $order //= $up_order;
1980 20   33     55 $limit //= $up_limit;
1981 20   33     66 $offset //= $up_offset;
1982 20         20 my $where;
1983 20         26 my $need_where = 0;
1984 20         23 my @where_bind;
1985 20   100     60 my $simple_table = (not ref $table and index($table, " ") == -1);
1986 20         21 my $ncn;
1987              
1988 20 100       35 if ($simple_table) {
1989 15         27 $ncn = make_name($table);
1990 15         40 $ncn = _schema_name($ncn);
1991 15         37 setup_row($table, $ncn);
1992 15 100 100     69 if ($have_conditions and not ref $conditions) {
1993 4 50       20 my $id = ($ncn->selectKeys())[0]
1994             or error_message {
1995             result => 'SQLERR',
1996             message => "unknown primary key",
1997             query => "select * from $table",
1998             };
1999 4 50       11 if (defined $conditions) {
2000 4         8 $where = "where $id = ?";
2001 4         10 @where_bind = ($conditions);
2002             } else {
2003 0         0 $where = "where $id is null";
2004             }
2005             } else {
2006 11         15 $need_where = 1;
2007             }
2008             } else {
2009 5         6 $need_where = 1;
2010             }
2011 20 100       34 if ($need_where) {
2012 16         54 ($where, @where_bind) = $sql_abstract->where($conditions);
2013             }
2014 20 100       1781 if (defined $sql_grp) {
2015 2         5 $where .= " $sql_grp";
2016 2         3 push @where_bind, @$having_bind;
2017             }
2018 20 50       42 if ($order) {
2019 0         0 my ($order_sql, @order_bind) = $sql_abstract->where(undef, $order);
2020 0         0 $where .= " $order_sql";
2021 0         0 push @where_bind, @order_bind;
2022             }
2023 20 50       39 if (defined($limit)) {
2024 0         0 $limit += 0;
2025 0         0 $where .= " limit $limit";
2026             }
2027 20 50       31 if (defined($offset)) {
2028 0         0 $offset += 0;
2029 0 0       0 $where .= " offset $offset" if $offset;
2030             }
2031 20         30 my $query;
2032             my @query_bind;
2033 20         27 my $one_column = 0;
2034 20 100       29 if ($simple_table) {
2035 15         38 $query = qq{select$distinct * from $table $where};
2036             } else {
2037 5 100       13 if (not ref $table) {
2038 1         3 $query = "$table $where";
2039             } else {
2040 4         22 ($query, $one_column) = _cached_complex_query($table, \@query_bind, $where);
2041             }
2042 5         12 $ncn = make_name($query);
2043             }
2044 20 50       38 if ($sql) {
2045 0 0       0 if ('CODE' eq ref $sql) {
    0          
2046 0         0 $sql->($query, \@where_bind);
2047             } elsif ('SCALAR' eq ref $sql) {
2048 0         0 $$sql = $query;
2049             }
2050             }
2051 20 50       28 return if $dry_run;
2052 20         49 '' =~ /()/;
2053 20         22 my $sth;
2054             return DBIx::Struct::connect->run(
2055             sub {
2056 20 50   20   169 $sth = $_->prepare($query)
2057             or error_message {
2058             result => 'SQLERR',
2059             message => $_->errstr,
2060             query => $query,
2061             };
2062 20 50       670 $sth->execute(@query_bind, @where_bind)
2063             or error_message {
2064             result => 'SQLERR',
2065             message => $_->errstr,
2066             query => $query,
2067             where_bind => Dumper(\@where_bind),
2068             query_bind => Dumper(\@query_bind),
2069             conditions => Dumper($conditions),
2070             };
2071 20         257 setup_row($sth, $ncn, $up_interface);
2072 20         60 return $code->($sth, $ncn, $one_column);
2073             }
2074 20         51 );
2075             }
2076              
2077             sub one_row {
2078             return execute(
2079             sub {
2080 20     20   40 my ($sth, $ncn, $one_column) = @_;
2081 20         54 my $data = $sth->fetchrow_arrayref;
2082 20         288 $sth->finish;
2083 20 100       60 return if not $data;
2084 19 100       33 if ($one_column) {
2085             #<<<
2086             # json type is not working yet here
2087             # no strict 'refs';
2088             # my @f = %{$ncn . "::field_types"};
2089             # if ($f[1] eq 'json') {
2090             # return (defined($data->[0]) ? from_json($data->[0]) : undef);
2091             # } else {
2092 2         11 return $data->[0];
2093             #>>> }
2094             }
2095 17         330 return $ncn->new([@$data]);
2096             },
2097             @_
2098 20     20 1 5003 );
2099             }
2100              
2101             sub all_rows {
2102 0     0 1 0 my $mapfunc;
2103 0         0 for (my $i = 0; $i < @_; ++$i) {
2104 0 0       0 if (ref($_[$i]) eq 'CODE') {
2105 0         0 $mapfunc = splice @_, $i, 1;
2106 0         0 last;
2107             }
2108             }
2109             return execute(
2110             sub {
2111 0     0   0 my ($sth, $ncn, $one_column) = @_;
2112 0         0 my @rows;
2113             my $row;
2114 0 0       0 if ($mapfunc) {
2115 0         0 while ($row = $sth->fetch) {
2116 0         0 local $_ = $ncn->new([@$row]);
2117 0         0 push @rows, $mapfunc->();
2118             }
2119             } else {
2120 0 0       0 if ($one_column) {
2121             #<<<
2122             # json type is not working yet here
2123             # no strict 'refs';
2124             # my @f = %{$ncn . "::field_types"};
2125             # if ($f[1] eq 'json') {
2126             # push @rows, (defined($row->[0]) ? from_json($row->[0]) : undef) while ($row = $sth->fetch);
2127             # } else {
2128 0         0 push @rows, $row->[0] while ($row = $sth->fetch);
2129             # }
2130             #>>>
2131             } else {
2132 0         0 push @rows, $ncn->new([@$row]) while ($row = $sth->fetch);
2133             }
2134             }
2135 0         0 return \@rows;
2136             },
2137             @_
2138 0         0 );
2139             }
2140              
2141             sub for_rows {
2142 0     0 1 0 my $itemfunc;
2143 0         0 for (my $i = 0; $i < @_; ++$i) {
2144 0 0       0 if (ref($_[$i]) eq 'CODE') {
2145 0         0 $itemfunc = splice @_, $i, 1;
2146 0         0 last;
2147             }
2148             }
2149 0 0       0 error_message {
2150             result => 'SQLERR',
2151             message => "Item function is required",
2152             query => "(not parsed)",
2153             where_bind => "(not parsed)",
2154             query_bind => "(not parsed)",
2155             conditions => "(not parsed)",
2156             }
2157             if not $itemfunc;
2158             return execute(
2159             sub {
2160 0     0   0 my ($sth, $ncn) = @_;
2161 0         0 my $rows = 0;
2162 0         0 my $row;
2163 0         0 my $dbh = $_;
2164 0 0       0 local $dbh->{mysql_use_result} = 1 if $connector_driver eq 'mysql';
2165 0         0 local $_;
2166 0         0 while ($row = $sth->fetch) {
2167 0         0 ++$rows;
2168 0         0 $_ = $ncn->new([@$row]);
2169 0 0       0 last if not $itemfunc->();
2170             }
2171 0         0 return $rows;
2172             },
2173             @_
2174 0         0 );
2175             }
2176              
2177             sub new_row {
2178 3     3 1 983 my ($table, @data) = @_;
2179 3         9 my $simple_table = (index($table, " ") == -1);
2180 3 50       19 error_message {
2181             result => 'SQLERR',
2182             message => "insert row can't work for queries"
2183             }
2184             unless $simple_table;
2185 3         7 my $ncn = make_name($table);
2186 3         7 $ncn = _schema_name($ncn);
2187 3         6 $ncn = setup_row($table, $ncn);
2188 3         57 return $ncn->new(@data);
2189             }
2190              
2191             1;