I'm trying to run a shell scrpit which executes a SQL. Within the SQL file I have queries which check something from the database and when one of the checks fail, it tirggers an email alert to the id's specified.
My code is something like:
Declare
numrows number(4,2);
c UTL_SMTP.CONNECTION;
PROCEDURE send_header(name IN VARCHAR2, header IN VARCHAR2) AS
BEGIN
UTL_SMTP.WRITE_DATA(c, name || ': ' || header || UTL_TCP.CRLF);
END;
Begin
select count (*) into numrows
<some sub queries>
Minus
select count(*)
<from something else>
;
--if differences are found, then trigger an email.
IF numrows <> 0 THEN
BEGIN
c := UTL_SMTP.OPEN_CONNECTION('localhost',25);
UTL_SMTP.HELO(c, 'localhost');
UTL_SMTP.MAIL(c, 'a#b.com');
UTL_SMTP.RCPT(c, 'c#d.com');
UTL_SMTP.OPEN_DATA(c);
send_header('From', '"noreply" <a#b.com>');
send_header('To', '"c#d.com');
send_header('Subject', '<subject here>');
UTL_SMTP.WRITE_DATA(c,'brief error message'|| chr(13));
FOR I IN (
<some logic>
)
LOOP
UTL_SMTP.WRITE_DATA(c, xyz || ' - ');
UTL_SMTP.WRITE_DATA(c, qwe );
UTL_SMTP.WRITE_DATA(c,UTL_TCP.CRLF);
END LOOP;
UTL_SMTP.WRITE_DATA(c, chr(13)||'-Sent by the batch process.');
UTL_SMTP.CLOSE_DATA(c);
UTL_SMTP.QUIT(c);
EXCEPTION
WHEN utl_smtp.transient_error OR utl_smtp.permanent_error THEN
BEGIN
UTL_SMTP.QUIT(c);
EXCEPTION
WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
NULL; -- When the SMTP server is down or unavailable, we don't have
-- a connection to the server. The QUIT call will raise an
-- exception that we can ignore.
END;
raise_application_error(-20000,
'Failed to send mail due to the following error: ' || sqlerrm);
END;
END IF;
END;
Now the errors I get are:
SP2-0027: Input is too long (> 2499 characters) - line ignored
SQL> SP2-0734: unknown command beginning "ggg..." - rest of line ignored.
SQL> SP2-0734: unknown command beginning "ddd..." - rest of line ignored.
SQL> SP2-0734: unknown command beginning "eee..." - rest of line ignored.
SQL> SP2-0734: unknown command beginning "nnn..." - rest of line ignored.
SP2-0044: For a list of known commands enter HELP
and to leave enter EXIT.
SQL> SP2-0734: unknown command beginning "hfhf..." - rest of line ignored.
SQL> Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, Oracle Label Security, OLAP, Data Mining
and Real Application Testing options
I looked up the SP2-0027 and I gathered that the input line has more than 2499 characters. None of my input lines have so many characters and the total character count on the SQL file itself is about 4000.
I don't know if there is any internal implementation of UTL_SMTP that is passing in some data behind the scenes. Or if there is something else that is happening.
This runs perfectly when using TOAD.
Can someone help?
I think I've found an answer:
All I had to do was to split the SQL file which the shell script was executing into 2 files and volia` it worked :D
#tbone: fyi & thanks for your help!
Related
I am migrating stored procedures to pl/sql blocks, and I have little knowledge in error handling in oracle and nothing in sybase can you help me.
example: sql SYBASE
DELETE table_1
WHERE N=0
SELECT #myrowcount = ##rowcount, #myerror = ##error, #mystat = ##sqlstatus
if (#myerror <> 0)
begin
raiserror 40900 "Error: When Generating Exception List #table_1 (error= %1!)", #mystat
select #cod_err= 1
return #cod_err
end
Edit: sql oracle i dont know if this is right
begin
DELETE table_1
WHERE N=0
EXCEPTION WHEN OTHERS THEN
SWV_error := SQLCODE;
end;
v_mi_error := SWV_error;
if v_mi_error != 0 then
RAISE_APPLICATION_ERROR(-40900,'Error: When Generating Exception List table_1');
return;
end if;
Which error do you expect for delete? It'll either delete some rows, or won't. If table (or column) doesn't exist, code wouldn't even compile so you wouldn't reach runtime error.
Anyway: in Oracle, it looks like this:
begin
delete table_1 where n = 0;
exception
when others then
raise_application_error(-20000, 'Error: ' || sqlerrm);
end;
/
others is exception which handles various things; you don't really care which error it is, as could be any error. Oracle reserved error codes from -20000 to -20999 for us, developers so you have to pick one of these (which means that -40900 won't work).
sqlerrm is error message (its description). If you want, you can get its code via sqlcode.
Example which shows how it actually works (not with delete, though) is query that fetches employee name for non-existent employee number. That raises predefined no_data_found error (whose code is -01403) so you could handle it, directly, or - as my previous example shows - use others.
SQL> declare
2 l_name varchar2(10);
3 begin
4 select ename
5 into l_name
6 from emp
7 where empno = -1;
8 exception
9 when others then
10 raise_application_error(-20000, 'Error: ' || sqlcode ||': '|| sqlerrm);
11 end;
12 /
declare
*
ERROR at line 1:
ORA-20000: Error: 100: ORA-01403: no data found
ORA-06512: at line 10
SQL>
I have a problem running my PL/SQL script in SQL*Plus. I can run SQL commands normally but when I want to a run any PL/SQL code it gives nothing. See code and output below.
DECLARE
x_salary employee.salary%TYPE;
BEGIN
select salary
into x_salary
from employee
where ssn=&enter_ssn;
--Output the result
DBMS_OUTPUT.PUT_LINE('Salary is ' || x_salary);
EXCEPTION
--Output when no records are returned
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('No employee found');
WHEN others THEN
DBMS_OUTPUT.PUT_LINE ('Error encountered, but cause unknown');
END;
PL/SQL procedures needs / after procedure definition under sqlplus
DECLARE
...
BEGIN
...
END;
/
Put a slash / in a new line after END; in your script.
From the documentation:
You must include a semicolon at the end of each SQL command and a slash (/) on a line by itself after each PL/SQL block in the file.
Then execute the SQL file in SQL*Plus command line as:
#C:\your_script.sql;
I want to understand when this oracle error occurs 'Error: ORA-06502: PL/SQL: numeric or value error: host bind array too small'
I have a plsql block and it has a normal annonymus block which has DBMS_output.put_line and also has DBMS_output.put in a for loop and in the code its calls another package in for loop itself and the out variables like success( as S) and errormessage (as err_msg) of package are displayed using DBMS_output.put and the out put gets printed for few records but suddenly a above error occurs and blocks gets completed don't how this happemed. Can any one explain why such error occurs.
The error occurs on the client-side, not on the server-side, so it's an error with specific versions of sqlplus. It happens when sqlplus utility tries to read the dbms_output buffer from the server-side to the client-side buffer, and the buffer contains a string that is more than 255 characters long. So error happens when:
you are using sqlplus with version less than 10.2 and the version of Database server is 10.2 or higher. For example:
client is 10.1.0.5 and server is 12.2.9
client is 10.1.0.5 and server is 10.2.0.5
client is 10.1.0.5 and server is 11.2.0.1
the serveroutput on option is set
you have put a line with more than 255 characters into the dbms_output buffer.
Please, note that if your server is 10.1.0.5 or less (9.2.0.8) you would get different error: ORA-20000: ORU-10028: line length overflow, limit of 255 chars per line.
Demo
Put 256 characters and we get the error:
SET SERVEROUTPUT ON
begin
dbms_output.put_line(a => rpad('1',256,'1'));
end;
/
ERROR:
ORA-06502: PL/SQL: numeric or value error: host bind array too small
ORA-06512: at line 1
Or you can do it in a slightly different way:
SET SERVEROUTPUT ON
begin
dbms_output.put(a => rpad('1',200,'1'));
dbms_output.put(a => rpad('1',200,'1'));
dbms_output.put_line('');
end;
/
ERROR:
ORA-06502: PL/SQL: numeric or value error: host bind array too small
ORA-06512: at line 1
Put 255 characters and it' ok we get the output:
SET SERVEROUTPUT ON
begin
dbms_output.put_line(a => rpad('1',255,'1'));
end;
/
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111
How to solve
Option 1:
Oracle fixed the error in the 10.2 release, so if you upgrade your server-side and the client-side components to at least 10.2.0.5, the error will be fixed.
Option 2:
If you absolutely have to use 10.1 version or older versions of sqlplus (and version of Database is 10.2 or higher) and you cannot rewrite your pl/sql code that generates long characters string, then you can work around this bug using dbms_output.get_lines (or dbms_output.get_line) procedures:
SET SERVEROUTPUT OFF
begin
-- enable the server-side buffer. When SET SERVEROUTPUT ON sqlplus does it automatically.
-- Now we do it manually.
dbms_output.enable(1000000);
dbms_output.put_line(a => rpad('1',300,'1'));
end;
/
PL/SQL procedure successfully completed.
Then use this block just to get the content of the buffer:
variable l_cursor REFCURSOR
set autoprint on
declare
-- Then use this block to get the contents of the buffer.
l number:=10000;
l_array DBMSOUTPUT_LINESARRAY;
begin
dbms_output.get_lines(l_array,l);
open :l_cursor for select * from table(l_array);
end;
/
PL/SQL procedure successfully completed.
COLUMN_VALUE
--------------------------------------------------------------------------------
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111
usually this error will occur when you either try to set a variable with too big a value. for instance.
v_number number(2);
BEGIN
v_number := 100;
END;
or when you try to save text into a number variable
v_number number(2);
BEGIN
v_number := 'a';
END;
see here for more info: https://www.techonthenet.com/oracle/errors/ora06502.php
I have the a sql file containing the following:
Note that the CDB column in v$database only exists when version (not Oracle but of some other unrelated product) is greater than 11.
It runs fine when I execute it on DB host using sqlplus
sqlplus / as sysdba #abc.sql 1000 60 10
However, when I connect to this DB host remotely using connection string, it complains about "Invalid identifier" if CDB column does not exist (no matter what version value I specify)
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage
Management, Oracle Label Security,
OLAP, Data Mining, Oracle Database Vault and Real Application Testing options
.
old 2: num_ret_period NUMBER(9) :=
new 2: num_ret_period NUMBER(9) := 43200;
old 3: num_interval NUMBER(9) :=
new 3: num_interval NUMBER(9) := 60;
old 5: lcm_version NUMBER(4) :=
new 5: lcm_version NUMBER(4) := 1;
SELECT CDB INTO isCDB from v$database;
*
ERROR at line 10:
ORA-06550: line 10, column 12:
PL/SQL: ORA-00904: "CDB": invalid identifier
ORA-06550: line 10, column 5:
PL/SQL: SQL Statement ignored
Any ideas why?
DECLARE
num_ret_period NUMBER(9) := &1;
num_interval NUMBER(9) := &2;
isCDB VARCHAR2(10);
version NUMBER(4) := &3;
BEGIN
IF version > 11 THEN
SELECT CDB INTO isCDB from v$database;
IF (isCDB = 'YES') THEN
EXECUTE IMMEDIATE 'ALTER session SET CONTAINER = CDB$ROOT';
dbms_output.put_line('CDB is set to YES');
ELSE
dbms_output.put_line('CDB is set to NO');
END IF;
END IF;
callSomeProcedure();
commit;
EXCEPTION
when others then
dbms_output.put_line('There is an exception in abc.sql' || SQLCODE || ' ' || SQLERRM);
ROLLBACK;
raise_application_error(-20001, 'There is an exception in abc.sql' || SQLCODE || ' ' || SQLERRM);
END;
/
Even if you do not intend to execute the code guarded by if version > 11 it still needs to be parsed. At this point the fact that CDB does not exist (version 11) is revealed.
To avoid this, use conditional compilation:
$IF DBMS_DB_VERSION.VER_LE_11 $THEN
-- version 11 code
NULL;
$ELSE
-- version 12 and later code
NULL;
$END
The conditional compilation directives act similar to a preprocessor.
See https://oracle-base.com/articles/10g/conditional-compilation-10gr2
I need to debug in pl/sql to figure times of procedures, I want to use:
SELECT systimestamp FROM dual INTO time_db;
DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);
but I don't understand where the output goes to and how can I redirect it to a log file that will contain all the data I want to collect?
DBMS_OUTPUT is not the best tool to debug, since most environments don't use it natively. If you want to capture the output of DBMS_OUTPUT however, you would simply use the DBMS_OUTPUT.get_line procedure.
Here is a small example:
SQL> create directory tmp as '/tmp/';
Directory created
SQL> CREATE OR REPLACE PROCEDURE write_log AS
2 l_line VARCHAR2(255);
3 l_done NUMBER;
4 l_file utl_file.file_type;
5 BEGIN
6 l_file := utl_file.fopen('TMP', 'foo.log', 'A');
7 LOOP
8 EXIT WHEN l_done = 1;
9 dbms_output.get_line(l_line, l_done);
10 utl_file.put_line(l_file, l_line);
11 END LOOP;
12 utl_file.fflush(l_file);
13 utl_file.fclose(l_file);
14 END write_log;
15 /
Procedure created
SQL> BEGIN
2 dbms_output.enable(100000);
3 -- write something to DBMS_OUTPUT
4 dbms_output.put_line('this is a test');
5 -- write the content of the buffer to a file
6 write_log;
7 END;
8 /
PL/SQL procedure successfully completed
SQL> host cat /tmp/foo.log
this is a test
As an alternative to writing to a file, how about writing to a table? Instead of calling DBMS_OUTPUT.PUT_LINE you could call your own DEBUG.OUTPUT procedure something like:
procedure output (p_text varchar2) is
pragma autonomous_transaction;
begin
if g_debugging then
insert into debug_messages (username, datetime, text)
values (user, sysdate, p_text);
commit;
end if;
end;
The use of an autonomous transaction allows you to retain debug messages produced from transactions that get rolled back (e.g. after an exception is raised), as would happen if you were using a file.
The g_debugging boolean variable is a package variable that can be defaulted to false and set to true when debug output is required.
Of course, you need to manage that table so that it doesn't grow forever! One way would be a job that runs nightly/weekly and deletes any debug messages that are "old".
use
set serveroutput on;
for example:
set serveroutput on;
DECLARE
x NUMBER;
BEGIN
x := 72600;
dbms_output.put_line('The variable X = '); dbms_output.put_line(x);
END;
If you are just testing your PL/SQL in SQL Plus you can direct it to a file like this:
spool output.txt
set serveroutput on
begin
SELECT systimestamp FROM dual INTO time_db;
DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);
end;
/
spool off
IDEs like Toad and SQL Developer can capture the output in other ways, but I'm not familiar with how.
In addition to Tony's answer, if you are looking to find out where your PL/SQL program is spending it's time, it is also worth checking out this part of the Oracle PL/SQL documentation.
Using UTL_FILE instead of DBMS_OUTPUT will redirect output to a file:
http://oreilly.com/catalog/oraclebip/chapter/ch06.html
As a side note, remember that all this output is generated in the server side.
Using DBMS_OUTPUT, the text is generated in the server while it executes your query and stored in a buffer. It is then redirected to your client app when the server finishes the query data retrieval. That is, you only get this info when the query ends.
With UTL_FILE all the information logged will be stored in a file in the server. When the execution finishes you will have to navigate to this file to get the information.
Hope this helps.
Its possible write a file directly to the DB server that hosts your database, and that will change all along with the execution of your PL/SQL program.
This uses the Oracle directory TMP_DIR; you have to declare it, and create the below procedure:
CREATE OR REPLACE PROCEDURE write_log(p_log varchar2)
-- file mode; thisrequires
--- CREATE OR REPLACE DIRECTORY TMP_DIR as '/directory/where/oracle/can/write/on/DB_server/';
AS
l_file utl_file.file_type;
BEGIN
l_file := utl_file.fopen('TMP_DIR', 'my_output.log', 'A');
utl_file.put_line(l_file, p_log);
utl_file.fflush(l_file);
utl_file.fclose(l_file);
END write_log;
/
Here is how to use it:
1) Launch this from your SQL*PLUS client:
BEGIN
write_log('this is a test');
for i in 1..100 loop
DBMS_LOCK.sleep(1);
write_log('iter=' || i);
end loop;
write_log('test complete');
END;
/
2) on the database server, open a shell and
tail -f -n500 /directory/where/oracle/can/write/on/DB_server/my_output.log
An old thread, but there is another alternative.
Since 9i you can use pipelined table function.
First, create a type as a table of varchar:
CREATE TYPE t_string_max IS TABLE OF VARCHAR2(32767);
Second, wrap your code in a pipelined function declaration:
CREATE FUNCTION fn_foo (bar VARCHAR2) -- your params
RETURN t_string_max PIPELINED IS
-- your vars
BEGIN
-- your code
END;
/
Replace all DBMS_OUTPUT.PUT_LINE for PIPE ROW.
Finally, call it like this:
SELECT * FROM TABLE(fn_foo('param'));
Hope it helps.
Try This:
SELECT systimestamp INTO time_db FROM dual ;
DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);