package hdb; our $VERSION = '0.1.0pre1'; use DBI; use strict; $hdb::mysqldbh=undef; sub enc { # encode my $str=shift; $str =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg; return $str; } sub dec { # decode my $str=shift; $str =~ s/\%([A-Fa-f\d]{2})/chr hex $1/eg; return $str; } sub tracenode { # internal: find unique id for the node at this path my @key_list = @_; my $node=0; return 0 if ($#key_list==-1); for my $i (0..$#key_list) { ($node)=$hdb::mysqldbh->selectrow_array("SELECT id FROM nodes WHERE (nparent=\"$node\" AND nkey=\"".$key_list[$i]."\")") || return undef; } return $node; } sub initdb { # initialize the database (connect to MySQL, create nodes-table) my $db_name = shift || "default"; my $db_host = shift || "localhost"; my $username = shift || "root"; my $password = shift || ""; $hdb::mysqldbh = DBI->connect("dbi:mysql:$db_name:$db_host", $username, $password, { RaiseError => 0, AutoCommit => 1 }) || return 0; $hdb::mysqldbh->do('CREATE TABLE IF NOT EXISTS nodes (id int(4) unique primary key not null AUTO_INCREMENT, nparent int(4), nkey text, nvalue text)'); return 1; } sub exitdb { # nicely disconnect from the database $hdb::mysqldbh->disconnect; } sub getvalue { # get value of the entry of the node my @key_list = @_; my $node=tracenode(@key_list); my ($nvalue)=$hdb::mysqldbh->selectrow_array("SELECT nvalue FROM nodes WHERE (id=\"$node\")"); return dec($nvalue); } sub getchildren { # return a list of children of the node my @key_list = @_; my $node=tracenode(@key_list); my $sth=$hdb::mysqldbh->prepare("SELECT nkey FROM nodes WHERE nparent=\"$node\""); $sth->execute; my @children; while (my @row=$sth->fetchrow_array) { push @children,$row[0]; } return @children; } sub setvalue { my $nvalue = shift; my @key_list = @_; my $node=tracenode(@key_list); return $hdb::mysqldbh->do('UPDATE nodes SET nvalue="'.enc($nvalue).'" WHERE id="'.$node.'"'); } sub createchild { my $child = shift; my $childvalue = shift; my @key_list = @_; my $node =tracenode(@key_list); if (defined $childvalue) { return $hdb::mysqldbh->do('INSERT INTO nodes(nparent, nkey, nvalue) VALUES ("'.$node.'","'.enc($child).'","'.enc($childvalue).'")'); } else { return $hdb::mysqldbh->do('INSERT INTO nodes(nparent, nkey) VALUES ("'.$node.'","'.enc($child).'")'); } } sub delnode { my @key_list = @_; my $node=tracenode(@key_list) || return 0; return $hdb::mysqldbh->do("DELETE FROM nodes WHERE id=\"$node\""); } 1; __END__ =head1 NAME hdb - Hierarchical Database Module for Perl =head1 SYNOPSIS use hdb; hdb::initdb; hdb::createchild("1","2"); print hdb::getvalue("1"); hdb::exitdb; =head1 DESCRIPTION --- What is it? The HDB is a database that provides an object-oriented, hierarchical data storage and retrival system. The data is organised in directories and files, as in a traditional filesystem like FAT, AmigaDOS or Minix. Unlike these filesystems, the attributes for each node are different. One node can contain data and have children at the same time. There is no metadata (like length, type, creation date) for a node (at least not currently). --- Why do I need it? I don't know whether YOU need it, but I tend to create datastructures for my programs that could be easier implemented in a hierarchical than a flat, relational database. --- What can I do? datatypes a @key_hierarchie is a list which points to an entry, e.g. @key_hierarchie=("users","tom","files","foo.bar","length"); primitives BOOL = initdb( [$db_name [, $db_host [, $username [, $password ]]]] ) opens the database using standard SQL parameters if necessare BOOL = exitdb() closes the database connection $value = hdb_getvalue ( @key_hierarchie ) returns the value of the node @key_list = hdb_getchildren ( @key_hierarchie ) returns a list of children beyond this node BOOL = hdb_setvalue ( $value, @key_hierarchie) sets the value of a node BOOL = hdb_createchild ( $newkey, $value, @key_hierarchie) creates a child with a value beyond this node BOOL = hdb_delnode ( @key_hierarchie ) deletes a node high-layer - none yet - --- How do you implement this thing? The first version is implemented using Perl's DBI-module and a mysql backend. Thus you should be able to use this module on a variety of platforms and installations. There is a significant overhead in this solution that makes your program slow and your data big. But it was the easiest way to write the first version and to make it highly portable. --- Who is responsible for this mess? Ruwen Böhm Send me flowers, money, postcard, loveletters and mailbombs! --- To-do - a lot --- Changes & History 0.1.0pre1 (2005-03-03) - first version - basic outline of the API =cut