File Coverage

blib/lib/DBD/File.pm
Criterion Covered Total %
statement 361 443 81.4
branch 168 300 56.0
condition 53 149 35.5
subroutine 60 70 85.7
pod 0 1 0.0
total 642 963 66.6


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             # DBD::File - A base class for implementing DBI drivers that
4             # act on plain files
5             #
6             # This module is currently maintained by
7             #
8             # H.Merijn Brand & Jens Rehsack
9             #
10             # The original author is Jochen Wiedmann.
11             #
12             # Copyright (C) 2009-2013 by H.Merijn Brand & Jens Rehsack
13             # Copyright (C) 2004 by Jeff Zucker
14             # Copyright (C) 1998 by Jochen Wiedmann
15             #
16             # All rights reserved.
17             #
18             # You may distribute this module under the terms of either the GNU
19             # General Public License or the Artistic License, as specified in
20             # the Perl README file.
21              
22             require 5.008;
23              
24 48     48   20587 use strict;
  48         75  
  48         1635  
25 48     48   199 use warnings;
  48         60  
  48         1339  
26              
27 48     48   191 use DBI ();
  48         56  
  48         969  
28              
29             package DBD::File;
30              
31 48     48   181 use strict;
  48         65  
  48         1292  
32 48     48   250 use warnings;
  48         66  
  48         1312  
33              
34 48     48   191 use base qw( DBI::DBD::SqlEngine );
  48         65  
  48         24366  
35 48     48   266 use Carp;
  48         68  
  48         2744  
36 48     48   207 use vars qw( @ISA $VERSION $drh );
  48         76  
  48         4874  
37              
38             $VERSION = "0.43";
39              
40             $drh = undef; # holds driver handle(s) once initialized
41              
42             sub driver ($;$)
43             {
44 40     40 0 81 my ($class, $attr) = @_;
45              
46             # Drivers typically use a singleton object for the $drh
47             # We use a hash here to have one singleton per subclass.
48             # (Otherwise DBD::CSV and DBD::DBM, for example, would
49             # share the same driver object which would cause problems.)
50             # An alternative would be to not cache the $drh here at all
51             # and require that subclasses do that. Subclasses should do
52             # their own caching, so caching here just provides extra safety.
53 40 50       158 $drh->{$class} and return $drh->{$class};
54              
55 40   50     123 $attr ||= {};
56 48     48   225 { no strict "refs";
  48         72  
  48         8073  
  40         69  
57 40 100       137 unless ($attr->{Attribution}) {
58 16 50       94 $class eq "DBD::File" and
59             $attr->{Attribution} = "$class by Jeff Zucker";
60 16   0     63 $attr->{Attribution} ||= ${$class . "::ATTRIBUTION"} ||
      33        
61             "oops the author of $class forgot to define this";
62             }
63 40   33     201 $attr->{Version} ||= ${$class . "::VERSION"};
  40         173  
64 40 50       327 $attr->{Name} or ($attr->{Name} = $class) =~ s/^DBD\:\://;
65             }
66              
67 40         358 $drh->{$class} = $class->SUPER::driver ($attr);
68              
69             # XXX inject DBD::XXX::Statement unless exists
70              
71 40         151 return $drh->{$class};
72             } # driver
73              
74             sub CLONE
75             {
76 0     0   0 undef $drh;
77             } # CLONE
78              
79             # ====== DRIVER ================================================================
80              
81             package DBD::File::dr;
82              
83 48     48   272 use strict;
  48         82  
  48         1325  
84 48     48   232 use warnings;
  48         70  
  48         1548  
85              
86 48     48   190 use vars qw( @ISA $imp_data_size );
  48         69  
  48         2004  
87              
88 48     48   200 use Carp;
  48         69  
  48         14412  
89              
90             @DBD::File::dr::ISA = qw( DBI::DBD::SqlEngine::dr );
91             $DBD::File::dr::imp_data_size = 0;
92              
93             sub dsn_quote
94             {
95 128     128   139 my $str = shift;
96 128 50       219 ref $str and return "";
97 128 100       313 defined $str or return "";
98 80         211 $str =~ s/([;:\\])/\\$1/g;
99 80         354 return $str;
100             } # dsn_quote
101              
102             # XXX rewrite using TableConfig ...
103 68     68   174 sub default_table_source { "DBD::File::TableSource::FileSystem" }
104              
105             sub connect
106             {
107 624     624   7995 my ($drh, $dbname, $user, $auth, $attr) = @_;
108              
109             # We do not (yet) care about conflicting attributes here
110             # my $dbh = DBI->connect ("dbi:CSV:f_dir=test", undef, undef, { f_dir => "text" });
111             # will test here that both test and text should exist
112 624 50       2498 if (my $attr_hash = (DBI->parse_dsn ($dbname))[3]) {
113 0 0 0     0 if (defined $attr_hash->{f_dir} && ! -d $attr_hash->{f_dir}) {
114 0         0 my $msg = "No such directory '$attr_hash->{f_dir}";
115 0         0 $drh->set_err (2, $msg);
116 0 0       0 $attr_hash->{RaiseError} and croak $msg;
117 0         0 return;
118             }
119             }
120 624 100 100     8905 if ($attr and defined $attr->{f_dir} && ! -d $attr->{f_dir}) {
      33        
121 48         117 my $msg = "No such directory '$attr->{f_dir}";
122 48         313 $drh->set_err (2, $msg);
123 48 100       6702 $attr->{RaiseError} and croak $msg;
124 12         70 return;
125             }
126              
127 576         3780 return $drh->SUPER::connect ($dbname, $user, $auth, $attr);
128             } # connect
129              
130             sub disconnect_all
131 40     40   906 {
132             } # disconnect_all
133              
134             sub DESTROY
135             {
136 0     0   0 undef;
137             } # DESTROY
138              
139             # ====== DATABASE ==============================================================
140              
141             package DBD::File::db;
142              
143 48     48   248 use strict;
  48         75  
  48         1284  
