Database-Abstraction

 view release on metacpan or  search on metacpan

lib/Database/Abstraction.pm  view on Meta::CPAN

						[a-zA-Z0-9]       # username: first char must be alnum
						[a-zA-Z0-9._-]*   # username: rest may include dots and hyphens
						\@                # literal at-sign; \@ prevents array interpolation
					)?
					[a-zA-Z0-9:]          # first char of host or IP; colon allows IPv6
					[a-zA-Z0-9._:-]*      # rest: hostname, dotted IPv4, or IPv6 hex groups
				\z/x;
		}
		if(defined $src->{'url'}) {
			croak("$class: unsafe url '$src->{url}'")
				unless $src->{'url'} =~ /\Ahttps?:\/\//i;
		}
		if(defined $src->{'table'}) {
			croak("$class: unsafe table name '$src->{table}'")
				unless $src->{'table'} =~ $SAFE_QUALIFIED;
		}
	}

	# Defaults are set first so that %args keys override them
	return bless {
		no_entry => 0,
		no_fixate => 0,
		id => 'entry',
		cache_duration => '1 hour',
		max_slurp_size => DEFAULT_MAX_SLURP_SIZE,
		%defaults,
		%args,
	}, $class;
}

=head2	set_logger

Sets the class, code reference, or file that will be used for logging.

=cut

sub set_logger
{
	my $self = shift;
	my $params = Params::Get::get_params('logger', @_);

	if(my $logger = $params->{'logger'}) {
		if(Scalar::Util::blessed($logger)) {
			$self->{'logger'} = $logger;
		} else {
			$self->{'logger'} = Log::Abstraction->new($logger);
		}
		return $self;
	}
	Carp::croak('Usage: set_logger(logger => $logger)')
}

# Open the database connection based on the specified type (e.g., SQLite, CSV).
# Read the data into memory or establish a connection to the database file.
# column_names allows the column names to be overridden on CSV files

sub _open :Protected
{
	# Enforce that _open is only reachable from within this class hierarchy;
	# caller() returns the calling package name as a plain string.
	do { my $c = (caller)[0]; Carp::croak('Illegal Operation: _open may only be called within ', __PACKAGE__) unless $c && $c->isa(__PACKAGE__) };

	my $self = shift;
	my $params = Params::Get::get_params(undef, @_);

	$params->{'sep_char'} ||= $self->{'sep_char'} ? $self->{'sep_char'} : '!';
	my $max_slurp_size = $params->{'max_slurp_size'} || $self->{'max_slurp_size'};

	my $table = $self->{'table'} || ref($self);
	$table =~ s/\A.*:://;

	$self->_trace(ref($self), ": _open $table");

	return if($self->{$table});

	# Read in the database
	my $dbh;

	# DSN-based connection bypasses file detection entirely
	if(my $dsn = $self->{'dsn'} || $defaults{'dsn'}) {
		my $dialect = 'generic';
		if    ($dsn =~ /^dbi:SQLite:/i) { $dialect = 'sqlite'   }
		elsif ($dsn =~ /^dbi:Pg:/i)     { $dialect = 'postgres' }
		elsif ($dsn =~ /^dbi:mysql:/i)  { $dialect = 'mysql'    }
		$self->{'_dialect'} = $dialect;

		$dbh = DBI->connect(
			$dsn,
			$self->{'username'},
			$self->{'password'},
			{ RaiseError => 1, AutoCommit => 1 },
		) or Carp::croak(ref($self), ": cannot connect: $DBI::errstr");

		if($dialect eq 'sqlite') {
			$dbh->do('PRAGMA synchronous = OFF');
			$dbh->do('PRAGMA cache_size = -4096');
			$dbh->do('PRAGMA journal_mode = OFF');
			$dbh->do('PRAGMA temp_store = MEMORY');
			$dbh->do('PRAGMA mmap_size = 1048576');
			$dbh->sqlite_busy_timeout(100000);
		}

		$self->{'type'} = 'DBI';
		$self->{$table} = $dbh;
		$self->{'_updated'} = time();
		return $self;
	}

	# URL-based HTML table backend — lazy-loads LWP::UserAgent and HTML::TableExtract.
	# Both modules are optional; they are not required for file-based backends.
	if(my $url = $self->{'url'} || $defaults{'url'}) {
		require LWP::UserAgent::Cached;
		require HTML::TableExtract;

		my $ua = $self->{ua} // LWP::UserAgent::Cached->new(timeout => 30, agent => __PACKAGE__ . '/' . $VERSION);
		$ua->env_proxy(1);
		my $response = $ua->get($url);
		Carp::croak(ref($self), ": cannot fetch '$url': ", $response->status_line)
			unless $response->is_success;

		my $te = HTML::TableExtract->new();



( run in 1.556 second using v1.01-cache-2.11-cpan-aadc1410aed )