#!/usr/bin/perl # # $Header: /Users/claude/fuzz/lib/Genezzo/PushHash/RCS/HPHRowBlk.pm,v 7.2 2007/11/18 08:15:51 claude Exp claude $ # # copyright (c) 2003,2004,2005 Jeffrey I Cohen, all rights reserved, worldwide # # use strict; use warnings; package Genezzo::PushHash::HPHRowBlk; use Genezzo::Util; use Genezzo::PushHash::hph; use Genezzo::PushHash::PushHash; use Genezzo::PushHash::PHArray; use Carp; use warnings::register; our @ISA = qw(Genezzo::PushHash::hph) ; our $GZERR = sub { my %args = (@_); return unless (exists($args{msg})); if (exists($args{self})) { my $self = $args{self}; if (defined($self) && exists($self->{GZERR})) { my $err_cb = $self->{GZERR}; return &$err_cb(%args); } } carp $args{msg} if warnings::enabled(); }; # private sub _init { #whoami; #greet @_; my $self = shift; my %args = (@_); return 1; } sub TIEHASH { #sub new # greet @_; my $invocant = shift; my $class = ref($invocant) || $invocant ; my $self = $class->SUPER::TIEHASH(@_); my %args = (@_); return undef unless (_init($self,%args)); return bless $self, $class; } # end new # NOTE: block routine for index operations sub _make_new_block { my $self = shift; # whoami; # try to create a new block in the latest chunk (file) my $chunk = $self->_get_current_chunk(); unless (defined($chunk)) { my %earg = (self => $self, msg => "no chunk to make new block!"); &$GZERR(%earg) if (defined($GZERR)); return undef; } my $blockno; for my $i (1..2) { return undef unless (defined($chunk)); $blockno = $chunk->_make_new_block(); # greet $blockno; last if (defined($blockno)); # only one try if could not make a new block in new chunk return undef if ($i > 1); $chunk = $self->_make_new_chunk(); } my $chunkno = $self->_currchunkno(); return $self->_joinrid($chunkno, $blockno); } # NOTE: block routine for index operations and row splitting sub _get_current_block { my $self = shift; # whoami; my $chunk = $self->_get_current_chunk(); unless (defined($chunk)) { my %earg = (self => $self, msg => "no chunk to get block!"); &$GZERR(%earg) if (defined($GZERR)); return undef; } my $blockno = $chunk->_get_current_block(); return $self->_make_new_block() unless (defined($blockno)); my $chunkno = $self->_currchunkno(); return $self->_joinrid($chunkno, $blockno); } # _get_current_block # NOTE: block routine for index operations sub _get_block_and_bce { my ($self, $place) = @_; my ($chunk, $sliceno) = $self->_get_chunk_and_slice($place); return undef unless (defined($chunk)); return $chunk->_get_block_and_bce($sliceno); } sub First_Blockno { my $chunkno = $_[0]->_First_Chunkno(); return undef unless (defined($chunkno)); my $chunk = $_[0]->_get_a_chunk($chunkno); return undef unless (defined($chunk)); my $blockno = $chunk->First_Blockno(); return undef unless (defined($blockno)); return $_[0]->_joinrid($chunkno, $blockno); } # end First_Blockno sub Next_Blockno { my ($self, $prevkey) = @_; return (undef) unless (defined ($prevkey)); # need the chunkno for the rid, so use splitrid vs get_chunk_and_slice my ($chunkno, $prevsliceno) = $self->_splitrid($prevkey); return (undef) unless (defined($chunkno)); while (defined($chunkno)) { my $sliceno; my $chunk = $self->_get_a_chunk($chunkno); unless (defined($chunk)) { my %earg = (self => $self, msg => "No such key: $prevkey "); &$GZERR(%earg) if (defined($GZERR)); return undef; } my $blockno; if (defined ($prevsliceno)) { $blockno = $chunk->Next_Blockno($prevsliceno); } else { $blockno = $chunk->First_Blockno(); return undef unless (defined($blockno)); } return $self->_joinrid($chunkno, $blockno) if (defined($blockno)); $prevsliceno = (); $chunkno = $self->_Next_Chunkno($chunkno); } return undef; } # end Next_Blockno # an augmented fetch. FETCH returns a scalar, # but fetch2 can return an array sub _fetch2 { # whoami; my $self = shift; my $place = shift; my ($chunk, $sliceno) = $self->_get_chunk_and_slice($place); unless (defined($chunk)) { my %earg = (self => $self, msg => "No such key: $place "); &$GZERR(%earg) if (defined($GZERR)); return undef; } #whoami $chunkno, $sliceno; return ($chunk->_fetch2($sliceno, @_)); } # end fetch2 # return existance and row status info. Reset the row status if # to newrowstat if it is set (use @_ for extra args) sub _exists2 { # whoami; my $self = shift; my $place = shift; my ($chunk, $sliceno) = $self->_get_chunk_and_slice($place); unless (defined($chunk)) { my %earg = (self => $self, msg => "No such key: $place "); &$GZERR(%earg) if (defined($GZERR)); return undef; } #whoami $chunkno, $sliceno; return ($chunk->_exists2($sliceno, @_)); } END { } 1; __END__ # Below is stub documentation for your module. You better edit it! =head1 NAME Genezzo::PushHash::HPHRowBlk.pm - a 90% pure virtual class module that extends hierarchical "push hash" L with Row/Block methods. These methods facilitate the construction of classes that manipulate data blocks directly, such as index access methods and functions that split rows over multiple blocks.. =head1 SYNOPSIS use Genezzo::PushHash::HPHRowBlk; use Genezzo::PushHash::hph; # need more info here!! =head1 DESCRIPTION Like a standard hierarchical pushhash (hph), the HPHRowBlk is a pushhash built upon a collection of other pushhashes. A push into the top-level hash is routed into one of the bottom hashes. If the bottom hashes are full (push fails), the top-level pushhash uses the factory method to create or obtain a new pushhash. The HPHRowBlk class is designed to layer on top of hph's built of hash-tied byte block storage, such as L. =head1 CONCEPTS and INTERNALS - useful for implementors A hph is constructed of N pushhash "chunks", and the elements of each chunk are referred to as "slices". Typically, one chunk is "current" -- we push into the current chunk until it fills up, at which point the hph attempts to make a new one. HPHRowBlk is designed to expose the underlying block mechanism to the uppermost layer of the pushhash. It provides some additional methods: _make_new_block, _get_current_block, and _get_block_and_bce, which provide functionality somewhat similar to _get_current_chunk/_make_new_chunk, but on a block level, versus individual scalar (packed row) operations. In addition, these methods "short-circuit" the hph tree of pushhashes, making the bottom block operations directly available to the top hph layer. The penultimate layer of the hph stack (see L) must implement the internal block access methods on the bottom pushhash. =over 4 =item _make_new_block create a new block in the current chunk and return the block number as a rid. =item _get_current_block return the block number of the insertion position in the current chunk. =item _get_block_and_bce return an array of the tied block, the buffer cache element (see L), and other useful information. =item First_Blockno/Next_Blockno iterate over all the blocks in the HPHRowBlk push hash. =back =head1 WHY? =over 4 =item Indexes Btree indexes are implemented as a tree of data blocks. Tree operations directly manipulate the blocks directly, bypassing the hph mechanisms that typically isolate the persistent tuple storage from the top layer. See L. =item Row/Column Splitting When a packed tuple exceeds the size of an individual block, the row may be split over multiple blocks. The basic semantics of the row contents is only understood at the uppermost layer, which packs and interprets tuple data, while the bottommost layer is solely responsible for storing and accessing scalar byte string data in persistent storage. The HPHRowBlk methods provide handles into the basic block storage so the upper layer can split and reconstruct row data over multiple blocks. See L. =back =head1 TODO =over 4 =item fix synopsis =back =head1 AUTHOR Jeffrey I. Cohen, jcohen@genezzo.com =head1 SEE ALSO L, L, L. Copyright (c) 2003, 2004, 2005 Jeffrey I Cohen. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Address bug reports and comments to: jcohen@genezzo.com For more information, please visit the Genezzo homepage at L =cut