144 48     48   182 use warnings;
  48         61  
  48         1304  
145              
146 48     48   184 use vars qw( @ISA $imp_data_size );
  48         70  
  48         2014  
147              
148 48     48   202 use Carp;
  48         74  
  48         2930  
149             require File::Spec;
150             require Cwd;
151 48     48   234 use Scalar::Util qw( refaddr ); # in CORE since 5.7.3
  48         65  
  48         35034  
152              
153             @DBD::File::db::ISA = qw( DBI::DBD::SqlEngine::db );
154             $DBD::File::db::imp_data_size = 0;
155              
156             sub data_sources
157             {
158 24     24   302 my ($dbh, $attr, @other) = @_;
159 24 50       85 ref ($attr) eq "HASH" or $attr = {};
160 24 50       105 exists $attr->{f_dir} or $attr->{f_dir} = $dbh->{f_dir};
161 24 50       83 exists $attr->{f_dir_search} or $attr->{f_dir_search} = $dbh->{f_dir_search};
162 24         131 return $dbh->SUPER::data_sources ($attr, @other);
163             } # data_source
164              
165             sub set_versions
166             {
167 576     576   1556 my $dbh = shift;
168 576         1376 $dbh->{f_version} = $DBD::File::VERSION;
169              
170 576         1908 return $dbh->SUPER::set_versions ();
171             } # set_versions
172              
173             sub init_valid_attributes
174             {
175 576     576   1906 my $dbh = shift;
176              
177 576         5484 $dbh->{f_valid_attrs} = {
178             f_version => 1, # DBD::File version
179             f_dir => 1, # base directory
180             f_dir_search => 1, # extended search directories
181             f_ext => 1, # file extension
182             f_schema => 1, # schema name
183             f_lock => 1, # Table locking mode
184             f_lockfile => 1, # Table lockfile extension
185             f_encoding => 1, # Encoding of the file
186             f_valid_attrs => 1, # File valid attributes
187             f_readonly_attrs => 1, # File readonly attributes
188             };
189 576         1939 $dbh->{f_readonly_attrs} = {
190             f_version => 1, # DBD::File version
191             f_valid_attrs => 1, # File valid attributes
192             f_readonly_attrs => 1, # File readonly attributes
193             };
194              
195 576         2098 return $dbh->SUPER::init_valid_attributes ();
196             } # init_valid_attributes
197              
198             sub init_default_attributes
199             {
200 1152     1152   3508 my ($dbh, $phase) = @_;
201              
202             # must be done first, because setting flags implicitly calls $dbdname::db->STORE
203 1152         3514 $dbh->SUPER::init_default_attributes ($phase);
204              
205             # DBI::BD::SqlEngine::dr::connect will detect old-style drivers and
206             # don't call twice
207 1152 50       2219 unless (defined $phase) {
208             # we have an "old" driver here
209 0         0 $phase = defined $dbh->{sql_init_phase};
210 0 0       0 $phase and $phase = $dbh->{sql_init_phase};
211             }
212              
213 1152 100       2241 if (0 == $phase) {
214             # f_ext should not be initialized
215             # f_map is deprecated (but might return)
216 576         6810 $dbh->{f_dir} = Cwd::abs_path (File::Spec->curdir ());
217              
218 576         802 push @{$dbh->{sql_init_order}{90}}, "f_meta";
  576         1545  
219              
220             # complete derived attributes, if required
221 576         2509 (my $drv_class = $dbh->{ImplementorClass}) =~ s/::db$//;
222 576         1614 my $drv_prefix = DBI->driver_prefix ($drv_class);
223 576 100 66     2860 if (exists $dbh->{$drv_prefix . "meta"} and !$dbh->{sql_engine_in_gofer}) {
224 384         739 my $attr = $dbh->{$drv_prefix . "meta"};
225 384 50       1055 defined $dbh->{f_valid_attrs}{f_meta}
226             and $dbh->{f_valid_attrs}{f_meta} = 1;
227              
228 384         994 $dbh->{f_meta} = $dbh->{$attr};
229             }
230             }
231              
232 1152         2029 return $dbh;
233             } # init_default_attributes
234              
235             sub validate_FETCH_attr
236             {
237 0     0   0 my ($dbh, $attrib) = @_;
238              
239 0 0 0     0 $attrib eq "f_meta" and $dbh->{sql_engine_in_gofer} and $attrib = "sql_meta";
240              
241 0         0 return $dbh->SUPER::validate_FETCH_attr ($attrib);
242             } # validate_FETCH_attr
243              
244             sub validate_STORE_attr
245             {
246 4072     4072   9669 my ($dbh, $attrib, $value) = @_;
247              
248 4072 100 66     9314 if ($attrib eq "f_dir" && defined $value) {
249 492 50       13324 -d $value or
250             return $dbh->set_err ($DBI::stderr, "No such directory '$value'");
251 492 100       5012 File::Spec->file_name_is_absolute ($value) or
252             $value = Cwd::abs_path ($value);
253             }
254              
255 4072 100       6861 if ($attrib eq "f_ext") {
256 108 50 33     944 $value eq "" || $value =~ m{^\.\w+(?:/[rR]*)?$} or
257             carp "'$value' doesn't look like a valid file extension attribute\n";
258             }
259              
260 4072 50 33     8076 $attrib eq "f_meta" and $dbh->{sql_engine_in_gofer} and $attrib = "sql_meta";
261              
262 4072         10789 return $dbh->SUPER::validate_STORE_attr ($attrib, $value);
263             } # validate_STORE_attr
264              
265             sub get_f_versions
266             {
267 16     16   26 my ($dbh, $table) = @_;
268              
269 16         39 my $class = $dbh->{ImplementorClass};
270 16         65 $class =~ s/::db$/::Table/;
271 16         26 my $dver;
272 16         26 my $dtype = "IO::File";
273 16         25 eval {
274 16         304 $dver = IO::File->VERSION ();
275              
276             # when we're still alive here, everything went ok - no need to check for $@
277 16         64 $dtype .= " ($dver)";
278             };
279              
280 16         22 my $f_encoding;
281 16 50       53 if ($table) {
282 0         0 my $meta;
283 0 0       0 $table and (undef, $meta) = $class->get_table_meta ($dbh, $table, 1);
284 0 0 0     0 $meta and $meta->{f_encoding} and $f_encoding = $meta->{f_encoding};
285             } # if ($table)
286 16   33     82 $f_encoding ||= $dbh->{f_encoding};
287              
288 16 50       42 $f_encoding and $dtype .= " + " . $f_encoding . " encoding";
289              
290 16         95 return sprintf "%s using %s", $dbh->{f_version}, $dtype;
291             } # get_f_versions
292              
293             # ====== STATEMENT =============================================================
294              
295             package DBD::File::st;
296              
297 48     48   259 use strict;
  48         203  
  48         1496  
