Running oracle script as oracle user from a shell script that runs as root - sql

I have a shell script that runs as root. I want the script to switch to oracle user, run sqlplus and run some .sql files.
I am trying to followung :
su - oracle << -EOF1 2>&1
sqlplus $user/$password << -EOF2
#oracle.sql;
#quartz.sql;
EOF2
EOF1
first of all i get stty: standard input: Inappropriate ioctl for device what does it mean ?
second, can someone explain to me how the redirect (should) work in this case ?
Thanks

Use:
if [ "$(id -un)" -eq "root" ]; then
exec su - oracle -c $0
fi
sqlplus <<EOF
blablabla
EOF
If your script potentially takes arguments, the solution will differ.
What this does it checking whether the user running is currently root. If so, it re-executes the script ($0) as user oracle instead.
But BTW, why does the script run as root in the first place?

su - oracle -c " echo 'select 1 from dual;
select 2 from dual;'| sqlpus / as sysdba "
if contain ' using following
su - oracle -c " echo \"select 1 from dual;
select 2 from dual;\" | sqlpus / as sysdba "

Related

How to pass a parameter from shell script to SQL script?

I have 2 scripts - one shell and one sql.
My shell script is similar to this:
export nbr=&1
runsql script_name.sql
Im trying to pass a parameter for nbr while running the script.
The corresponding sql script is as such:
insert into table1
select * from table2
where year='&1'
I get the error as below:
"enter value for year: old 22: where year='$1')
new 22: where year='commit')"
I'll show how to deal with it easily with an example based on my own scripts.
In this script I define a simple function run_sql(DESCR, SCRIPT, DATABASES), where
DESCR - short description
SCRIPT - sqlplus run arguments, ie script name and its parameters
DATABASES - list of dbname defined above on which you want to run it
Then we can easily use it like this:
run_sql "1st script" "#sql1.sql param1" db1 db2
run_sql "2nd script" "#sql2.sql param1 param2 param3" db1 db2 db3
Here we execute:
"sql1.sql" with 1 argument on db1 and db2
"sql2.sql" with 3 arguments on db1,db2 and db3
And we save all output into own log files.
Full example with test output: https://gist.github.com/xtender/465951befeed7f0ae1a3fe112dcd7fe4
Simple script test.sh:
#!/bin/bash
# Here you can define your db connection strings:
db1=xtender/pass#PDB19C_11
db2=xtender/pass#PDB19C_11
db3=xtender/pass#PDB19C_11
#####################################################################
# functions:
# Function syntax: run_sql(DESCR, SCRIPT, DATABASES)
# where
# DESCR - short description
# SCRIPT - sqlplus run arguments, ie script name and its parameters
# DATABASES - list of dbname defined above on which you want to run it
run_sql(){
local DESCR="$1"; shift
local SCRIPT="$1"; shift
local databases=("$#")
echo =================================================
echo = $DESCR
echo = Going to execute $SCRIPT...
read -a res -p "Enter 'skip' to skip this step or press Enter to execute: "
if [[ $res = "skip" ]]
then
echo Skipping $SCRIPT...
else
echo Executing $SCRIPT...
for db in "${databases[#]}"
do
local cur=${!db}
echo Executing $SCRIPT on $db - $cur...
sqlplus -L -S ${cur} $SCRIPT >>log-$db.log 2>&1
echo Done.
done
echo =================================================
fi
}
#####################################################################
# Here we execute a script "sql1.sql" with one argument "param1" on db1 and db2:
run_sql "1st script" "#sql1.sql param1" db1 db2
# Here we execute a script "sql2.sql" with 3 arguments on db1,db2 and db3:
run_sql "2nd script" "#sql2.sql param1 param2 param3" db1 db2 db3
echo ============================================
echo === Done
echo ============================================
Then we can create sql scripts, for example sql1.sql and sql2.sql: sql1.sql requires one argument and sql2.sql - 3 arguments:
sql1.sql:
select '&1' as output from dual;
exit;
sql2.sql:
select
'&1' out1,
'&2' out2,
'&3' out3
from dual;
exit;

