Mailing List Archive

perl, $Db refs, Safe, and Windows
I've been porting parts of an existing application from a Linux server
to a standalone Windows box. In the process of doing so, I ran into
a problem accessing various tables from inside [perl] blocks.

At first I didn't know it only happened when accessing tables from inside
[perl] - I thought it was a general setup problem. (And yes, when I say
[perl], I really mean [perl tables="xxx"].)

I did finally solve my problem after struggling for quite a while, so I
thought I'd post the problem and solution so it will get archived in case
someone else has the same problem in the future. Although I haven't
tried this on Interchange 4.6.x, I suspect it would exhibit the same
behavior.

The Setup:
----------
Win 95 and/or Win NT4 (behaves the same on both)
Active State Perl build 522 (Perl 5.00503)
Minivend 4.04a
DB_File
DBI
SQL::Statement
Safe::Hole
Storable
other standard modules required by Minivend

Basically, a duplicate of my Linux environment.

The Problem:
------------
On the first access of a table from a [perl tables="xxx"]
block, I can read entires from the "xxx" table. On the next access
from another [perl tables="xxx"] block (in reality, the same block
accessed via reloading the page with different cgi values), I get the
infamous error:
Can't call method "open_table" on an undefined value at
\Minivend\mvend/lib/Vend/Data.pm at line 762.

The db/table being opened is a Berkeley db database, accessed via DB_File.
(I have $ENV{MINIVEND_DBFILE} = 1 uncommented in minivend.bat to force
DB_File.

I know the db is OK because, as I said, it could read it the first time.
The perl code is using tag_data() to access the table.

The Debug Process:
------------------
I uncommented some of the ::logDebug() lines in Data.pm and added a few others.
This showed that the db filename and text filenames were correct and that
the db class (6) was correct for DB_File, however both
$Vend::Interpolate::Db{$class_config->{Class}} and $Vend::Interpolate::Db
were undefined.

After wallowing a bit more, I started looking at tag_perl() in Interpolate.pm,
as it's the only one that appears to set $Db. Uncommenting some ::logDebugs
here showed that the first time thru $Db{$_} (a wrapped reference to the
current table from the 'tables="xxx"' option to the [perl] tag) was undefined,
so the table gets initialized, wrapped, and saved away via
$Db{$_} = $hole->wrap($db);
Everything then continues on normally.

Now, the *next* time thru tag_perl() from a subsequent page load, using the
same db table, $Db{$_} is (still) defined - which is what one would expect.
However, by the time it gets to import_database() in Data.pm, $Db
(referenced via $Vend::Interpolate::Db) is undefined!

The Analysis:
-------------
I have no real idea what causes this - and it only happens on Windows. I
suspect it may have to do with an interaction between the way Safe::Hole
works (required to use SQL::Statement) and the way the Windows version
of Perl is wrapped in an object interface. Somehow either the $Db local
to Interpolate.pm is not the same as the global accessed $Vend::Interpolate::Db
in Data.pm, or they are the same, but the value is no longer valid on
subsequent invocations of the interpreter - I really don't know. I just
know it works OK the first time any given table is accessed.

The Solution/Workaround:
------------------------
Here is the solution I implemented that is working for me:

--- Interpolate.pm.orig Sat Apr 15 17:18:42 2000
+++ Interpolate.pm Wed Jan 03 16:29:03 2001
@@ -1506,7 +1506,7 @@
my (@tab) = grep /\S/, split /\s+/, $tables;
for(@tab) {
#::logDebug("tag_perl: priming table $_");
- next if $Db{$_};
+ next if $Db{$_} && ! $Global::Windows;
my $db = Vend::Data::database_exists_ref($_);
next unless $db;
#::logDebug("tag_perl: need to init table $_, ref=$db");

Basically, if running Windows, I forced it to always create a new safe
ref each time.

There may be other, more correct, solutions/fixes, as well. I don't know
what the side effect of this fix is - other than some increased overhead.
Another solution may be to remove Safe::Hole - haven't tried that - but
that may (at least according to the docs) prevent SQL::Statement from
working.

Perhaps Mike may add his $0.02. Even though Windows is not officially
supported, there are some uses for Minivend on Windows - particularly
as a single user setup, not as a multi-user server.

-Bill
perl, $Db refs, Safe, and Windows [ In reply to ]
Quoting Bill Randle (billr@exgate.tek.com):
> The Problem:
> ------------
> On the first access of a table from a [perl tables="xxx"]
> block, I can read entires from the "xxx" table. On the next access
> from another [perl tables="xxx"] block (in reality, the same block
> accessed via reloading the page with different cgi values), I get the
> infamous error:
> Can't call method "open_table" on an undefined value at
> \Minivend\mvend/lib/Vend/Data.pm at line 762.
>
> The db/table being opened is a Berkeley db database, accessed via DB_File.
> (I have $ENV{MINIVEND_DBFILE} = 1 uncommented in minivend.bat to force
> DB_File.
>
> I know the db is OK because, as I said, it could read it the first time.
> The perl code is using tag_data() to access the table.
>

I am guessing that $MVSAFE::Safe is not getting undefined -- I have not
gone through and vetted all of the reset logic for Windows with IC/MV4,
as I did with MV3.

Remember, any changes to $Config/$Vend::Cfg stick on Windows; and there
also are many other places where a $Vend::foo variable gets set and
doesn't get reset properly.

To get IC running well on Windows, we would have to identify all of the
transitory $Vend:: variables and set them in a symbol area which could
get reset(), or use $Instance->{foo} instead of $Vend::foo. I have been
jealous of the extra microseconds for the hash lookup, which is why I didn't
do that with MV4 as I had initially planned to.

Many of the inconsistencies in IC/MV have to do with my zeal for performance.
Lots of things can be made more portable by sacrificing performance, which
is an age-old story. 8-(

--
Akopia, Inc., 131 Willow Lane, Floor 2, Oxford, OH 45056
phone +1.513.523.7621 fax 7501 <heins@akopia.com>

I have a cop friend who thinks he ought be able to give a new ticket;
"too dumb for conditions".