298 48     48   238 use warnings;
  48         67  
  48         1316  
299              
300 48     48   194 use vars qw( @ISA $imp_data_size );
  48         64  
  48         16270  
301              
302             @DBD::File::st::ISA = qw( DBI::DBD::SqlEngine::st );
303             $DBD::File::st::imp_data_size = 0;
304              
305             my %supported_attrs = (
306             TYPE => 1,
307             PRECISION => 1,
308             NULLABLE => 1,
309             );
310              
311             sub FETCH
312             {
313 2006     2006   3972 my ($sth, $attr) = @_;
314              
315 2006 100       3789 if ($supported_attrs{$attr}) {
316 744         976 my $stmt = $sth->{sql_stmt};
317              
318 744 50 33     7342 if (exists $sth->{ImplementorClass} &&
      33        
319             exists $sth->{sql_stmt} &&
320             $sth->{sql_stmt}->isa ("SQL::Statement")) {
321              
322             # fill overall_defs unless we know
323 0 0 0     0 unless (exists $sth->{f_overall_defs} && ref $sth->{f_overall_defs}) {
324 0         0 my $all_meta =
325             $sth->{Database}->func ("*", "table_defs", "get_sql_engine_meta");
326 0         0 while (my ($tbl, $meta) = each %$all_meta) {
327 0 0 0     0 exists $meta->{table_defs} && ref $meta->{table_defs} or next;
328 0         0 foreach (keys %{$meta->{table_defs}{columns}}) {
  0         0  
329 0         0 $sth->{f_overall_defs}{$_} = $meta->{table_defs}{columns}{$_};
330             }
331             }
332             }
333              
334 0         0 my @colnames = $sth->sql_get_colnames ();
335              
336 0 0       0 $attr eq "TYPE" and
337 0 0       0 return [ map { $sth->{f_overall_defs}{$_}{data_type} || "CHAR" }
338             @colnames ];
339              
340 0 0       0 $attr eq "PRECISION" and
341 0 0       0 return [ map { $sth->{f_overall_defs}{$_}{data_length} || 0 }
342             @colnames ];
343              
344 0         0 $attr eq "NULLABLE" and
345 0 0       0 return [ map { ( grep { $_ eq "NOT NULL" }
  0 0       0  
346 0 0       0 @{ $sth->{f_overall_defs}{$_}{constraints} || [] })
347             ? 0 : 1 }
348             @colnames ];
349             }
350             }
351              
352 2006         4831 return $sth->SUPER::FETCH ($attr);
353             } # FETCH
354              
355             # ====== TableSource ===========================================================
356              
357             package DBD::File::TableSource::FileSystem;
358              
359 48     48   254 use strict;
  48         75  
  48         1316  
360 48     48   179 use warnings;
  48         78  
  48         1110  
361              
362 48     48   25279 use IO::Dir;
  48         707361  
  48         33469  
