Beefy Boxes and Bandwidth Generously Provided by pair Networks DiBona
There's more than one way to do things
 
PerlMonks  

DBI, DBD::Oracle and LD_LIBRARY_PATH

by Shendal (Hermit)
on Aug 18, 2000 at 20:52 UTC ( #28533=perlquestion: print w/ replies, xml ) Need Help??
Shendal has asked for the wisdom of the Perl Monks concerning the following question:

I'm having trouble with DBI and DBD::Oracle. I think that it has to do with setting of the LD_LIBRARY_PATH variable, so that DBI can dynamically load what it needs to access the Oracle database.

First, my initial code:
use DBI; $ENV{ORACLE_HOME} = '/common/oracle/product/v8.1.6'; $ENV{LD_LIBRARY_PATH} = "$ENV{ORACLE_HOME}/lib"; $ENV{TNS_ADMIN} = '/common/oracle/env'; $dbh = DBI->connect('','scmuser/scmuser@itopstst','','Oracle'); die $DBI::errstr unless defined $dbh;
This fails with the following error:
install_driver(Oracle) failed: Can't load '/app/eol/perl/lib/site_perl +/5.005/sun4-solaris/auto/DBD/Oracle/Oracle.so' for module DBD::Oracle +: ld.so.1: /app/eol/perl/bin/perl: fatal: libclntsh.so.8.0: open fail +ed: No such file or directory at /app/eol/perl/lib/5.00503/sun4-solar +is/DynaLoader.pm line 169. at (eval 1) line 3 at ./tst line 15

This file, is indeed in $LD_LIBRARY_PATH/libclntsh.so.8.0. As evidence, if I set LD_LIBRARY_PATH in my shell before executing this script, it works fine. That led me to try this with a BEGIN block:
BEGIN { $ENV{ORACLE_HOME} = '/common/oracle/product/v8.1.6'; $ENV{LD_LIBRARY_PATH} = "$ENV{ORACLE_HOME}/lib"; $ENV{TNS_ADMIN} = '/common/oracle/env'; } use DBI; $dbh = DBI->connect('','scmuser/scmuser@itopstst','','Oracle'); die $DBI::errstr unless defined $dbh;
...which still doesn't work.

Anyone know what I'm doing wrong?
Thanks,
Shendal

Comment on DBI, DBD::Oracle and LD_LIBRARY_PATH
Select or Download Code
Re: DBI, DBD::Oracle and LD_LIBRARY_PATH
by lhoward (Vicar) on Aug 18, 2000 at 21:02 UTC
    If you are running on Solaris (it looks to me like you are) LD_LIBRARY_PATH is cached by the linker when your script is first started. The effect of this is that any changes made to LD_LIBRARY_PATH inside your program (even in a BEGIN block) will not be reflected in how the linker searches for libraries that need to be dynamically linked.

    The only decent solution I ever came up with was to ensure that LD_LIBRARY_PATH was set before my perl program was ever started. Not very elegant; but the problem isn't perl's fault.

      Yes, I am indeed running Solaris. Is it possible to do something like this then, in order for the script to retain control?
      #!/app/eol/perl/bin/perl -- # -*-Perl-*- BEGIN { unless ($ENV{BEGIN_BLOCK_DONE}) { $ENV{ORACLE_HOME} = '/common/oracle/product/v8.1.6'; $ENV{LD_LIBRARY_PATH} = "$ENV{ORACLE_HOME}/lib"; $ENV{TNS_ADMIN} = '/common/oracle/env'; $ENV{BEGIN_BLOCK_DONE} = 1; exec $0 @ARGV; } } use DBI; $dbh = DBI->connect('','scmuser/scmuser@itopstst','','Oracle'); die $DBI::errstr unless defined $dbh;
      I've never run an exec in a BEGIN block... I don't know what possible caveats there may be.

      Thanks,
      Shendal

      Update: Well, I tried it and it still doesn't work. ARGH!
        The problem is even if you do it that way the linker already has LD_LIBRARY_PATH cached and you can't change it. The linker is started (and caches LD_LIBRARY_PATH) as soon as perl is started. You really need to set LD_LIBRARY_PATH before perl even starts. Other that just setting LD_LIBRARY_PATH in the environment the only solution I came up with was to set the environment, fork, and have the new process do the DB work. But that was kind of hokey and I didn't like spawning the second process to do the work. I tried to look for ways of chainging solaris's linker to not cache LD_LIBRARY_PATH but never got very far.

        BTW, LD_LIBRARY_PATH (and any other environmental vars used by the linker) are the only ones you need to worry about. You're OK setting other environmental vars (ORACLE_HOME, TNS_ADMIN, etc...) in your perl script. The problem is strictly related to the behavior of solaris's linker.

      After much work (and tye and lhoward's assistance), here's what I've got to work:
      BEGIN { unless ($ENV{BEGIN_BLOCK}) { $ENV{ORACLE_HOME} = '/common/oracle/product/v8.1.6'; $ENV{LD_LIBRARY_PATH} = "$ENV{ORACLE_HOME}/lib"; $ENV{TNS_ADMIN} = '/common/oracle/env'; $ENV{BEGIN_BLOCK} = 1; exec 'env',$0,@ARGV; } } use DBI; $dbh = DBI->connect('','scmuser/scmuser@itopstst','','Oracle'); die $DBI::errstr unless defined $dbh;
      lhoward says this is a problem with the way Solaris handles locating dynamically loaded libraries. ld.so.1(1) caches the LD_LIBRARY_PATH, so any changes we make don't matter. env(1) forces this to update.

      *phew*
        Hi,
        I added the same lines in my perl program . BEGIN { unless ($ENV{BEGIN_BLOCK}) { $ENV{ORACLE_HOME} = '/usr/local/lib/instantclient_10_2'; $ENV{LD_LIBRARY_PATH} = '/usr/local/lib/instantclient_10_2'; #$ENV{TNS_ADMIN} = '/common/oracle/env'; $ENV{BEGIN_BLOCK} = 1; exec 'env',$0,@ARGV; } } I am getting the below error message env: this is not a file and directory Please help
        Thanks Sri

        I ran into this problem recently on Linux. (I think it was triggered by an update of the Oracle InstantClient package; it used to run okay with caring about LD_LIBRARY_PATH. I'm not certain of that, though.)

        I ended up using the solution above, but changing the first condition from

          unless ($ENV{BEGIN_BLOCK}) {

        to

          unless (($ENV{BEGIN_BLOCK}) or $^C) {

        so that my habitual "perl -c script.pl" reflexes would work. Without that change, the BEGIN block will re-execute the script in fully-operational mode, which is almost certainly not what you want.

        (Of course, in versions of Perl that have it - certainly 5.6.0 onward, maybe later revisions of 5.005 - you should just use an INIT block instead. But that wasn't an option when the question was originally asked.)

        Thanks, this sparked a recollection and I got a solution working that didn't require exec() or messy wrappers. In fact, it is desirable in context of this code to put the "use DBD::Oracle;" in a module, so the exec was questionable for me anyway...

        Use a .conf file and ldconfig. RedHat and others support this nowadays. (Google knows more about this than I do, so I leave the research to the reader.) Now on my "standard platforms" I have a .conf file listing the various LD_LIBRARY_PATHs I use, including the standardized path to Oracle client, and did ldconfig to reload that cache, and all is right with the world. No fussing with wrappers or messy execs.

        Now if only we could prod Perl to force a reload of that cache (and subsequently its own), we wouldn't have an OS dependency...

      I know this is an old post, but it seems to still be relevant. On both Solaris and Linux, I have always used a wrapper shell script to set LD_LIBRARY_PATH etc.

      When I recently had to run a PERL script using Oracle as a CGI via an Apache web server, I found that I could use Apache's SetEnv directive to set these environmental vars in my httpd.conf file, like so:

      SetEnv LD_LIBRARY_PATH /lib64:/lib:/opt/oraClient/11.2.0.2/lib SetEnv ORACLE_HOME /opt/oraClient/11.2.0.2

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://28533]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (10)
As of 2014-04-23 18:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (552 votes), past polls