FlatFile-DataStore
view release on metacpan or search on metacpan
lib/FlatFile/DataStore.pm view on Meta::CPAN
}
my $datafile = $self->which_datafile( $fnum );
my $datafh = $self->locked_for_read( $datafile );
my $preamble = $self->read_preamble( $datafh, $seekpos );
# if we got the record via key file, check that preambles match
if( $keystring ) {
croak qq/Mismatch: "$preamble" ne "$keystring"/
if $preamble ne $keystring;
}
# if not via key file, we still need the record length
else {
my $parms = $self->burst_preamble( $preamble );
$reclen = $parms->{'reclen'};
}
$seekpos += $self->preamblelen; # skip to record data
sysseek $datafh, $seekpos, 0 or
croak qq/Can't seek to $seekpos in $datafile: $!/;
return $datafh, $seekpos, $reclen;
}
#---------------------------------------------------------------------
=head2 update( $record )
or update( { string => $preamble_string, data => \$record_data, user => $user_data } )
or update( { preamble => $preamble_obj, data => \$record_data, user => $user_data } )
or update( { record => $record_obj
[, preamble => $preamble_obj]
[, string => $preamble_string]
[, data => \$record_data]
[, user => $user_data] } )
Updates a record. If the parameter is a record object,
the preamble, record data, and user data will be gotten
from it. Otherwise, if the parameter is a hash reference,
the expected keys are:
- record => FlatFile::DataStore::Record object
- preamble => FlatFile::DataStore::Preamble object
- string => a preamble string (the string attribute of a preamble object)
- data => string or scalar reference
- user => string
If no record is passed, 'preamble' (or 'string'), 'data', and
'user' are required. Otherwise, if a record is passed, the
preamble, record data and user data will be gotten from it
unless any of them are explicitly provided.
Returns a Flatfile::DataStore::Record object.
=cut
sub update {
my $self = shift;
my( $data_ref, $user_data, $pr_obj ) = $self->normalize_parms( @_ );
croak qq/Must have at least a previous preamble for update/
unless $pr_obj;
my $prevnext = $self->prevfnum; # boolean
my $prevpreamble = $pr_obj->string;
my $keyint = $pr_obj->keynum;
my $prevind = $pr_obj->indicator;
my $prevfnum = $pr_obj->thisfnum;
my $prevseek = $pr_obj->thisseek;
# update is okay for these:
my $create = $self->crud->{'create'};
my $update = $self->crud->{'update'};
my $delete = $self->crud->{'delete'};
croak qq/update not allowed: $prevind/
unless $prevind =~ /[\Q$create$update$delete\E]/;
# get keyfile
# need to lock files before getting seek positions
# want to lock keyfile before datafile
my( $keyfile, $keyfint ) = $self->keyfile( $keyint );
my $keyfh = $self->locked_for_write( $keyfile );
my $keyseek = $self->keyseek( $keyint );
my $try = $self->read_preamble( $keyfh, $keyseek );
croak qq/Mismatch: "$try" ne "$prevpreamble"/ unless $try eq $prevpreamble;
# get datafile ($datafnum may increment)
my $top_toc = $self->new_toc( { int => 0 } );
my $datafnum = int2base $top_toc->datafnum, $self->fnumbase, $self->fnumlen;
my $reclen = length $$data_ref;
my $datafile;
( $datafile, $datafnum ) = $self->datafile( $datafnum, $reclen );
my $datafh = $self->locked_for_write( $datafile );
my $dataseek = -s $datafile; # seekpos into datafile
# get next transaction number
my $transint = $self->nexttransnum( $top_toc );
# make new record
my $preamble_hash = {
indicator => $update,
transind => $update,
date => now( $self->dateformat ),
transnum => $transint,
keynum => $keyint,
reclen => $reclen,
thisfnum => $datafnum,
thisseek => $dataseek,
user => $user_data,
};
if( $prevnext ) {
lib/FlatFile/DataStore.pm view on Meta::CPAN
# and seek pos for reading a record.
#
# Private method.
#
# =cut
#
sub burst_preamble {
my( $self, $string ) = @_;
croak qq/No preamble to burst/ unless $string;
my @fields = $string =~ $self->regx;
croak qq/Something is wrong with preamble: $string/ unless @fields;
my %parms;
my $i;
for( $self->specs ) { # specs() returns an array of hashrefs
my( $key, $aref ) = %$_;
my( $pos, $len, $parm ) = @$aref;
my $field = $fields[ $i++ ];
for( $key ) {
if( /indicator|transind|date/ ) {
$parms{ $key } = $field;
}
elsif( /user/ ) {
my $try = $field;
$try =~ s/\s+$//;
$parms{ $key } = $try;
}
elsif( /fnum/ ) {
next if $field =~ /^-+$/;
$parms{ $key } = $field;
}
else {
next if $field =~ /^-+$/;
$parms{ $key } = base2int( $field, $parm );
}
}
}
return \%parms;
}
#---------------------------------------------------------------------
#
# =head2 update_preamble()
#
# Called by update() and delete() to flag old recs.
#
# Takes a preamble string and a hash ref of values to change, and
# returns a new preamble string with those values changed.
#
# Will croak if the new preamble does not match the regx attribute.
#
# Private method.
#
# =cut
#
sub update_preamble {
my( $self, $preamble, $parms ) = @_;
my $omap = $self->specs;
for( keys %$parms ) {
my $value = $parms->{ $_ };
my $specs = omap_get_values( $omap, $_ );
croak qq/Unrecognized field: $_/ unless $specs;
my( $pos, $len, $parm ) = @{$specs};
my $try;
if( /indicator|transind|date|user/ ) {
$try = sprintf "%-${len}s", $value;
croak qq/Invalid value for $_: $try/
unless $try =~ $Ascii_chars;
}
# the fnums should be in their base form already
elsif( /fnum/ ) {
$try = sprintf "%0${len}s", $value;
}
else {
$try = int2base $value, $parm, $len;
}
croak qq/Value of $_ too long: $try/ if length $try > $len;
substr $preamble, $pos, $len, $try; # update the field
}
croak qq/Something is wrong with preamble: $preamble/
unless $preamble =~ $self->regx;
return $preamble;
}
#---------------------------------------------------------------------
# file read/write:
#---------------------------------------------------------------------
#---------------------------------------------------------------------
#
# =head2 locked_for_read()
#
# Takes a file name, opens it for input, locks it, sets binmode, and
# returns the open file handle.
#
# Private method.
#
# =cut
#
sub locked_for_read {
my( $self, $file ) = @_;
untaint path => $file;
my $fh;
( run in 1.704 second using v1.01-cache-2.11-cpan-63c85eba8c4 )