NAME
ObjStore - Perl Extension For ObjectStore
OODBMS
SYNOPSIS
Enjoy perl? Sick of SQL? Ready to try something new?
DESCRIPTION
ObjectStore
is the market leader in object-oriented databases http://www.odi.com (NASDAQ: ODIS). They use a unique Cache-Forward Architecture to make persistent data available in literally the most efficient manner possible.
Prior to this joining of forces,
ObjectStore
was too radical a design decision for many applications.Perl5 did not have a simple way of storing complex data persistently.
Now there is an easy way to build database applications, especially if you are concerned about preserving your ideals of encapsulation. (See below!)
PREREQUISITES
ObjectStore
OODBMS (see http://www.odi.com; evaluations are available!)Perl 5.004_04 (or perl 5.005, once released)
ExtUtils::ExCxx (only needed on Digital UNIX, HP (5.x), OS/2 (3.x), or MS Windows NT/95; send email)
TUTORIAL
One way to get started is to win the tutorial. See ObjStore::Tutorial
or, if you just can't wait, run the following code and start molding it right now:
Quick Start!
#!/usr/local/bin/perl -w
use ObjStore;
use ObjStore::Config; #for TMP_DBDIR
my $db = ObjStore::open(TMP_DBDIR."/myjunk", 'update', 0666);
begin 'update', sub {
# $junk can be any arbitrary non-circular structure up to 2GB in size
my $junk = {
pork => [1,2,3],
chicken => [1,2,3],
wheat => [1,2,3],
trials => 0,
};
$db->root("junk", sub { $junk }); #store it, if it's not there already
my $pjunk = $db->root("junk"); #fetch it
$pjunk->{trials} ++; #update something
ObjStore::peek($db); #dump to stdout
};
TOOLS
ospeek
Prints out databases in a style similar to Data::Dumper.
posh
Like
sh
/csh
, except that you can change directory into your database and walk around the data from the inside. You can also invoke methods on your objects or write custom reports (in perl, of course :-).osperlserver
Provides remote method invokation, database job scheduling, and server-object collaboration services. Completely generic.
WHAT IS PERSISTENT PERL?
It's just like normal perl, except that you can create data that doesn't go away when your program exits. This more permenant data lives in files or raw disk partitions that are divided into databases. And databases are comprised of...
Segments
Segments dynamically resize from very small to very big. You should split your data into lots segments when it makes sense. Segments improve locality and can be a unit of locking or caching.
When you create a database object you must specify the segment in which it is to be allocated. All objects use the form 'new $class($near, ...)'
. You may pass any persistent object (or database, or segment) in place of $near and the new object will be created appropriately!
Hashes
The following code snippet creates a persistent hash reference with an expected cardinality of ten elements.
my $h7 = ObjStore::HV->new($store, 10);
An array representation is used for low cardinalities. Arrays do not scale well, but they do afford a pleasingly compact representation. ObjectStore
's os_Dictionary
is transparently used for large cardinalities [MAYCHANGE].
Persistent data structures can be built with the normal perl construction:
$h7->{foo} = { 'fwaz'=> { 1=>'blort', 'snorf'=>3 }, b=>'ouph' };
Or the equally effective, but unbearibly tedious:
my $h1 = $dict->{foo} ||= ObjStore::HV->new($dict);
my $h2 = $h1->{fwaz} ||= ObjStore::HV->new($h1);
$h2->{1}='blort';
$h2->{snorf}=3;
$h1->{b}='ouph';
Perl saves us again! (Relief.)
Arrays
The following code snippet creates a persistent array reference with an expected cardinality of ten elements.
my $a7 = ObjStore::AV->new($store, 10);
Complete array support is available now, but you must must have at least perl 5.004_57. If you don't, none of the usually array operations are supported except fetch and store. (Actually push, pop, shift and unshift might be available but are undocumented.) At least the following works:
$a7->[1] = [1,2,3,[4,5],6];
References
You can generate a reference to any persistent object with the method new_ref($segment)
. Since refcnts are not updated remotely, refs are the safest way to refer across databases. They are also designed to be allocated transiently.
$r->open($how); # attempts to open the focus' database
$yes = $r->deleted; # is the focus deleted?
$f = $r->focus; # returns the focus of the ref
$str = $r->dump; # the ref as a string
Be aware that references can return garbage if they are not open. You will need to open
them explicitly (see ObjStore::Ref::POSH_ENTER
). Also note that references use significantly more memory than pointers. (Look up os_reference_protected
in the ODI FAQ.)
Unsafe, unprotected references are also available:
$r = $o->new_ref('transient', 'unsafe');
Care must to taken that these hard reference do not point to objects that have already been deleted. SEGV
or garbled data can result.
Fortunately, it is always safe to use hard references when they are used to merely to avoid circular references within a single database. A nice trick:
my $o = ObjStore::HV->new($db);
$$o{SELF} = $o->new_ref($o,'hard');
Then, use this single reference to point back to the top of the data structure (but only from below to the top!).
Cursors
All containers have a method, new_cursor($near)
, that creates a persistent cursor for the given container. The following methods are available.
$cs->focus(); # returns the cursor's collection
$cs->moveto($pos); # seek to the nth element
($k,$v) = $cs->at; # returns the current element
($k,$v) = $cs->each(1); # returns the next element
First-class cursors for arrays & hashes are incomplete and under construction. If you can avoid them, do so! (Array cursors might be depreciated entirely.)
Array cursors return (index,value) pairs. Hash cursors return (key,value) pairs. All cursors return the empty list () when no more elements are available.
For hashes & arrays, you should not assume the order of iteration will follow any particular pattern (but it probably will).
If you change membership of a collection while you're iterating through it, something could break, so don't.
Indices
SQL has this fantastic facility called indices. ObjStore
does too!
my $nx = ObjStore::Index->new($near);
$nx->configure(unique => 1, path=>"name");
$nx->add({name=> 'Square'}, {name=> 'Round'}, {name=> 'Triangular'});
my $c = $nx->new_cursor;
$c->seek('T');
$c->step(1);
warn $c->at()->{name}; # Triangular
Index cursors are a lot more powerful than hash or array cursors. Here are the available methods:
$c->focus();
$c->moveto();
$c->step($delta);
$c->seek(@keys);
my $pos = $c->pos();
my @keys = $c->keys();
my $v = $c->at();
my $v = $c->each($delta);
Where the following invariants hold:
$c->moveto($c->pos() + $delta) is $c->step($delta)
$c->each($delta) is { $c->step($delta); $c->at(); }
Be aware that index cursors may only be used by one thread at a time. Therefore, it is not particularly useful to store pre-created cursors in a database. Just to create them transiently when needed.
To eliminate the possibility of indices becoming out-of-sync with actual data, keys are marked read-only as they are indexed. (Another scheme for keeping indices up-to-date is to use os_backptr. This scheme is not supported because it has considerable memory overhead (12-bytes per record!) and provides little benefit beyond our read-only schemes.)
For example:
my $category = { name => 'Bath Toy' };
my $row = { name => 'Rubber Ducky', category => $category };
$index->configure(path => 'category/name, name', excl => 1);
$index->add($row);
$row->{category}{name} = 'Beach Toy'; #READONLY exception
$row->{name} = "Rubber Doggie"; #READONLY exception
$row->{owner} = $bob; #ok
The first key is the category's name. The second key is the row's name. This is one more detail to be aware of. There are two types of index read-only modes: exclusive and shared. Exclusive mode is simple. This is the default. In exclusive mode, you can only index a key once. If this is not sufficient, shared mode can be used. In shared mode, multiple indices can index the same keys. However, all shared keys remain read-only until the record is removed from all shared indices. Shared indices also impose a slight performance penalty compared to exclusive indices.
Indices should not be used unless other data structures prove inadequate. Hashes and arrays are easier in most cases. Indices are best suited to very large datasets that must be searchable in a variety of ways. If you can just iterate through every record, indices probably aren't worth the trouble.
And Access Paths (Oh My!)
If you cross your eyes, you will see an array of references or cursors as an access path. Two simple implementations are provided for manipulating access paths:
ObjStore::Path::Ref # access path composed of refs
ObjStore::Path::Cursor # cursor based access path (EXPERIMENTAL)
See the source code for details.
DATABASE DESIGN
The best design is to be flexible!
ospeek [-all] [-addr] [-refcnt] [-raw] <database>
While there is no formalized schema for a perl database, the ospeek
utility generates a sample of data content and structure. ospeek
never outputs more than a short summary, without regard to the size of your database.
You can also get the same thing from within perl:
ObjStore::peek($whatever);
Wait! No Schema?! How Can This Scale? Wont I get lost in a morass of disorganization?
How can a relational database scale?! When you write down a central schema, you are violating the principle of encapsulation. This is dumb. None of the usual database management operations require a central schema. Why create artificial dependencies between your classes when you can avoid it?
The Theory of Lazy Evolution
When you practice lazy evolution, you avoid changing data layouts in favor of making queries smarter. That's what it means. Since data usually out-bulks code, this is the correct trade. And since there isn't a artificial split between the implementation language and some goofy database language, schema evolution is reduced to the same problem as source code compatibility. (Database are essentially just a rich API.)
Now, I'm not saying that data layouts never have to change. When thinking about data, the first thing to consider is how to partition your data into manageable chunks. Fortunately, databases interoperate and can be used simultaniously. Therefore, it is entirely unnecessary to store all data in one database. By splitting your data into logical chunks, you should be able to minimize the impact of structural changes. (This is the same problem as designing good APIs and adding new features while maintaining backward compatibility.)
The way you split up your database generally depends on three things (wisdom gleened from developing a real-time trading system):
LOCALITY OF REFERENCE
The less data you have, the faster you can access it. Aim for a perfect awareness of nothing.
REAL-TIME WRITERS
It is usually good design to create a separate database for each group of real-time writers. Readers of real-time data can open databases in mvcc mode and writers will never encounter lock contention (which is a major performance win).
COUPLING TIGHTNESS
Loosely coupled data can easily (and should) be split into multiple databases. (The most exciting data is tightly coupled; keep this data close together. :-)
Additional considerations can be found in the ObjectStore
documentation, but that's about it. Don't over-complicate things; this isn't a relational database, remember? The power and simplicity is hard to describe because there's just not much to it. (Just the absolute minimum to satisfy every requirement. :-)
(For extra integrity verification, see the iscorrupt
method.)
RDBMS Emulation
Un-structured perl databases are probably under-constrained for most applications. Fortunately, RDBMS style tables have been adapted, adopted, and included with this package. While they are a little different from traditional tables, with a few doses of Prozac relational developers should feel right at home. See ObjStore::Index
and ObjStore::Table3
.
API REFERENCE
Fortunately, you will probably not need to use most of the API. It is exhibited here mainly to make it seem like this extension has a difficult and steep learning curve. In general, the API mostly mirrors the C++
API. Refer to the ObjectStore
documentation for exact symantics. The API for ::UNIVERSAL
is probably of most interest to ex-C++
developers. If you need an API that isn't available in OS/Perl, send your request to the mailing list (see the README). If you are just getting started, skip to the next section.
ObjStore
$db = ObjStore::open($pathname, $read_only, $mode);
Also see
ObjStore::HV::Database
&ObjStore::Table3::Database
.$name = ObjStore::release_name()
$major = ObjStore::release_major()
$minor = ObjStore::release_minor()
$maintenance = ObjStore::release_maintenance()
$yes = ObjStore::network_servers_available();
$num = ObjStore::return_all_pages();
$size = ObjStore::get_page_size();
@Servers = ObjStore::get_all_servers();
$in_abort = ObjStore::abort_in_progress();
$num = ObjStore::get_n_databases();
::Server
$name = $s->get_host_name();
$is_broken = $s->connection_is_broken();
$s->disconnect();
$s->reconnect();
@Databases = $s->get_databases();
::Database
See ObjStore::HV::Database
, ObjStore::Table3::Database
$open_mode = $db->is_open();
$s = $db->create_segment($comment);
$value = $db->root($root_name => sub{ $new_value });
This is the recommended API for roots. If the given root is not found, creates a new one. Returns the root's current value.
In general, you should try to avoid using roots. Roots have an unnatural API for perl: hashes can nest but roots cannot. Roots are like a necessary but annoying accounting detail. It is much better practice to use or inherit
ObjStore::HV::Database
orObjStore::ServerDB
.$s = $db->get_segment($segment_number);
Note that this method (correctly) never returns an error. The only way to know which segments are actually created in a database is to iterate through
get_all_segments
.@Segments = $db->get_all_segments();
$db->close();
$db->destroy();
$db->get_default_segment_size();
$db->get_sector_size();
$db->size();
$db->size_in_sectors();
$ctime = $db->time_created();
$can_write = $db->is_writable();
$db->set_fetch_policy(policy[, blocksize]);
Policy can be one of
segment
,page
, orstream
.$db->set_lock_whole_segment(policy);
Policy can be one of
as_used
,read
, orwrite
.@Roots = $db->get_all_roots();
$root = $db->create_root($root_name);
$root = $db->find_root($root_name);
$db->destroy_root($root_name);
Destroys the root with the given name if it exists.
::Root
$root->get_name();
$root->get_value();
$root->set_value($new_value);
$root->destroy();
::Transaction
ObjectStore
transactions and exceptions are seemlessly integrated into perl. ObjectStore
exceptions cause a die
in perl just as perl exceptions cause a transaction abort.
begin 'update', sub {
$top = $db->root('top');
$top->{abc} = 3;
die "Oops! abc should not change!"; # aborts the transaction
};
There are three types of transactions: read
, update
, and abort_only
. The default is read
. Read transaction are blindingly fast.
begin 'read', sub {
my $var = $db->root('top');
$var->{abc} = 7; # write to $var triggers die(...)
};
(In a read transaction, you are not allowed to modify persistent data.)
$T = ObjStore::Transaction::get_current();
$type = $T->get_type();
$pop = $T->get_parent();
$T->prepare_to_commit();
$yes = $T->is_prepare_to_commit_invoked();
$yes = $T->is_prepare_to_commit_completed();
$ObjStore::TRANSACTION_PRIORITY
ObjStore::set_max_retries($oops);
ObjStore::fatal_exceptions($yes);
my $oops = ObjStore::get_max_retries();
my $yes = ObjStore::is_lock_contention();
my $type = ObjStore::get_lock_status($ref);
my $tm = ObjStore::lock_timeout($rw);
$rw
should be either 'read' or 'write'. Return value of 1 == 1 second. Undef indicates that there is no timeout.ObjStore::lock_timeout($rw, $tm);
Set lock timeouts.
Dynamic transactions are also available. See ObjStore::Process
.
::Segment
$s->set_comment($comment);
$s->destroy();
$size = $s->size();
$yes = $s->is_empty();
$yes = $s->is_deleted();
$num = $s->get_number();
$comment = $s->get_comment();
$s->lock_into_cache();
$s->unlock_from_cache();
$s->set_fetch_policy($policy[, $size]);
Policy can be one of
segment
,page
, orstream
.$s->set_lock_whole_segment($policy);
Policy can be one of
as_used
,read
, orwrite
.
::Notification
Instant, effortless event dispatching for network distributed objects.
ObjStore::subscribe(...);
ObjStore::unsubscribe(...);
set_queue_size($size);
($size, $pending, $overflow) = queue_status();
$fd = _get_fd();
$n = receive([$timeout]);
Receive();
$db = $n->get_database;
$p = $n->focus;
$why = $n->why;
::UNIVERSAL
All persistent objects inherit from ObjStore::UNIVERSAL
.
overload
Stringify, boolean coersion, and equality tests.
os_class
Reports the natural persistent class of the object. All persistent objects must have this class in their
@ISA
tree.rep_class
Reports the representation's class.
bless
bless
stores the current@ISA
tree and theVERSION
of every member of the@ISA
tree. (In addition to the usual meaning of bless.)$o-
isa($baseclass)>Whether the $baseclass was part of the
@ISA
tree (at the moment of blessing). (UNIVERSAL::can
remains un-modified.)$o-
versionof($baseclass)>Returns the version of the $baseclass (at the moment of blessing).
$o-
subscribe()> and$o-
unsubscribe()>These might not be available per-object. Only per-segment or per-database.
$o-
notify($why, $when)>Sends a notification to subscribers. When can be either 'now' or 'commit'. The $when parameter might be required. Also being considered is a means to bunch notifications together for batch send.
$errs = $o->iscorrupt($verbosity_level)
Application specific integrity checking can be achieved by providing an
_iscorrupt
method.$o-
is_evolved>By default, checks version numbers of every class in the old
@ISA
tree. Your$o->evolve()
method should bring stuff up-to-date. You may be able to simply re-bless
the object to accomplish the evolve.Of
database_of
andsegment_of
are always available as methods.posh
posh
behavior can be customized by adding special methods. See the section onposh
.
To make everything seem apparently consistent, ObjStore::Database
(while not really being a storable object) is lavishly special-cased to support most (maybe all!) of the above features.
THE ADVANCED CHAPTER
Performance Check List
The word tuning implies too high a brain-level requirement. Getting performance out of ObjectStore
is not rocket science. On the other hand, you shouldn't think that perl will ever go as fast as optimized C++
. (C++
has to be better for something. :-)
COMPACTNESS
You get 90% of your performance because you can fit your whole working data set into RAM. If you are doing a good job, your un-indexed database should be less than twice the size of it's un-compressed ASCII dump; i.e., less than 2 times expansion. (See the section on representation.)
DO AS MUCH AS POSSIBLE PER TRANSACTION
Transactions, especially update transactions, involve a good deal of setup/cleanup. The more you do per transaction the better.
IS MUTABLE DATA MOSTLY SEPARATED FROM READ-ONLY DATA?
Update transaction commit time is proportional to the amount of data written AND to the number of pages with modifications. Examine your data-flow and data-dependencies.
SEGMENTS
Is your data partitioned into as many segments as reasonable?
AVOID THE NETWORK
Run your program on the same machine as the
ObjectStore
server.DO STUFF IN PARALLEL
If you have an MP machine, you can do reads/updates in parallel (even without kernel threads).
WHERE IS THE REAL BOTTLENECK?
Use
Devel::*Prof
or a similar tools to analyze your program. Make your client-side cache bigger/smaller.SPEED UP PERL
Try using the perl compiler. See http://www.perl.com
LOCKING AND CACHING
Object Design claims that caching and locking parameters also impact performance. (See
os_segment::set_lock_whole_segment
andos_database::set_fetch_policy
.)THROW MONEY AT THE PROBLEM
Get more memory, more CPUs, and upgrade to your network. Or throw in the towel and hire
C++
programmers to redo your project inC++
. (But don't forget to multiple your timeline by a factor of ten. :-)
Transactions Redux
A BRIEF HISTORY OF TRANSACTIONAL MEMORY USAGE
Each time you access a persistent object, a small amount of transient memory is reserved until the transaction completes (to cope with perl scoping rules). For this reason, and for speed, you should avoid repeating long access paths.
++ $at->{bob}{house}{fridge}{beer}; #go to the minimart -- $at->{bob}{house}{fridge}{beer}; #big gulp
Instead, use a lexical variable to keep the fridge door open:
my $fridge = $at->{bob}{house}{fridge}; ++ $fridge->{beer}; -- $fridge->{beer};
NESTING
Nested transactions are supported but transaction modes must match. You can nest reads within reads or updates within updates, but not reads within updates (nor updates within reads). If you need to do a read but you don't care if the parent transaction is an update or not, you can leave the mode unspecified.
sub do_extra_push_ups_in_a_transaction { begin sub { ... # Unspecified mode assumes a read or the same mode as the parent. ... }; }
MIXING WITH EVAL
It is possible to use
eval
within transactions, but you absolutely must not use theObjectStore
API or access any persistent memory.begin('read', sub { ... eval { $db->root('new root' => [1,2,3]); }; ... });
In the above code, the update in a read transaction will cause an exception that crashes perl. This is due to the excellent but imperfect integration of
ObjectStore
exceptions and perl exceptions. I understand how to fix it, just haven't had time. In general, you should global replaceeval
s withbegin
s.RELAXING EXCEPTION SEVERITY
Transactions are always executed within an implicit
eval
. By default, ObjectStore exceptions are fatal (by demand of my co-workers). If you do not want your program to become suicidal when anObjectStore
exception occurs, you should indicate that you want to have control over your own reflexive behavior:ObjStore::fatal_exceptions(0);
This is global to the whole process. If you want control, however, then you have to accept responsibility: after a transaction you absolutely must remember to check the value of
$@
to see if anything went wrong.begin(sub { ... }); die if $@; # Don't forget to remember! Always check for errors!
Is it recommended that any perl extensions explicitly check for errors, just in case exceptions are not automatically re-thrown.
DEADLOCK
Built-in automatic deadlock retry is experimental, not because it doesn't work but because it doesn't make much sense in perl. Since it's so easy to implement yourself, you probably should.
Top level transactions are automatically retried in the case of a deadlock. You can increase the number of retries with
ObjStore::set_max_retries($retries)
. Or if you need to handle deadlocks yourself, you can set the number of retries to zero.(There is not much point to retrying non-top-level transactions because locks are released only at the top-level [OS 4-5.0.*].)
Stargate Mechanics
Create hashes and arrays pre-sized to exactly the right number of slots:
ObjStore::HV->new($near, { key => 'value' }); # 1 slot
ObjStore::AV->new($near, [1..3]); # 3 slots
Or you can address the stargate directly:
my $persistent_junk = ObjStore::translate($near, [1,2,3,{fat=>'dog'}]);
If you want to design your own stargate, you may inspect the default stargate in ObjStore.pm
for inspiration. (Not recommended. :-)
How Can I Rescue Persistent Objects From Oblivion?
All data stored in ObjectStore
is reference counted. This is a fantastically efficient way to manage memory (for most applications). It has very good locality and low overhead. However, as soon as an object's refcnt reaches zero, it is permenantly deleted from the database. You only get one chance to save the object: the NOREFS
method is invoked just prior to deletion. You must create a new persistent reference to it or kiss the object goodbye.
Note that the DESTROY
method is still invoked every time an object becomes unreachable from the current scope. However, contrary to transient objects, this method does not truly preview object destruction. (Hacking DESTROY
such that it can be used as NOREFS
is desirable, but would require changes to the core perl code-base. This change is under consideration...)
Also see ObjStore::Mortician
!
posh
posh
is your interactive window into databases.
It is designed treat your data in an application specific manner. Customize by providing your own implementation for these methods:
$o-
help();>$o->POSH_PEEK($peeker, $o_name);
$o->POSH_CD($path);
$o->POSH_ENTER();
There are lots of good examples throughout the standard ObjStore::
libraries.
Arrays-as-Hashes
Fantastically efficient records:
use base 'ObjStore::AVHV';
use fields qw(f1 f2 f3);
$ObjStore::COMPILE_TIME XXX
See ObjStore::AVHV
XXX
Autoloading
When you use a database, ObjStore tries to require
each class that doesn't seem loaded. This means that you can write generic data processing programs that load the appropriate libraries to manipulate data in application specific ways.
To disable class autoloading behavior call this function before you open any databases:
ObjStore::disable_class_auto_loading();
This mechanism is orthogonal to the AUTOLOAD
mechanism for autoloading functions.
Where's the Hard Part?
I don't know. I haven't found it yet!
DIRECTION
PERFECT NATURAL CLARITY
The overwhelming top priority is to make this extension work seemlessly, obviously, and effortlessly. Really, the only difference between lisp and perl is ease of use. No detail will be overlooked, all must conform to effortless styistic perfection.
MORE APIs
Support for any other interesting
ObjectStore
APIs.MORE BUILT-IN DATA TYPES
Support for bit vectors (Bit::Vector) and matrices (PDL)?
How Does CORBA Fit In?
CORBA
standardizes remote method invokation (RMI). ObjectStore
greatly reduces the need for remote method invokation and also provides a simple but effective RMI mechanism (see ObjStore::Notification
and ObjStore::ServerDB
). The two technologies address different problems, but here is a rough comparison:
CORBA ObjectStore
-------------------- ------------------------- -------------------------
flow you follow the data the data comes to you
network important what network?
flexibility brittle up to you
object-oriented yes up to you
data copying 2-4 times 0-2 times
complex data no yes
custom types no yes
binary portability yes partially
reference counted no (does not store data) yes
multivendor yes not yet
Why Is Perl a Better Fit For Databases Than SQL
, C++
, or Java
?
struct CXX_or_Java_style {
char *name;
char *title;
double size;
};
When you write a structure declaration in C++
or Java
you are declaring field-names, field-types, and field-order. Programs almost always require a re-compile to change such rigid declarations. This is fine for small applications but becomes cumbersome quickly. It is too hard to change (brittle). An SQL
-style language is needed. When you create a table in SQL
you are declaring only field-names and field-types.
create table SQL_style
(name varchar(80),
title varchar(80),
size double)
This is more flexible, but SQL
gives you far less expressive power than C++
or Java
. Applications end up being written in C++
or Java
, while their data is stored with SQL
. Managing the syncronization between the two languages creates enormous extra complexity. So much so that there are lots of software companies that exist solely to address this headache. (You'd think they'd try to cure the problem instead of addressing the symptom!) Perl is better, because it transparently spans all the requirements in a single language.
my $h1 = { name => undef, title => undef, size => 'perl' };
Only the field-names are specified. This declaration is actually even more flexible than SQL
because the field-types are left dynamic. But not only is perl more flexible, it's also fast. Malcolm Beattie is working on a perl compiler which is currently in beta. Here is his brief description of a new hybrid hash-array that is supported: An array ref $a can be dereferenced as if it were a hash ref. $a->{foo} looks up the key "foo" in %{$a->[0]}. The value is the index in the true underlying array @$a. As an addition, if the array ref is in a lexical variable tagged with a classname ("my CXX $obj" to match your example above) then constant key dereferences of the form $obj->{foo} are mapped to $obj->[123] at compile time by looking up the index in %CXX::FIELDS.
For example:
my $schema_hashref = { 'field1' => 1, 'field2' => 2 };
my $arr = [$schema_hashref, 'fwaz', 'snorf'];
print "$arr->{field1} : $arr->{field2}\n"; # "fwaz : snorf"
So perl will soon compile to optimized C
(with C
-like data access times!). It's almost too good to be true.
Why Is Perl Easier Than Other Programming Languages?
I have no idea!
Summary (LONG)
SQL
All perl databases use the same flexible schema that can be examined and updated with generic tools. This is the key advantage of
SQL
, now available in perl. In addition, Perl /ObjectStore
is blatantly faster thanSQL
/C++
. (Not to mention that perl is a fun programming language whileSQL
is at best a clunky query language andC++
is at best an engineering language.)C++
Special purpose data types can be coded in
C++
and dynamically linked into perl. SinceC++
will always be able to beatJava
benchmarks, this gives perl an edge in the long run. Perl is toC/C++
asC/C++
is to assembly language.JAVA
Java
has the buzz, but:Just like
C++
, the lack of a universal generic schema limits use to single applications. Without some sort oftie
mechanism, I can't imagine how this could be remedied. (Even withtie
, it might already be too late. Java was not conceived to evolve quite as rapidly as is perl. Imagine that: "designed to evolve"!)All
Java
databases must serialize data to store and retrieve it. UntilJava
supports memory-mapped persistent allocation, database operations will always be sluggish compared toC++
. Not even god-like compiler technology can completely cure poor language design.Perl integrates with
Java
and theSwingSet / AWT
API. (Of course, this is a moot point if you can useQt
- http://www.troll.no/.)
Summary (SHORT)
Perl can store data
optimized for flexibility and/or for speed
in transient memory and persistent memory
without violating the principle of encapsulation or obstructing general ease of use.
ETA: 1-3 Months
perl compiler
kernel-managed perl threads
dynamically loaded application schemas
EXPORTS
bless
and begin
by default. Most other static methods can also be exported.
ENVIRONMENT VARIABLES
OSPERL_SCHEMA_DB
AUTHOR
Copyright © 1997-1998 Joshua Nathaniel Pritikin. All rights reserved.
This package is free software and is provided "as is" without express or implied warranty. It may be used, redistributed and/or modified under the terms of the Perl Artistic License (see http://www.perl.com/perl/misc/Artistic.html)
Perl / ObjectStore
extension is available via any CPAN mirror site. See http://www.perl.com/CPAN/authors/id/JPRIT/ !
Portions of the collection code were snapped from splash, Jim Morris's delightful C++
library ftp://ftp.wolfman.com/users/morris/public/splash .
Also, a poignant thanks to all the wonderful teachers with which I've had the opportunity of studying. If you have never had a teacher, I highly recommend it!
SEE ALSO
ObjStore::Tutorial
, ObjStore::Table3
, examples in the t/ directory, ObjStore::Internals
, Event
, PerlQt
, and The SQL Reference Manual
(just kidding :-)
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 1174:
Non-ASCII character seen before =encoding in '©'. Assuming CP1252