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