develooper Front page | perl.perl5.porters | Postings from March 2003

[perl #21602] Strange problem with tied hashes

Thread Next
From:
perlbug-followup
Date:
March 17, 2003 11:53
Subject:
[perl #21602] Strange problem with tied hashes
Message ID:
rt-21602-53773.9.6404979096674@bugs6.perl.org
# New Ticket Created by  miko@idocs.com 
# Please include the string:  [perl #21602]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt2/Ticket/Display.html?id=21602 >



This is a bug report for perl from miko@idocs.com,
generated with the help of perlbug 1.33 running under perl v5.6.1.

I've run into an interesting problem working with tied hashes. Although I've managed to kludge a solution, it's a sufficiently strange problem that it would be worthwhile to understand the cause of it.  I don't know if the problem is a bug in perl or in my code.  I've produced the bug in perl 5.6.1 running on NetBSD.

The quick summary of the problem is this.  If the FETCH routine in a tied hash module reads data from a reference to another tied hash, and then returns yet another reference to a hash (tied or not), and if the calling code tries to directly modify that returned hash ref, then a fatal error results: "Can't use an undefined value as a HASH reference".

Let's walk through an example. The following code example demonstrates the problem. The test code consists of a module for implementing a tied hash, and a script that uses that module.  I've labelled the three main pieces of code that are crucial to reproducing the error as [A], [B], and [C].

Let's start with the script:

 ------------- begin cut ----------------
 #!/usr/local/bin/perl -w
 use strict;
 use lib './';
 use Borman;
 use Tie::Hash;

 # variables
 my (%srchash, %outer, $record);

 # [A]
 # tie %srchash to anything
 # if you do not tie the source hash something, there is no error
 tie(%srchash, 'Tie::StdHash') or die 'unable to tie %srchash';

 # tie %outer to Borman, passing %srchash as the source of data
 tie(%outer, 'Borman', \%srchash) or die 'unable to tie %srchash';

 # attempt to store data directly into stored hash
 $outer{'miko'}->{'email'} = 'whatever@whatever.com';

 # [B]
 # if we retrieve the hashref into a scalar,
 # THEN modify the hash, there is no error
 # $record = $outer{'miko'};
 # $record->{'email'} = 'whatever@whatever.com';
 ------------- end cut ----------------


In the [A] block above, we start by tying %srchash to Tie::StdHash.  I found that it doesn't seem to matter what %srchash is tied to... if it's tied then the error occurs, otherwise no error occurs.

In the next block the code ties %outer to our example module, Borman, passing a reference to %srchash. The next block retrieves the 'miko' element, which should be a hash ref, and attempts to assign a value to one of the elements of the hash referenced by the return value.   I've found that if the value returned from %outer is first stored in a scalar, and THEN the referenced hash is modified, no error occurs. See [B] for (commented out) code that implements this kludge.

OK, now let's look at the module:


 ------------- begin cut ----------------
 package Borman;
 use strict;

 sub TIEHASH {
     my ($class, $source) = @_;
     my $self = bless({}, $class);
     $self->{'source'} = $source;
     return $self;
 }

 sub FETCH {
     my ($self, $key) = @_;
     my (%record, $raw);
     
     # [C]
     # if we eval the following statement, there is no error
     # but if it is run straight, we get the error
     # eval '$raw = $self->{\'source\'}->{$key}';
     $raw = $self->{'source'}->{$key};
     
     
     # I use the following line to assure myself that
     # we are getting to this line even though the 
     # error eventually occurs.  We do get to this line.
     # print '%record: ', \%record, "\n";
     
     return \%record;
 }

 1;
 ------------- end cut ----------------

Obviously this isn't a complete tied hash module, but it serves to show the problem.  This module does two things.  TIEHASH instantiates the object underlying the tied hash. It accepts two arguments: the class name, and a reference to another hash (which we'll call the "source hash", since the intention is that the source of the data is stored in that passed in hash).  The object holds on to a reference to the source hash.

In FETCH, the code does just a few, innocent seeming actions.  It accepts two arguments which are stored in $self and $key as usual in FETCH. But next comes one of the problem areas.  See [C] above.  In the real-world production version of this module, the code needs to pull out the value from the source hash that was passed in to and stored in TIEHASH.  In my original module, the code simply retrieves the hash element like this:

   $raw = $self->{'source'}->{$key};

In the example above, that's all that is done with that data.  $raw is never used again.  Again, obviously in the real world the routine would do something with the data, but it's not necessary to reproduce the problem. I eventually found that if I ran that line of code in an eval then I don't get the error.  See the (commented out) code in the [C] block.

The routine then simply returns a reference to a plain hash. 

So there's the bug.  If any one of three kludges are implemented, no error occurs.  In my project I simply eval the assignment from the source hash, so my project is proceeding forward.  However, this is a weird problem.  Is this a bug in perl?
---
Flags:
    category=core
    severity=low
---
Site configuration information for perl v5.6.1:

Configured by briggs at Mon Jan 28 20:27:09 EST 2002.

Summary of my perl5 (revision 5.0 version 6 subversion 1) configuration:
  Platform:
    osname=netbsd, osvers=1.5.2, archname=i386-netbsd
    uname='netbsd canolog 1.5.2 netbsd 1.5.2 (canolog) #2: tue sep 25 16:31:33 edt 2001 briggs@canolog2:usrsrcsysarchi386compilecanolog i386 '
    config_args='-sde -Dprefix=/usr/pkg -Dscriptdir=/usr/pkg/bin -Darchname=i386-netbsd -Doptimize=-O2 -Dcc=cc -Uusemymalloc -Uinstallusrbinperl -Dlibswanted=m crypt -Duseshrplib'
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef
    useperlio=undef d_sfio=undef uselargefiles=define usesocks=undef
    use64bitint=undef use64bitall=undef uselongdouble=undef
  Compiler:
    cc='cc', ccflags ='-fno-strict-aliasing -I/usr/pkg/include',
    optimize='-O2',
    cppflags='-fno-strict-aliasing -I/usr/pkg/include'
    ccversion='', gccversion='egcs-2.91.66 19990314 (egcs-1.1.2 release)', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=4, usemymalloc=n, prototype=define
  Linker and Libraries:
    ld='cc', ldflags ='-Wl,-R/usr/pkg/lib  -L/usr/pkg/lib'
    libpth=/usr/pkg/lib /usr/lib
    libs=-lm -lcrypt
    perllibs=-lm -lcrypt
    libc=/usr/lib/libc.so, so=so, useshrplib=true, libperl=libperl.so
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-whole-archive -lgcc -Wl,-no-whole-archive 			-Wl,-E -Wl,-R/lib  -Wl,-R/usr/pkg/lib/perl5/5.6.1/i386-netbsd/CORE'
    cccdlflags='-DPIC -fPIC ', lddlflags='--whole-archive -shared  -Wl,-R/usr/pkg/lib -L/usr/pkg/lib'

Locally applied patches:
    

---
@INC for perl v5.6.1:
    /a/biz/miko/perls/CpanLib
    /usr/pkg/lib/perl5/site_perl/5.6.1/i386-netbsd
    /usr/pkg/lib/perl5/site_perl/5.6.1
    /usr/pkg/lib/perl5/site_perl
    /usr/pkg/lib/perl5/5.6.1/i386-netbsd
    /usr/pkg/lib/perl5/5.6.1
    .

---
Environment for perl v5.6.1:
    HOME=/a/biz/miko
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/usr/bin:/bin:/usr/pkg/bin:/usr/local/bin:/usr/pkg/bin:/usr/pkg/pgsql/bin:/a/biz/miko/shells:.
    PERL5LIB=/a/biz/miko/perls/CpanLib
    PERL_BADLANG (unset)
    SHELL=/usr/pkg/bin/bash


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About