$START_PRI_AA=1;
$expression = "$SQLPLUS_DIR\\$SQLPLUS_EXEC -S $PLANSTAGE_DB_USER/$PLANSTAGE_DB_PASSWORD\#$PLANSTAGE_DB_ALIAS
'set pagesize 0
set feedback off
set verify off
set heading off
set echo off
select STATUS from jdaabppd.DFXHA_ENGINE_STATUS where ENGINE_NAME ='$ENV{PRI_AA_ENGINE}';
exit;
/'
";
print "\n\n expression is $expression \n\n";
$status = system($expression);
print "$status\n\n";
Why use SQLPLUS from within Perl, while it already has excellent modules to interact with databases ?
First of all you need to install modules DBI and DBD::Oracle, and then you can do something like :
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect(
"dbi:Oracle:host=locahost;port=1521;sid=$PLANSTAGE_DB_ALIAS", # DSN of the database to connect
$PLANSTAGE_DB_USER, # username
$PLANSTAGE_DB_PASSWORD, # password
{ RaiseError => 1 } # die on any DBI error
);
my ($status) = $dbh->selectrow_array(
"select STATUS from jdaabppd.DFXHA_ENGINE_STATUS where ENGINE_NAME ='?", # your sql query
undef, # no specific options needed
$ENV{PRI_AA_ENGINE} # bind value
);
You may need to adjust the DSN according to your use case, I made a few assumptions based on the code snippet you showed. Read the DBI docs for more details.
Related
I'm trying to execute SQL script that has lot of DML commands using Perl.
Basically there is some reference number which is required to be updated every time in SQL script before it executes, so I'm passing it as a parameter to Perl script which will further replace with parameter and execute. I'm doing this task in IntelliJ IDEA 2022.3.1 (Community Edition).
Problem: Unable to understand but Database connectivity is completely fine, and is fetching SQL script correctly, even replacement of reference number is also fine. But when trying to execute, it's not doing any changes in the Database. It looks like SQL commands are not really executing (as it is executing quickly which is not supposed to). I added warn with execute, so I got that warn message, but there is no error/exception message.
Please try to understand more details via code.
If anywhere I'm doing it wrong, please tell me. I have basic knowledge of Perl so might be doing it wrong. Can't provide more details about SQL script, it is confidential. But I can make sure that there is no error, as it is working fine if execute in it's dedicated tool. Thank you in advance!
#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';
use DBI;
# Connect to the database
my $EnvDatabase = $ENV{"DATABASE"};
if (! defined $EnvDatabase) {
&ERROR ("The environment variable DATABASE is not defined");
exit (-1);
}
print "Updating in Database: $EnvDatabase\n" ;
my ($DBUser, $DBPass, $SID) = split (/\/|\#/,$EnvDatabase);
if (! (defined $DBUser && defined $DBPass && defined $SID )) {
&ERROR ('The environment variable DATABASE is not properly defined');
exit (-1);
}
my $dbh = DBI->connect( "dbi:Oracle:".$SID, $DBUser, $DBPass,{AutoCommit=>0,PrintError=>0})
|| &errorExit( "Unable to connect to database [$DBI::errstr] ");
# Get the input parameters
my $param1 = shift;
my $param2 = "###123###";
# Read the SQL file into a variable
my $sql = do {
local $/ = undef;
open my $fh, "<", "C:/someScript/someSampleSqlWithDMLCommands.sql" or die "could not open file: $!";
<$fh>;
};
# Replace with the parameter
$sql =~ s/$param2/$param1/g;
# Execute the SQL statements
$dbh->do($sql) || warn("Unable to execute ", $dbh->errstr);
$dbh->commit;
$dbh->disconnect;
OUTPUT: Updating in Database: database name declared in environment
Unable to execute ORA-00900: invalid SQL statement (DBD ERROR: OCIStmtExecute) at C:/**/autoScript.pl line 37.
Process finished with exit code 0
I am unfamiliar with linux/linux environment so do pardon me if I make any mistakes, do comment to clarify.
I have created a simple perl script. This script creates a sql file and as shown, it would execute the lines in the file to be inserted into the database.
#!/usr/bin/perl
use strict;
use warnings;
use POSIX 'strftime';
my $SQL_COMMAND;
my $HOST = "i";
my $USERNAME = "need";
my $PASSWORD = "help";
my $NOW_TIMESTAMP = strftime '%Y-%m-%d_%H-%M-%S', localtime;
open my $out_fh, '>>', "$NOW_TIMESTAMP.sql" or die 'Unable to create sql file';
printf {$out_fh} "INSERT INTO BOL_LOCK.test(name) VALUES ('wow');";
sub insert()
{
my $SQL_COMMAND = "mysql -u $USERNAME -p'$PASSWORD' ";
while( my $sql_file = glob '*.sql' )
{
my $status = system ( "$SQL_COMMAND < $sql_file" );
if ( $status == 0 )
{
print "pass";
}
else
{
print "fail";
}
}
}
insert();
This works if I execute it while I am logged in as a user(I do not have access to Admin). However, when I set a cronjob to run this file let's say at 10.08am by using the line(in crontab -e):
08 10 * * * perl /opt/lampp/htdocs/otpms/Data_Tsunami/scripts/test.pl > /dev/null 2>&1
I know the script is being executed as the sql file is created. However no new rows are inserted into the database after 10.08am. I've searched for solutions and some have suggested using the DBI module but it's not available on the server.
EDIT: Didn't manage to solve it in the end. A root/admin account was used to to execute the script so that "solved" the problem.
First things first, get rid of the > /dev/null 2>&1 at the end of your crontab entry (at least temporarily) so you can actually see any errors that may be occurring.
In other words, change it temporarily to something like:
08 10 * * * perl /opt/lampp/htdocs/otpms/Data_Tsunami/scripts/test.pl >/tmp/myfile 2>&1
Then you can examine the /tmp/myfile file to see what's being output.
The most likely case is that mysql is not actually on the path in your cron job, because cron itself gives a rather minimal environment.
To fix that problem (assuming that's what it is), see this answer, which gives some guidelines on how best to expand the cron environment to give you what you need. That will probably just involve adding the MySQL executable directory to your PATH variable.
The other thing you may want to consider is closing the out_fh file before trying to pass it to mysql - if the buffers haven't been flushed, it may still be an empty file as far as other processes are concerned.
The expression glob(".* *") matches all files in the current working
directory.
- http://perldoc.perl.org/functions/glob.html
you should not rely on the wd in a cron job. If you want to use a glob (or any file operation) with a relative path, set the wd with chdir first.
source: http://www.perlmonks.org/bare/?node_id=395387
So if your working directory is, for example /home/user, you should insert
chdir('/home/user/');
before the WHILE, ie:
sub insert()
{
my $SQL_COMMAND = "mysql -u $USERNAME -p'$PASSWORD' ";
chdir('/home/user/');
while( my $sql_file = glob '*.sql' )
{
...
replace /home/user with wherever your sql files are being created.
It's better to do as much processing within Perl as possible. It avoids the overhead of generating a separate shell process and leaves everything under the control of the program so that you can handle any errors much more simply
Database access from Perl is done using the DBI module. This program demonstrates how to achieve what you have written using the mysql utility. As you can see it's also much more concise
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $host = "i";
my $username = "need";
my $password = "help";
my $dbh = DBI->connect("DBI:mysql:database=test;host=$host", $username, $password);
my $insert = $dbh->prepare('INSERT INTO BOL_LOCK.test(name) VALUES (?)');
my $rv = $insert->execute('wow');
print $rv ? "pass\n" : "fail\n";
I have two scripts. One is named sqlscript.sql and the other is named script.sh I have all of the queries needed written in my sql script. They are just a bunch of update statements. For example:
UPDATE xxDev.SYS_PARAMS SET val = 'serverName' WHERE lower(name) = 'enginebaseurl';
I'm running the .sql script IN the .sh script. When the .sh script runs, I want it to prompt the user for a server name and take that user input and replace it in serverName in the sql statements.
I'm brand new to both bash scripting and this website, so I hope I'm making sense asking this question. I'm using PuTTY if that makes a difference at all.
Suppose you use MySQL, try something like:
# TODO: prompt user for server name and store it into variable serverName
serverName="get from user"
cat <<"EOF" | mysql -u user1 -p passwd -h server1 -P 3306 -D db1
UPDATE xxDev.SYS_PARAMS SET val = '$serverName' WHERE lower(name) = 'enginebaseurl';
EOF
So in this example, you embed the sql script into the .sh so that you don't have to maintain two files.
I would probably use a variable
set #val 'serverName'
UPDATE xxDev.SYS_PARAMS SET val = #val WHERE lower(name) = 'enginebaseurl';
You can split the sqlscript.sql into
set-val.sql
set #val 'serverName'
and the actual update statements. Then you can recreate the set-val.sql from your user input:
echo -n "enter server: "
read server
echo "set #val '$server' > set-val.sql
and then you forward both files to mysql:
cat set-val.sql sqlscript.sql | mysql
You should probably use this only for internal things, it seems a little fragile.
I'm going let you figure out how to pass a shell parameter into your sql command, but here's an incredibly cool way to query the user for the server name. It might even be POSIX compliant.
#!/bin/sh
echo -n "Hit me with that server name: "; read serverName
echo "${serverName}! Outstanding! Pick up \$200 when you pass Go!"
I am trying to connect with database and perform some SQL queries by using this code, but every time it hangs.
my $connect_str = `/osp/local/etc/.oralgn $srv_name PSMF`;
my $sqlFile = "/osp/local/home/linus/amit/mytest.sql";
my ($abc, $cde)= split (/\#/ , $connect_str );
print "$abc";
$ORACLE_SID=SDDG00;
`export $ORACLE_SID`;
#chomp($abc);
#$abc=~ s/\s+$//;
`sqlplus $abc`;
open (SQL, "$sqlFile");
while (my $sqlStatement = <SQL>) {
$sth = dbi->prepare($sqlStatement)
or die (qq(Can't prepare $sqlStatement));
$sth->execute()
or die qq(Can't execute $sqlStatement);
}
How do I invoke a SQL command inside Perl?
Reading the documentation for the DBI module would be a good start.
Your problem seems to be this line.
$sth = dbi->prepare($sqlStatement)
You're trying to call the prepare method on the class "dbi". But you don't have a class called "dbi" in your program (or, at least, I can't see one in the code you've shown us).
To use a database from Perl you need to do these things:
1/ Load the DBI module (note, "DBI", not "dbi" - Perl is case sensitive).
use DBI;
2/ Connect to the database and get a database handle (Read the DBD::Oracle documentation for more details on the arguments to the connect() method).
my $dbh = DBI->connect('dbi:Oracle:dbname', $user, $password);
3/ You can then use this database handle to prepare SQL statements.
my $sth = $dbh->prepare($sqlStatement);
I have tried the following script to connect to the DB.
This way works in oracle,but i am not sure why it is not working for Sybase.
#!/usr/bin/perl
use strict;
use warnings;
my $result = qx { isql -Uxx -Pxxxxxxx -Dxxxxx <<EOF
select count(*) from XXX;
exit;
EOF
};
print "result is :";
print $result;
print "\nbye bye\n";
I tried to connect to sybase DB without DBI.
Please dont tell me to use DBI.even i have know that we can use DBI for this.but unfortunately DBI is not installed here in my server and i am not the admin where i have a authorization to install modules for perl.what ever given to me i have to fully use it. but thats an off topic here.
I repeat the question:
how to connect to sybase DB from perl without using DBI?
The output of the above script is :
> temp.pl
result is :
bye bye
when i manually execute the same thing:
> isql -Uxx -Pxxxxxxxx -Dxxxxx
1> select count(*) from XXX
2> go
-----------
26
(1 row affected)
1> exit
>
I got the solution:
it is because of the semicolon and the go statement.I modified the script as below and its working now.
#!/usr/bin/perl
use strict;
use warnings;
my $result = qx { isql -Uxx -Pxxxxxxx -Dxxxx <<EOF
set nocount on
select count(*) from XXX
go
exit
EOF
};
print "result is :";
print $result;
print "\nbye bye\n";