I have a sub in a Perl module, Advancer.pm:
sub validate_extra {
my ($dbh, $customer_id, $site) = #_;
my $sth = $dbh->prepare(qq{
SELECT key
FROM master
WHERE
code = ?
AND set = ?
AND type_id = 2
ORDER BY customer_id DESC, site DESC
LIMIT 1
});
$sth->execute($customer_id, $site);
But when I call this module from a test (.t) I get an error:
Can't call method "prepare" on an undefined value at Advancer.pm line 511.
my $sth = $dbh->prepare(qq{
is line 511.
You're passing an undefined value for the first parameter to validate_extra.
Most obviously this would happen if your call was just
validate_extra();
but you may have passed the wrong variable by mistake, or perhaps the original connect has failed but gone unchecked:
my $dbh = DBI->connect('DBI:mysql:database=mydb', 'user', 'pass', {PrintError => 0});
validate_extra($dbh, $customer, $site);
Related
I want to add trigger into my database. I use DBIx::Class and follow these examples: 1, 2.
My code is:
package App::Schema;
use base qw/DBIx::Class::Schema/;
__PACKAGE__->load_namespaces();
sub sqlt_deploy_hook {
my ($self, $schema) = #_;
$schema->add_trigger( name => 'foo' );
}
1;
But I get this error:
Failed to translate to YAML: translate: Error with producer 'SQL::Translator::Producer::YAML': Can't call method "name" on an undefined value at /home/kes/work/projects/x/app/local/lib/perl5/SQL/Translator/Schema/Trigger.pm line 198
When run command with all environment variables as required by dbic-migration:
dbic-migration --force --schema_class App::Schema --database PostgreSQL -Ilib prepare
Which point me somewhere into SQL::Translator::Schema::Trigger
What did I miss? How to fix this error?
UPD
Even when I add all arguments I got error:
Failed to translate to YAML: translate: Error with parser 'SQL::Translator::Parser::DBIx::Class': Table named users doesn't exist at /home/kes/work/projects/x/app/local/lib/perl5/SQL/Translator/Schema/Trigger.pm line 54
Here the target line:
my $table = $args->{schema}->get_table($arg)
or die "Table named $arg doesn't exist";
Modified code:
sub sqlt_deploy_hook {
my ($self, $schema) = #_;
warn "TABLES: " ,$schema->get_tables ,"\n";
$schema->add_trigger(()
,name => 'foo'
,perform_action_when => 'after'
,database_events => 'insert'
,on_table => 'users'
,action => 'text'
,scope => 'row'
);
}
This code produce next warnings:
TABLES: users
TABLES: dbix_class_deploymenthandler_versions
But DB has only one table at the moment. I expect it at least should produce:
TABLES: users dbix_class_deploymenthandler_versions
How to create trigger in DB?
There maybe the problem with DBIx::Class::ResultSource::default_sqlt_deploy_hook:
which was originally designed to expect the Result class name and the $sqlt_table instance of the table being deployed
As work around add next line of code before add_trigger:
return unless grep $_ eq 'users', $schema->get_tables;
But the recommend way is to create deploy/upgrade/downgrade .sql files manually
I invoke my perl script by passing a number of command line options. If the required command line options are not passed by the user while invoking the script, the script should terminate. Currently I'm doing a simple check using if statement. If the required arguments are more than 10, using If statement looks clunky. I'm just wondering if there is a better way to do it than just using an if statement.
Command line options :
sub startup {
my ($self) = #_;
GetOptions (
"endpoint|e=s" => \$self->{'endpoint'},
"port|pt=s" => \$self->{'port'},
"client|c=s" => \$self->{'client'},
"client_interface|ci=s" => \$self->{'client_interface'},
"origin|o=s" => \$self->{'origin'},
"origin_interface|oi=s" => \$self->{'origin_interface'},
"customer_id|cid=s" => \$self->{'customer_id'},
"endpoint_id|eid=s" => \$self->{'endpoint_id'},
) || $self->abort( "Invalid command line options.
Valid options are endpoint,port,client,client_interface,
origin,origin_interface,customer_id,endpoint_id");
#Terminate the script execution if --endpoint ip and --customer id and --client are not passed
if ( !$self->{'endpoint'} || !$self->{'customer_id'} || !$self->{'client'}){
$self->abort( '[Startup] endpoint customer and client are required arguments.'
. 'Please provide --endpoint and --customer id and -- client ');
}
command to invoke the script :
./testframework --scriptname -- --endpoint=198.18.179.42 --port=5000 --client=1.1.1.1 --client_interface=2.2.2.2 --origin=3.3.3.3 --origin_interface= --Outertunnel=Tunnel0 --Innertunnel=Tunnel2 --customer_id=900010 --endpoint_id=2859588
The version below removes some of the clunkiness while providing a more specific error message.
my #required = qw( endpoint customer_id client );
if ( my #missing = grep { !$self->{$_} } #required ) {
$self->abort("[Startup] Missing required arguments: #missing");
}
One way is to use all from List::Util:
unless ( all { defined $self->{$_} } qw(endpoint customer_id client) ){
# error
}
If you don't have a recent version of List::Util, use List::MoreUtils
Can you just check to see that you have the proper number of defined keys in your hash?
my #options = grep { defined $self->{$_} } keys %{$self};
die "Missing options\n" unless #options == 10;
Or if you want your usage statement to be more explicit:
for my $opt (keys %{$self}) {
die "Missing option --$opt\n" unless defined $self->{$opt};
}
Whenever I'm executing the program below, I get the error message Can't call method "execute" on an undefined value from following line:
$sth->execute($agent_name,$service_id,$call_start_time,$call_end_time);
but in same program I'm able to execute the first SQL query indicated in the comments below:
#!/usr/bin/perl -w
use strict;
use DBI;
my $DSN = q/dbi:ODBC:SQLSERVER/;
my $uid = q/ivr/;
my $pwd = q/ivr/;
my $DRIVER = "Freetds";
my $dbh = DBI->connect($DSN,$uid,$pwd) or die "Coudn't Connect SQL";
my $servernumber = 2;
my $service_name = "JM";
my $agent_name= 'Balaji';
my $call_start_time='2013-07-01 15:46:50.865';
my $call_end_time='2013-07-15 15:46:50.789';
my $call_rec_file_name;
my $rows_fund = $dbh->selectrow_array("select count(service_name) from cti_services where service_name='$service_name'");
my $rows_agent = $dbh->selectrow_array("select count(agent_name) from cti_agents where agent_name='$agent_name'");
# This query successfully executes:
my $sql_fund = "select service_id from cti_services where service_name='$service_name'";
my $sth_fund = $dbh->prepare($sql_fund);
$sth_fund->execute() or die $DBI::errstr;
my $service_id = $sth_fund->fetchrow();
print $service_id,"\n";
if( $rows_fund == 1 && $rows_agent == 1 )
{
my $sql="select top(10) service_name,agent_name,call_rec_file_name,call_start_time,call_end_time from cti_agents join cti_call_master on (agent_name = call_agent_name) join cti_services on (call_service_id = service_id) where agent_name = ? and call_rec_file_name is not null and service_id=? and call_start_time between ? and ?";
my $sth = $dbh->prepare($sql);
# The problem is with this query. I'm getting the error "Can't call method "execute" on an undefined value".
$sth->execute($agent_name,$service_id,$call_start_time,$call_end_time);
print "Service Name","Agent Name","Call Start Time ","Call End Time","Sound File " ;
while (my #data = $sth->fetchrow_array())
{
my ($service_name,$agent_name,$call_rec_file_name,$call_start_time,$call_end_time ) = #data;
print "$service_name","$agent_name ","$call_start_time ","$call_end_time "," $call_rec_file_name ";
}
}
else
{
print "<em>","There is no data found","</em>";
}
$dbh->disconnect;
What could be causing the error message?
$sth is undefined because your call to $dbh->prepare is failing for some reason.
If you replace your DBI->connect() call with the following, you'll get the error from the prepare call rather than it failing silently and bombing out when you try to call execute:
my $dbh = DBI->connect($DSN,$uid,$pwd, { RaiseError => 1 });
You can read more about RaiseError and the other attributes available to DBI calls here: https://metacpan.org/module/DBI#RaiseError
I must admit I can't immediately see the error, my hunch is an SQL syntax issue, but I don't speak SQLServer.
Change the first two lines inside the if to this
my $sth = $dbh->prepare(<<__SQL__) or die $dbh->errstr;
SELECT TOP(10) service_name, agent_name, call_rec_file_name, call_start_time, call_end_time
FROM cti_agents
JOIN cti_call_master ON (agent_name = call_agent_name)
JOIN cti_services ON (call_service_id = service_id)
WHERE agent_name = ?
AND call_rec_file_name IS NOT NULL
AND service_id = ?
AND call_start_time BETWEEN ? AND ?
__SQL__
then you will see the reason for the error.
Note that there must be no spaces before or after __SQL__.
Put quotes inside the sql for the datetimes, it may be seeing the white space during parsing?
I've been banging my head over this issue for about 5 hours now, I'm really frustrated and need some assistance.
I'm writing a Perl script that pulls jobs out of a MySQL table and then preforms various database admin tasks. The current task is "creating databases". The script successfully creates the database(s), but when I got to generating the config file for PHP developers it blows up.
I believe it is an issue with referencing and dereferencing variables, but I'm not quite sure what exactly is happening. I think after this function call, something happens to
$$result{'databaseName'}. This is how I get result: $result = $select->fetchrow_hashref()
Here is my function call, and the function implementation:
Function call (line 127):
generateConfig($$result{'databaseName'}, $newPassword, "php");
Function implementation:
sub generateConfig {
my($inName) = $_[0];
my($inPass) = $_[1];
my($inExt) = $_[2];
my($goodData) = 1;
my($select) = $dbh->prepare("SELECT id FROM $databasesTableName WHERE name = '$inName'");
my($path) = $documentRoot.$inName."_config.".$inExt;
$select->execute();
if ($select->rows < 1 ) {
$goodData = 0;
}
while ( $result = $select->fetchrow_hashref() )
{
my($insert) = $dbh->do("INSERT INTO $configTableName(databaseId, username, password, path)".
"VALUES('$$result{'id'}', '$inName', '$inPass', '$path')");
}
return 1;
}
Errors:
Use of uninitialized value in concatenation (.) or string at ./dbcreator.pl line 142.
Use of uninitialized value in concatenation (.) or string at ./dbcreator.pl line 154.
Line 142:
$update = $dbh->do("UPDATE ${tablename}
SET ${jobStatus}='${newStatus}'
WHERE id = '$$result{'id'}'");
Line 154:
print "Successfully created $$result{'databaseName'}\n";
The reason I think the problem comes from the function call is because if I comment out the function call, everything works great!
If anyone could help me understand what's going on, that would be great.
Thanks,
p.s. If you notice a security issue with the whole storing passwords as plain text in a database, that's going to be addressed after this is working correctly. =P
Dylan
You do not want to store a reference to the $result returned from fetchrow_hashref, as each subsequent call will overwrite that reference.
That's ok, you're not using the reference when you are calling generate_config, as you are passing data in by value.
Are you using the same $result variable in generate_config and in the calling function? You should be using your own 'my $result' in generate_config.
while ( my $result = $select->fetchrow_hashref() )
# ^^ #add my
That's all that can be said with the current snippets of code you've included.
Some cleanup:
When calling generate_config you are passing by value, not by reference. This is fine.
you are getting an undef warning, this means you are running with 'use strict;'. Good!
create lexical $result within the function, via my.
While $$hashr{key} is valid code, $hashr->{key} is preferred.
you're using dbh->prepare, might as well use placeholders.
sub generateConfig {
my($inName, inPass, $inExt) = #_;
my $goodData = 1;
my $select = $dbh->prepare("SELECT id FROM $databasesTableName WHERE name = ?");
my $insert = $dbh->prepare("
INSERT INTO $configTableName(
databaseID
,username
,password
,path)
VALUES( ?, ?, ?, ?)" );
my $path = $documentRoot . $inName . "_config." . $inExt;
$select->execute( $inName );
if ($select->rows < 1 ) {
$goodData = 0;
}
while ( my $result = $select->fetchrow_hashref() )
{
insert->execute( $result->{id}, $inName, $inPass, $path );
}
return 1;
}
EDIT: after reading your comment
I think that both errors have to do with your using $$result. If $result is the return value of fetchrow_hashref, like in:
$result = $select->fetchrow_hashref()
then the correct way to refer to its values should be:
print "Successfully created " . $result{'databaseName'} . "\n";
and:
$update = $dbh->do("UPDATE ${tablename}
SET ${jobStatus}='${newStatus}'
WHERE id = '$result{'id'}'");
OLD ANSWER:
In function generateConfig, you can pass a reference in using this syntax:
generateConfig(\$result{'databaseName'},$newPassword, "php");
($$ is used to dereference a reference to a string; \ gives you a reference to the object it is applied to).
Then, in the print statement itself, I would try:
print "Successfully created $result->{'databaseName'}->{columnName}\n";
indeed, fetchrow_hashref returns a hash (not a string).
This should fix one problem.
Furthermore, you are using the variable named $dbh but you don't show where it is set. Is it a global variable so that you can use it in generateConfig? Has it been initialized when generateConfig is executed?
This was driving me crazy when I was running hetchrow_hashref from Oracle result set.
Turened out the column names are always returned in upper case.
So once I started referencing the colum in upper case, problem went away:
insert->execute( $result->{ID}, $inName, $inPass, $path );
I've got the following code:
package MyPackage::ResultSet::Case;
use base 'DBIx::Class::ResultSet';
sub cases_last_fourteen_days {
my ($self, $username) = #_;
return $self->search({
username => $username,
date => { '>=' => 'DATE_SUB(CURDATE(),INTERVAL 14 DAY)' },
});
};
But when I try to use it this way:
$schema->resultset('Case')->cases_last_fourteen_days($username)
I always get zero results, can anyone tell what I'm doing wrong?
Thanks!
The way you use the SQL::Abstract condition would result in this where condition:
WHERE username = ? AND date >= 'DATE_SUB(CURDATE(),INTERVAL 14 DAY)'
When you wish to use database functions in a where clause you need to use a reference to a scalar, like this:
date => { '>=' => \'DATE_SUB(CURDATE(),INTERVAL 14 DAY)' },
ProTip: if you set the environment variable DBIC_TRACE to 1, DBIx::Class will print the queries it generates to STDERR ... this way you can check if it really does what you wish.