[ic] perl, $Db refs, Safe, and Windows

Bill Randle billr@exgate.tek.com
Fri, 5 Jan 2001 14:56:31 -0800


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