363              
364             @DBD::File::TableSource::FileSystem::ISA = "DBI::DBD::SqlEngine::TableSource";
365              
366             sub data_sources
367             {
368 32     32   52 my ($class, $drh, $attr) = @_;
369 32 50 33     182 my $dir = $attr && exists $attr->{f_dir}
370             ? $attr->{f_dir}
371             : File::Spec->curdir ();
372 32 50       75 defined $dir or return; # Stream-based databases do not have f_dir
373 32 50 33     1507 unless (-d $dir && -r $dir && -x $dir) {
      33        
374 0         0 $drh->set_err ($DBI::stderr, "Cannot use directory $dir from f_dir");
375 0         0 return;
376             }
377 32         44 my %attrs;
378 32 50       217 $attr and %attrs = %$attr;
379 32         90 delete $attrs{f_dir};
380 32         195 my $dsn_quote = $drh->{ImplementorClass}->can ("dsn_quote");
381 32         97 my $dsnextra = join ";", map { $_ . "=" . &{$dsn_quote} ($attrs{$_}) } keys %attrs;
  88         150  
  88         150  
382 32         78 my @dir = ($dir);
383 0         0 $attr->{f_dir_search} && ref $attr->{f_dir_search} eq "ARRAY" and
384 32 50 33     110 push @dir, grep { -d $_ } @{$attr->{f_dir_search}};
  0         0  
385 32         41 my @dsns;
386 32         63 foreach $dir (@dir) {
387 32         233 my $dirh = IO::Dir->new ($dir);
388 32 50       2604 unless (defined $dirh) {
389 0         0 $drh->set_err ($DBI::stderr, "Cannot open directory $dir: $!");
390 0         0 return;
391             }
392              
393 32         388 my ($file, %names, $driver);
394 32 50       309 $driver = $drh->{ImplementorClass} =~ m/^dbd\:\:([^\:]+)\:\:/i ? $1 : "File";
395              
396 32         134 while (defined ($file = $dirh->read ())) {
397 72         1715 my $d = File::Spec->catdir ($dir, $file);
398             # allow current dir ... it can be a data_source too
399 40         87 $file ne File::Spec->updir () && -d $d and
400 72 50 66     1174 push @dsns, "DBI:$driver:f_dir=" . &{$dsn_quote} ($d) . ($dsnextra ? ";$dsnextra" : "");
    100          
401             }
402             }
403 32         1492 return @dsns;
404             } # data_sources
405              
406             sub avail_tables
407             {
408 36     36   59 my ($self, $dbh) = @_;
409              
410 36         92 my $dir = $dbh->{f_dir};
411 36 50       94 defined $dir or return; # Stream based db's cannot be queried for tables
412              
413 36         50 my %seen;
414             my @tables;
415 36         67 my @dir = ($dir);
416 4         80 $dbh->{f_dir_search} && ref $dbh->{f_dir_search} eq "ARRAY" and
417 36 100 66     146 push @dir, grep { -d $_ } @{$dbh->{f_dir_search}};
  4         15  
418 36         75 foreach $dir (@dir) {
419 40         372 my $dirh = IO::Dir->new ($dir);
420              
421 40 50       2837 unless (defined $dirh) {
422 0         0 $dbh->set_err ($DBI::stderr, "Cannot open directory $dir: $!");
423 0         0 return;
424             }
425              
426 40         228 my $class = $dbh->FETCH ("ImplementorClass");
427 40         468 $class =~ s/::db$/::Table/;
428 40         65 my ($file, %names);
429             my $schema = exists $dbh->{f_schema}
430             ? defined $dbh->{f_schema} && $dbh->{f_schema} ne ""
431             ? $dbh->{f_schema} : undef
432 40 50 33     201 : eval { getpwuid ((stat $dir)[4]) }; # XXX Win32::pwent
  24 100       8842  
433 40         236 while (defined ($file = $dirh->read ())) {
434 176 100       2305 my ($tbl, $meta) = $class->get_table_meta ($dbh, $file, 0, 0) or next; # XXX
435             # $tbl && $meta && -f $meta->{f_fqfn} or next;
436 48 100       503 $seen{defined $schema ? $schema : "\0"}{$dir}{$tbl}++ or
    50          
437             push @tables, [ undef, $schema, $tbl, "TABLE", "FILE" ];
438             }
439 40 50       428 $dirh->close () or
440             $dbh->set_err ($DBI::stderr, "Cannot close directory $dir: $!");
441             }
442              
443 36         1129 return @tables;
444             } # avail_tables
445              
446             # ====== DataSource ============================================================
447              
448             package DBD::File::DataSource::Stream;
449              
450 48     48   414 use strict;
  48         97  
  48         1526  
451 48     48   211 use warnings;
  48         70  
  48         1386  
452              
453 48     48   195 use Carp;
  48         83  
  48         26850  