connect to sqlplus only once without writing to a file in a loop

I have a requirement for which I need to write a ksh script that reads command line parameters into arrays and creates DML statements to insert records into an oracle database. I've created a script as below to achieve this. However, the user invoking the script doesn't have permission to write into the directory where the script has to run. So, is there a way we can fire multiple inserts on the database without connecting to sqlplus multiple times within the loop and at the same time, NOT create temp sql file as below? Any ideas are highly appreciated. Thanks in advance!
i=0
while (( i<$src_tbl_cnt ))
do
echo "insert into temp_table values ('${src_tbl_arr[$i]}', ${ins_row_arr[$i]}, ${rej_row_arr[$i]});" >> temp_scrpt.sql
(( i+=1 ))
done
echo "commit; disc; quit" >> temp_scrpt.sql
sqlplus user/pass#db # temp_scrpt.sql
Just use the /tmp directory.
The /tmp directory is guaranteed to be present on any unix-family server. It is there precisely for needs like this. Definitely do something like add the current process ID in the file name so that multiple users don't step on each other. So the total name is something like /tmp/temp_$PID_scrpt.sql or the like.
When done, be sure to also delete that file--say, in a line right after the sqlplus call. Thus be sure to store the file name in a variable and delete what's in that variable.
It should go without saying, but in a well run shop: 1) The admins should have put more than enough space in /tmp, 2) All the users in the community should not be deleting other's files in /tmp or overloading it so it runs out of space. 3) The admins should setup a job that deletes files from /tmp after a certain age so that if your script fails before it deletes the temporary file, it won't be there forever.
So really, this answer is more about /tmp and managing it effectively--but that really is what you need. Using temporary files is a powerful technique, so your design is good. And the reality that users often won't have rights in a directory is common, so /tmp is your answer.
Instead of creating a temporary file you can directly pipe the output of an input generating block into sqlplus, in your shell script.
Example:
{
echo 'set auto off;'
for ((i=0; i<100; i++)); do
echo "insert into itest(i) values ($i);"
done
# echo 'rollback;' # for testing
echo 'commit;'
} | sqlplus -S juser/secret#db > /dev/null
This works with Ksh 93 and Bash (perhaps even with Ksh 88 modulo the (( expression syntax).
The corresponding DDL statement for the test table:
create table itest ( i number(36) ) ;
PS: Btw, even when creating a temporary file is preferred - redirecting the output is way more efficient than doing an append-style redirect for each line, e.g.:
{ for ((i=0; i<100; i++)); do echo "line $i"; done; echo end; } > foo.tmp
the below piece of code will keep connecting to SQLplus multiple times or it will connect only once ?
{
echo 'set auto off;'
for ((i=0; i<100; i++)); do
echo "insert into itest(i) values ($i);"
done
echo 'rollback;' # for testing
echo 'commit;'
} | sqlplus -S juser/secret#db > /dev/null

Error executing shell command in pig script

I have a pig script where in the beginning I would like to generate a string of the dates of the past 7 days from a certain date (later used to retrieve log files for those days).
I attempt to do this with this line:
%declare CMD7 input= ; for i in {1..6}; do d=$(date -d "$DATE -i days" "+%Y-%m-%d"); input="\$input\$d,"; done; echo \$input
I get an error :
" ERROR 2999: Unexpected internal error. Error executing shell command: input= ; for i in {1..6}; do d=$(date -d "2012-07-10 -i days" "+%Y-%m-%d"); input="$input$d,"; done;. Command exit with exit code of 127"
however the shell command runs perfectly fine outside of pig. I am really not sure what is going wrong here.
Thank you!
I have got a working solution but not as streamlined as you want, essentially I don't manage to get Pig to execute a complex shell statement in the declare.
I first wrote a shell script (let's call it 6-days-back-from.sh):
#!/bin/bash
DATE=$1
for i in {1..6}; do d=$( date -d "$DATE -$i days" +%F ) ; echo -n "$d "; done
Then a pig script as follow (let's call it days.pig):
%declare my_date `./6-days-back-from.sh $DATE`
A = LOAD 'dual' USING PigStorage();
B = FOREACH A GENERATE '$my_date';
DUMP B
note that dual is a directory containing a text file with a single line of text, for the purpose of displaying our variable
I called the script as follow:
pig -x local -param DATE="2012-08-03" days.pig
and got the following output:
({(2012-08-02),(2012-08-01),(2012-07-31),(2012-07-30),(2012-07-29),(2012-07-28)})

Sqlplus parameters

everyone!
I want to know what this line does:
sqlplus -s /nolog <<EOF
Any ideas?
Thanks for the help!
From the information that you provided in the comments:
sqlplus -s /nolog <<EOF
Fires up an instance of sqlplus with silent mode enabled (which, I believe, doesn't send out any output to the console screen), and without a login explicitly provided (hence the /nolog), and it's taking input from the string contained in the EOF heredoc (which probably contains login credentials).
Here is a quick overview of Oracle's documentation on sqlplus.
From HERE:
-s The silent option: it suppreses the output of the SQL*Plus banner, the command prompt and the echoing of commands.
/nolog Starts SQL*Plus but does not log on (connect) a user/session.
So it seems that starts SQL*PLUS without logging on a user/session (nolog option) and don't display info (silent option).
The full excerpt should probably be:
sqlplus -s /nolog << ABCDE
CONNECT user/pwd#database
-- DO SQL AND PLSQL STUFF
EXIT
ABCDE
Which is similar to running sqlplus -s user/pwd#database #script.sql where script.sql contains the sql, plsql stuff and the exit command. The << syntax is shell operator for heredoc, which means all following lines are variable-expanded if ${variables} are found, and the first line beginning with ABCDE (at the very beginning of the line, no spaces, no tabs) ends the input.

Execute SQL from file in bash

I'm trying to load a sql from a file in bash and execute the loaded sql. The sql file needs to be versatile, meaning it cannot be altered in order to make things easy while being run in bash (escaping special characters like * )
So I have run into some problems:
If I read my sample.sql
SELECT * FROM SAMPLETABLE
to a variable with
ab=`cat sample.sql`
and execute it
db2 `echo $ab`
I receive an sql error because by doing a cat the * has been replaced by all the files in the directory of sample.sql.
Easy solution would be to replace "" with "\" . But I cannot do this, because the file needs to stay executable in programs like DB Visualizer etc.
Could someone give me hint in the right direction?
The DB2 command line processor has options that accept a filename as input, so you shouldn't need to load statements from a text file into a shell variable.
This command will execute all SQL statements in the file, with newline treated as the statement terminator:
db2 -f sample.sql
This command will execute all SQL statements in the file, with semicolon treated as the statement terminator:
db2 -t -f sample.sql
Other useful CLP flags are:
-x : Suppress the column headings
-v : Echo the statement text immediately before execution
-z : Tee a copy of all CLP output to the filename immediately following this flag
Redirect stdin from the file.
db2 < sample.sql
In case, you have a variable used in your script and wanted to get it replaced by the shell before executed in DB2 then use this approach:
Contents of File.sql:
cat <<xEOF
insert values(1,2) into ${MY_SCHEMA}.${MY_TABLE};
select * from ${MY_SCHEMA}.${MY_TABLE};
xEOF
In command prompt do:
export MY_SCHEMA='STAR'
export MY_TAVLE='DIMENSION'
Then you are all good to get it executed in DB2:
eval File.sq |db2 +p -t
The shell will replace the global variables and then DB2 will execute it.
Hope it helps.