File Coverage

blib/lib/ORLite.pm
Criterion Covered Total %
statement 340 374 90.9
branch 159 206 77.1
condition 26 39 66.6
subroutine 52 54 96.3
pod 1 2 50.0
total 578 675 85.6


line stmt bran cond sub pod time code
1             package ORLite; # git description: v1.99-6-gde873f7
2              
3             # See POD at end of file for documentation
4              
5 31     31   2783317 use 5.006;
  30         428  
6 32     29   3436 use strict;
  30         104  
  29         657  
7 27     27   162 use Carp ();
  27         86  
  27         729  
8 27     27   158 use File::Spec 0.80 ();
  27         751  
  27         763  
9 27     27   196 use File::Path 2.08 ();
  27         1092  
  27         3749  
10 27     27   215 use File::Basename ();
  27         75  
  27         697  
11 27     27   13138 use Params::Util 1.00 ();
  27         153372  
  27         808  
12 27     27   41933 use DBI 1.607 ();
  27         464515  
  27         955  
13 27     27   21023 use DBD::SQLite 1.27 ();
  27         241327  
  27         10181  
14              
15             our $VERSION = '2.00';
16              
17             # Support for the 'prune' option
18             my @PRUNE = ();
19             END {
20 25     25   233946 foreach ( reverse @PRUNE ) {
21 0 50       0 next unless -e $_;
22 0         0 require File::Remove;
23 0         0 File::Remove::remove( \1, $_ );
24             }
25             }
26              
27              
28              
29              
30              
31             #####################################################################
32             # Code Generation
33              
34             sub import {
35 28   33 28   1034040 my $class = ref($_[0]) || $_[0];
36              
37             # Check for debug mode
38 28         105 my $DEBUG = 0;
39 28 50 66     412 if ( defined Params::Util::_STRING($_[-1]) and $_[-1] eq '-DEBUG' ) {
40 0         0 $DEBUG = 1;
41 0         0 pop @_;
42             }
43              
44             # Check params and apply defaults
45 28         410 my %params = (
46             # Simple defaults here, complex defaults later
47             package => scalar(caller),
48             create => 0,
49             cleanup => '',
50             array => 0,
51             xsaccessor => 0,
52             shim => 0,
53             tables => 1,
54             views => 0,
55             unicode => 0,
56             );
57 28 100       701 if ( defined Params::Util::_STRING($_[1]) ) {
    50          
58             # Support the short form "use ORLite 'db.sqlite'"
59 4         22 $params{file} = $_[1];
60             } elsif ( Params::Util::_HASHLIKE($_[1]) ) {
61 24         165 %params = ( %params, %{$_[1]} );
  24         212  
62             } else {
63 0         0 Carp::croak("Missing, empty or invalid params HASH");
64             }
65 28 50 66     953 unless (
      66        
66             defined Params::Util::_STRING($params{file})
67             and (
68             $params{create}
69             or
70             -f $params{file}
71             )
72             ) {
73 0         0 Carp::croak("Missing or invalid file param");
74             }
75 28 100       241 unless ( defined $params{readonly} ) {
76 26 100       537 $params{readonly} = $params{create} ? 0 : ! -w $params{file};
77             }
78 28 50       286 unless ( Params::Util::_CLASS($params{package}) ) {
79 0         0 Carp::croak("Missing or invalid package class");
80             }
81              
82             # Check caching params
83 28         634 my $cached = undef;
84 28         94 my $pkg = $params{package};
85 28 100       130 if ( defined $params{cache} ) {
86             # Caching is illogical or invalid in some situations
87 2 50       5 if ( $params{prune} ) {
88 0         0 Carp::croak("Cannot set a 'cache' directory while 'prune' enabled");
89             }
90 2 50       7 unless ( $params{user_version} ) {
91 0         0 Carp::croak("Cannot set a 'cache' directory without 'user_version'");
92             }
93              
94             # To make the caching work, the version be defined before ORLite is called.
95 27     27   320 no strict 'refs';
  27         3816  
  25         90368  
96 2 50       2 unless ( ${"$pkg\::VERSION"} ) {
  2         10  
97 0         0 Carp::croak("Cannot set a 'cache' directory without a package \$VERSION");
98             }
99              
100             # Build the cache file from the super path using an inlined Class::ISA
101 2         6 my @queue = ( $class );
102 2         5 my %seen = ( $pkg => 1 );
103 2         4 my @parts = ( $pkg => ${"$pkg\::VERSION"} );
  2         5  
104 2         7 while ( @queue ) {
105 2 50       9 my $c = Params::Util::_STRING(shift @queue) or next;
106 2         4 push @parts, $c => ${"$c\::VERSION"};
  2         8  
107 2         3 unshift @queue, grep { not $seen{$c}++ } @{"$c\::ISA"};
  0         0  
  2         14  
108             }
109 2         10 $cached = join '-', @parts, user_version => $params{user_version};
110 2         18 $cached =~ s/[:.-]+/-/g;
111             $cached = File::Spec->rel2abs(
112 2         94 File::Spec->catfile( $params{cache}, "$cached.pm" )
113             );
114             }
115              
116             # Create the parent directory if needed
117 28         1772 my $file = File::Spec->rel2abs($params{file});
118 28         610 my $created = ! -f $params{file};
119 28 100       219 if ( $created ) {
120 4         315 my $dir = File::Basename::dirname($file);
121 4 50       79 unless ( -d $dir ) {
122 0         0 my @dirs = File::Path::mkpath( $dir, { verbose => 0 } );
123 0 0       0 $class->prune(@dirs) if $params{prune};
124             }
125 4 50       32 $class->prune($file) if $params{prune};
126             }
127              
128             # Connect to the database
129 28         171 my $dsn = "dbi:SQLite:$file";
130             my $dbh = DBI->connect( $dsn, undef, undef, {
131             PrintError => 0,
132             RaiseError => 1,
133             ReadOnly => $params{create} ? 0 : 1,
134 28 100       457 $params{unicode} ? ( sqlite_unicode => 1 ) : ( ),
    100          
135             } );
136              
137             # Schema custom creation support
138 28 100 100     23725 if ( $created and Params::Util::_CODELIKE($params{create}) ) {
139 2         56 $params{create}->($dbh);
140             }
141              
142             # Check the schema version before generating
143 28         96293 my $user_version = $dbh->selectrow_arrayref('pragma user_version')->[0];
144 27 50 66     9523 if ( exists $params{user_version} and $user_version != $params{user_version} ) {
145 0         0 Carp::croak("Schema user_version mismatch (got $user_version, wanted $params{user_version})");
146             }
147              
148             # If caching and the cached version exists, load and shortcut.
149             # Don't try to catch exceptions, just let them blow up.
150 27 100 100     195 if ( $cached and -f $cached ) {
151 1         78 $dbh->disconnect;
152 1         369 require $cached;
153 1         1449 return 1;
154             }
155              
156             # Prepare to generate code
157 26         127 my $cleanup = $params{cleanup};
158 26 100       190 my $readonly = $params{readonly} ? "\n\t\tReadOnly => 1," : '';
159 26 100       151 my $unicode = $params{unicode} ? "\n\t\tsqlite_unicode => 1," : '';
160 26 100       163 my $version = $unicode ? '5.008005' : '5.006';
161              
162             # Generate the support package code
163 26         467 my $code = <<"END_PERL";
164             package $pkg;
165              
166             use $version;
167             use strict;
168             use Carp ();
169             use DBI 1.607 ();
170             use DBD::SQLite 1.27 ();
171              
172             my \$DBH = undef;
173              
174             sub orlite { '$VERSION' }
175              
176             sub sqlite { '$file' }
177              
178             sub dsn { '$dsn' }
179              
180             sub dbh {
181             \$DBH or \$_[0]->connect;
182             }
183              
184             sub connect {
185             DBI->connect( \$_[0]->dsn, undef, undef, {
186             PrintError => 0,
187             RaiseError => 1,$readonly$unicode
188             } );
189             }
190              
191             sub connected {
192             defined \$DBH;
193             }
194              
195             sub prepare {
196             shift->dbh->prepare(\@_);
197             }
198              
199             sub do {
200             shift->dbh->do(\@_);
201             }
202              
203             sub selectall_arrayref {
204             shift->dbh->selectall_arrayref(\@_);
205             }
206              
207             sub selectall_hashref {
208             shift->dbh->selectall_hashref(\@_);
209             }
210              
211             sub selectcol_arrayref {
212             shift->dbh->selectcol_arrayref(\@_);
213             }
214              
215             sub selectrow_array {
216             shift->dbh->selectrow_array(\@_);
217             }
218              
219             sub selectrow_arrayref {
220             shift->dbh->selectrow_arrayref(\@_);
221             }
222              
223             sub selectrow_hashref {
224             shift->dbh->selectrow_hashref(\@_);
225             }
226              
227             sub pragma {
228             \$_[0]->do("pragma \$_[1] = \$_[2]") if \@_ > 2;
229             \$_[0]->selectrow_arrayref("pragma \$_[1]")->[0] if defined wantarray;
230             }
231              
232             sub iterate {
233             my \$class = shift;
234             my \$call = pop;
235             my \$sth = \$class->prepare(shift);
236             \$sth->execute(\@_);
237             while ( \$_ = \$sth->fetchrow_arrayref ) {
238             \$call->() or return 1;;
239             }
240             }
241              
242             sub begin {
243             \$DBH or
244             \$DBH = \$_[0]->connect;
245             \$DBH->begin_work;
246             }
247              
248             sub rollback {
249             \$DBH or return 1;
250             \$DBH->rollback;
251             \$DBH->disconnect;
252             undef \$DBH;
253             return 1;
254             }
255              
256             sub rollback_begin {
257             if ( \$DBH ) {
258             \$DBH->rollback;
259             \$DBH->begin_work;
260             } else {
261             \$_[0]->begin;
262             }
263             return 1;
264             }
265              
266             END_PERL
267              
268             # If you are a read-write database, we even allow you
269             # to commit your transactions.
270 26 100       277 $code .= <<"END_PERL" unless $readonly;
271             sub commit {
272             \$DBH or return 1;
273             \$DBH->commit;
274             \$DBH->disconnect;
275             undef \$DBH;
276             return 1;
277             }
278              
279             sub commit_begin {
280             if ( \$DBH ) {
281             \$DBH->commit;
282             \$DBH->begin_work;
283             } else {
284             \$_[0]->begin;
285             }
286             return 1;
287             }
288              
289             END_PERL
290              
291             # Cleanup and shutdown operations
292 26 100       198 if ( $cleanup ) {
293 2         11 $code .= <<"END_PERL";
294             END {
295             if ( \$DBH ) {
296             \$DBH->rollback;
297             \$DBH->do('$cleanup');
298             \$DBH->disconnect;
299             undef \$DBH;
300             } else {
301             $pkg->do('$cleanup');
302             }
303             }
304              
305             END_PERL
306             } else {
307 24         129 $code .= <<"END_PERL";
308             END {
309             $pkg->rollback if \$DBH;
310             }
311              
312             END_PERL
313             }
314              
315             # Optionally generate the table classes
316 26         87 my $tables = undef;
317 26 100       154 if ( $params{tables} ) {
318             # Capture the raw schema table information
319 23         536 $tables = $dbh->selectall_arrayref(
320             'select * from sqlite_master where name not like ? and type in ( ?, ? )',
321             { Slice => {} }, 'sqlite_%', 'table', 'view',
322             );
323              
324             # Capture the raw schema information and do first-pass work
325 23         14902 foreach my $t ( @$tables ) {
326             # Convenience pre-quoted form of the table name
327 35         510 $t->{qname} = $dbh->quote_identifier(undef, undef, $t->{name});
328              
329             # What will be the class for this table
330 35         58149 $t->{class} = $t->{name};
331 35 100       275 if ( $t->{class} ne lc $t->{class} ) {
332 1         24 $t->{class} =~ s/([a-z])([A-Z])/${1}_${2}/g;
333 1         13 $t->{class} =~ s/_+/_/g;
334             }
335 35         192 $t->{class} = ucfirst lc $t->{class};
336 35         310 $t->{class} =~ s/_([a-z])/uc($1)/ge;
  23         177  
337 35         182 $t->{class} = "${pkg}::$t->{class}";
338              
339             # Load the structural column list
340 35         575 my $columns = $t->{columns} = $dbh->selectall_arrayref(
341             "pragma table_info('$t->{name}')",
342             { Slice => {} },
343             );
344              
345             # The list of columns we will select, which can
346             # be different to the general list.
347 35         12940 my $select = $t->{select} = [ @$columns ];
348              
349             # Track array vs hash implementation on a per-table
350             # basis so that we can force views to always be done
351             # array-wise (to compensate for some weird SQLite
352             # column quoting differences between tables and views
353 35         129 $t->{array} = $params{array};
354 35 100       184 if ( $t->{type} eq 'view' ) {
355 4         10 $t->{array} = 1;
356             }
357              
358             # Track usage of rowid on a per-table basis because
359             # views don't always support rowid.
360 35         145 $t->{rowid} = $t->{type} eq 'table';
361              
362 35         135 foreach my $c ( @$select ) {
363             # Convenience escaping for the column names
364 82         442 $c->{qname} = $dbh->quote_identifier($c->{name});
365              
366             # Affinity detection
367 82 100 100     4003 if ( $c->{type} =~ /INT/i ) {
    100          
    100          
    100          
368 43         226 $c->{affinity} = 'INTEGER';
369             } elsif ( $c->{type} =~ /(?:CHAR|CLOB|TEXT)/i ) {
370 16         81 $c->{affinity} = 'TEXT';
371             } elsif ( $c->{type} =~ /BLOB/i or not $c->{type} ) {
372 2         8 $c->{affinity} = 'BLOB';
373              
374             # Unicode currently breaks BLOB columns
375 2 50       7 if ( $unicode ) {
376 0         0 die "BLOB column $t->{name}.$c->{name} is not supported in unicode database";
377             }
378             } elsif ( $c->{type} =~ /(?:REAL|FLOA|DOUB)/i ) {
379 1         6 $c->{affinity} = 'REAL';
380             } else {
381 20         109 $c->{affinity} = 'NUMERIC';
382             }
383             }
384              
385             # Analyze the primary keys structure
386 35         166 $t->{pk} = [ grep { $_->{pk} } @$columns ];
  82         319  
387 35         90 $t->{pkn} = scalar @{$t->{pk}};
  35         126  
388 35 100       159 if ( $t->{pkn} == 1 ) {
389 28         101 $t->{pk1} = $t->{pk}->[0];
390 28 100       171 if ( $t->{pk1}->{affinity} eq 'INTEGER' ) {
391 27         106 $t->{pki} = $t->{pk1};
392             }
393             }
394 35 100       171 if ( $t->{pki} ) {
    100          
395 27   33     221 $t->{rowid} &&= $t->{pki};
396 27 100       182 if ( $t->{pki}->{name} eq $t->{name} . '_id' ) {
397 3         18 $t->{id} = $t->{pki};
398             }
399              
400             } elsif ( $t->{rowid} ) {
401             # Add rowid to the query
402             $t->{rowid} = {
403 4         37 cid => -1,
404             name => 'rowid',
405             qname => '"rowid"',
406             type => 'integer',
407             affinity => 'INTEGER',
408             notnull => 1,
409             dflt_value => undef,
410             pk => 0,
411             };
412 4         18 push @$select, $t->{rowid};
413             }
414              
415             # Do we allow object creation?
416 35         217 $t->{create} = $t->{pkn};
417 35 100       166 $t->{create} = 1 if $t->{rowid};
418 35 100       121 $t->{create} = 0 if $readonly;
419              
420             # Generate the object keys for the columns
421 35 100       132 if ( $t->{array} ) {
422 15         81 foreach my $i ( 0 .. $#$select ) {
423 40         104 $select->[$i]->{xs} = $i;
424 40         166 $select->[$i]->{key} = "[$i]";
425             }
426             } else {
427 20         70 foreach my $c ( @$select ) {
428 46         153 $c->{xs} = "'$c->{name}'";
429 46         168 $c->{key} = "{$c->{name}}";
430             }
431             }
432              
433             # Generate the main SQL fragments
434 35         147 $t->{sql_scols} = join ', ', map { $_->{qname} } @$select;
  86         340  
435 35         134 $t->{sql_icols} = join ', ', map { $_->{qname} } @$columns;
  82         305  
436 35         229 $t->{sql_ivals} = join ', ', ( '?' ) x scalar @$columns;
437 35         185 $t->{sql_select} = "select $t->{sql_scols} from $t->{qname}";
438             $t->{sql_insert} =
439 35         317 "insert into $t->{qname} " .
440             "( $t->{sql_icols} ) " .
441             "values ( $t->{sql_ivals} )";
442             $t->{sql_where} = join ' and ',
443 35         90 map { "$_->{qname} = ?" } @{$t->{pk}};
  32         176  
  35         159  
444              
445             # Generate the new Perl fragments
446             $t->{pl_new} = join "\n", map {
447 35         135 $t->{array}
448 82 100       485 ? "\t\t\$attr{$_->{name}},"
449             : "\t\t$_->{name} => \$attr{$_->{name}},"
450             } @$columns;
451              
452             $t->{pl_insert} = join "\n", map {
453 35         133 "\t\t\$self->$_->{key},"
  82         318  
454             } @$columns;
455              
456 35         138 $t->{pl_fill} = '';
457 35 100       180 if ( $t->{pki} ) {
    100          
458             $t->{pl_fill} =
459 27         224 "\t\$self->$t->{pki}->{key} " .
460             "= \$dbh->func('last_insert_rowid') " .
461             "unless \$self->$t->{pki}->{key};";
462             } elsif ( $t->{rowid} ) {
463             $t->{pl_fill} =
464 4         31 "\t\$self->$t->{rowid}->{key} " .
465             "= \$dbh->func('last_insert_rowid');";
466             }
467             }
468              
469             # Generate the foreign key metadata
470 23         108 my %tindex = map { $_->{name} => $_ } @$tables;
  35         276  
471 23         98 foreach my $t ( @$tables ) {
472             # Locate the foreign keys
473 35         107 my %fk = ();
474 35         239 my @fk_sql = $t->{sql} =~ /[(,]\s*(.+?REFERENCES.+?)\s*[,)]/g;
475              
476             # Extract the details
477 35         256 foreach ( @fk_sql ) {
478 4 50       46 unless ( /^(\w+).+?REFERENCES\s+(\w+)\s*\(\s*(\w+)/ ) {
479 0         0 die "Invalid foreign key $_";
480             }
481 4         49 $fk{"$1"} = [ "$2", $tindex{"$2"}, "$3" ];
482             }
483 35         148 foreach ( @{$t->{columns}} ) {
  35         131  
484 82         260 $_->{fk} = $fk{$_->{name}};
485             }
486              
487             # One final code fragment we need the fk for
488             $t->{pl_accessor} = join "\n",
489 78         478 map { "\t\t$_->{name} => $_->{xs}," }
490 35         113 grep { ! $_->{fk} } @{$t->{columns}};
  82         261  
  35         115  
491             }
492              
493             # Generate the per-table code
494 23         138 foreach my $t ( @$tables ) {
495 35         81 my @select = @{$t->{select}};
  35         134  
496 35         96 my @columns = @{$t->{columns}};
  35         157  
497             my $slice = $t->{array}
498 35 100       217 ? '{}'
499             : '{ Slice => {} }';
500              
501             # Generate the package header
502 35 100       147 if ( $params{shim} ) {
503             # Generate a shim-wrapper class
504 1         6 $code .= <<"END_PERL";
505             package $t->{class};
506              
507             \@$t->{class}::ISA = '$t->{class}::Shim';
508              
509             package $t->{class}::Shim;
510              
511             END_PERL
512             } else {
513             # Plain vanilla package header
514 34         201 $code .= <<"END_PERL";
515             package $t->{class};
516              
517             END_PERL
518             }
519              
520             # Generate the common elements for all classes
521 35         571 $code .= <<"END_PERL";
522             sub base { '$pkg' }
523              
524             sub table { '$t->{name}' }
525              
526             sub table_info {
527             $pkg->selectall_arrayref(
528             "pragma table_info('$t->{name}')",
529             { Slice => {} },
530             );
531             }
532              
533             sub select {
534             my \$class = shift;
535             my \$sql = '$t->{sql_select} ';
536             \$sql .= shift if \@_;
537             my \$rows = $pkg->selectall_arrayref( \$sql, $slice, \@_ );
538             bless \$_, '$t->{class}' foreach \@\$rows;
539             wantarray ? \@\$rows : \$rows;
540             }
541              
542             sub count {
543             my \$class = shift;
544             my \$sql = 'select count(*) from $t->{qname} ';
545             \$sql .= shift if \@_;
546             $pkg->selectrow_array( \$sql, {}, \@_ );
547             }
548              
549             END_PERL
550              
551             # Handle different versions, because arrayref acts funny
552 35 100       179 if ( $t->{array} ) {
553 15         218 $code .= <<"END_PERL";
554             sub iterate {
555             my \$class = shift;
556             my \$call = pop;
557             my \$sql = '$t->{sql_select} ';
558             \$sql .= shift if \@_;
559             my \$sth = $pkg->prepare(\$sql);
560             \$sth->execute(\@_);
561             while ( \$_ = \$sth->fetchrow_arrayref ) {
562             \$_ = bless [ \@\$_ ], '$t->{class}';
563             \$call->() or last;
564             }
565             \$sth->finish;
566             }
567              
568             END_PERL
569             } else {
570 20         171 $code .= <<"END_PERL";
571             sub iterate {
572             my \$class = shift;
573             my \$call = pop;
574             my \$sql = '$t->{sql_select} ';
575             \$sql .= shift if \@_;
576             my \$sth = $pkg->prepare(\$sql);
577             \$sth->execute(\@_);
578             while ( \$_ = \$sth->fetchrow_hashref ) {
579             bless \$_, '$t->{class}';
580             \$call->() or last;
581             }
582             \$sth->finish;
583             }
584              
585             END_PERL
586             }
587              
588             # Add the primary key based single object loader
589 35 100       173 if ( $t->{pkn} ) {
590 30 100       130 if ( $t->{array} ) {
591 10         129 $code .= <<"END_PERL";
592             sub load {
593             my \$class = shift;
594             my \@row = $pkg->selectrow_array(
595             '$t->{sql_select} where $t->{sql_where}',
596             undef, \@_,
597             );
598             unless ( \@row ) {
599             Carp::croak("$t->{class} row does not exist");
600             }
601             bless \\\@row, '$t->{class}';
602             }
603              
604             END_PERL
605             } else {
606 20         175 $code .= <<"END_PERL";
607             sub load {
608             my \$class = shift;
609             my \$row = $pkg->selectrow_hashref(
610             '$t->{sql_select} where $t->{sql_where}',
611             undef, \@_,
612             );
613             unless ( \$row ) {
614             Carp::croak("$t->{class} row does not exist");
615             }
616             bless \$row, '$t->{class}';
617             }
618              
619             END_PERL
620             }
621             }
622              
623             # Generate the elements for tables with primary keys
624 35 100       136 if ( $t->{create} ) {
625 30 100       200 my $l = $t->{array} ? '[' : '{';
626 30 100       129 my $r = $t->{array} ? ']' : '}';
627             my $set = $t->{array}
628 30 100       143 ? '$self->set( $_ => $set{$_} ) foreach keys %set;'
629             : '$self->{$_} = $set{$_} foreach keys %set;';
630 30         759 $code .= <<"END_PERL";
631             sub new {
632             my \$class = shift;
633             my \%attr = \@_;
634             bless $l
635             $t->{pl_new}
636             $r, \$class;
637             }
638              
639             sub create {
640             shift->new(\@_)->insert;
641             }
642              
643             sub insert {
644             my \$self = shift;
645             my \$dbh = $pkg->dbh;
646             \$dbh->do(
647             '$t->{sql_insert}',
648             {},
649             $t->{pl_insert}
650             );
651             $t->{pl_fill}
652             return \$self;
653             }
654              
655             sub update {
656             my \$self = shift;
657             my \%set = \@_;
658             my \$rows = $pkg->do(
659             'update $t->{qname} set ' .
660             join( ', ', map { "\\"\$_\\" = ?" } keys \%set ) .
661             ' where "rowid" = ?',
662             {},
663             values \%set,
664             \$self->rowid,
665             );
666             unless ( \$rows == 1 ) {
667             Carp::croak("Expected to update 1 row, actually updated \$rows");
668             }
669             $set
670             return 1;
671             }
672              
673             sub delete {
674             return $pkg->do(
675             'delete from $t->{qname} where "rowid" = ?', {},
676             shift->rowid,
677             ) if ref \$_[0];
678             Carp::croak("Static $pkg->delete has been deprecated");
679             }
680              
681             sub delete_where {
682             shift; $pkg->do('delete from $t->{qname} where ' . shift, {}, \@_);
683             }
684              
685             sub truncate {
686             $pkg->do('delete from $t->{qname}');
687             }
688              
689             END_PERL
690             }
691              
692 35 100 100     322 if ( $t->{create} and $t->{array} ) {
693             # Add an additional set method to avoid having
694             # the user have to enter manual positions.
695 11         109 $code .= <<"END_PERL";
696             sub set {
697             my \$self = shift;
698             my \$i = {
699             $t->{pl_accessor}
700             }->{\$_[0]};
701             Carp::croak("Bad name '\$_[0]'") unless defined \$i;
702             \$self->[\$i] = \$_[1];
703             }
704              
705             END_PERL
706             }
707              
708             # Generate the boring accessors
709 35 100       161 if ( $params{xsaccessor} ) {
710 4 50       14 my $type = $t->{create} ? 'accessors' : 'getters';
711             my $xsclass = $t->{array}
712 4 100       15 ? 'Class::XSAccessor::Array'
713             : 'Class::XSAccessor';
714             my $id = $t->{id}
715 4 50       17 ? "\t\t$t->{id}->{name} => $t->{id}->{xs},\n"
716             : '';
717             my $rowid = ($t->{id} and $t->{rowid})
718 4 50 33     22 ? "\t\t$t->{rowid}->{name} => $t->{rowid}->{xs},\n"
719             : '';
720              
721 4         19 $code .= <<"END_PERL";
722             use $xsclass 1.05 {
723             getters => {
724             ${rowid}${id}$t->{pl_accessor}
725             },
726             };
727              
728             END_PERL
729             } else {
730 31 50 66     281 if ( $t->{pki} and $t->{rowid} ) {
731 23         220 $code .= <<"END_PERL";
732             sub rowid {
733             \$_[0]->$t->{rowid}->{key};
734             }
735              
736             END_PERL
737             }
738              
739 31 100       183 if ( $t->{id} ) {
740 3         83 $code .= <<"END_PERL";
741             sub id {
742             \$_[0]->$t->{id}->{key};
743             }
744              
745             END_PERL
746             }
747              
748 31         164 $code .= join "\n\n", map { <<"END_PERL" } grep { ! $_->{fk} } @select;
  76         443  
  78         264  
749             sub $_->{name} {
750             \$_[0]->$_->{key};
751             }
752             END_PERL
753             }
754              
755             # Generate the foreign key accessors
756 35         139 $code .= join "\n\n", map { <<"END_PERL" } grep { $_->{fk} } @columns;
  4         114  
  82         294  
757             sub $_->{name} {
758             ($_->{fk}->[1]->{class}\->select('where \"$_->{fk}->[1]->{pk}->[0]->{name}\" = ?', \$_[0]->$_->{key}))[0];
759             }
760             END_PERL
761             }
762             }
763              
764             # We are finished with the database
765 26         2049 $dbh->disconnect;
766              
767             # Start the post-table content again
768 26 100       355 $code .= "\npackage $pkg;\n" if $params{tables};
769              
770             # Append any custom code for the user
771 26 100       188 $code .= "\n$params{append}" if defined $params{append};
772              
773             # Load the overload classes for each of the tables
774 26 100       222 if ( $tables ) {
775             $code .= join( "\n",
776             "local \$@ = undef;",
777             map {
778 23         135 "eval { require $_->{class} };"
  35         219  
779             } @$tables
780             );
781             }
782              
783             # End the class normally
784 26         108 $code .= "\n\n1;\n";
785              
786             # Save to the cache location if caching is enabled
787 26 100       112 if ( $cached ) {
788 1         74 my $dir = File::Basename::dirname($cached);
789 1 50       19 unless ( -d $dir ) {
790 0         0 File::Path::mkpath( $dir, { verbose => 0 } );
791             }
792              
793             # Save a copy of the code to the file
794 1         5 local *FILE;
795 1 50       74 open( FILE, ">$cached" ) or Carp::croak("open($cached): $!");
796 1         10 print FILE $code;
797 1         46 close FILE;
798             }
799              
800             # Compile the code
801 26         117 local $@;
802 26 50 33     757 if ( $^P and $^V >= 5.008009 ) {
    0          
803 26         198 local $^P = $^P | 0x800;
804 26 100   80   2418 eval($code);
  40 50   61   2577  
  25 50   36   112  
  25 100   23   179  
  26 100   210   92  
  23 100   38   570  
  211 100   46   690  
  40 100   43   5018  
  65 100   189   174045  
  58 50   25   231  
  58 100   12   834  
  58 100   26   787  
  63 100   194   107870  
  221 50   33   3729  
  58 0   29   118626  
  44 100   8   56259  
  4 100   19   16182  
  4 100   31   64  
  28 100   11   12721  
  196 50   12   2632  
  34 0   13   7611  
  43 50   18   8591  
  26 50   1   9789  
  26 50   23   290004  
  26 50   25   2543  
  12 0   63   48  
  11 0   5   44  
  11 50   2   2994  
  11 50   52   93047  
  11     12   918  
  6     16   1367  
  4     11   52  
  6     1   1074  
  6     4   32358  
  4     8   551  
  6     8   21  
  6     0   29  
  6     1   434  
  6     1   17371  
  6     0   149  
  5         2831  
  18         11796  
  22         245  
  22         15926  
  6         1048  
  15         104  
  33         2933  
  33         138  
  33         3745  
  9         316  
  18         7215  
  21         212  
  18         128  
  14         3775  
  22         39797  
  19         41990  
  9         245  
  9         65  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  22         1853  
  24         38514  
  24         1119  
  27         1811  
  23         102  
  23         21753  
  23         321  
  61         14180  
  16         50  
  16         87  
  67         260  
  28         16641  
  31         256  
  11         341  
  1         6  
  4         36  
  8         9213  
  8         1887  
  4         19  
  5         913  
  6         64  
  4         41234  
  1         5  
  4         926  
  4         42  
  1         5  
  1         3  
  1         8  
  1         7  
  1         1151  
  1         13  
  0         0  
  0         0  
  1         15  
  2         19  
  2         20  
  2         40  
  3         18809  
  2         13  
  3         50  
  2         13  
  1         823  
  1         9  
  0            
  0            
  0            
  0            
  0            
  0            
805 26 50       230 die $@ if $@;
806             } elsif ( $DEBUG ) {
807 0         0 dval($code);
808             } else {
809 0         0 eval($code);
810 0 0       0 die $@ if $@;
811             }
812              
813 26         2384 return 1;
814             }
815              
816             sub dval {
817             # Write the code to the temp file
818 4     4 0 6434 require File::Temp;
819 15         11197 my ($fh, $filename) = File::Temp::tempfile();
820 19         11195 $fh->print($_[0]);
821 34         13745 close $fh;
822 39         9270 require $filename;
823 14         3480 unlink $filename;
824              
825             # Print the debugging output
826             # my @trace = map {
827             # s/\s*[{;]$//;
828             # s/^s/ s/;
829             # s/^p/\np/;
830             # "$_\n"
831             # } grep {
832             # /^(?:package|sub)\b/
833             # } split /\n/, $_[0];
834             # print STDERR @trace, "\nCode saved as $filename\n\n";
835              
836 4         60786 return 1;
837             }
838              
839             sub prune {
840 4     15 1 377 my $class = shift;
841 4         250 push @PRUNE, map { File::Spec->rel2abs($_) } @_;
  4         72  
842             }
843              
844             1;
845              
846             __END__