454              
455             @DBD::File::DataSource::Stream::ISA = "DBI::DBD::SqlEngine::DataSource";
456              
457             # We may have a working flock () built-in but that doesn't mean that locking
458             # will work on NFS (flock () may hang hard)
459             my $locking = eval {
460             my $fh;
461             my $nulldevice = File::Spec->devnull ();
462             open $fh, ">", $nulldevice or croak "Can't open $nulldevice: $!";
463             flock $fh, 0;
464             close $fh;
465             1;
466             };
467              
468             sub complete_table_name
469             {
470 0     0   0 my ($self, $meta, $file, $respect_case) = @_;
471              
472 0         0 my $tbl = $file;
473 0 0 0     0 if (!$respect_case and $meta->{sql_identifier_case} == 1) { # XXX SQL_IC_UPPER
    0 0        
474 0         0 $tbl = uc $tbl;
475             }
476             elsif (!$respect_case and $meta->{sql_identifier_case} == 2) { # XXX SQL_IC_LOWER
477 0         0 $tbl = lc $tbl;
478             }
479              
480 0         0 $meta->{f_fqfn} = undef;
481 0         0 $meta->{f_fqbn} = undef;
482 0         0 $meta->{f_fqln} = undef;
483              
484 0         0 $meta->{table_name} = $tbl;
485              
486 0         0 return $tbl;
487             } # complete_table_name
488              
489             sub apply_encoding
490             {
491 44     44   78 my ($self, $meta, $fn) = @_;
492 44 50       191 defined $fn or $fn = "file handle " . fileno ($meta->{fh});
493 44 50       147 if (my $enc = $meta->{f_encoding}) {
494 44 50   8   726 binmode $meta->{fh}, ":encoding($enc)" or
  8         59  
  8         9  
  8         54  
495             croak "Failed to set encoding layer '$enc' on $fn: $!";
496             }
497             else {
498 0 0       0 binmode $meta->{fh} or croak "Failed to set binary mode on $fn: $!";
499             }
500             } # apply_encoding
501              
502             sub open_data
503             {
504 0     0   0 my ($self, $meta, $attrs, $flags) = @_;
505              
506 0 0       0 $flags->{dropMode} and croak "Can't drop a table in stream";
507 0         0 my $fn = "file handle " . fileno ($meta->{f_file});
508              
509 0 0 0     0 if ($flags->{createMode} || $flags->{lockMode}) {
510 0 0       0 $meta->{fh} = IO::Handle->new_from_fd (fileno ($meta->{f_file}), "w+") or
511             croak "Cannot open $fn for writing: $! (" . ($!+0) . ")";
512             }
513             else {
514 0 0       0 $meta->{fh} = IO::Handle->new_from_fd (fileno ($meta->{f_file}), "r") or
515             croak "Cannot open $fn for reading: $! (" . ($!+0) . ")";
516             }
517              
518 0 0       0 if ($meta->{fh}) {
519 0         0 $self->apply_encoding ($meta, $fn);
520             } # have $meta->{$fh}
521              
522 0 0 0     0 if ($self->can_flock && $meta->{fh}) {
523 0 0 0     0 my $lm = defined $flags->{f_lock}
    0          
524             && $flags->{f_lock} =~ m/^[012]$/
525             ? $flags->{f_lock}
526             : $flags->{lockMode} ? 2 : 1;
527 0 0       0 if ($lm == 2) {
    0          
528 0 0       0 flock $meta->{fh}, 2 or croak "Cannot obtain exclusive lock on $fn: $!";
529             }
530             elsif ($lm == 1) {
531 0 0       0 flock $meta->{fh}, 1 or croak "Cannot obtain shared lock on $fn: $!";
532             }
533             # $lm = 0 is forced no locking at all
534             }
535             } # open_data
536              
537 404     404   2337 sub can_flock { $locking }
538              
539             package DBD::File::DataSource::File;
540              
541 48     48   296 use strict;
  48         82  
  48         1394  
542 48     48   203 use warnings;
  48         91  
  48         1740  
543              
544             @DBD::File::DataSource::File::ISA = "DBD::File::DataSource::Stream";
545              
546 48     48   223 use Carp;
  48         115  
  48         60642  
