Boulder::Store(3pm)
๐พ Boulder::Store โ the vibe โจ
okay so bestie, welcome to Boulder::Store, aka Simple persistent storage for Stone tag/value objects ๐ซถ she's a Perl module (the 3pm in Boulder::Store(3pm) is just her little manual-page badge, iykyk) and she is so ready to hold your Stones for you. literally obsessed. ๐
โจ SYNOPSIS โจ
Boulder:Store;
my $store=new Boulder::Store('test.db',1);
my $s = new Stone (Name=>'george',
Age=>23,
Sex=>M,
Address=>{
Street=>'29 Rockland drive',
Town=>'Fort Washington',
ZIP=>'77777'
}
);
$store->put($s);
$store->put(new Stone(Name=>'fred',
Age=>30,
Sex=>M,
Address=>{
Street=>'19 Gravel Path',
Town=>'Bedrock',
ZIP=>'12345'},
Phone=>{
Day=>'111-1111',
Eve=>'222-2222'
}
));
$store->put(new Stone(Name=>'andrew',
Age=>18,
Sex=>M));
$store->add_index('Name');
my $stone = $store->get(0);
print "name = ",$stone->Name;
๐ธ DESCRIPTION ๐ธ
so Boulder::Store gives you persistent storage for Boulder objects using a simple DB_File implementation. no cap, it's simple on purpose. ๐
to use her you need to have Berkeley db installed (she also answers to libdb, same girl different name ๐) plus the Perl DB_File module. if you don't already have Berkeley db, go peek at the DB_File package โ it's got the details on how to get it. ๐ฆ
she also comes with an unsophisticated query mechanism that takes advantage of indexes you specify. and bestie, the way that despite her lack of sophistication the query system is often very helpful? iconic. she's not fancy, she just works. periodt. โจ
๐ CLASS METHODS ๐
$store = Boulder::Store->new("database/path",$writable)โ thenew()method creates a brand newBoulder::Storeobject and pairs her up with the database file you hand her in the first parameter. and yes,undefis a totally valid pathname โ in that case all the methods still work, the data just isn't stored anywhere (ghost store, main character energy ๐ซฅ). the second parameter should be a true value if you want the database opened for writing; otherwise it's opened read only. ๐
bc the underlying storage implementation is not multi-user, only one process can have the database for writing at a time. there's an fcntl()-based locking mechanism doing the heavy lifting, giving a process that has the database open for writing exclusive access to it. this also stops the database from being opened for reading while another process is writing to it โ and that's a good thing, we love data integrity in this house. ๐ multiple processes can open the database read only at the same time, no drama.
physically, the data lives in a human-readable file with the extension ".data". cute and legible, we stan. ๐
๐ฆ OBJECT METHODS ๐ฆ
$stone = $store->read_record(@taglist)โ the semantics of this call are exactly the same as inBoulder::Stream, fr fr. Stones come back in sequential order, starting with the first record. on top of their built-in tags, every stone you get back from this call carries an extra tag called"record_no"โ that's the zero-based record number of the stone in the database. wanna start over from the beginning? use thereset()method. ๐
if you call read_record() in an array context, it returns a list of all the stones in the database that contain one or more of the tags you provided. slay.
$stone = $store->write_record($stone [,$index])โ same semantics asBoulder::Stream. a stone gets appended to the end of the database. if it works, this call returns the record number of the new entry. ๐ and if you pass that optional second parameter, you get to control where the stone lands: a positive numeric index writes the stone into the database at that position, and a value of-1uses the Stone's internal record number (if it has one) to figure out where to place it. ๐$stone = $store->get($record_no)โ this is your random access to the database. give her a record number and this call returns the stone stored at that position. no waiting, just grab and go. โก$record_number = $store->put($stone,$record_no)โ and this is the random write. give her a record number and this call stores the stone at the indicated position, replacing whatever was there before. out with the old, in with the new. ๐
if you don't provide a record number, this call will look for a 'record_no' tag inside the stone itself and put it back in that position. so you can pull a stone out of the database, modify it, and put it right back without ever stressing about its record number. iconic behaviour honestly. if no record is found in the stone, then the effect is identical to write_record(). ๐ช
the record number of the inserted stone is returned from this call, or -1 if an error occurred. ๐
$store->delete($stone),Boulder::Store::delete($record_no)โ these delete a stone from the database. you can hand her either the record number or a stone containing the'record_no'tag.
โ ๏ธ warning bestie: if the database is heavily indexed, deletes can be time-consuming โ it requires the index to be brought back into synch. ๐ญ
$record_count = $store->length()โ this returns the length of the database, in records. simple. clean. perfect. โจ$store->reset()โ this resets the database, nullifying any queries in effect and causingread_record()to begin fetching stones from the first record again. fresh start energy. ๐$store->query(%query_array)โ this creates a query on the database, used for selecting stones inread_record(). the query is an associative array, and three types of key/value pairs are allowed:
(1) $index=>$value โ tells Boulder::Store to look for stones containing the specified tags in which the tag's value (determined by the Stone index() method) exactly matches the provided value. example:
$db->query('STS.left_primer.length'=>30);
only the non-bracketed forms of the index string are allowed โ and yeah, this is probably a bug... ๐ if the tag path was declared to be an index, then this search will be fast. otherwise Boulder::Store has to iterate over every record in the database. ๐ฎโ๐จ
(2) EVAL=>'expression' โ tells Boulder::Store to look for stones in which the provided expression evaluates to true. when the expression is evaluated, the variable $s gets set to the current record's stone. and as a shortcut, you can use "<index.string>" as shorthand for "$s->index('index.string')". ๐ง
(3) EVAL=>['expression1','expression2','expression3'...] โ this lets you provide a whole bunch of expressions, and it's exactly equivalent to:
EVAL=>'(expression1) && (expression2) && (expression3)'
you can mix query types in the parameter you give to query(). for example, here's how to look up all stones in which the sex is male and the age is greater than 30:
$db->query('sex'=>'M',EVAL=>'<age> > 30');
when a query is in effect, read_record() returns only Stones that satisfy the query. in an array context, read_record() returns a list of all Stones that satisfy the query. and when no more satisfactory Stones are found, read_record() returns undef until a new query is entered or reset() is called. she's patient like that. ๐
$store->add_index(@indices)โ declare one or more tag paths to be part of a fast index.read_record()will take advantage of this record when processing queries. example:
$db->add_index('age','sex','person.pets');
you can add indexes any time you like โ when the database is first created, or later. but there's a trade off: write_record(), put(), and other data-modifying calls will become slower as more indexes are added. ๐
the index is stored in an external file with the extension ".index". an index file is created even if you haven't indexed any tags. noted. ๐
$store->reindex_all()โ call this if the index gets screwed up (or lost). it rebuilds it from scratch. glow-up, but for indexes. โจ
๐ญ CAVEATS ๐ญ
Boulder::Store makes heavy use of the flock() call in order to avoid corruption of DB_File databases when multiple processes try to write simultaneously.
โ ๏ธ but here's the tea:flock()may not work correctly across NFS mounts, particularly on Linux machines that are not running therpc.lockddaemon. please confirm that yourflock()works across NFS before attempting to useBoulder::Store. and if thestore.ttest hangs during testing, this is the likely culprit. ๐
๐ AUTHOR ๐
Lincoln D. Stein <lstein@cshl.org>, Cold Spring Harbor Laboratory, Cold Spring Harbor, NY.
this module can be used and distributed on the same terms as Perl itself. ๐ซถ
๐ SEE ALSO ๐
BoulderBoulder::StreamStone
and that's the whole page bestie!! ๐ this one shipped with perl v5.34.0 and is dated 2022-06-08 โ so go forth, store your Stones, and remember: one writer at a time, and check your flock() across NFS. mwah. ๐
โจ๐ธ
๐ฆ where this page comes from
- debian bookworm: libboulder-perl 1.30-6 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libboulder-perl 1.30-6 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libboulder-perl 1.30-6 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libboulder-perl 1.30-6 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libboulder-perl 1.30-6 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms