flexvault has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

I've been working on a pure perl database and the following code gets a warning message ('Use of uninitialized value in hash element at pyrdb.plx line 1179.') after about 2 million calls to the subroutine.

Line 1179 is marked with the '*' in column 1.

$$db{CurrentKey} = $SavSubtree[$curuser][$$db{RSubtreeno}]; $$keyptr = $$db{CurrentKey}; my $datapos; * if ( defined $RSubtree{$curuser}{$$keyptr} ) { $datapos = $RSubtre +e{$curuser}{$$keyptr}; } if ( ( defined $$keyptr )&&( defined $datapos ) ) { $ret = &GetDataRecord( $db, $keyptr, $dataptr, $datapos ); if ( $ret == TRUE ) { flock( $$db{btree}, LOCK_UN ); return("") +; } }

The warning doesn't affect the results, since I'm testing for the record being in cache, and if not it falls through to check for the record on disk. But the warning message is an irritation.

I've tried several different ways of re-writing the line but I still get the warning message twice in 4 million calls to the routine.

Is there another way of writing the line to eliminate the warning?

UPDATE: I solved the problem. The warning message was correct. In database programming, you try to balance the trees. At the tree extremes, I was incorrectly testing for the end, and calling the routine with undefined data. I solved it by putting a 'print STDERR' in the routine, and then calling the script with redirecting the STDERR to a file. The error was then obvious. Thanks to everyone's help and some of the answers were very interesting.

Thank you

"Well done is better than well said." - Benjamin Franklin

Replies are listed 'Best First'.
Re: How to eliminate warning message on hash value?
by ikegami (Patriarch) on Dec 08, 2011 at 11:15 UTC
    $$keyptr is undef. (Well, it could be the result of $curuser being undefined, but that would also get you a warning earlier.) Assuming that's normal,
    $$keyptr = $$db{CurrentKey} = $SavSubtree[$curuser][$$db{RSubtreeno}]; if ( defined($$keyptr) ) { my $datapos = $RSubtree{$curuser}{$$keyptr}; if ( defined($datapos) ) { $ret = GetDataRecord( $db, $keyptr, $dataptr, $datapos ); if ( $ret == TRUE ) { flock( $$db{btree}, LOCK_UN ); return ""; } } }

    What's with == TRUE? Perl subs don't tend to define what true value they return on success.

    $$keyptr = $$db{CurrentKey} = $SavSubtree[$curuser][$$db{RSubtreeno}]; if ( defined($$keyptr) ) { my $datapos = $RSubtree{$curuser}{$$keyptr}; if ( defined($datapos) ) { if ( GetDataRecord( $db, $keyptr, $dataptr, $datapos ) ) { flock( $$db{btree}, LOCK_UN ); return ""; } } }
      As a minor note, I would get rid of the nested if and replace that with an "and".

      Update: fixed line wrap as per Ikegami's suggestion.
      line length settings are different in my normal editor than PM. When I do complex multi-part if's requiring more than one line, I do it like below with the conditions on top of each other and the connecting "or" or "and" to the left.

      if ( defined($$keyptr) ) { my $datapos = $RSubtree{$curuser}{$$keyptr}; if ( defined($datapos) #update and GetDataRecord( $db, $keyptr, $dataptr, $datapos ) ) { flock( $db->{btree}, LOCK_UN ); return ""; } }
      instead of:
      if ( defined($$keyptr) ) { my $datapos = $RSubtree{$curuser}{$$keyptr}; if ( defined($datapos) ) { if ( GetDataRecord( $db, $keyptr, $dataptr, $datapos ) ) { flock( $$db{btree}, LOCK_UN ); return ""; } } }

        As a minor note, I would get rid of the nested if and replace that with an "and".

        Your code gets wrapped (with default PM settings), and is thus less readable. The line break was intentional, at which point "if" works better.

      If you use BerkeleyDB and you use c_get / db_put / etc., you get a '' on success and a 'Err message' on failure. So you would have to '!' the function call. Only for BerkeleyDB, I define my TRUE/FALSE in reverse. But as you pointed out, I should have done as you proposed and it would have been a true perl function call, only it still has to return to the application a '' for success.

      Thank you

      "Well done is better than well said." - Benjamin Franklin

        Please don't define TRUE to be something false!!! Call it SUCCESS or something. And if it does return '' as you say, then you'll get a warning for using == since '' is not a number. Use eq to do a string comparison.
Re: How to eliminate warning message on hash value?
by choroba (Cardinal) on Dec 08, 2011 at 10:24 UTC
    Check for definedness of $$keyptr.
Re: How to eliminate warning message on hash value?
by Marshall (Canon) on Dec 08, 2011 at 11:36 UTC
    I would just add that I find this $$ref confusing when used with a subscript, I prefer the arrow notation: $db->{CurentKey} over $$db{CurrentKey}. I just think that it is more clear.
      Funny, I was thinking the same.

      $$keyptr = $$db{CurrentKey};
      What exactly does this statement do? Mentally, I'm converting it to
      $keyptr-> = $db->{CurrentKey};
      which doesn't make any sense to me. What am I missing?

      -- Time flies when you don't know what you're doing
        Well $$keyptr dereferences presumably a pointer to the $key. This is fine if say X(\$key); sub X{$keyptr = shift} got called. Then $$keyptr = 3; would set $key to be 3 (just for example).

        As far as $$db{CurrentKey}, I figure that $db->{CurrentKey} is better although the other syntax is allowed.

        As to why the OP did this, I'm not quite sure. Passing a reference to a scalar is usually not necessary because Perl can return multiple values in a list. Normally if I modify your input scalar, I return it back as a modified value in a list. That is different than passing me a reference to an array where that might be some huge thing.

Re: How to eliminate warning message on hash value?
by flexvault (Monsignor) on Dec 08, 2011 at 13:38 UTC

    Update: The original code failed to compile! But by using Marshall's suggestion, if I replace '$$db{User}' with '$db->{User}, it compiles. But I still get the warning message around 2 million calls.

    Thank you

    "Well done is better than well said." - Benjamin Franklin

      Also be aware that there is a difference between checking if a hash value is "defined" vs "exists". It has to "exist" in order to be "defined".
      if ( exists $RSubtree{$curuser}{$$keyptr} ) { $datapos = $RSubtree{$curuser}{$$keyptr}; }
      Yes, there is no difference between the two. It's purely a stylistic improvement.
Re: How to eliminate warning message on hash value?
by Anonymous Monk on Dec 08, 2011 at 22:09 UTC
    Is anything in this scenario "tied?" It looks like a hash yes, but is it? Just asking.

      I am using hashes and arrays, but nothing is 'tied'. I'm doing all I/O as 'sysread/syswrite' in blocks that the user defines. This routine is checking the user's cache to determine if we need to do I/O, or can we get the block from cache. Please check the update, as I have solved the problem.

      "Well done is better than well said." - Benjamin Franklin