547              
548             my $fn_any_ext_regex = qr/\.[^.]*/;
549              
550             sub complete_table_name
551             {
552 500     500   1021 my ($self, $meta, $file, $respect_case, $file_is_table) = @_;
553              
554 500 100 100     2826 $file eq "." || $file eq ".." and return; # XXX would break a possible DBD::Dir
555              
556             # XXX now called without proving f_fqfn first ...
557 420         742 my ($ext, $req) = ("", 0);
558 420 50       1064 if ($meta->{f_ext}) {
559 420         1800 ($ext, my $opt) = split m{/}, $meta->{f_ext};
560 420 100 66     1969 if ($ext && $opt) {
561 288 50       1497 $opt =~ m/r/i and $req = 1;
562             }
563             }
564              
565             # (my $tbl = $file) =~ s/$ext$//i;
566 420         543 my ($tbl, $basename, $dir, $fn_ext, $user_spec_file, $searchdir);
567 420 100 100     1720 if ($file_is_table and defined $meta->{f_file}) {
568 4         11 $tbl = $file;
569 4         119 ($basename, $dir, $fn_ext) = File::Basename::fileparse ($meta->{f_file}, $fn_any_ext_regex);
570 4         11 $file = $basename . $fn_ext;
571 4         10 $user_spec_file = 1;
572             }
573             else {
574 416         18270 ($basename, $dir, undef) = File::Basename::fileparse ($file, $ext);
575             # $dir is returned with trailing (back)slash. We just need to check
576             # if it is ".", "./", or ".\" or "[]" (VMS)
577 416 100 66     3941 if ($dir =~ m{^(?:[.][/\\]?|\[\])$} && ref $meta->{f_dir_search} eq "ARRAY") {
578 32         53 foreach my $d ($meta->{f_dir}, @{$meta->{f_dir_search}}) {
  32         67  
579 52         384 my $f = File::Spec->catdir ($d, $file);
580 52 100       1091 -f $f or next;
581 24         720 $searchdir = Cwd::abs_path ($d);
582 24         36 $dir = "";
583 24         43 last;
584             }
585             }
586 416         691 $file = $tbl = $basename;
587 416         621 $user_spec_file = 0;
588             }
589              
590 420 100 66     3528 if (!$respect_case and $meta->{sql_identifier_case} == 1) { # XXX SQL_IC_UPPER
    50 33        
591 16         33 $basename = uc $basename;
592 16         24 $tbl = uc $tbl;
593             }
594             elsif (!$respect_case and $meta->{sql_identifier_case} == 2) { # XXX SQL_IC_LOWER
595 404         640 $basename = lc $basename;
596 404         586 $tbl = lc $tbl;
597             }
598              
599 420 100       937 unless (defined $searchdir) {
600 396 100       21090 $searchdir = File::Spec->file_name_is_absolute ($dir)
601             ? ($dir =~ s{/$}{}, $dir)
602             : Cwd::abs_path (File::Spec->catdir ($meta->{f_dir}, $dir));
603             }
604 420 50       4727 -d $searchdir or
605             croak "-d $searchdir: $!";
606              
607 420 100       1474 $searchdir eq $meta->{f_dir} and
608             $dir = "";
609              
610 420 100       961 unless ($user_spec_file) {
611 416 100       1023 $file_is_table and $file = "$basename$ext";
612              
613             # Fully Qualified File Name
614 416         465 my $cmpsub;
615 416 50       952 if ($respect_case) {
616             $cmpsub = sub {
617 0     0   0 my ($fn, undef, $sfx) = File::Basename::fileparse ($_, $fn_any_ext_regex);
618 0 0 0     0 $^O eq "VMS" && $sfx eq "." and
619             $sfx = ""; # no extension turns up as a dot
620 0 0 0     0 $fn eq $basename and
621             return (lc $sfx eq lc $ext or !$req && !$sfx);
622 0         0 return 0;
623             }
624 0         0 }
625             else {
626             $cmpsub = sub {
627 1856     1856   40792 my ($fn, undef, $sfx) = File::Basename::fileparse ($_, $fn_any_ext_regex);
628 1856 50 33     5433 $^O eq "VMS" && $sfx eq "." and
629             $sfx = ""; # no extension turns up as a dot
630 1856 100 100     6396 lc $fn eq lc $basename and
631             return (lc $sfx eq lc $ext or !$req && !$sfx);
632 1268         2512 return 0;
633             }
634 416         2817 }
635              
636 416         539 my @f;
637 416 50       520 { my $dh = IO::Dir->new ($searchdir) or croak "Can't open '$searchdir': $!";
  416         3169  
638 8         36 @f = sort { length $b <=> length $a }
  1856         10467  
639 416         28688 grep { &$cmpsub ($_) }
640             $dh->read ();
641 416 50       1735 $dh->close () or croak "Can't close '$searchdir': $!";
642             }
643 416 100 66     18885 @f > 0 && @f <= 2 and $file = $f[0];
644 416 50 33     2347 !$respect_case && $meta->{sql_identifier_case} == 4 and # XXX SQL_IC_MIXED
645             ($tbl = $file) =~ s/$ext$//i;
646              
647 416         659 my $tmpfn = $file;
648 416 100 66     2523 if ($ext && $req) {
649             # File extension required
650 284 100       3668 $tmpfn =~ s/$ext$//i or return;
651             }
652             }
653              
654 372         4688 my $fqfn = File::Spec->catfile ($searchdir, $file);
655 372         2462 my $fqbn = File::Spec->catfile ($searchdir, $basename);
656              
657 372         962 $meta->{f_fqfn} = $fqfn;
658 372         724 $meta->{f_fqbn} = $fqbn;
659 372 100 66     2191 defined $meta->{f_lockfile} && $meta->{f_lockfile} and
660             $meta->{f_fqln} = $meta->{f_fqbn} . $meta->{f_lockfile};
661              
662 372 50 33     1040 $dir && !$user_spec_file and $tbl = File::Spec->catfile ($dir, $tbl);
663 372         989 $meta->{table_name} = $tbl;
664              
665 372         1650 return $tbl;
666             } # complete_table_name
667              
668             sub open_data
669             {
670 452     452   706 my ($self, $meta, $attrs, $flags) = @_;
671              
672 452 50 33     2504 defined $meta->{f_fqfn} && $meta->{f_fqfn} ne "" or croak "No filename given";
673              
674 452         602 my ($fh, $fn);
675 452 100       1240 unless ($meta->{f_dontopen}) {
676 52         69 $fn = $meta->{f_fqfn};
677 52 100       108 if ($flags->{createMode}) {
678 8 50       195 -f $meta->{f_fqfn} and
679             croak "Cannot create table $attrs->{table}: Already exists";
680 8 50       59 $fh = IO::File->new ($fn, "a+") or
681             croak "Cannot open $fn for writing: $! (" . ($!+0) . ")";
682             }
683             else {
684 44 100       330 unless ($fh = IO::File->new ($fn, ($flags->{lockMode} ? "r+" : "r"))) {
    100          
685 8         2869 croak "Cannot open $fn: $! (" . ($!+0) . ")";
686             }
687             }
688              
689 44         4616 $meta->{fh} = $fh;
690              
691 44 50       122 if ($fh) {
692 44 50       257 $fh->seek (0, 0) or
693             croak "Error while seeking back: $!";
694              
695 44         576 $self->apply_encoding ($meta);
696             }
697             }
698 444 100       79136 if ($meta->{f_fqln}) {
699 400         637 $fn = $meta->{f_fqln};
700 400 100       869 if ($flags->{createMode}) {
701 52 50       1177 -f $fn and
702             croak "Cannot create table lock at '$fn' for $attrs->{table}: Already exists";
703 52 50       396 $fh = IO::File->new ($fn, "a+") or
704             croak "Cannot open $fn for writing: $! (" . ($!+0) . ")";
705             }
706             else {
707 348 100       3520 unless ($fh = IO::File->new ($fn, ($flags->{lockMode} ? "r+" : "r"))) {
    100          
708 40         14565 croak "Cannot open $fn: $! (" . ($!+0) . ")";
709             }
710             }
711              
712 360         44737 $meta->{lockfh} = $fh;
713             }
714              
715 404 50 33     2594 if ($self->can_flock && $fh) {
716 404 100 33     2124 my $lm = defined $flags->{f_lock}
    50          
717             && $flags->{f_lock} =~ m/^[012]$/
718             ? $flags->{f_lock}
719             : $flags->{lockMode} ? 2 : 1;
720 404 100       1095 if ($lm == 2) {
    50          
721 312 50       3172 flock $fh, 2 or croak "Cannot obtain exclusive lock on $fn: $!";
722             }
723             elsif ($lm == 1) {
724 92 50       964 flock $fh, 1 or croak "Cannot obtain shared lock on $fn: $!";
725             }
726             # $lm = 0 is forced no locking at all
727             }
728             } # open_data
729              
730             # ====== SQL::STATEMENT ========================================================
731              
732             package DBD::File::Statement;
733              
734 48     48   293 use strict;
  48         80  
  48         1482  
