This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
error of importing DBI in Perl
I have a problem when I use the DBI module in another module, script.pm.
package CC;
use DBI;
use strict;
use Alias;
my $dbFile = 'XXXXXXXX.db';
my $db = DBI->connect("dbi:SQLite:$dbFile","","",
{RaiseError =>1, AutoCommit => 1})or "Unable to connect: $DBI::errstr\n";
use Alias qw(attr);
our ($CURRENTOFFSET,#LANGUAGE);
sub new {
my $that = shift;
my $class = ref($that)|| $that;
my $self = {
CURRENTOFFSET=> undef,
LANGUAGE => []
};
bless($self, $class);
return $self;
}
Substantive
Conventionally, a package XYZ is kept in a file XYZ.pm; Perl won't find your package otherwise. Thus, your file should be CC.pm rather than script.pm.
Note that a package Organization::Team::Purpose is kept in a file Purpose.pm, but the file is kept in a sub-directory Organization/Team and the base directory holding Organization has to be found by Perl (using -I/some/where if Organization is a sub-directory of the directory /some/where, for example; if it is a sub-directory of the current directory, it will be found anyway).
You should probably review the or clause after your connection attempt. Normally, you do a die or croak there. You simply evaluate a string, which is not very useful.
You have:
my $db = DBI->connect("dbi:SQLite:$dbFile","","",
{RaiseError =>1, AutoCommit => 1})or "Unable to connect: $DBI::errstr\n";
You should consider what to do, but one technique is:
use Carp;
my $db = DBI->connect("dbi:SQLite:$dbFile", "", "",
{ RaiseError => 1, AutoCommit => 1 })
or croak "Unable to connect: $DBI::errstr\n";
The downside of that is that this is going into a module, and it isn't necessarily a good idea to croak in the BEGIN code of a module (and I'm making an assumption that the code is executed as the module is loaded). You might need to store the undef database handle and protect other methods from using it. You might be better off deferring the 'connect to database' operation until the constructor new is used (possibly for the first time). It is at least legitimate to raise errors at that point.
As the answer by DVK noted (before I wrote my answer), modules should end with 1; to indicate successful loading. Maybe you can exploit that to report an error on failure to load - the final condition might be 'defined $db ? 0 : 1;' (or even just 'defined $db;'), but it would be crucial to generate an error message somehow to explain the problem.
Trivia
You should be ruthlessly consistent in the spacing around operators, too. Your example includes:
{RaiseError =>1, AutoCommit => 1}
my $class = ref($that)|| $that;
CURRENTOFFSET=> undef,
which would be better written as:
{RaiseError => 1, AutoCommit => 1}
my $class = ref($that) || $that;
CURRENTOFFSET => undef,
The first might benefit from a little more space:
{ RaiseError => 1, AutoCommit => 1 }
It doesn't directly affect the operation of the code. It does make it a little less easy to read. Learning to be consistent is an important part of learning to program.
Judging by the fact that this is a package, and your code sample doesn't end with "1;", try adding a last line to your .pm file as follows:
1;
Perl modules must return a true value upon evaluation via do, to be loaded successfully via use or require:
The file must return true as the last statement to indicate successful execution of any initialization code, so it's customary to end such a file with 1; unless you're sure it'll return true otherwise. But it's better just to put the 1; , in case you add more statements.
Related
I overrode CORE::GLOBAL::die as described in brian d foy's article on the subject:
BEGIN {
*CORE::GLOBAL::die = sub {
say "In CORE::GLOBAL::die: [#_]";
CORE::die(#_) if $^S;
exit 1;
}
}
The ultimate goal is to write fatal errors to a log, but this is good enough for now. Let's create a database handle with RaiseError turned on and do something to trigger an error:
use DBI;
# Wrong password
my $dbh = DBI->connect(..., { PrintError => 0, RaiseError => 1 });
Outputs:
In CORE::GLOBAL::die: [DBI connect(...) failed: Access denied for user ...
So far so good. Let's throw some bad SQL at it instead:
use DBI;
my $dbh = DBI->connect(..., { PrintError => 0, RaiseError => 1 });
my $sth = $dbh->prepare('SLECT 1');
$sth->execute;
Outputs:
DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SLECT 1' at line 1 at ...
Hmmm, not what I expected (there's no In CORE::GLOBAL::die message). According to the docs, when RaiseError is on,
any method which results in an error will cause the DBI to effectively do a die("$class $method failed: $DBI::errstr")
I guess the key word is effectively, since I would expect an actual call to die to invoke my version of CORE::GLOBAL::die.
I can force all errors to die by setting HandleError:
my $dbh = DBI->connect(..., { HandleError => sub { die shift; } })
But since RaiseError "can be used to force errors to raise exceptions rather than simply return error codes in the normal way," I don't understand why this is necessary. Why doesn't setting RaiseError always result in a call to CORE::GLOBAL::die on error? Isn't that the whole point of using it?
die is a Perl opcode (not a subroutine). It can't simply be called. Parts of DBI and DBD modules are written in C, so they wouldn't be able to use die (without calling back into Perl code as in your workaround).
You can still hook $SIG{__DIE__}, though. Like the article says, GLOBAL::CORE::die is for when "you really need to catch calls to die" (as opposed to catching exceptions being thrown).
I want to use Perl module and wrap it into a standalone socket which would publish the subroutines from the module to other programmes. However, I probably cannot overcome namespace issues, since in the client script, I am still getting an error message:
RPC::Serialized::X::Application\',\'MESSAGE\' => \'No handler for 'predejPOS' .
My server script:
use RPC::Serialized::Server::NetServer::Single;
use RPC::Serialized::Handler::HashTree;
my $s = RPC::Serialized::Server::NetServer::Single->new({
net_server => {log_file => '', port => 20203 },
rpc_serialized => {handler_namespaces => ''},
});
$s->run;
My client script:
use RPC::Serialized::Client::INET;
my $client = RPC::Serialized::Client::INET->new({
io_socket_inet => {PeerAddr => '127.0.0.1', PeerPort => 20203,}
});
my $result = $client->predejPOS('flu-like');
My module (HastTree.pm):
package RPC::Serialized::Handler::HashTree;
require Exporter;
#ISA = qw(Exporter);
#EXPORT = qw(predejPOS);
use base 'RPC::Serialized::Handler';
our $VERSION = '0.01';
sub predejPOS {
my %POS;
$POS{'flu-like'}='<JJ>';
return $POS{$_[0]};
};
1;
I am using Windows 7, Strawberry Perl 5.12.3, and the module sits on the correct address
(C:\PROGS\Strawberry\perl\site\lib\RPC\Serialized\Handler). The function predejPOS is recognised inside the server script (ie. I can print its result from it), but I cannot access it through the client-server communication. I assume that it has something to do with the subtle difference between calling the function and calling the method. I am afraid that it is probably something simple, but even after a substantial effort and googling I was not able to make it work.
Thanks in advance!
Well I eventually solved it by myself:
First, I got completely wrong the concept of calling functions - over the network you can only call so called RPC Handlers. Moreover for each handler there must be a module in RPC::Serialized::Handler directory with the same name and a specific structure with only one subroutine inside called invoke(). Thus I changed my module (named now 'PredejPOS.pm') to:
package RPC::Serialized::Handler::PredejPOS;
{
$RPC::Serialized::Handler::PredejPOS::VERSION = '0.01';
}
use strict;
use warnings FATAL => 'all';
use base 'RPC::Serialized::Handler';
sub invoke {
my $self = shift;
my $key = shift;
my %POS;
$POS{'flu-like'}='<JJ>';
return scalar $POS{$key};
}
1;
But it was still not working.
Finally secondly I found that under Windows environment, the Perl Data::Serialize module does not work properly.
In the package Serialized.pm, subroutine recv (row 115), the chomp does not remove the damned Windows line ending '\cM'. When I corrected it, it started working as envisaged. Actually there is a lenghty discussion of this behaviour here ( http://www.perlmonks.org/?node_id=549385 )
Thanks for the suggestions.
For object method the first argument is always the current object instance itself. Sorry, if I am not clear enough, try to figure out the difference from this example:
Try this:
sub predejPOS {
my $self = shift;
my $key = shift;
my %POS;
$POS{'flu-like'}='<JJ>';
return $POS{$key};
};
I have a perl script that uses the CGI::Session::Drive::memcached, but I want to be able to fallback on the default Session driver or another driver if it's available on the system...
This is how I started off using Memcache, but this doesnt necessarily solve the problem of the case when Cache::Memecached and/or CGI::Session::Driver::memcached are not available...
package MySession;
use Moose::Role;
use Moose::Util::TypeConstraints;
use namespace::autoclean;
use CGI::Session ('-ip_match');
use CGI::Session::Driver::memcached;
use Cache::Memcached::Fast;
#would be nice to create this conditionally, or use a delegate maybe
has 'memeCached' => (
is => 'rw',
isa => 'Maybe[Cache::Memcached::Fast]',
default => sub{ return Cache::Memcached::Fast->new( {'servers' => [ '10.x.x.x.:10001' ],'compress_threshold' => '100000','nowait' => 1,'utf8' => 1} ) },
);
sub buildSession{
my($this,$cgi,$sessionDir) = #_;
$cgi = $cgi || $this->getCGI();
my $sid = $this->SID();
my $mem = $this->memeCached();
my $sss;
if(!$mem){
$sss = CGI::Session->load(undef, $cgi, {Directory=>$sessionDir}) or die CGI::Session->errstr();
}else{
$sss = CGI::Session->load( "driver:memcached", $cgi, { Memcached => $mem }) or die CGI::Session->errstr();
}
...
Then this got me thinking, how do I do this -- in a general sense? or what's the best way to do this (especially using Moose)?
I had a similar situation. We use Windows domains, which I can connect to Net::LDAP. In my program, I want to be able to take the user ID jsmith, and instead of printing on the user ID, I want to be able to print out the name John Smith.
Many people at my company use my program, but not all are Perl experts and most wouldn't know how to install a Perl module. And, since Net::LDAP is not a standard module, many people don't have it.
Instead, I wanted a fallback routine. If I could look up the name with Net::LDAP, I would print the name, if I couldn't load Net::LDAP, I would fallback and just print the user ID.
I used the following for testing if Net::LDAP was installed, and load it if possible:
BEGIN {
eval { require Net::LDAP; };
our $Net_Ldap_Status = 1 if (not $#);
}
What you have to understand is that:
use Foo::Bar;
is the same as:
BEGIN {
require Foo::Bar;
}
It loads in the module at compile time. By surrounding the require with an eval I can test whether the statement succeeds (and the module is loaded) or fails (the module doesn't load, but the program doesn't crash either.) I can then check $# to see if the module loaded or not. $# is the error message that eval sets. If $# is null, then the module exists and was loaded successfully.
I need to use a package variable (the our $Net_Ldap_Status instead of my $Net_Ldap_Status) or the variable will be lost when the program runs. (I'm not even sure if my $Net_Ldap_Status would work in a BEGIN statement).
Now, here's where things get funky...
When I need to check $Net_Ldap_Status, I need to redeclare it:
our $Net_Ldap_Status;
or I tend to get that non-declared variable error. The funny thing is that it doesn't lose its previous value by redeclaring it. Thus, somewhere in my code is:
our $Net_Ldap_Status;
if ($Net_Ldap_Status) {
print "Code if Net::LDAP is loaded.\n";
}
else {
print "Fallback Code if no Net::LDAP\n";
}
Is there a way in Perl to declare that a method can throw an error (or die)?
EDIT: What interests me the most is a way to get the compiler or IDE to tell me I have an unchecked exception somewhere in my code.
I always loved how in Java, a method could handle an Exception and/or throw it. The method signature allows to put "throws MyException", so a good IDE/compiler would know that if you use said method somewhere in your code, you'd have to check for the Exception or declare your function to "throws" the Exception further.
I'm unable to find something alike in Perl. A collegue of mine wrote a method which "dies" on incorrect input, but I forget to eval-if($#) it... offcourse the error was only discovered while a user was running the application.
(offcourse I doubt if there is any existing IDE that could find these kind of things for Perl, but atleast perl -cw should be able to, no?)
Two potential answers. Pick whichever you like better:
In Perl, this is indicated by the module's POD. There's no way of marking it programmatically, so you need to rely on the documentation instead.
Any method can die, or at least any nontrivial method. It's going to call something else, which probably calls something else, etc., so the only way to guarantee that no exception will be thrown is to trace down through all the levels of (potential) calls to verify that there's nothing there that might die. Much more pragmatic to just assume that exceptions are always a possibility and code accordingly.
Edited to add: As a general rule, Perl5 and static code analysis don't really get along all that well. My understanding is that this is one of the motivations behind the language redesign in Perl6, so you may have better luck there.
Not seen anything like this but perhaps subroutine attributes may get your part of the way?
Here is a small proof of concept using Attribute::Handlers
ThrowsExceptionHandler.pm
package ThrowsExceptionHandler;
use Modern::Perl;
use Attribute::Handlers;
our #subs;
sub ThrowsException :ATTR(CODE) {
push #subs, {
package => $_[0],
symbol => $_[1],
subname => *{$_[1]}{NAME},
referent => $_[2],
attr => $_[3],
data => $_[4],
phase => $_[5],
filename => $_[6],
linenum => $_[7],
};
}
sub does_throw {
my ($class, $subname) = #_;
(grep { $_->{subname} eq $subname } #subs) ? 1 : 0;
}
1;
example.pl
use Modern::Perl;
use base qw(ThrowsExceptionHandler);
sub baz :ThrowsException {
die "Throws error";
}
sub foo {
warn "warning only";
}
say ThrowsExceptionHandler->does_throw( 'baz' ); # => 1
say ThrowsExceptionHandler->does_throw( 'foo' ); # => 0
Perhaps (a mixture of) PPI, Perl::Critic and/or Padre can be adapted to use something like this?
/I3az/
Have you checked CPAN? Error::TryCatch is one option, Exception::Class is another, etc. etc.
Also, see Object Oriented Exception Handling in Perl.
from document "Exceptions"
$# doesn't tell us where the error occurred
We can get around this with a custom function:
sub throw {
my $mess = join('', #_);
$mess =~ s/\n?$/\n/;
my $i = 1;
local $" = "', '";
package DB;
while (my #parts = caller($i++)) {
my $q; $q = "'" if #DB::args;
$mess .= " -> $parts3" .
" at $parts1 line $parts2\n";
}
die $mess;
}
With that you can also take references from "CPAN" and "Object Oriented Exception Handling in Perl"
my $now = &GetDate;
my $myHeader = &ReadMessage;
my $mySoftwareVersions = param('mySoftwareVersions');
my $q = new CGI;print $q->header();
use CGI::Carp(fatalsToBrowser);
getAllSoftwareVersions();
sub getAllSoftwareVersions
{
my $user = "zxxx";
my $passwd = "xxxx";
# my $tableName = "config_table";
# my $connection = DBI->connect("DBI:mysql:MESCI:hamysql02.stl.mo.boeing.com:18080", $user, $passwd, { AutoCommit => 0, RaiseError => 1}) or die "Couldn't connect to Database: " . DBI->errstr;
print "Must be connected\n\n";
print "\n\n";
# Error here.
my #Rows = &getConfigTableRows($connection, $config_table, $mySoftwareVersions );
my $total = #Rows;
print "total is ";
print $total;
The Above code dies with:
Global symbol "$connection" requires explicit package name
Edit
This question is related to :
https://stackoverflow.com/questions/682695/how-do-i-resolve
https://stackoverflow.com/questions/681557/xml-cannot-be-displayed-error-from-perl-cgi-script-using-mysql
Hopefully the original poster will be able to clean this up so it makes more sense, but here's what we've got so far so we can attempt to help.
Although Kent's answer is thinking ahead, the error is simply telling you that you did not declare $connection as a lexical ( "my" ) variable. So therefore, perl interprets it as that you must be referring to a package global.
Raw Perl does not complain when you use an undefined variable, it considers it a package global. You appear to have strict on somewhere (a standard and recommended practice), which wants you to declare your variables before using them. If you didn't declare the variable in the current package (or "namespace"), it assumes you're referring to a variable declared in another package, so it asks you to append the package name, just to keep everything clear and aboveboard.
Perl uses my to declare scoped variables, and our to declare package globals.
my $connection = "Rainbow";
OR
our $connection = 'French';
Just in case you got the wrong idea, the error message would go away if you turned strict off, your problem wouldn't. And they might go underground.
{ no strict;
my #rows = getConfigTableRows( $nothing, #other_stuff );
}
Perl just won't complain that $nothing is nothing. And this easy-to-fix error could cause subtler errors in other places. Plus think if you had assigned $connection successfully, only to type:
{ no strict;
my #rows = getConfigTableRows( $connecion, $config_table, $mySoftwareVersions );
}
Perl gives you a message about '$connecion' and hopefully, you can tell that it was a typo, and forgo at least 30 minutes not seeing it and wondering if your query is wrong, or whatever.
Well, if this is the related content for this other question,
The reason this is erroring is because you have commented out the line that creates the connection variable.
How are you going to query the database for a table row when you don't have the database connection defined?
Re:
my $now = &GetDate;
my $myHeader = &ReadMessage;
If you are going to use & on your function calls, make sure you use parentheses too:
my $now = &GetDate();
my $myHeader = &ReadMessage();
Otherwise, the currently executing sub's parameters are made available to (and alterable by) the subroutine you are calling.
This means that if you use your cgi script under mod_perl, suddenly you are in effect doing
my $now = &GetDate( Apache2::RequestUtil->request );
which is likely to be very wrong if GetDate takes an optional argument.