DBI

 view release on metacpan or  search on metacpan

lib/DBD/File.pm  view on Meta::CPAN

    # their own caching, so caching here just provides extra safety.
    $drh->{$class} and return $drh->{$class};

    $attr ||= {};
    {	no strict "refs";
	unless ($attr->{Attribution}) {
	    $class eq "DBD::File" and
		$attr->{Attribution} = "$class by Jeff Zucker";
	    $attr->{Attribution} ||= ${$class . "::ATTRIBUTION"} ||
		"oops the author of $class forgot to define this";
	    }
	$attr->{Version} ||= ${$class . "::VERSION"};
	$attr->{Name} or ($attr->{Name} = $class) =~ s/^DBD\:\://;
	}

    $drh->{$class} = $class->SUPER::driver ($attr);

    # XXX inject DBD::XXX::Statement unless exists

    return $drh->{$class};
    } # driver

sub CLONE {
    undef $drh;
    } # CLONE

# ====== DRIVER ================================================================

package DBD::File::dr;

use strict;
use warnings;

use Carp;

our @ISA           = qw( DBI::DBD::SqlEngine::dr );
our $imp_data_size = 0;

sub dsn_quote {
    my $str = shift;
    ref     $str and return "";
    defined $str or  return "";
    $str =~ s/([;:\\])/\\$1/g;
    return $str;
    } # dsn_quote

# XXX rewrite using TableConfig ...
sub default_table_source { "DBD::File::TableSource::FileSystem" }

sub connect {
    my ($drh, $dbname, $user, $auth, $attr) = @_;

    # We do not (yet) care about conflicting attributes here
    # my $dbh = DBI->connect ("dbi:CSV:f_dir=test", undef, undef, { f_dir => "text" });
    # will test here that both test and text should exist
    #
    # Parsing on our own similar to parse_dsn to find attributes in 'dbname' parameter.
    if ($dbname) {
	my $attr_hash = {
	    map { (m/^\s* (\S+) \s*(?: =>? | , )\s* (\S*) \s*$/x) }
	    split m/;/ => $dbname };
	if (defined $attr_hash->{f_dir}) {
	    my $f_dir = $attr_hash->{f_dir};
	    # DSN escapes the : in Windows' path, which is not accepted by -d
	    #   D\\:\\\\Test\\\\DBI-01\\\\test_output_12345
	    # ->  D:\\\\Test\\\\DBI-01\\\\test_output_12345
	    $^O eq "MSWin32" and $f_dir =~ s{^([a-zA-Z])\\+:}{$1:};
	    unless (-d $f_dir) {
		my $msg = "No such directory '$attr_hash->{f_dir}";
		$drh->set_err (2, $msg);
		$attr_hash->{RaiseError} and croak $msg;
		return;
		}
	    }
	}
    if ($attr and defined $attr->{f_dir}) {
	my $f_dir = $attr->{f_dir};
	$^O eq "MSWin32" and $f_dir =~ s{^([a-zA-Z])\\+:}{$1:};
	unless (-d $f_dir) {
	    my $msg = "No such directory '$attr->{f_dir}";
	    $drh->set_err (2, $msg);
	    return;
	    }
	}

    return $drh->SUPER::connect ($dbname, $user, $auth, $attr);
    } # connect

sub disconnect_all {
    } # disconnect_all

sub DESTROY {
    undef;
    } # DESTROY

# ====== DATABASE ==============================================================

package DBD::File::db;

use strict;
use warnings;

use Carp;
require File::Spec;
require Cwd;
use Scalar::Util qw( refaddr ); # in CORE since 5.7.3

our @ISA           = qw( DBI::DBD::SqlEngine::db );
our $imp_data_size = 0;

sub data_sources {
    my ($dbh, $attr, @other) = @_;
    ref ($attr) eq "HASH" or $attr = {};
    exists $attr->{f_dir}        or $attr->{f_dir}        = $dbh->{f_dir};
    exists $attr->{f_dir_search} or $attr->{f_dir_search} = $dbh->{f_dir_search};
    return $dbh->SUPER::data_sources ($attr, @other);
    } # data_source

sub set_versions {
    my $dbh = shift;
    $dbh->{f_version} = $DBD::File::VERSION;

lib/DBD/File.pm  view on Meta::CPAN

	binmode $meta->{fh} or croak "Failed to set binary mode on $fn: $!";
	}
    } # apply_encoding