735 48     48   244 use warnings;
  48         89  
  48         2335  
736              
737             @DBD::File::Statement::ISA = qw( DBI::DBD::SqlEngine::Statement );
738              
739             # ====== SQL::TABLE ============================================================
740              
741             package DBD::File::Table;
742              
743 48     48   201 use strict;
  48         79  
  48         1290  
744 48     48   210 use warnings;
  48         71  
  48         1164  
745              
746 48     48   216 use Carp;
  48         85  
  48         45460  
747             require IO::File;
748             require File::Basename;
749             require File::Spec;
750             require Cwd;
751             require Scalar::Util;
752              
753             @DBD::File::Table::ISA = qw( DBI::DBD::SqlEngine::Table );
754              
755             # ====== UTILITIES ============================================================
756              
757             if (eval { require Params::Util; }) {
758             Params::Util->import ("_HANDLE");
759             }
760             else {
761             # taken but modified from Params::Util ...
762             *_HANDLE = sub {
763             # It has to be defined, of course
764             defined $_[0] or return;
765              
766             # Normal globs are considered to be file handles
767             ref $_[0] eq "GLOB" and return $_[0];
768              
769             # Check for a normal tied filehandle
770             # Side Note: 5.5.4's tied () and can () doesn't like getting undef
771             tied ($_[0]) and tied ($_[0])->can ("TIEHANDLE") and return $_[0];
772              
773             # There are no other non-object handles that we support
774             Scalar::Util::blessed ($_[0]) or return;
775              
776             # Check for a common base classes for conventional IO::Handle object
777             $_[0]->isa ("IO::Handle") and return $_[0];
778              
779             # Check for tied file handles using Tie::Handle
780             $_[0]->isa ("Tie::Handle") and return $_[0];
781              
782             # IO::Scalar is not a proper seekable, but it is valid is a
783             # regular file handle
784             $_[0]->isa ("IO::Scalar") and return $_[0];
785              
786             # Yet another special case for IO::String, which refuses (for now
787             # anyway) to become a subclass of IO::Handle.
788             $_[0]->isa ("IO::String") and return $_[0];
789              
790             # This is not any sort of object we know about
791             return;
792             };
793             }
794              
795             # ====== FLYWEIGHT SUPPORT =====================================================
796              
797             # Flyweight support for table_info
798             # The functions file2table, init_table_meta, default_table_meta and
799             # get_table_meta are using $self arguments for polymorphism only. The
800             # must not rely on an instantiated DBD::File::Table
801             sub file2table
802             {
803 0     0   0 my ($self, $meta, $file, $file_is_table, $respect_case) = @_;
804              
805 0         0 return $meta->{sql_data_source}->complete_table_name ($meta, $file, $respect_case, $file_is_table);
806             } # file2table
807              
808             sub bootstrap_table_meta
809             {
810 508     508   919 my ($self, $dbh, $meta, $table, @other) = @_;
811              
812 508         1817 $self->SUPER::bootstrap_table_meta ($dbh, $meta, $table, @other);
813              
814 508 100       1537 exists $meta->{f_dir} or $meta->{f_dir} = $dbh->{f_dir};
815 508 100       1474 exists $meta->{f_dir_search} or $meta->{f_dir_search} = $dbh->{f_dir_search};
816 508 100       1230 defined $meta->{f_ext} or $meta->{f_ext} = $dbh->{f_ext};
817 508 100       1720 defined $meta->{f_encoding} or $meta->{f_encoding} = $dbh->{f_encoding};
818 508 100       1562 exists $meta->{f_lock} or $meta->{f_lock} = $dbh->{f_lock};
819 508 100       1700 exists $meta->{f_lockfile} or $meta->{f_lockfile} = $dbh->{f_lockfile};
820 508 50       1433 defined $meta->{f_schema} or $meta->{f_schema} = $dbh->{f_schema};
821              
822 508 100       5338 defined $meta->{f_open_file_needed} or
823             $meta->{f_open_file_needed} = $self->can ("open_file") != DBD::File::Table->can ("open_file");
824              
825 508 50       17943 defined ($meta->{sql_data_source}) or
    100          
826             $meta->{sql_data_source} = _HANDLE ($meta->{f_file})
827             ? "DBD::File::DataSource::Stream"
828             : "DBD::File::DataSource::File";
829             } # bootstrap_table_meta
830              
831             sub get_table_meta ($$$$;$)
832             {
833 1096     1096   1797 my ($self, $dbh, $table, $file_is_table, $respect_case) = @_;
834              
835 1096         4058 my $meta = $self->SUPER::get_table_meta ($dbh, $table, $respect_case, $file_is_table);
836 1096         1558 $table = $meta->{table_name};
837 1096 100       2394 return unless $table;
838              
839 968         3499 return ($table, $meta);
840             } # get_table_meta
841              
842             my %reset_on_modify = (
843             f_file => [ "f_fqfn", "sql_data_source" ],
844             f_dir => "f_fqfn",
845             f_dir_search => [],
846             f_ext => "f_fqfn",
847             f_lockfile => "f_fqfn", # forces new file2table call
848             );
849              
850             __PACKAGE__->register_reset_on_modify (\%reset_on_modify);
851              
852             my %compat_map = map { $_ => "f_$_" } qw( file ext lock lockfile );
853              
854             __PACKAGE__->register_compat_map (\%compat_map);
855              
856             # ====== DBD::File <= 0.40 compat stuff ========================================
857              
858             # compat to 0.38 .. 0.40 API
859             sub open_file
860             {
861 0     0   0 my ($className, $meta, $attrs, $flags) = @_;
862              
863 0         0 return $className->SUPER::open_data ($meta, $attrs, $flags);
864             } # open_file
865              
866             sub open_data
867             {
868 452     452   694 my ($className, $meta, $attrs, $flags) = @_;
869              
870             # compat to 0.38 .. 0.40 API
871 452 50       2379 $meta->{f_open_file_needed}
872             ? $className->open_file ($meta, $attrs, $flags)
873             : $className->SUPER::open_data ($meta, $attrs, $flags);
874              
875 404         785 return;
876             } # open_data
877              
878             # ====== SQL::Eval API =========================================================
879              
880             sub drop ($)
881             {
882 72     72   685 my ($self, $data) = @_;
883 72         436 my $meta = $self->{meta};
884             # We have to close the file before unlinking it: Some OS'es will
885             # refuse the unlink otherwise.
886 72 100       329 $meta->{fh} and $meta->{fh}->close ();
887 72 100       680 $meta->{lockfh} and $meta->{lockfh}->close ();
888 72         897 undef $meta->{fh};
889 72         210 undef $meta->{lockfh};
890 72 50       8065 $meta->{f_fqfn} and unlink $meta->{f_fqfn}; # XXX ==> sql_data_source
891 72 100       2155 $meta->{f_fqln} and unlink $meta->{f_fqln}; # XXX ==> sql_data_source
892 72         328 delete $data->{Database}{sql_meta}{$self->{table}};
893 72         212 return 1;
894             } # drop
895              
896             sub seek ($$$$)
897             {
898 0     0   0 my ($self, $data, $pos, $whence) = @_;
899 0         0 my $meta = $self->{meta};
900 0 0 0     0 if ($whence == 0 && $pos == 0) {
    0 0        
901 0 0       0 $pos = defined $meta->{first_row_pos} ? $meta->{first_row_pos} : 0;
902             }
903             elsif ($whence != 2 || $pos != 0) {
904 0         0 croak "Illegal seek position: pos = $pos, whence = $whence";
905             }
906              
907 0 0       0 $meta->{fh}->seek ($pos, $whence) or
908             croak "Error while seeking in " . $meta->{f_fqfn} . ": $!";
909             } # seek
910              
911             sub truncate ($$)
912             {
913 0     0   0 my ($self, $data) = @_;
914 0         0 my $meta = $self->{meta};
915 0 0       0 $meta->{fh}->truncate ($meta->{fh}->tell ()) or
916             croak "Error while truncating " . $meta->{f_fqfn} . ": $!";
917 0         0 return 1;
918             } # truncate
919              
920             sub DESTROY
921             {
922 404     404   598 my $self = shift;
923 404         634 my $meta = $self->{meta};
924 404 100       1084 $meta->{fh} and $meta->{fh}->close ();
925 404 100       2254 $meta->{lockfh} and $meta->{lockfh}->close ();
926 404         4156 undef $meta->{fh};
927 404         3913 undef $meta->{lockfh};
928             } # DESTROY
929              
930             1;
931              
932             __END__