1#!/usr/bin/perl
2#
3# $Header: /Users/claude/fuzz/lib/Genezzo/Row/RCS/RSFile.pm,v 7.21 2007/11/18 08:13:27 claude Exp claude $
4#
5# copyright (c) 2003-2007 Jeffrey I Cohen, all rights reserved, worldwide
6#
7#
8use strict;
9use warnings;
10
11use Carp qw(cluck);
12use Genezzo::Row::RSBlock;
13
14package Genezzo::Row::RSFile;
15use Genezzo::Util;
16use Genezzo::BufCa::BCFile;
17use Genezzo::SpaceMan::SMFile;
18use Genezzo::SpaceMan::SMExtent;
19use Genezzo::PushHash::HPHRowBlk;
20
21use Carp;
22use warnings::register;
23
24our @ISA = qw(Genezzo::PushHash::HPHRowBlk) ;
25
26our $GZERR = sub {
27    my %args = (@_);
28
29    return
30        unless (exists($args{msg}));
31
32    if (exists($args{self}))
33    {
34        my $self = $args{self};
35        if (defined($self) && exists($self->{GZERR}))
36        {
37            my $err_cb = $self->{GZERR};
38            return &$err_cb(%args);
39        }
40    }
41
42    my $warn = 0;
43    if (exists($args{severity}))
44    {
45        my $sev = uc($args{severity});
46        $sev = 'WARNING'
47            if ($sev =~ m/warn/i);
48
49        # don't print 'INFO' prefix
50        if ($args{severity} !~ m/info/i)
51        {
52            printf ("%s: ", $sev);
53            $warn = 1;
54        }
55
56    }
57    # XXX XXX XXX
58    print __PACKAGE__, ": ",  $args{msg};
59#    print $args{msg};
60#    carp $args{msg}
61#      if (warnings::enabled() && $warn);
62
63};
64
65our $ROW_DIR_BLOCK_CLASS  = 'Genezzo::Row::RSBlock';
66
67# private
68sub _init
69{
70    #whoami;
71    #greet @_;
72    my $self = shift;
73    my %required = (
74                    tablename  => "no tablename !",
75                    object_id  => "no object id !",
76                    filename   => "no filename !",
77                    numbytes   => "no bytes !",
78                    numblocks  => "no blocks !",
79                    bufcache   => "no bufcache !",
80                    tso        => "no tso !",
81                    object_type => "no object type"
82                    );
83    my %optional = (
84                    RDBlock_Class  => "Genezzo::Block::RDBlock",
85                    dbh_ctx        => {}
86                    );
87
88    my %args = (
89                %optional,
90                @_);
91
92    return 0
93        unless (Validate(\%args, \%required));
94
95    # array of push hashes from make_new_chunk
96    $self->{filename}  = $args{filename};
97    $self->{filenumber}  = $args{filenumber};
98
99    $self->{numbytes}  = $args{numbytes};
100    $self->{numblocks} = $args{numblocks};
101    $self->{tablename} = $args{tablename};
102    $self->{realbc}    = $args{bufcache};
103    $self->{object_id} = $args{object_id};
104    $self->{tso}       = $args{tso};
105    $self->{object_type} = $args{object_type};
106
107#    $self->{initial_extent} = $args{initial_extent};
108#    $self->{next_extent}    = $args{next_extent};
109
110    my %nargs = (filename   => $args{filename},
111                 numbytes   => $args{numbytes},
112                 numblocks  => $args{numblocks},
113                 bufcache   => $args{bufcache},
114                 filenumber => $args{filenumber},
115                 tablename  => $args{tablename},
116                 object_id  => $args{object_id},
117                 object_type  => $args{object_type}
118                 );
119
120    if ((exists($args{GZERR}))
121        && (defined($args{GZERR}))
122        && (length($args{GZERR})))
123    {
124        # NOTE: don't supply our GZERR here - will get
125        # recursive failure...
126        $nargs{GZERR} = $args{GZERR};
127    }
128
129    $self->{smf} = Genezzo::SpaceMan::SMExtent->new(%nargs);
130
131    return 0
132        unless (defined($self->{smf}));
133
134    my $blockpkg = $args{RDBlock_Class};
135
136    # NOTE: check if the rdblock class for RSBlock tie exists...
137    unless (eval "require $blockpkg")
138    {
139        whisper "could not load class $blockpkg";
140        return 0;
141    }
142    $self->{RDBlock_Class} = $blockpkg;
143
144    # keep track of which block is currently buffered.
145    $self->{bc} = {};
146
147    my $bc = $self->{bc};
148
149    $bc->{bufblockno} = ();
150    $bc->{bceref} = ();
151    $bc->{realbcfileno} =
152        $self->{realbc}->FileReg(FileName   => $self->{filename},
153                                 FileNumber => $self->{filenumber});
154
155    # current insertion point - (not necessarily the current block)
156    $self->{current_chunk_for_insert} = ();
157
158    # Contrib is the counterpart to the CPAN Genezzo::Contrib
159    # namespace.  Add hash keys according to your package name, e.g.
160    #   $self->{Contrib}->{Clustered} = 'foo'
161    $self->{Contrib} = {};
162
163    return 1;
164}
165
166sub TIEHASH
167{ #sub new
168#    greet @_;
169    my $invocant = shift;
170    my $class = ref($invocant) || $invocant ;
171    my $self = $class->SUPER::TIEHASH(@_);
172
173    my %args = (@_);
174    return undef
175        unless (_init($self,%args));
176
177    if ((exists($args{GZERR}))
178        && (defined($args{GZERR}))
179        && (length($args{GZERR})))
180    {
181        # NOTE: don't supply our GZERR here - will get
182        # recursive failure...
183        $self->{GZERR} = $args{GZERR};
184    }
185
186    return bless $self, $class;
187
188} # end new
189
190# private routines
191sub _get_smf
192{
193    my $self = shift;
194    return $self->{smf};
195}
196
197sub _buffered_blockno  # current buffered block, as distinct from currchunkno
198{
199#    whoami;
200    local $Genezzo::Util::QUIETWHISPER = 1; # XXX: quiet the whispering
201
202    my $self = shift;
203#    greet $self->{tablename};
204
205    my $bc        = $self->{bc};
206    my $blockno   = $bc->{bufblockno};
207    return $blockno if (defined($blockno));
208
209    # load the first block if don't have it yet
210    my $smf       = $self->{smf};
211    my $tablename = $self->{tablename};
212    my $object_id = $self->{object_id};
213
214    # NOTE: some tricky stuff here -- first always define bufblockno.
215    $bc->{bufblockno} = $self->_currchunkno();
216
217    # NOTE: calling get_a_chunk will call currchunkno again, but since
218    # bufblockno is defined it should exit at the first return.
219
220    if (defined($bc->{bufblockno}))
221    {
222        whisper "try to load first chunk";
223        my $chunk1 = $self->_get_a_chunk($bc->{bufblockno});
224        unless (defined($chunk1))
225        {
226            whisper "could not load 1st chunk!";
227            return undef;
228        }
229    }
230    return ($bc->{bufblockno});
231
232}
233
234sub _currchunkno     # override the hph method
235{
236#    whoami;
237    my $self = shift;
238#    greet $self->{tablename};
239
240    # load the first block if don't have it yet
241    my $smf       = $self->{smf};
242    my $tablename = $self->{tablename};
243    my $object_id = $self->{object_id};
244
245    unless (defined($self->{current_chunk_for_insert}))
246    {
247        $self->{current_chunk_for_insert} =
248            $smf->currblock(tablename => $tablename,
249                            object_id => $object_id);
250    }
251
252    return ($self->{current_chunk_for_insert});
253}
254
255sub _get_current_chunk # override the hph method
256{
257#    whoami;
258#    local $Genezzo::Util::QUIETWHISPER = 1; # XXX: quiet the whispering
259
260    my $self = shift;
261#    greet $self->{tablename};
262
263    my $blockno = $self->_currchunkno();
264
265    unless (defined($blockno))
266    {
267        return $self->_make_new_chunk();
268    }
269
270    # XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
271    #
272    # Note: currchunkno is the current insertion point, not the
273    # current _buffered_ block.  BE SURE TO CLEAR OUT THE BUFFERED BLOCK
274    # so we can load the current insertion point.
275    #
276    # XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
277    my $bc = $self->{bc};
278
279    if (defined($bc->{bufblockno})
280        && ($bc->{bufblockno} != $blockno)) # no match!
281    {
282        $bc->{bufblockno} = ();
283        $bc->{bceref}     = ();
284
285        # clear out the current tied block if it's not current
286        # insertion point
287
288        $self->_untie_block();
289    } # buffered block didn't match
290
291    unless (defined ($self->{rowd}))
292    {
293        whisper "try to load first chunk";
294        my $chunk1 = $self->_get_a_chunk($blockno);
295        unless (defined($chunk1))
296        {
297            whisper "could not load 1st chunk!";
298            return undef;
299        }
300    }
301    return ($self->{rowd})
302}
303
304sub _make_new_chunk # override the hph method
305{
306#    whoami;
307    my $self = shift;
308
309    my $smf  = $self->{smf};
310    my $bc   = $self->{bc};
311    my $tso  = $self->{tso};
312    my $tablename = $self->{tablename};
313    my $object_id = $self->{object_id};
314
315    my $gotnewextent = 0; # true if get new extent
316
317    # release tied blocks
318    $self->_untie_block();
319
320    my ($blockinfo, $blockno);
321    for my $num_tries (1..2)
322    {
323        my %nargs = (tablename   => $tablename,
324                     object_id   => $object_id,
325                     all_info    => 1 # ask for all info
326                     );
327
328        # XXX XXX: get from TSO
329        $nargs{pctincrease} = 50;
330
331        $blockinfo =
332            $smf->nextfreeblock(%nargs);
333
334        $gotnewextent = 0; # true if get new extent
335
336        if (defined($blockinfo))
337        {
338            $gotnewextent = $blockinfo->IsNewExtent();
339
340            if ($gotnewextent)
341            {
342                greet "new extent", $blockinfo ;
343            }
344
345            $bc->{bufblockno} = $blockinfo->GetBlocknum();
346
347            $blockno   = $bc->{bufblockno};
348        }
349        last if (defined ($blockno));
350
351        # no space left?  See if can extend this file.
352        # need to update numbytes, numblocks
353        last unless ($tso->TSGrowFile(smf => $smf,
354                                      tablename => $tablename,
355                                      object_id => $object_id,
356                                      pctincrease => 50 # extent size increase
357                                      ));
358    }
359#    greet $blockno;
360
361    unless (defined ($blockno))
362    {
363        whisper "out of free blocks!";
364        return undef;
365    }
366
367    $self->{current_chunk_for_insert} = $blockno;
368
369    $bc->{bceref} =
370        $self->{realbc}->ReadBlock(filenum  => $bc->{realbcfileno},
371                                   blocknum => $blockno);
372
373    unless ($bc->{bceref})
374    {
375        whisper "failed to read block!";
376        return (undef);
377    }
378
379    my $bce = ${$bc->{bceref}};
380#    $smf->flush();
381#    greet $bce;
382
383    # tie the block -- set up the rowd and reftiebufa
384    $self->_tie_block($blockno, $bce);
385
386    if ($gotnewextent)
387    {
388        # size of extent is last entry in blockinfo
389        my $extent_size = $blockinfo->GetExtentSize();
390
391#        print "e:", $extent_size, "\n";
392
393        # get meta data for the extent header
394        my $row = $self->{rowd}->_get_meta_row("XHA");
395
396#        if ($row && scalar(@{$row}) && ($row->[0] == $extent_size))
397#        {
398#            print "match for first extent\n";
399#        }
400#        else
401#        {
402#            print "no match for first extent - $extent_size\n";
403#        }
404
405        $self->{currextent}  = $blockno;
406        $self->{extent_size} = $extent_size;
407        $self->{extent_posn} = 0;
408    }
409    else
410    {
411        $self->{extent_posn} += 1;
412        my $posn = $self->{extent_posn};
413        # get meta data for the extent header
414        my $row = $self->{rowd}->_get_meta_row("XHP");
415
416#        if ($row && scalar(@{$row}) && ($row->[0] == $posn))
417#        {
418#            print "match for position\n";
419#        }
420#        else
421#        {
422#            print "no match for position - $posn \n";
423#        }
424
425    }
426
427    return ($self->{rowd});
428}
429
430# NOTE: block routine for index operations
431sub _make_new_block # override HPHRowBlk
432{
433    my $self = shift;
434
435#    whoami;
436
437    my $chunk = $self->_make_new_chunk();
438
439    return undef
440        unless (defined($chunk));
441    my $blockno = $self->_currchunkno();
442    # NOTE: add 0 as slotnumber
443    return $self->_joinrid($blockno, '0');
444}
445
446# NOTE: block routine for index operations and row splitting
447sub _get_current_block # override HPHRowBlk
448{
449    my $self = shift;
450
451#    whoami;
452
453    my $chunk = $self->_get_current_chunk();
454
455    return undef
456        unless (defined($chunk));
457
458    my $blockno = $self->_currchunkno();
459    # NOTE: add 0 as slotnumber
460    return $self->_joinrid($blockno, '0');
461}
462
463# NOTE: block routine for index operations
464sub _get_block_and_bce # override HPHRowBlk
465{
466    my ($self, $place) = @_;
467
468    my ($chunk, $sliceno) = $self->_get_chunk_and_slice($place);
469
470    return undef
471        unless (defined($chunk));
472
473    my $bc = $self->{bc};
474    my $blockno = $self->_currchunkno();
475
476    # XXX XXX : need method to get tie rdblock
477    #   tiedblock , block number, bceref,  tied hash
478    return ($chunk->{tie_rdblock},
479            $blockno,
480            ($bc->{bceref}),
481            ($self->{reftiebufa}));
482}
483
484sub First_Blockno # override HPHRowBlk
485{
486    my $self = shift;
487
488    return $self->_First_Chunkno();
489} # end First_Blockno
490
491sub Next_Blockno # override HPHRowBlk
492{
493    my $self = shift;
494
495    return $self->_Next_Chunkno(@_);
496} # end Next_Blockno
497
498sub _get_a_chunk # override the hph method
499{
500    my ($self, $blocknum) = @_;
501#    whoami @_;
502
503    if ($blocknum !~ /\d+/)
504    {
505        carp "Non-numeric key: $blocknum "
506            if warnings::enabled();
507        return (undef); # protect us from non-numeric array offsets
508    }
509
510    my $buffered_blockno = $self->_buffered_blockno();
511    #
512    # XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
513    #
514    # NOTE: we might get called from within currchunkno for the very
515    # first time.  In this case, the above call to to currchunkno
516    # shouldn't recurse forever because the bufblockno is defined at
517    # the beginning of the first call to currchunkno.
518    #
519    # However, in this routine, we need to check if self->rowd exists.
520    # On the first pass currchunkno gets defined, but we haven't
521    # loaded the first tie for the block, so rowd is undefined.  In
522    # this case drop thru and read the block and tie it.  For
523    # subsequent cases rowd will exists and we don't have to keep
524    # going to read the block and retie.  May need to rethink this
525    # strategy for more complicated locking model.
526    #
527    # In one case, might try to read the hash first, so call to
528    # FIRSTKEY/NEXTKEY will call smf->firstblock (via _First_Chunkno),
529    # and then this function.  In get_a_chunk we call currchunkno to
530    # see if the chunkno = current.  currchunkno will set bufblockno
531    # via smf->currblock, and then call this function *AGAIN* to load
532    # the block.  Which calls currchunkno again, but bufblockno is
533    # set, so it short-circuits.  Then this function finally loads the
534    # block.
535    #
536    # In other case, might try to insert into the hash.  STORE can
537    # call get_chunk_and_slice, which calls get_a_chunk, or HPush can
538    # call get_current_chunk.  Either way currchunkno gets called
539    # which loads the current block.  We need a smarter optimization
540    # to avoid loading the current block for NEXTKEY, since we will
541    # immediately discard it for the first block.
542    #
543    # XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
544    #
545    if (    (defined($buffered_blockno))
546         && (defined($self->{rowd})))
547    {
548        return ($self->{rowd})
549            if  ($buffered_blockno == $blocknum);
550    }
551
552    my $smf  = $self->{smf};
553    my $bc   = $self->{bc};
554    my $tablename = $self->{tablename};
555    my $object_id = $self->{object_id};
556
557    unless ($smf->hasblock(tablename => $tablename,
558                           object_id => $object_id,
559                           blocknum  => $blocknum))
560    {
561        carp "key out of range: $blocknum "
562            if warnings::enabled();
563        return (undef);
564    }
565
566    $self->_untie_block();
567
568#    print "RSFILE READ BLOCK: ", $blocknum, "\n";
569
570    $bc->{bceref} =
571        $self->{realbc}->ReadBlock(filenum  => $bc->{realbcfileno},
572                                   blocknum => $blocknum);
573
574    unless ($bc->{bceref})
575    {
576        whisper "failed to read block!";
577        return (undef);
578    }
579
580    my $bce = ${$bc->{bceref}};
581
582    # tie the block -- set up the rowd and reftiebufa
583    $self->_tie_block($blocknum, $bce);
584
585    $bc->{bufblockno} = $blocknum;
586
587    return ($self->{rowd});
588
589}
590
591sub STORE # override the hph method and standard hash method
592{
593    my $self = shift;
594    my $stat = $self->SUPER::STORE(@_);
595    return $stat;
596}
597
598sub _First_Chunkno # override the hph method
599{
600#    whoami;
601    my $self = shift;
602    my $smf  = $self->{smf};
603    my $tablename = $self->{tablename};
604    my $object_id = $self->{object_id};
605
606    my $chunkno =
607        $smf->firstblock(tablename => $tablename,
608                         object_id => $object_id);
609
610    return ($chunkno);
611}
612
613sub _Next_Chunkno # override the hph method
614{
615#    whoami;
616    my ($self, $prevkey) = @_;
617    my $smf  = $self->{smf};
618    my $tablename = $self->{tablename};
619    my $object_id = $self->{object_id};
620
621    return (undef)
622        unless (defined ($prevkey));
623
624    my $chunkno = $smf->nextblock(tablename => $tablename,
625                                  object_id => $object_id,
626                                  prevblock => $prevkey);
627    return $chunkno;
628}
629
630# count estimation
631sub FirstCount
632{
633#    whoami;
634    my $self = shift;
635    my $smf  = $self->{smf};
636    my $tablename = $self->{tablename};
637    my $object_id = $self->{object_id};
638
639    my ($sum, $sumsq) = (0,0);
640
641    my $totchunk =
642        $smf->countblock(tablename => $tablename,
643                         object_id => $object_id);
644
645    my $chunkno = $self->_First_Chunkno();
646
647    my $chunkcount = 0;
648
649    while (defined($chunkno))
650    {
651#        greet $chunkno, $sum;
652        my $chunk = $self->_get_a_chunk($chunkno);
653        $chunkcount++;
654
655        if (defined($chunk))
656        {
657            $sum += $chunk->HCount();
658            $sumsq = $sum ** 2; # variance is (0-count) ^ 2
659            last;
660        }
661
662        $chunkno = $self->_Next_Chunkno($chunkno);
663    }
664
665    my @outi;
666
667    my $sliceno = 0;
668    my $keyplace;
669    $keyplace = $self->_joinrid($chunkno, $sliceno)
670        if (defined($chunkno));
671
672    my $esttot = 0;
673    $esttot = $sum * ($totchunk/$chunkcount)
674        if (($sum > 0) && ($chunkcount > 0) && ($totchunk > 0));
675
676    push @outi, $keyplace, $esttot;
677    push @outi, $sum, $sumsq, $chunkcount, $totchunk;
678
679    return (@outi);
680} # FirstCount
681
682# count estimation
683sub NextCount
684{
685#    whoami;
686    my ($self, $prevkey, $esttot, $sum, $sumsq, $chunkcount, $totchunk) = @_;
687
688    return undef
689        unless (defined($prevkey));
690    my ($chunkno, $prevsliceno) = $self->_splitrid($prevkey);
691
692    $chunkno = $self->_Next_Chunkno($chunkno);
693
694    my $quitLoop = 1; # XXX XXX
695    my $loopCnt  = 0;
696    my $lastone  = 0;
697
698    while (1)
699    {
700        my $oldChunkno;
701
702        $loopCnt++;
703
704        unless (defined($chunkno))
705        {
706            $totchunk = $chunkcount; # NOTE: we are done -
707                                     # fix the total chunk count
708            $chunkno = $oldChunkno;
709            $lastone = 1;
710            last;
711        }
712#        greet $chunkno, $sum;
713        my $chunk = $self->_get_a_chunk($chunkno);
714        $chunkcount++;
715
716        # readjust the estimated total if chunkcount now exceeds it --
717        # make it slightly larger so pct_complete < 100%...
718        $totchunk = $chunkcount + 1
719            if ($chunkcount >= $totchunk);
720
721        if (defined($chunk))
722        {
723            my $hcnt  = $chunk->HCount();
724            $sum     += $hcnt;
725
726#            my $mean = 0;
727#            $mean = $hcnt/$chunkcount
728#                if ($chunkcount);
729
730            # variance = 1/n-1 * Sum( (observed - mean)^2 )
731
732#            $sumsq   += (($hcnt - $mean)**2);
733            $sumsq   += (($hcnt)**2);
734
735            last if $quitLoop;
736        }
737
738        $oldChunkno = $chunkno;
739        $chunkno = $self->_Next_Chunkno($chunkno);
740
741        # XXX XXX: add logic here
742        $quitLoop = 1
743            if $loopCnt > 10;
744    }
745
746    my @outi;
747
748    my $sliceno = 0;
749    my $keyplace;
750    $keyplace = $self->_joinrid($chunkno, $sliceno)
751        if (defined($chunkno));
752
753    # current sum + (current avg * remaining chunks)
754#    $esttot = $sum + (($sum/$chunkcount)*($totchunk-$chunkcount))
755    if (($sum > 0) && ($chunkcount > 0) && ($totchunk > 0))
756    {
757        if ($lastone)
758        {
759            $esttot = $sum;
760        }
761        else
762        {
763            $esttot = $sum * ($totchunk/$chunkcount);
764        }
765    }
766    push @outi, $keyplace, $esttot;
767    push @outi, $sum, $sumsq, $chunkcount, $totchunk;
768
769#    greet @outi;
770
771    return (@outi);
772} # NextCount
773
774sub CLEAR
775{
776#    whoami;
777    my $self = shift;
778    my $smf  = $self->{smf};
779    my $tablename = $self->{tablename};
780    my $object_id = $self->{object_id};
781
782    $self->SUPER::CLEAR();
783
784    $smf->freetable(tablename => $tablename,
785                    object_id => $object_id);
786}
787
788
789END {
790
791}
792
793sub _tie_block
794{
795    my ($self, $blocknum, $bce) = @_;
796
797    return undef
798        unless (defined($blocknum) && defined($bce));
799
800    # BCE to RDBlock - please respond
801    my $mailbag = Genezzo::Util::AddMail(To => 'Genezzo::Block::RDBlock',
802                                         From => $bce,
803                                         Msg => 'RSVP');
804
805    # RSFile to RDBlock - register in Contrib hash (for SMHook)
806    $mailbag = Genezzo::Util::AddMail(To => 'Genezzo::Block::RDBlock',
807                                      From => $self,
808                                      Msg  => 'RegisterSender',
809                                      MailBag => $mailbag);
810
811    my %tiebufa;
812    # tie array to buffer
813    $self->{rowd} =
814        tie %tiebufa, $ROW_DIR_BLOCK_CLASS,
815         (RDBlock_Class => $self->{RDBlock_Class},
816          blocknum  => $blocknum,
817          refbufstr => $bce->{bigbuf},
818          blocksize => $bce->{blocksize}, # XXX XXX : get blocksize from bce!!
819          MailBag   => $mailbag
820          );
821
822    $self->{reftiebufa} = \%tiebufa;
823
824    if (defined(&tie_block_post_hook))
825    {
826        (tie_block_post_hook(self      => $self,
827                             rowd      => $self->{rowd},
828                             blocknum  => $blocknum));
829    }
830
831    $self->{blocknum} = $blocknum;
832
833    return $self->{rowd};
834
835} # end tie_block
836
837sub _untie_block
838{
839    my $self = shift;
840
841    my $reftb     = $self->{reftiebufa};
842
843    if (defined(&untie_block_pre_hook))
844    {
845        (untie_block_pre_hook(self     => $self,
846                              rowd     => $self->{rowd},
847                              blocknum => $self->{blocknum},
848                              filename   => $self->{filename},
849                              filenumber => $self->{filenumber}
850                              ));
851    }
852
853    $self->{rowd} = ();  # clear out to force reload
854
855    if (defined($reftb))
856    {
857        untie $reftb;
858    }
859
860    if (defined(&untie_block_post_hook))
861    {
862        (untie_block_post_hook(self     => $self,
863                               blocknum => $self->{blocknum}));
864    }
865
866}
867
8681;
869
870__END__
871
872# Below is stub documentation for your module. You better edit it!
873
874=head1 NAME
875
876Genezzo::Row::RSFile - Row Source File tied hash class.
877
878=head1 SYNOPSIS
879
880 use Genezzo::Row::RSFile;
881
882=head1 DESCRIPTION
883
884RSFile is a hierarchical pushhash (see L<Genezzo::PushHash::hph>)
885class that stores scalar data in a block (byte buffer) via
886L<Genezzo::Block::RDBlock>.
887
888=head1 ARGUMENTS
889
890=over 4
891
892=item tablename
893(Required) - the name of the table
894
895=item tso
896(Required) - tablespace object from L<Genezzo::Tablespace>
897
898=item bufcache
899(Required) - buffer cache object from L<Genezzo::BufCa::BCFile>
900
901
902=back
903
904
905=head1 CONCEPTS
906
907RSFile can persistently store scalar data in a single file.  It
908doesn't know anything about rows -- that's all in
909L<Genezzo::Row::RSTab>.
910
911RSFile has some extensions to directly manipulate the underlying
912blocks.  These extensions are useful for building specialized index
913mechanisms (see L<Genezzo::Index>) like B-trees, or for supporting
914scalars that span multiple blocks.
915
916=head2 Basic PushHash
917
918You can use RSFile as a persistent hash of scalars if you like.
919RSFile can only support strings that fit with a single database block.
920Use L<Genezzo::Row::RSTab> if you need to split data over multiple
921blocks.
922
923=head2 HPHRowBlk - Row and Block operations
924
925HPHRowBlk is a special pushhash subclass with certain direct block
926manipulation methods.  One very useful function is HSuck, which
927provides support for rows that span multiple blocks.  While the
928standard HPush fails if a row exceeds the space in a single block, the
929HSuck api lets the underlying blocks consume the rows in pieces --
930each block "sucks up" as much of the row as it can.  However, RSFile
931does not provide the HSuck api.  Instead, it provides some utility
932functions so RSTab can get direct access to the low-level block
933routines.
934
935=head2 Counting, Estimation, Approximation
936
937RSFile has some support for count estimation, inspired by some of Peter
938Haas' work (Sequential Sampling Procedures for Query Size Estimation,
939ACM SIGMOD 1992, Online Aggregation (with J. Hellerstein and H. Wang),
940ACM SIGMOD 1997 Ripple Joins for Online Aggregation (with
941J. Hellerstein) ACM SIGMOD 1999).
942
943=head1 FUNCTIONS
944
945RSFile support all standard hph hierarchical pushhash operations.
946
947=head2 EXPORT
948
949=head1 LIMITATIONS
950
951various
952
953=head1 TODO
954
955=over 4
956
957=item need error handlers vs "whisper"
958
959=back
960
961=head1 AUTHOR
962
963Jeffrey I. Cohen, jcohen@genezzo.com
964
965=head1 SEE ALSO
966
967L<perl(1)>.
968
969Copyright (c) 2003-2007 Jeffrey I Cohen.  All rights reserved.
970
971    This program is free software; you can redistribute it and/or modify
972    it under the terms of the GNU General Public License as published by
973    the Free Software Foundation; either version 2 of the License, or
974    any later version.
975
976    This program is distributed in the hope that it will be useful,
977    but WITHOUT ANY WARRANTY; without even the implied warranty of
978    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
979    GNU General Public License for more details.
980
981    You should have received a copy of the GNU General Public License
982    along with this program; if not, write to the Free Software
983    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
984
985Address bug reports and comments to: jcohen@genezzo.com
986
987For more information, please visit the Genezzo homepage
988at L<http://www.genezzo.com>
989
990=cut
991