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