sub open_data {
    my ($self, $meta, $attrs, $flags) = @_;

    $flags->{dropMode} and croak "Can't drop a table in stream";
    my $fn = "file handle " . fileno ($meta->{f_file});

    if ($flags->{createMode} || $flags->{lockMode}) {
	$meta->{fh} = IO::Handle->new_from_fd (fileno ($meta->{f_file}), "w+") or
	    croak "Cannot open $fn for writing: $! (" . ($!+0) . ")";
	}
    else {
	$meta->{fh} = IO::Handle->new_from_fd (fileno ($meta->{f_file}), "r") or
	    croak "Cannot open $fn for reading: $! (" . ($!+0) . ")";
	}

    if ($meta->{fh}) {
	$self->apply_encoding ($meta, $fn);
	} # have $meta->{$fh}

    if ($self->can_flock && $meta->{fh}) {
	my $lm = defined $flags->{f_lock}
		      && $flags->{f_lock} =~ m/^[012]$/
		       ? $flags->{f_lock}
		       : $flags->{lockMode} ? 2 : 1;
	if ($lm == 2) {
	    flock $meta->{fh}, 2 or croak "Cannot obtain exclusive lock on $fn: $!";
	    }
	elsif ($lm == 1) {
	    flock $meta->{fh}, 1 or croak "Cannot obtain shared lock on $fn: $!";
	    }
	# $lm = 0 is forced no locking at all
	}
    } # open_data

sub can_flock { $locking }

package DBD::File::DataSource::File;

use strict;
use warnings;

our @ISA = "DBD::File::DataSource::Stream";

use Carp;
require List::Util;

my $fn_any_ext_regex = qr/\.[^.]*/;

sub complete_table_name {
    my ($self, $meta, $file, $respect_case, $file_is_table) = @_;

    $file eq "." || $file eq ".."	and return; # XXX would break a possible DBD::Dir

    # XXX now called without proving f_fqfn first ...
    my ($ext, $req) = ("", 0);
    if ($meta->{f_ext}) {
	($ext, my $opt) = split m{/}, $meta->{f_ext};
	if ($ext && $opt) {
	    $opt =~ m/r/i and $req = 1;
	    }
	}

    # (my $tbl = $file) =~ s/\Q$ext\E$//i;
    my ($tbl, $basename, $dir, $fn_ext, $user_spec_file, $searchdir);
    if ($file_is_table and defined $meta->{f_file}) {
	$tbl = $file;
	($basename, $dir, $fn_ext) = File::Basename::fileparse ($meta->{f_file}, $fn_any_ext_regex);
	$file = $basename . $fn_ext;
	$user_spec_file = 1;
	}
    else {
	($basename, $dir, undef) = File::Basename::fileparse ($file, qr{\Q$ext\E});
	# $dir is returned with trailing (back)slash. We just need to check
	# if it is ".", "./", or ".\" or "[]" (VMS)
	if ($dir =~ m{^(?:[.][/\\]?|\[\])$} && ref $meta->{f_dir_search} eq "ARRAY") {
	    foreach my $d ($meta->{f_dir}, @{$meta->{f_dir_search}}) {
		my $f = File::Spec->catdir ($d, $file);
		-f $f or next;
		$searchdir = Cwd::abs_path ($d);
		$dir = "";
		last;
		}
	    }
	$file = $tbl = $basename;
	$user_spec_file = 0;
	}

    if (!$respect_case and $meta->{sql_identifier_case} == 1) { # XXX SQL_IC_UPPER
        $basename = uc $basename;
        $tbl = uc $tbl;
	}
    elsif (!$respect_case and $meta->{sql_identifier_case} == 2) { # XXX SQL_IC_LOWER
        $basename = lc $basename;
        $tbl = lc $tbl;
	}

    unless (defined $searchdir) {
	$searchdir = File::Spec->file_name_is_absolute ($dir)
	    ? ($dir =~ s{/$}{}, $dir)
	    : Cwd::abs_path (File::Spec->catdir ($meta->{f_dir}, $dir));
	}
    -d $searchdir or
	croak "-d $searchdir: $!";

    # If the file location is outside the current folder,
    # its absolute path should be in ($f_dir, @f_dir_search)
    # Note this triggers only when *used*, not at definition time
    #   $dbh->{csv_tables}{foo}{file} = "/out/side/scope/foo.csv"; # OK
    #   $dbh->do ("create table foo (c char)"); # FAIL
    {   my @sd = map { Cwd::abs_path ($_) } $meta->{f_dir}, @{$meta->{f_dir_search} || []};
	unless (List::Util::first { $_ eq $searchdir } @sd) {
	    croak "Using data files in $searchdir is unsafe and not allowed.\nUse f_dir or f_dir_search.\n";
	    }
	}

    $searchdir eq $meta->{f_dir} and
	$dir = "";



( run in 0.316 second using v1.01-cache-2.11-cpan-71847e10f99 )