UTL_FILE.FOPEN() how to declare and assign the directory path? - plsql

I have this code to extract Images from an Oracle database using toad but i have challenges when its comes to declare the directory where to save the images once extracted. I want these images to be saved in C:\Images\ a folder in a local machine. Is this possible or how should i specify the directory?.
when i execute the code am getting Invalid file operation
DECLARE
t_blob BLOB;
t_len NUMBER;
t_file_name VARCHAR2(100);
t_output UTL_FILE.file_type;
t_TotalSize number;
t_position number := 1;
t_chucklen NUMBER := 4096;
t_chuck raw(4096);
t_remain number;
BEGIN
-- Get length of blob
SELECT DBMS_LOB.getlength (PHOTO), ename || '_1.jpg'
INTO t_TotalSize, t_file_name FROM DEMO WHERE ENAME ='moon';
t_remain := t_TotalSize;
-- The directory TEMPDIR should exist before executing
t_output := UTL_FILE.fopen ('C:\Images\', t_file_name, 'wb', 32760);
-- Get BLOB
SELECT PHOTO INTO t_blob FROM DEMO WHERE ENAME ='moon';
-- Retrieving BLOB
WHILE t_position < t_TotalSize
LOOP
DBMS_LOB.READ (t_blob, t_chucklen, t_position, t_chuck);
UTL_FILE.put_raw (t_output, t_chuck);
UTL_FILE.fflush (t_output);
t_position := t_position + t_chucklen;
t_remain := t_remain - t_chucklen;
IF t_remain < 4096
THEN
t_chucklen := t_remain;
END IF;
END LOOP;
END;

you should use a DIRECTORY object,
create or replace directory my_dir as 'c:\Images\';
declare
v_dir varchar2(10) := 'MY_DIR';
.....
begin
....
t_output := utl_file.fopen(v_dir, t_file_name, 'wb', 32760);
....

First to know, when you execute any PL/SQL script that handle files this always execute into the scope of the server that run the oracle instance.
In this case the path "C:\Image\" need to exists on the server that Oracle run.
One strategy that you can use is execute the script into the folder of the server and then obtain the folder data via Copy Command on the terminal/console.

Related

How to make a create a XML using DBMS_XMLGEN and then fetch and print using a single SYS_REFCURSOR?

I have a SYS_REFCURSOR opened for some query. I am making an XML for all the records in the SYS_REFCURSOR using DBMS_XMLGEN. Also I am trying to fetch the records into a record variable and then print OUTPUT. The issue is if I am creating only XML from the SYS_REFCURSOR or If I am only Fetching, it is working just fine. But if I am trying to do both, It is throwing a fetch out of sequence error.
Please help. Below is my Sample Code.
I have declared p_individual as a record type.
set serveroutput on;
declare
l_ctx dbms_xmlgen.ctxhandle;
l_xmltype clob;
l_xml xmltype;
L_REFCURSOR SYS_REFCURSOR;
begin
OPEN L_REFCURSOR FOR select * from some_table;
//Creating XML
L_CTX := dbms_xmlgen.newcontext(L_REFCURSOR);
dbms_xmlgen.setrowsettag(L_CTX, 'root');
dbms_xmlgen.setrowtag(L_CTX, 'data');
L_XML := dbms_xmlgen.getXmlType(L_CTX) ;
dbms_xmlgen.closeContext(L_CTX);
dbms_output.put_line(L_XML.getclobval());
//Fetching the same cursor to print output
loop
fetch L_REFCURSOR into p_individual;
exit when L_REFCURSOR%NOTFOUND;
dbms_output.put_line(p_individual.id);
end loop;
close L_REFCURSOR;
end;
/
Do your conventional fetch first then use DBMS_XMLGEN.RESTARTQUERY to output as XML.
Based on your example, something like this...
DECLARE
l_xmltype XMLTYPE;
l_refcursor SYS_REFCURSOR;
l_ctxhandle DBMS_XMLGEN.ctxhandle;
l_record v$version%ROWTYPE;
BEGIN
OPEN l_refcursor FOR SELECT * FROM v$version;
-- output conventional
LOOP
FETCH l_refcursor INTO l_record;
EXIT WHEN l_refcursor%NOTFOUND;
DBMS_OUTPUT.put_line (l_record.banner);
END LOOP;
-- output as xml
l_ctxhandle := DBMS_XMLGEN.newcontext (l_refcursor);
-- set some format stuff
DBMS_XMLGEN.setrowsettag (l_ctxhandle, 'root');
DBMS_XMLGEN.setrowtag (l_ctxhandle, 'data');
-- cursor already fetched so restart it
DBMS_XMLGEN.restartquery (l_ctxhandle);
l_xmltype := DBMS_XMLGEN.getxmltype (l_ctxhandle);
DBMS_XMLGEN.closecontext (l_ctxhandle);
IF l_refcursor%ISOPEN THEN
CLOSE l_refcursor;
END IF;
DBMS_OUTPUT.put_line (l_xmltype.getclobval);
END;
/

PLS-00103 ERROR, what is wrong in the code

CREATE OR REPLACE PROCEDURE proc2_del_rows
(v_tname VARCHAR2,
v_condition VARCHAR2 DEFAULT NULL)
AS
sql_stmt VARCHAR2(500);
where_clause VARCHAR2(200) := 'WHERE'||' '||v_condition;
BEGIN
IF v_condition IS NULL THEN
where_clause := NULL;
END IF;
sql_stmt := 'DELETE FROM :1'||' '||where_clause;
EXECUTE IMMEDIATE sql_stmt USING v_tname;
COMMIT;
END;
/
The table name can't be a bind variable. Do a DBMS_ASSERT on the input table name parameter and make sure it is a valid table name literal, and then directly concatenate it to the delete statement. This will at least protect you against sql injection.
I'd like to know the reason behind doing a delete using a procedure and granting execute on this procedure to individual users, rather than granting a delete on the table to a user directly, which would somewhat be easier to control/restrict. I don't see how this is better in terms on security if that is what you are going for.
CREATE or replace PROCEDURE proc2_del_rows
(v_tname VARCHAR2,
v_condition VARCHAR2 DEFAULT NULL)
AS
sql_stmt VARCHAR2(500);
where_clause VARCHAR2(200) := 'WHERE'||' '||v_condition;
BEGIN
IF v_condition IS NULL THEN
where_clause := NULL;
END IF;
sql_stmt := 'DELETE FROM '||v_tname||' '||where_clause;
EXECUTE IMMEDIATE sql_stmt;
END;
/
To include a single-quote character within a string literal you need to double up the single quotes, as in proc2_del_rows('EMP', 'JOB=''CLERK''').
Documentation here

Error reading a file using UTL_FILE

I am facing some issue in reading a file using utl_file.
The same code is working fine in some of the instances and failing in one instance.
Below is the issue:
Code:
procedure del_mul_tags(p_request_id in number)
is
fileID UTL_FILE.FILE_TYPE;
fileID1 UTL_FILE.FILE_TYPE;
line varchar2(32000);
l_dir varchar2(1000);
l_file varchar2(20);
l_file1 varchar2(20);
l_request_id number;
l_count number := 0;
l_count_t number :=0;
l_write boolean := TRUE;
cursor c_dir is
select substr(OUTFILE_NAME,1,instr(OUTFILE_NAME,'/',-1,1)-1),substr(OUTFILE_NAME,instr(OUTFILE_NAME,'/',-1,1)+1)
from fnd_concurrent_requests
where request_id = p_request_id;
begin
open c_dir;
fetch c_dir into l_dir,l_file;
close c_dir;
execute immediate 'create or replace directory W2_OUT_DIR as ''' || l_dir || '''';
begin
fileID1 := UTL_FILE.FOPEN ('W2_OUT_DIR', l_file, 'R'); -----Getting the error right here!!!-----------
loop
begin
UTL_FILE.GET_LINE (fileID1, line);
--Some Logic
EXCEPTION WHEN NO_DATA_FOUND THEN
UTL_FILE.FCLOSE(fileID1);
EXIT;
end;
end loop;
fileID := UTL_FILE.FOPEN ('W2_OUT_DIR', l_file, 'R');
loop
begin
UTL_FILE.GET_LINE (fileID, line);
--Some Logic
EXCEPTION WHEN NO_DATA_FOUND THEN
UTL_FILE.FCLOSE(fileID);
EXIT;
end;
end loop;
end;
EXCEPTION WHEN OTHERS THEN
fnd_file.put_line(fnd_file.log,'Error while deleting the xml tags: '||SQLERRM);
end;
................
In this instance file, l_file 'ACHAKR01.23067873' was created through another concurrent process. Its OS user is 'appsofde'.
And the unix permissions are as below:
-rw-r--r-- 1 appsofde dba 192092429 jan 27 05:00 ACHAKR01.23067873
The directory W2_OUT_DIR is created and the oracle user apps has EXECUTE,READ,WRITE privileges.
The file exists and the W2_OUT_DIR dir patch is correct.
Exact error coming is:
Error while deleting the xml tags: ORA-29283: invalid file operation
ORA-06512: at "SYS.UTL_FILE", line 536
ORA-29283: invalid file operation
Any thoughts here?
You will want to make sure the filesystem is presented to your Oracle DB host (not just your application server). I'm assuming it is otherwise you would get an invalid directory path error instead. I would suggest you double check however and also ensure the oracle account on the DB host has access to read the file in question. The account appsofde sounds as if its likely the application account.

Cipher: failed to reserve an envelope space

I am attempting to encrypt/decrypt a SQLite database via FireDAC in a Delphi XE7 application running on Windows 7 (64 bit).
The code looks like this:
Procedure TMain.ActionBtnClick(Sender: TObject);
Begin
If ActionBtn.Caption = 'Encrypt' Then
Begin
SetPassword;
FDSQLiteSecurity.SetPassword;
End
Else
FDSQLiteSecurity.RemovePassword;
SetStatus;
End;
Procedure TMain.DBNamePropertiesButtonClick(Sender: TObject; AButtonIndex: Integer);
Begin
If OpenDialog.Execute Then
Begin
DBName.Text := OpenDialog.FileName;
SetStatus;
End;
End;
Procedure TMain.FormClose(Sender: TObject; Var Action: TCloseAction);
Var
Reg: TRegistry;
Begin
Reg := TRegistry.Create;
Try
Reg.OpenKey('\SQLiteSecurity', True);
Reg.WriteString('Database', DBName.Text);
Finally
Reg.CloseKey;
Reg.Free;
End;
End;
Procedure TMain.FormShow(Sender: TObject);
Var
Reg: TRegistry;
Begin
DBStatus.Caption := '';
Reg := TRegistry.Create;
Try
Reg.OpenKey('\SQLiteSecurity', True);
If Reg.ValueExists('Database') Then
Begin
DBName.Text := Reg.ReadString('Database');
SetStatus;
End;
Finally
Reg.CloseKey;
Reg.Free;
End;
End;
Procedure TMain.SetPassword;
Var
s: String;
Begin
FDSQLiteSecurity.Database := DBName.Text;
BEK(s);
FDSQLiteSecurity.Password := s;
End;
Procedure TMain.SetStatus;
Begin
DBStatus.Caption := FDSQLiteSecurity.CheckEncryption;
If DBStatus.Caption = '<unencrypted>' Then
ActionBtn.Caption := 'Encrypt'
Else
ActionBtn.Caption := 'Decrypt';
End;
When trying to encrypt, at the line that reads "FDSQLiteSecurity.SetPassword;", I get the following error message:
[FireDAC][Phys][SQLite] ERROR: Cipher: failed to reserve an envelope space.
I have tried to find the meaning of this error message without success. Does anyone know what the error message from SQLite is trying to tell me?
TFDSQLiteSecurityOptions FireDAC.Phys.SQLite.TFDSQLiteSecurity.Options
Have you set option soSetLargeCache ?
Use the Options property to specify the database encryption options.
Due to current SQLite encryption limitation the SetPassword / ChangePassword / RemovePassword calls will fail, if the database has blob fields with a value size greater than 1 DB page, and the database does not fit into the SQLite cache.
If soSetLargeCache is set, then SetPassword / ChangePassword / RemovePassword automatically set the cache size greater than the DB size, to fit the database into the memory in full.
If the DB size is greater than the accessible system memory, then the corresponding call fails.

read array of "n" elements from user input

How to read array of n elements dynamically and display the elements in plsql.
Below is my code (i am new to plsql programming)
set serveroutput on
set verify on
declare
type myarray is table of number index by binary_integer;
x myarray;
i pls_integer;
n number;
begin
-- populate array
dbms_output.put_line('Enter number of array elements');
n := &n;
dbms_output.put_line('Enter elements one by one');
for i in 1..n loop
dbms_output.get_lines(&&x(i),n);
end loop;
i :=0;
-- print array
loop
i := i + 1;
begin
dbms_output.put_line(x(i));
exception
when no_data_found then exit;
end;
end loop;
end;
/
quit;
I stumble into the same issue and wanted to try something.
You could do this with a brutal approach that consists in using you shell to call the client (i.e. sqlplus) any time you want to add a value to your array. I wrote a little package that stores the values of the array in a table. Then you can change the way you store the values, but principle would remain the same:
-- this package uses "execute immediate" statements for everything
-- because it assumes the temporary table T_MY_ARRAY can be non-existent.
create or replace package my_array
authid current_user
is
TYPE a_TBL_Number IS TABLE OF Number INDEX BY BINARY_INTEGER;
procedure init;
procedure add(v in number);
procedure display;
function to_var return a_TBL_Number;
end my_array;
/
create or replace package body my_array
is
procedure init is
-- create table if needed, then make sure its empty;
begin
begin
execute immediate 'create table T_MY_ARRAY(c1 number)';
exception when others then
-- dbms_output.put_line(sqlerrm);
-- we're never sure the temp. table already exists. So create it and catch if existing.
null;
end;
execute immediate 'truncate table T_MY_ARRAY';
end init;
procedure add(v in number) is
-- add new value
begin
execute immediate 'insert into T_MY_ARRAY (c1) values ('||v||')';
end add;
function to_var return a_TBL_Number is
-- hand out an array with the values
t_TBL_n a_TBL_Number;
begin
execute immediate 'select c1 from T_MY_ARRAY ' bulk collect into t_TBL_n;
return t_TBL_n;
end to_var;
procedure display is
t_TBL_n a_TBL_Number;
begin
t_TBL_n:=my_array.to_var();
for i in 1..t_TBL_n.count loop
dbms_output.put_line(t_TBL_n(i));
end loop;
end display;
end my_array;
/
Then here is how you could call this from a shell (here it is for an old ksh):
read SQL_CONNECT?"Enter your SQL connection string:"
read n?"Enter number of array elements:"
# first initialize the array:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.init;
end;
/
_EOF
# then loop on the given number of elements
typeset -i10 i=0
while [[ ${i} -lt ${n} ]]
do
i=i+1
echo iter:${i}
read MY_VALUE?"Enter elements one by one:"
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.add(${MY_VALUE});
end;
/
commit;
_EOF
done
# at the end, use stored values to display result:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput on
set feedback off
begin
dbms_output.put_line('---------------');
dbms_output.put_line('your input was:');
my_array.display;
end;
/
_EOF

Resources