Ftp from pl/sql
Hi,
I want to use pl/sql to ftp files from local unix server to a remote unix server.
I'd like to have a basic set-up in place for ftp'ing, something like -
i) open connection & login
ii) ftp the file
iii) ftp a zero byte trigger file
iv) close connection
For me using java stored procedures is not an option, I'm working on 10g and would like to do everything in pl/sql. Can someone let me know how best can this be done or point me to some good articles?
Thanks
This should help:
FTP From PL/SQL.
Similar Messages
-
If you need to FTP with PL/SQL...
If you need to perform FTP from within PL/SQL and your database version has the UTL_TCP package, here is a free package you can use. The source code is hopefully documented well enough for you to tell what's going on and how to use the functions. Suggestions on improving the code are welcome, and I can provide limited support via email for what I've written, but I would encourage anyone who uses the code to modify/fix it according to their needs. If you modify the code, I respectfully request that you leave intact the authorship and note comments at the beginning of the package.
Please note that I have not rigorously tested this code, but it has successfully transferred files in both directions in the limited tests that I have performed.
-- Copy the code below and run it in your favorite SQL editor --
CREATE OR REPLACE PACKAGE FTP IS
Simplified FTP client API using UTL_TCP package
Author: Alan Wessman, Brigham Young University
Note: This FTP client attempts to adhere to the protocol and advice found at:
http://cr.yp.to/ftp.html
No warranties are made regarding the correctness of this code.
Notes:
1. Most of these functions will raise UTL_TCP.NETWORK_ERROR if the connection
is not open or is reset during the network transaction. They will also
raise VALUE_ERROR if the server response is ill-formed or a buffer is
too small to hold data. (Most buffers in this package are defined as
VARCHAR2(32767) to avoid size limitations; reduce this if memory overhead
is a concern.)
2. "Verbose mode" can be enabled/disabled by changing the default value of
the vDebug variable in the package body. Setting vDebug to TRUE will
cause a session transcript to be output to DBMS_OUTPUT.
3. The following is an example of how this package might be used:
declare
c utl_tcp.connection;
vresp varchar2(32767);
vbuf varchar2(32767);
vresp_code number;
vremote_host varchar2(32) := 'some.hostname.com';
vusername varchar2(8) := 'username';
vpassword varchar2(8) := 'password';
begin
dbms_output.put_line( 'Opening session...' );
vresp_code := ftp.open_session( c,
vremote_host,
vusername,
vpassword,
vresp,
5 );
vresp_code := ftp.put( c,
'/home/somebody',
'local.test',
'remote.test',
vresp );
vresp_code := ftp.remote_command( c, 'CHMOD 660 remote.test' );
vresp_code := ftp.chdir( c, '/home/somebody/subdir' );
vresp_code := ftp.pwd( c );
vresp_code := ftp.get( c,
'/home/somebody',
'new_file.test',
'another_remote_file.test',
vresp );
vresp_code := ftp.close_session( c );
dbms_output.put_line( 'Closed session.' );
exception
when others then dbms_output.put_line( sqlcode || ':' || sqlerrm );
end;
Function: Open_Session
Description: Begins an FTP session with the remote server.
Parameters:
conn OUT parameter that contains the connection info; to be passed
in to subsequent commands to maintain session state.
host Name or IP address of remote server
username User ID to use for login
password Password to use for login
response OUT parameter; buffer for server replies
timeout_secs Number of seconds for TCP timeout. Pass in NULL to disable
timeout (wait forever for responses). Pass in 0 (zero) for
no wait.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if host parameter is incorrect or if
some other networking error occurs.
May raise VALUE_ERROR if server response is ill-formed.
FUNCTION Open_Session( conn OUT NOCOPY UTL_TCP.Connection,
host IN VARCHAR2,
username IN VARCHAR2,
password IN VARCHAR2,
response OUT VARCHAR2,
timeout_secs IN NUMBER DEFAULT 60 ) RETURN NUMBER;
Function: Get
Description: Retrieves a file on the remote server and stores its contents in
a VARCHAR2 buffer.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
buf OUT parameter; buffer for retrieved file contents
remote_path Pathname (including file name) indicating location of remote
file to be retrieved
response OUT parameter; buffer for server replies.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed or buf is
too small for file contents.
FUNCTION Get( conn IN OUT NOCOPY UTL_TCP.Connection,
buf OUT VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER;
Function: Get
Description: Retrieves a file on the remote server and stores its contents in
a local file. Assumes an open file handle and does not close it.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
local_file IN OUT parameter; UTL_FILE file handle for input file. File
is assumed to be open for writing.
remote_path Pathname (including file name) indicating location of remote
file to be retrieved
response OUT parameter; buffer for server replies.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed or buf is
too small for file contents.
May raise any of the UTL_FILE exceptions if file write operations
fail. See UTL_FILE documentation for additional details.
FUNCTION Get( conn IN OUT NOCOPY UTL_TCP.Connection,
local_file IN OUT UTL_FILE.File_Type,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER;
Function: Get
Description: Retrieves a file on the remote server and stores its contents in
a local file. Opens and closes local file automatically.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
local_path Pathname of local directory in which to store the retrieved
file's contents
local_filename Name of local file in which to store retrieved file's contents
(creates new file or overwrites existing file)
remote_path Pathname (including file name) indicating location of remote
file to be retrieved
response OUT parameter; buffer for server replies.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed or buf is
too small for file contents.
May raise any of the UTL_FILE exceptions if file open, write, or
close operations fail. See UTL_FILE documentation for additional
details.
FUNCTION Get( conn IN OUT NOCOPY UTL_TCP.Connection,
local_path IN VARCHAR2,
local_filename IN VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER;
Function: Put
Description: Stores data as a file on the remote server
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
buf IN parameter; contains data to upload
remote_path Pathname (including file name) indicating location of remote
file to be created/overwritten
response OUT parameter; buffer for server replies.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed.
FUNCTION Put( conn IN OUT NOCOPY UTL_TCP.Connection,
buf IN VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER;
Function: Put
Description: Uploads a local file to the remote server. Assumes an open file
handle and does not close it.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
local_file IN OUT parameter; UTL_FILE file handle for input file. File
is assumed to be open for reading.
remote_path Pathname (including file name) indicating location of remote
file to be created/overwritten.
response OUT parameter; buffer for server replies.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed.
May raise any of the UTL_FILE exceptions if file read operations
fail. See UTL_FILE documentation for additional details.
FUNCTION Put( conn IN OUT NOCOPY UTL_TCP.Connection,
local_file IN OUT UTL_FILE.File_Type,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER;
Function: Put
Description: Uploads a local file to the remote server. Opens and closes local
file automatically.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
local_path Pathname of local directory in which file to upload exists.
local_filename Name of local file to upload.
remote_path Pathname (including file name) indicating location of remote
file to be created/overwritten.
response OUT parameter; buffer for server replies.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed.
May raise any of the UTL_FILE exceptions if file open, read, or
close operations fail. See UTL_FILE documentation for additional
details.
FUNCTION Put( conn IN OUT NOCOPY UTL_TCP.Connection,
local_path IN VARCHAR2,
local_filename IN VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER;
Function: Remote_Command
Description: Sends an arbitrary command to the server via the SITE command.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
command Command and parameter(s) to send to the server, e.g.
'CHMOD 600 foo.txt'
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed.
FUNCTION Remote_Command( conn IN OUT NOCOPY UTL_TCP.Connection,
command IN VARCHAR2 ) RETURN NUMBER;
Function: Chdir
Description: Changes current working directory on remote server to specified
path.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
remote_path Path on remote server to change to.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed.
FUNCTION Chdir( conn IN OUT NOCOPY UTL_TCP.Connection,
remote_path IN VARCHAR2 ) RETURN NUMBER;
Function: Pwd
Description: Prints current working directory (on remote server) to debugging
output if debugging is turned on.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
Return value: 0 (zero) if operation is successful; FTP error code if operation
is not successful.
Exceptions: May raise UTL_TCP.NETWORK_ERROR if some networking error occurs.
May raise VALUE_ERROR if server response is ill-formed.
FUNCTION Pwd( conn IN OUT NOCOPY UTL_TCP.Connection ) RETURN NUMBER;
Function: Close_Session
Description: Closes the TCP connection to the remote server.
Parameters:
conn IN OUT parameter that contains the connection info; to be
passed in to subsequent commands to maintain session state.
Return value: 0 (zero)
Exceptions: None raised.
FUNCTION Close_Session( conn IN OUT NOCOPY UTL_TCP.Connection ) RETURN NUMBER;
Function: Close_All_Sessions
Description: Closes all currently open TCP connections.
Parameters: None.
Return value: 0 (zero)
Exceptions: None raised.
FUNCTION Close_All_Sessions RETURN NUMBER;
END FTP;
CREATE OR REPLACE PACKAGE BODY FTP IS
vDebug BOOLEAN := TRUE;
FATAL_ERROR EXCEPTION;
PROCEDURE Debug( msg IN VARCHAR2 ) IS
BEGIN
IF vDebug THEN
DBMS_OUTPUT.Put_Line( msg );
END IF;
END Debug;
FUNCTION Get_Response( conn IN OUT NOCOPY UTL_TCP.Connection,
buf IN OUT VARCHAR2 ) RETURN NUMBER IS
vLen NUMBER;
vCode NUMBER;
vResp VARCHAR2(32767);
BEGIN
vLen := UTL_TCP.READ_LINE( conn, vResp );
Debug( vResp );
-- If TO_NUMBER below fails, let the exception propagate to calling proc
vCode := TO_NUMBER( SUBSTR( vResp, 1, 3 ) );
vResp := SUBSTR( vResp, 4 );
buf := buf || SUBSTR( vResp, 2 );
IF SUBSTR( vResp, 1, 1 ) = '-' THEN
LOOP
vLen := UTL_TCP.READ_LINE( conn, vResp );
Debug( vResp );
<<Get_Code>>
BEGIN
vCode := TO_NUMBER( SUBSTR( vResp, 1, 3 ) );
vResp := SUBSTR( vResp, 4 );
IF SUBSTR( vResp, 1, 1 ) = ' ' THEN
buf := buf || SUBSTR( vResp, 2 );
EXIT;
END IF;
EXCEPTION WHEN VALUE_ERROR THEN NULL;
END Get_Code;
buf := buf || vResp;
END LOOP;
END IF;
RETURN vCode;
END Get_Response;
FUNCTION Do_Command( conn IN OUT NOCOPY UTL_TCP.Connection,
cmd IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER IS
vResult NUMBER := 0;
BEGIN
vResult := UTL_TCP.WRITE_LINE( conn, cmd );
vResult := Get_Response( conn, response );
RETURN vResult;
END Do_Command;
FUNCTION Parse_Port_Number( port_string IN VARCHAR2 ) RETURN NUMBER IS
vResult NUMBER;
vNew_Port_String VARCHAR2(32767);
BEGIN
-- This stuff parses out the port number encoding from the server reply
-- Reply is in the format xyzh1,h2,h3,h4,p1,p2xyz
-- xyz = possible character data (server-dependent, may not exist)
-- h1-h4 = server IP elements; ignore since we know the host already
-- p1,p2 = port number encoding (port number = p1 * 256 + p2 )
vNew_Port_String := TRANSLATE( port_string, '0123456789', '0000000000' );
vNew_Port_String := SUBSTR( port_string,
INSTR( vNew_Port_String, '0' ),
INSTR( vNew_Port_String, '0', -1 ) -
INSTR( vNew_Port_String, '0' ) + 1 );
vNew_Port_String := SUBSTR( vNew_Port_String,
INSTR( vNew_Port_String, ',', 1, 4 ) + 1 );
vResult := 256 * TO_NUMBER( SUBSTR( vNew_Port_String,
1,
INSTR( vNew_Port_String, ',' ) - 1 ) );
vResult := vResult + TO_NUMBER( SUBSTR( vNew_Port_String,
INSTR( vNew_Port_String, ',' ) + 1 ) );
RETURN vResult;
-- Allow VALUE_ERROR to propagate
END Parse_Port_Number;
FUNCTION Open_Session( conn OUT NOCOPY UTL_TCP.Connection,
host IN VARCHAR2,
username IN VARCHAR2,
password IN VARCHAR2,
response OUT VARCHAR2,
timeout_secs IN NUMBER DEFAULT 60 ) RETURN NUMBER IS
vResp_Code NUMBER;
vGarbage NUMBER; -- For calling functions when we don't care about return val
BEGIN
conn := UTL_TCP.OPEN_CONNECTION( host,
21,
tx_timeout => timeout_secs );
vResp_Code := Get_Response( conn, response );
IF vResp_Code = 220 THEN
vResp_Code := Do_Command( conn, 'USER ' || username, response );
IF vResp_Code IN ( 331, 332 ) THEN
vResp_Code := Do_Command( conn, 'PASS ' || password, response );
IF vResp_Code NOT IN ( 202, 230 ) THEN
RAISE FATAL_ERROR;
END IF;
ELSE
RAISE FATAL_ERROR;
END IF;
END IF;
vResp_Code := Do_Command( conn, 'TYPE I', response );
Debug( 'Logged into ' || conn.remote_host || ' at port ' || conn.remote_port );
RETURN 0;
EXCEPTION
WHEN FATAL_ERROR THEN
Debug( 'Fatal error opening session:' );
Debug( ' Code: ' || vResp_Code );
Debug( ' Response: ' || response );
vGarbage := Close_Session( conn );
RETURN vResp_Code;
END Open_Session;
FUNCTION Get( conn IN OUT NOCOPY UTL_TCP.Connection,
buf OUT VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER IS
vResp VARCHAR2(32767);
vResp_Code NUMBER;
vNew_Conn UTL_TCP.Connection;
vNew_Port NUMBER;
BEGIN
-- do PASV
vResp_Code := Do_Command( conn, 'PASV', response );
IF vResp_Code = 227 THEN
<<Switch_Port>>
BEGIN
vNew_Port := Parse_Port_Number( response );
vNew_Conn := UTL_TCP.OPEN_CONNECTION( conn.remote_host,
vNew_Port,
tx_timeout => conn.tx_timeout );
Debug( 'Data connection: ' || vNew_Conn.remote_host || ':' || vNew_Conn.remote_port );
vResp_Code := Do_Command( conn, 'RETR ' || REPLACE( remote_path, CHR(12), CHR(0) ), response );
IF vResp_Code <> 150 THEN
RAISE FATAL_ERROR;
END IF;
<<Get_Download>>
BEGIN
LOOP
vResp := vResp || UTL_TCP.GET_LINE( vNew_Conn, FALSE );
END LOOP;
EXCEPTION
WHEN UTL_TCP.END_OF_INPUT THEN NULL;
END Get_Download;
vResp_Code := Close_Session( vNew_Conn );
vResp_Code := Get_Response( conn, response );
IF vResp_Code BETWEEN 400 AND 599 THEN
RAISE FATAL_ERROR;
END IF;
EXCEPTION
WHEN OTHERS THEN
Debug( SQLERRM );
RAISE FATAL_ERROR;
END Switch_Port;
ELSE
RAISE FATAL_ERROR;
END IF;
vResp_Code := Close_Session( vNew_Conn );
buf := vResp;
RETURN 0;
EXCEPTION
WHEN FATAL_ERROR THEN
Debug( 'Fatal error getting ' || remote_path || ':' );
Debug( ' Code: ' || vResp_Code );
Debug( ' Response: ' || response );
vResp_Code := Close_Session( vNew_Conn );
RETURN vResp_Code;
WHEN OTHERS THEN
Debug( vResp_Code || ': ' || SQLERRM );
RETURN vResp_Code;
END Get;
FUNCTION Get( conn IN OUT NOCOPY UTL_TCP.Connection,
local_file IN OUT UTL_FILE.File_Type,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER IS
vResp VARCHAR2(32767);
vResp_Code NUMBER := -1;
vNew_Conn UTL_TCP.Connection;
vNew_Port NUMBER;
BEGIN
-- do PASV
vResp_Code := Do_Command( conn, 'PASV', response );
IF vResp_Code = 227 THEN
<<Switch_Port>>
BEGIN
vNew_Port := Parse_Port_Number( response );
vNew_Conn := UTL_TCP.OPEN_CONNECTION( conn.remote_host,
vNew_Port,
tx_timeout => conn.tx_timeout );
Debug( 'Data connection: ' || vNew_Conn.remote_host || ':' || vNew_Conn.remote_port );
vResp_Code := Do_Command( conn, 'RETR ' || REPLACE( remote_path, CHR(12), CHR(0) ), response );
IF vResp_Code <> 150 THEN
RAISE FATAL_ERROR;
END IF;
<<Get_Download>>
BEGIN
LOOP
vResp := UTL_TCP.GET_LINE( vNew_Conn, FALSE );
UTL_FILE.Put( local_file, vResp );
END LOOP;
EXCEPTION
WHEN UTL_TCP.END_OF_INPUT THEN NULL;
END Get_Download;
vResp_Code := Close_Session( vNew_Conn );
vResp_Code := Get_Response( conn, response );
IF vResp_Code BETWEEN 400 AND 599 THEN
RAISE FATAL_ERROR;
END IF;
EXCEPTION
WHEN OTHERS THEN
Debug( SQLERRM );
RAISE FATAL_ERROR;
END Switch_Port;
ELSE
RAISE FATAL_ERROR;
END IF;
vResp_Code := Close_Session( vNew_Conn );
RETURN 0;
EXCEPTION
WHEN FATAL_ERROR THEN
Debug( 'Fatal error getting ' || remote_path || ':' );
Debug( ' Code: ' || vResp_Code );
Debug( ' Response: ' || response );
vResp_Code := Close_Session( vNew_Conn );
RETURN vResp_Code;
WHEN OTHERS THEN
Debug( vResp_Code || ': ' || SQLERRM );
RETURN vResp_Code;
END Get;
FUNCTION Get( conn IN OUT NOCOPY UTL_TCP.Connection,
local_path IN VARCHAR2,
local_filename IN VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER IS
vFile UTL_FILE.File_Type;
vResult NUMBER := -1;
BEGIN
vFile := UTL_FILE.FOPEN( local_path, local_filename, 'w' );
vResult := Get( conn, vFile, remote_path, response );
UTL_FILE.FCLOSE( vFile );
RETURN vResult;
EXCEPTION WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN( vFile ) THEN
UTL_FILE.FCLOSE( vFile );
END IF;
RAISE;
END Get;
FUNCTION Put( conn IN OUT NOCOPY UTL_TCP.Connection,
buf IN VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER IS
vResp VARCHAR2(32767);
vResp_Code NUMBER;
vNew_Conn UTL_TCP.Connection;
vNew_Port NUMBER;
BEGIN
-- do PASV
vResp_Code := Do_Command( conn, 'PASV', response );
IF vResp_Code = 227 THEN
<<Switch_Port>>
BEGIN
vNew_Port := Parse_Port_Number( response );
vNew_Conn := UTL_TCP.OPEN_CONNECTION( conn.remote_host,
vNew_Port,
tx_timeout => conn.tx_timeout );
Debug( 'Data connection: ' || vNew_Conn.remote_host || ':' || vNew_Conn.remote_port );
vResp_Code := Do_Command( conn, 'STOR ' || REPLACE( remote_path, CHR(12), CHR(0) ), response );
IF vResp_Code <> 150 THEN
RAISE FATAL_ERROR;
END IF;
vResp_Code := UTL_TCP.WRITE_TEXT( vNew_Conn, buf );
UTL_TCP.FLUSH( vNew_Conn );
vResp_Code := Close_Session( vNew_Conn );
vResp_Code := Get_Response( conn, response );
IF vResp_Code BETWEEN 400 AND 599 THEN
RAISE FATAL_ERROR;
END IF;
EXCEPTION
WHEN OTHERS THEN
Debug( SQLERRM );
RAISE FATAL_ERROR;
END Switch_Port;
ELSE
RAISE FATAL_ERROR;
END IF;
vResp_Code := Close_Session( vNew_Conn );
response := vResp;
RETURN 0;
EXCEPTION
WHEN FATAL_ERROR THEN
Debug( 'Fatal error putting ' || remote_path || ':' );
Debug( ' Code: ' || vResp_Code );
Debug( ' Response: ' || response );
vResp_Code := Close_Session( vNew_Conn );
RETURN vResp_Code;
WHEN OTHERS THEN
Debug( vResp_Code || ': ' || SQLERRM );
RETURN vResp_Code;
END Put;
FUNCTION Put( conn IN OUT NOCOPY UTL_TCP.Connection,
local_file IN OUT UTL_FILE.File_Type,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER IS
vResp VARCHAR2(32767);
vResp_Code NUMBER;
vNew_Conn UTL_TCP.Connection;
vNew_Port NUMBER;
vNew_Port_String VARCHAR2(32767);
BEGIN
-- do PASV
vResp_Code := Do_Command( conn, 'PASV', response );
IF vResp_Code = 227 THEN
<<Switch_Port>>
BEGIN
vNew_Port := Parse_Port_Number( response );
vNew_Conn := UTL_TCP.OPEN_CONNECTION( conn.remote_host,
vNew_Port,
tx_timeout => conn.tx_timeout );
Debug( 'Data connection: ' || vNew_Conn.remote_host || ':' || vNew_Conn.remote_port );
vResp_Code := Do_Command( conn, 'STOR ' || REPLACE( remote_path, CHR(12), CHR(0) ), response );
IF vResp_Code <> 150 THEN
RAISE FATAL_ERROR;
END IF;
<<Get_Download>>
BEGIN
LOOP
UTL_FILE.Get_Line( local_file, vResp );
vResp_Code := UTL_TCP.WRITE_LINE( vNew_Conn, vResp );
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN NULL;
END Get_Download;
vResp_Code := Close_Session( vNew_Conn );
vResp_Code := Get_Response( conn, response );
IF vResp_Code BETWEEN 400 AND 599 THEN
RAISE FATAL_ERROR;
END IF;
EXCEPTION
WHEN OTHERS THEN
Debug( SQLERRM );
RAISE FATAL_ERROR;
END Switch_Port;
ELSE
RAISE FATAL_ERROR;
END IF;
vResp_Code := Close_Session( vNew_Conn );
RETURN 0;
EXCEPTION
WHEN FATAL_ERROR THEN
Debug( 'Fatal error putting ' || remote_path || ':' );
Debug( ' Code: ' || vResp_Code );
Debug( ' Response: ' || response );
vResp_Code := Close_Session( vNew_Conn );
RETURN vResp_Code;
WHEN OTHERS THEN
Debug( vResp_Code || ': ' || SQLERRM );
RETURN vResp_Code;
END Put;
FUNCTION Put( conn IN OUT NOCOPY UTL_TCP.Connection,
local_path IN VARCHAR2,
local_filename IN VARCHAR2,
remote_path IN VARCHAR2,
response OUT VARCHAR2 ) RETURN NUMBER IS
vFile UTL_FILE.File_Type;
vResult NUMBER;
BEGIN
vFile := UTL_FILE.FOPEN( local_path, local_filename, 'r' );
vResult := Put( conn, vFile, remote_path, response );
UTL_FILE.FCLOSE( vFile );
RETURN vResult;
EXCEPTION WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN( vFile ) THEN
UTL_FILE.FCLOSE( vFile );
END IF;
RAISE;
END Put;
FUNCTION Remote_Command( conn IN OUT NOCOPY UTL_TCP.Connection,
command IN VARCHAR2 ) RETURN NUMBER IS
vResp_Code NUMBER;
vResponse VARCHAR2(32767);
BEGIN
vResp_Code := Do_Command( conn, 'SITE ' || command, vResponse );
IF vResp_Code BETWEEN 500 AND 599 THEN
RETURN vResp_Code;
END IF;
RETURN 0;
END Remote_Command;
FUNCTION Chdir( conn IN OUT NOCOPY UTL_TCP.Connection,
remote_path IN VARCHAR2 ) RETURN NUMBER IS
vResp_Code NUMBER;
vResponse VARCHAR2(32767);
BEGIN
vResp_Code := Do_Command( conn, 'CWD ' || REPLACE( remote_path, CHR(12), CHR(0) ), vResponse );
IF vResp_Code BETWEEN 500 AND 599 THEN
RETURN vResp_Code;
END IF;
RETURN 0;
END Chdir;
FUNCTION Pwd( conn IN OUT NOCOPY UTL_TCP.Connection ) RETURN NUMBER IS
vResp_Code NUMBER;
vResponse VARCHAR2(32767);
BEGIN
vResp_Code := Do_Command( conn, 'PWD', vResponse );
IF vResp_Code BETWEEN 500 AND 599 THEN
RETURN vResp_Code;
END IF;
RETURN 0;
END Pwd;
FUNCTION Close_Session( conn IN OUT NOCOPY UTL_TCP.Connection ) RETURN NUMBER IS
BEGIN
IF conn.remote_host IS NULL THEN
RETURN 0;
END IF;
Debug( 'Closing connection on ' || conn.remote_host || ':' || conn.remote_port );
UTL_TCP.Close_Connection( conn );
RETURN 0;
EXCEPTION
WHEN UTL_TCP.NETWORK_ERROR THEN RETURN 0;
END Close_Session;
FUNCTION Close_All_Sessions RETURN NUMBER IS
BEGIN
UTL_TCP.Close_All_Connections;
RETURN 0;
END Close_All_Sessions;
END FTP;Here's another PL/SQL package that will FTP ASCII text files. It assumes that you have proper permissions on the remote host and simply want to transfer one or more text files, not perform any other miscellaneous commands.
Also, from what I have read, in 9i UTL_FILE supports reading and writing of binary data so an FTP client could be written to transfer either ASCII or BINARY files.
Regards,
Russ
CREATE OR REPLACE PACKAGE BRNC_FTP_PKG
AS
* PL/SQL FTP Client
* Created by: Russ Johnson, Braun Consulting
* www.braunconsult.com
* OVERVIEW
* This package uses the standard packages UTL_FILE and UTL_TCP to perform
* client-side FTP functionality (PUT and GET) for text files as defined in
* the World Wide Web Consortium's RFC 959 document - http://www.w3.org/Protocols/rfc959/
* The procedures and functions in this package allow single or multiple file transfer using
* standard TCP/IP connections.
* LIMITATIONS
* Currently the API is limited to transfer of ASCII text files only. This is
* primarily because UTL_FILE only supports text I/O, but also because the original
* design was for creating text files from data in the Oracle database, then transferring the file to a remote host.
* Furthermore, the API does not support SSH/Secure FTP or connection through a proxy server.
* Keep in mind that FTP passes the username/password combo in plain text over TCP/IP.
* DB versions - 8i (8.1.x) and above. 8.0.x may work if it has the SYS.UTL_TCP package.
* Note: Since UTL_FILE is used for the client-side I/O, this package is also limited to
* transfer of files that exist in directories available to UTL_FILE for read/write.
* These directories are defined by the UTL_FILE_DIR parameter in the init.ora file.
* USAGE
* Three functions are available for FTP - PUT, GET, and FTP_MULTIPLE. FTP_MULTIPLE takes
* a table of records that define the files to be transferred (filename, directory, etc.).
* That table can have 1 record or multiple records. The PUT and GET functions are included
* for convenience to FTP one file at a time. PUT and GET return true if the file is transferred
* successfully and false if it fails. FTP_MULTIPLE returns true if no batch-level errors occur
* (such as an invalid host, refused connection, or invalid login information). It also takes the
* table of file records IN and passes it back OUT. Each record contains individual error information.
* EXAMPLE
* Transfer multiple files - 1 GET and 2 PUT from a Windows machine to a host (assuming UNIX here).
* Display any errors that occur.
* DECLARE
* v_username VARCHAR2(40) := 'rjohnson';
* v_password VARCHAR2(40) := 'password';
* v_hostname VARCHAR2(255) := 'ftp.oracle.com';
* v_error_message VARCHAR2(1000);
* b_put BOOLEAN;
* t_files BRNC_FTP_PKG.t_ftp_rec; -- Declare our table of file records
* BEGIN
* t_files(1).localpath := 'd:\oracle\utl_file\outbound';
* t_files(1).filename := 'myfile1.txt';
* t_files(1).remotepath := '/home/oracle/text_files';
* t_files(1).transfer_mode := 'PUT';
* t_files(2).localpath := 'd:\oracle\utl_file\inbound';
* t_files(2).filename := 'incoming_file.xml';
* t_files(2).remotepath := '/home/oracle/xml_files';
* t_files(2).transfer_mode := 'GET';
* t_files(3).localpath := 'd:\oracle\utl_file\outbound';
* t_files(3).filename := 'myfile2.txt';
* t_files(3).remotepath := '/home';
* t_files(3).transfer_mode := 'PUT';
* b_put := BRNC_FTP_PKG.FTP_MULTIPLE(v_error_message,
* t_files,
* v_username,
* v_password,
* v_hostname);
* IF b_put = TRUE
* THEN
* FOR i IN t_files.FIRST..t_files.LAST
* LOOP
* IF t_files.EXISTS(i)
* THEN
* DBMS_OUTPUT.PUT_LINE(t_files(i).status||' | '||
* t_files(i).error_message||' | '||
* to_char(t_files(i).bytes_transmitted)||' | '||
* to_char(t_files(i).trans_start,'YYYY-MM-DD HH:MI:SS')||' | '||
* to_char(t_files(i).trans_end,'YYYY-MM-DD HH:MI:SS'));
* END IF;
* END LOOP;
* ELSE
* DBMS_OUTPUT.PUT_LINE(v_error_message);
* END IF;
* EXCEPTION
* WHEN OTHERS
* THEN
* DBMS_OUTPUT.PUT_LINE(SQLERRM);
* END;
* CREDITS
* The W3C's RFC 959 that describes the FTP process.
* http://www.w3c.org
* Much of the PL/SQL code in this package was based on Java code written by
* Bruce Blackshaw of Enterprise Distributed Technologies Ltd. None of that code
* was copied, but the objects and methods greatly helped my understanding of the
* FTP Client process.
* http://www.enterprisedt.com
* VERSION HISTORY
* 1.0 11/19/2002 Unit-tested single and multiple transfers between disparate hosts.
* Exceptions
ctrl_exception EXCEPTION;
data_exception EXCEPTION;
* Constants - FTP valid response codes
CONNECT_CODE CONSTANT PLS_INTEGER := 220;
USER_CODE CONSTANT PLS_INTEGER := 331;
LOGIN_CODE CONSTANT PLS_INTEGER := 230;
PWD_CODE CONSTANT PLS_INTEGER := 257;
PASV_CODE CONSTANT PLS_INTEGER := 227;
CWD_CODE CONSTANT PLS_INTEGER := 250;
TSFR_START_CODE1 CONSTANT PLS_INTEGER := 125;
TSFR_START_CODE2 CONSTANT PLS_INTEGER := 150;
TSFR_END_CODE CONSTANT PLS_INTEGER := 226;
QUIT_CODE CONSTANT PLS_INTEGER := 221;
SYST_CODE CONSTANT PLS_INTEGER := 215;
TYPE_CODE CONSTANT PLS_INTEGER := 200;
* FTP File record datatype
* Elements:
* localpath - full directory name in which the local file resides or will reside
* Windows: 'd:\oracle\utl_file'
* UNIX: '/home/oracle/utl_file'
* filename - filename and extension for the file to be received or sent
* changing the filename for the PUT or GET is currently not allowed
* Examples: 'myfile.dat' 'myfile20021119.xml'
* remotepath - full directory name in which the local file will be sent or the
* remote file exists. Should be in UNIX format regardless of FTP server - '/one/two/three'
* filetype - reserved for future use, ignored in code
* transfer_mode - 'PUT' or 'GET'
* status - status of the transfer. 'ERROR' or 'SUCCESS'
* error_message - meaningful (hopefully) error message explaining the reason for failure
* bytes_transmitted - how many bytes were sent/received
* trans_start - date/time the transmission started
* trans_end - date/time the transmission ended
TYPE r_ftp_rec IS RECORD(localpath VARCHAR2(255),
filename VARCHAR2(255),
remotepath VARCHAR2(255),
filetype VARCHAR2(20),
transfer_mode VARCHAR2(5),
status VARCHAR2(40),
error_message VARCHAR2(255),
bytes_transmitted NUMBER,
trans_start DATE,
trans_end DATE);
* FTP File Table - used to store many files for transfer
TYPE t_ftp_rec IS TABLE of r_ftp_rec INDEX BY BINARY_INTEGER;
* Internal convenience procedure for creating passive host IP address
* and port number.
PROCEDURE CREATE_PASV(p_pasv_cmd IN VARCHAR2,
p_pasv_host OUT VARCHAR2,
p_pasv_port OUT NUMBER);
* Function used to validate FTP server responses based on the
* code passed in p_code. Reads single or multi-line responses.
FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,
p_code IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN;
* Function used to validate FTP server responses based on the
* code passed in p_code. Reads single or multi-line responses.
* Overloaded because some responses can have 2 valid codes.
FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,
p_code1 IN PLS_INTEGER,
p_code2 IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN;
* Procedure that handles the actual data transfer. Meant
* for internal package use. Returns information about the
* actual transfer.
PROCEDURE TRANSFER_ASCII(u_ctrl_con IN OUT UTL_TCP.CONNECTION,
p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_pasv_host IN VARCHAR2,
p_pasv_port IN PLS_INTEGER,
p_transfer_mode IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE);
* Function to handle FTP of many files.
* Returns TRUE if no batch-level errors occur.
* Returns FALSE if a batch-level error occurs.
* Parameters:
* p_error_msg - error message for batch level errors
* p_files - BRNC_FTP_PKG.t_ftp_rec table type. Accepts
* list of files to be transferred (may be any combination of PUT or GET)
* returns the table updated with transfer status, error message,
* bytes_transmitted, transmission start date/time and transmission end
* date/time
* p_username - username for FTP server
* p_password - password for FTP server
* p_hostname - hostname or IP address of server Ex: 'ftp.oracle.com' or '127.0.0.1'
* p_port - port number to connect on. FTP is usually on 21, but this may be overridden
* if the server is configured differently.
FUNCTION FTP_MULTIPLE(p_error_msg OUT VARCHAR2,
p_files IN OUT t_ftp_rec,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
p_port IN PLS_INTEGER DEFAULT 21)
RETURN BOOLEAN;
* Convenience function for single-file PUT
* Parameters:
* p_localpath - full directory name in which the local file resides or will reside
* Windows: 'd:\oracle\utl_file'
* UNIX: '/home/oracle/utl_file'
* p_filename - filename and extension for the file to be received or sent
* changing the filename for the PUT or GET is currently not allowed
* Examples: 'myfile.dat' 'myfile20021119.xml'
* p_remotepath - full directory name in which the local file will be sent or the
* remote file exists. Should be in UNIX format regardless of FTP server - '/one/two/three'
* p_username - username for FTP server
* p_password - password for FTP server
* p_hostname - FTP server IP address or host name Ex: 'ftp.oracle.com' or '127.0.0.1'
* v_status - status of the transfer. 'ERROR' or 'SUCCESS'
* v_error_message - meaningful (hopefully) error message explaining the reason for failure
* n_bytes_transmitted - how many bytes were sent/received
* d_trans_start - date/time the transmission started
* d_trans_end - date/time the transmission ended
* p_port - port number to connect to, default is 21
* p_filetype - always set to 'ASCII', reserved for future use, ignored in code
FUNCTION PUT(p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN;
* Convenience function for single-file GET
* Parameters:
* p_localpath - full directory name in which the local file resides or will reside
* Windows: 'd:\oracle\utl_file'
* UNIX: '/home/oracle/utl_file'
* p_filename - filename and extension for the file to be received or sent
* changing the filename for the PUT or GET is currently not allowed
* Examples: 'myfile.dat' 'myfile20021119.xml'
* p_remotepath - full directory name in which the local file will be sent or the
* remote file exists. Should be in UNIX format regardless of FTP server - '/one/two/three'
* p_username - username for FTP server
* p_password - password for FTP server
* p_hostname - FTP server IP address or host name Ex: 'ftp.oracle.com' or '127.0.0.1'
* v_status - status of the transfer. 'ERROR' or 'SUCCESS'
* v_error_message - meaningful (hopefully) error message explaining the reason for failure
* n_bytes_transmitted - how many bytes were sent/received
* d_trans_start - date/time the transmission started
* d_trans_end - date/time the transmission ended
* p_port - port number to connect to, default is 21
* p_filetype - always set to 'ASCII', reserved for future use, ignored in code
FUNCTION GET(p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN;
END BRNC_FTP_PKG;
CREATE OR REPLACE PACKAGE BODY BRNC_FTP_PKG
AS
** Create the passive host IP and port number to connect to
PROCEDURE CREATE_PASV(p_pasv_cmd IN VARCHAR2,
p_pasv_host OUT VARCHAR2,
p_pasv_port OUT NUMBER)
IS
v_pasv_cmd VARCHAR2(30) := p_pasv_cmd; --Host and port to connect to for data transfer
n_port_dec NUMBER;
n_port_add NUMBER;
BEGIN
p_pasv_host := REPLACE(SUBSTR(v_pasv_cmd,1,INSTR(v_pasv_cmd,',',1,4)-1),',','.');
n_port_dec := TO_NUMBER(SUBSTR(v_pasv_cmd,INSTR(v_pasv_cmd,',',1,4)+1,(INSTR(v_pasv_cmd,',',1,5)-(INSTR(v_pasv_cmd,',',1,4)+1))));
n_port_add := TO_NUMBER(SUBSTR(v_pasv_cmd,INSTR(v_pasv_cmd,',',1,5)+1,LENGTH(v_pasv_cmd)-INSTR(v_pasv_cmd,',',1,5)));
p_pasv_port := (n_port_dec*256) + n_port_add;
EXCEPTION
WHEN OTHERS
THEN
--DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
END CREATE_PASV;
** Read a single or multi-line reply from the FTP server and validate
** it against the code passed in p_code.
** Return TRUE if reply code matches p_code, FALSE if it doesn't or error
** occurs
** Send full server response back to calling procedure
FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,
p_code IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN
IS
n_code VARCHAR2(3) := p_code;
n_byte_count PLS_INTEGER;
v_msg VARCHAR2(255);
n_line_count PLS_INTEGER := 0;
BEGIN
LOOP
v_msg := UTL_TCP.GET_LINE(p_ctrl_con);
n_line_count := n_line_count + 1;
IF n_line_count = 1
THEN
p_reply := v_msg;
ELSE
p_reply := p_reply || SUBSTR(v_msg,4);
END IF;
EXIT WHEN INSTR(v_msg,'-',1,1) <> 4;
END LOOP;
IF to_number(SUBSTR(p_reply,1,3)) = n_code
THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
p_reply := SQLERRM;
RETURN FALSE;
END VALIDATE_REPLY;
** Reads a single or multi-line reply from the FTP server
** Return TRUE if reply code matches p_code1 or p_code2,
** FALSE if it doesn't or error occurs
** Send full server response back to calling procedure
FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,
p_code1 IN PLS_INTEGER,
p_code2 IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN
IS
v_code1 VARCHAR2(3) := to_char(p_code1);
v_code2 VARCHAR2(3) := to_char(p_code2);
v_msg VARCHAR2(255);
n_line_count PLS_INTEGER := 0;
BEGIN
LOOP
v_msg := UTL_TCP.GET_LINE(p_ctrl_con);
n_line_count := n_line_count + 1;
IF n_line_count = 1
THEN
p_reply := v_msg;
ELSE
p_reply := p_reply || SUBSTR(v_msg,4);
END IF;
EXIT WHEN INSTR(v_msg,'-',1,1) <> 4;
END LOOP;
IF to_number(SUBSTR(p_reply,1,3)) IN(v_code1,v_code2)
THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
p_reply := SQLERRM;
RETURN FALSE;
END VALIDATE_REPLY;
** Handles actual data transfer. Responds with status, error message, and
** transfer statistics.
** Potential errors could be with connection or file i/o
PROCEDURE TRANSFER_ASCII(u_ctrl_con IN OUT UTL_TCP.CONNECTION,
p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_pasv_host IN VARCHAR2,
p_pasv_port IN PLS_INTEGER,
p_transfer_mode IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE)
IS
u_data_con UTL_TCP.CONNECTION;
u_filehandle UTL_FILE.FILE_TYPE;
v_tsfr_mode VARCHAR2(3) := p_transfer_mode;
v_mode VARCHAR2(1);
v_tsfr_cmd VARCHAR2(10);
v_buffer VARCHAR2(32767);
v_localpath VARCHAR2(255) := p_localpath;
v_filename VARCHAR2(255) := p_filename;
v_host VARCHAR2(20) := p_pasv_host;
n_port PLS_INTEGER := p_pasv_port;
n_bytes NUMBER;
v_msg VARCHAR2(255);
v_reply VARCHAR2(1000);
v_err_status VARCHAR2(20) := 'ERROR';
BEGIN
/** Initialize some of our OUT variables **/
v_status := 'SUCCESS';
v_error_message := ' ';
n_bytes_transmitted := 0;
IF UPPER(v_tsfr_mode) = 'PUT'
THEN
v_mode := 'r';
v_tsfr_cmd := 'STOR ';
ELSIF UPPER(v_tsfr_mode) = 'GET'
THEN
v_mode := 'w';
v_tsfr_cmd := 'RETR ';
END IF;
/** Open data connection on Passive host and port **/
u_data_con := UTL_TCP.OPEN_CONNECTION(v_host,n_port);
/** Open the local file to read and transfer data **/
u_filehandle := UTL_FILE.FOPEN(v_localpath,v_filename,v_mode);
/** Send the STOR command to tell the server we're going to upload a file **/
n_bytes := UTL_TCP.WRITE_LINE(u_ctrl_con,v_tsfr_cmd||v_filename);
IF VALIDATE_REPLY(u_ctrl_con,TSFR_START_CODE1,TSFR_START_CODE2,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
d_trans_start := SYSDATE;
IF UPPER(v_tsfr_mode) = 'PUT'
THEN
LOOP
BEGIN
UTL_FILE.GET_LINE(u_filehandle,v_buffer);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
EXIT;
END;
n_bytes := UTL_TCP.WRITE_LINE(u_data_con,v_buffer);
n_bytes_transmitted := n_bytes_transmitted + n_bytes;
END LOOP;
ELSIF UPPER(v_tsfr_mode) = 'GET'
THEN
LOOP
BEGIN
v_buffer := UTL_TCP.GET_LINE(u_data_con,TRUE);
/** Sometimes the TCP/IP buffer sends null data **/
/** we only want to receive the actual data **/
IF v_buffer IS NOT NULL
THEN
UTL_FILE.PUT_LINE(u_filehandle,v_buffer);
n_bytes := LENGTH(v_buffer);
n_bytes_transmitted := n_bytes_transmitted + n_bytes;
END IF;
EXCEPTION
WHEN UTL_TCP.END_OF_INPUT
THEN
EXIT;
END;
END LOOP;
END IF;
/** Flush the buffer on the data connection **/
--UTL_TCP.FLUSH(u_data_con);
d_trans_end := SYSDATE;
/** Close the file **/
UTL_FILE.FCLOSE(u_filehandle);
/** Close the Data Connection **/
UTL_TCP.CLOSE_CONNECTION(u_data_con);
/** Verify the transfer succeeded **/
IF VALIDATE_REPLY(u_ctrl_con,TSFR_END_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
EXCEPTION
WHEN ctrl_exception
THEN
v_status := v_err_status;
v_error_message := v_reply;
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);
WHEN UTL_FILE.invalid_path
THEN
v_status := v_err_status;
v_error_message := 'Directory '||v_localpath||' is not available to UTL_FILE. Check the init.ora file for valid UTL_FILE directories.';
UTL_TCP.CLOSE_CONNECTION(u_data_con);
WHEN UTL_FILE.invalid_operation
THEN
v_status := v_err_status;
IF UPPER(v_tsfr_mode) = 'PUT'
THEN
v_error_message := 'The file '||V_filename||' in the directory '||v_localpath||' could not be opened for reading.';
ELSIF UPPER(v_tsfr_mode) = 'GET'
THEN
v_error_message := 'The file '||V_filename||' in the directory '||v_localpath||' could not be opened for writing.';
END IF;
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);
WHEN UTL_FILE.read_error
THEN
v_status := v_err_status;
v_error_message := 'The system encountered an error while trying to read '||v_filename||' in the directory '||v_localpath;
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);
WHEN UTL_FILE.write_error
THEN
v_status := v_err_status;
v_error_message := 'The system encountered an error while trying to write to '||v_filename||' in the directory '||v_localpath;
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);
WHEN UTL_FILE.internal_error
THEN
v_status := v_err_status;
v_error_message := 'The UTL_FILE package encountered an unexpected internal system error.';
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);
WHEN OTHERS
THEN
v_status := v_err_status;
v_error_message := SQLERRM;
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);
END TRANSFER_ASCII;
** Handles connection to host and FTP of multiple files
** Files can be any combination of PUT and GET
FUNCTION FTP_MULTIPLE(p_error_msg OUT VARCHAR2,
p_files IN OUT t_ftp_rec,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
p_port IN PLS_INTEGER DEFAULT 21)
RETURN BOOLEAN
IS
v_username VARCHAR2(30) := p_username;
v_password VARCHAR2(30) := p_password;
v_hostname VARCHAR2(30) := p_hostname;
n_port PLS_INTEGER := p_port;
u_ctrl_con UTL_TCP.CONNECTION;
n_byte_count PLS_INTEGER;
n_first_index NUMBER;
v_msg VARCHAR2(250);
v_reply VARCHAR2(1000);
v_pasv_host VARCHAR2(20);
n_pasv_port NUMBER;
invalid_transfer EXCEPTION;
BEGIN
p_error_msg := 'FTP Successful'; --Assume the overall transfer will succeed
/** Attempt to connect to the host machine **/
u_ctrl_con := UTL_TCP.OPEN_CONNECTION(v_hostname,n_port);
IF VALIDATE_REPLY(u_ctrl_con,CONNECT_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
/** Send username **/
n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'USER '||v_username);
IF VALIDATE_REPLY(u_ctrl_con,USER_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
/** Send password **/
n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'PASS '||v_password);
IF VALIDATE_REPLY(u_ctrl_con,LOGIN_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
/** We should be logged in, time to transfer all files **/
FOR i IN p_files.FIRST..p_files.LAST
LOOP
IF p_files.EXISTS(i)
THEN
BEGIN
/** Change to the remotepath directory **/
n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'CWD '||p_files(i).remotepath);
IF VALIDATE_REPLY(u_ctrl_con,CWD_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
/** Switch to IMAGE mode **/
n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'TYPE I');
IF VALIDATE_REPLY(u_ctrl_con,TYPE_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
/** Get a Passive connection to use for data transfer **/
n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'PASV');
IF VALIDATE_REPLY(u_ctrl_con,PASV_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;
CREATE_PASV(SUBSTR(v_reply,INSTR(v_reply,'(',1,1)+1,INSTR(v_reply,')',1,1)-INSTR(v_reply,'(',1,1)-1),v_pasv_host,n_pasv_port);
/** Transfer Data **/
IF UPPER(p_files(i).transfer_mode) = 'PUT'
THEN
TRANSFER_ASCII(u_ctrl_con,
p_files(i).localpath,
p_files(i).filename,
v_pasv_host,
n_pasv_port,
p_files(i).transfer_mode,
p_files(i).status,
p_files(i).error_message,
p_files(i).bytes_transmitted,
p_files(i).trans_start,
p_files(i).trans_end);
ELSIF UPPER(p_files(i).transfer_mode) = 'GET'
THEN
TRANSFER_ASCII(u_ctrl_con,
p_files(i).localpath,
p_files(i).filename,
v_pasv_host,
n_pasv_port,
p_files(i).transfer_mode,
p_files(i).status,
p_files(i).error_message,
p_files(i).bytes_transmitted,
p_files(i).trans_start,
p_files(i).trans_end);
ELSE
RAISE invalid_transfer; -- Raise an exception here
END IF;
EXCEPTION
WHEN ctrl_exception
THEN
p_files(i).status := 'ERROR';
p_files(i).error_message := v_reply;
WHEN invalid_transfer
THEN
p_files(i).status := 'ERROR';
p_files(i).error_message := 'Invalid transfer method. Use PUT or GET.';
END;
END IF;
END LOOP;
/** Send QUIT command **/
n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'QUIT');
/** Don't need to validate QUIT, just close the connection **/
UTL_TCP.CLOSE_CONNECTION(u_ctrl_con);
RETURN TRUE;
EXCEPTION
WHEN ctrl_exception
THEN
p_error_msg := v_reply;
UTL_TCP.CLOSE_ALL_CONNECTIONS;
RETURN FALSE;
WHEN OTHERS
THEN
p_error_msg := SQLERRM;
UTL_TCP.CLOSE_ALL_CONNECTIONS;
RETURN FALSE;
END FTP_MULTIPLE;
** Convenience function for single-file PUT
** Formats file information for FTP_MULTIPLE function and calls it.
FUNCTION PUT(p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN
IS
t_files t_ftp_rec;
v_username VARCHAR2(30) := p_username;
v_password VARCHAR2(50) := p_password;
v_hostname VARCHAR2(100) := p_hostname;
n_port PLS_INTEGER := p_port;
v_err_msg VARCHAR2(255);
b_ftp BOOLEAN;
BEGIN
t_files(1).localpath := p_localpath;
t_files(1).filename := p_filename;
t_files(1).remotepath := p_remotepath;
t_files(1).filetype := p_filetype;
t_files(1).transfer_mode := 'PUT';
b_ftp := FTP_MULTIPLE(v_err_msg,
t_files,
v_username,
v_password,
v_hostname,
n_port);
IF b_ftp = FALSE
THEN
v_status := 'ERROR';
v_error_message := v_err_msg;
RETURN FALSE;
ELSIF b_ftp = TRUE
THEN
v_status := t_files(1).status;
v_error_message := t_files(1).error_message;
n_bytes_transmitted := t_files(1).bytes_transmitted;
d_trans_start := t_files(1).trans_start;
d_trans_end := t_files(1).trans_end;
RETURN TRUE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
v_status := 'ERROR';
v_error_message := SQLERRM;
RETURN FALSE;
--DBMS_OUTPUT.PUT_LINE(SQLERRM);
END PUT;
** Convenience function for single-file GET
** Formats file information for FTP_MULTIPLE function and calls it.
FUNCTION GET(p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN
IS
t_files t_ftp_rec;
v_username VARCHAR2(30) := p_username;
v_password VARCHAR2(50) := p_password;
v_hostname VARCHAR2(100) := p_hostname;
n_port PLS_INTEGER := p_port;
v_err_msg VARCHAR2(255);
b_ftp BOOLEAN;
BEGIN
t_files(1).localpath := p_localpath;
t_files(1).filename := p_filename;
t_files(1).remotepath := p_remotepath;
t_files(1).filetype := p_filetype;
t_files(1).transfer_mode := 'GET';
b_ftp := FTP_MULTIPLE(v_err_msg,
t_files,
v_username,
v_password,
v_hostname,
n_port);
IF b_ftp = FALSE
THEN
v_status := 'ERROR';
v_error_message := v_err_msg;
RETURN FALSE;
ELSIF b_ftp = TRUE
THEN
v_status := t_files(1).status;
v_error_message := t_files(1).error_message;
n_bytes_transmitted := t_files(1).bytes_transmitted;
d_trans_start := t_files(1).trans_start;
d_trans_end := t_files(1).trans_end;
RETURN TRUE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
v_status := 'ERROR';
v_error_message := SQLERRM;
RETURN FALSE;
--DBMS_OUTPUT.PUT_LINE(SQLERRM);
END GET;
END BRNC_FTP_PKG;
/ -
Hi all,
i have the need to implement an FTP procedure and accordingly to what i found here : http://www.oracle-base.com/articles/misc/ftp-from-plsql.php i try this code:
DECLARE
l_conn UTL_TCP.connection;
l_result PLS_INTEGER;
BEGIN
l_conn := UTL_TCP.open_connection('remotehost', '21');
dbms_output.put_line('1 '||UTL_TCP.get_line(l_conn, TRUE));
l_result := UTL_TCP.write_line(l_conn, 'USER user');
dbms_output.put_line('2 '||UTL_TCP.get_line(l_conn, TRUE));
l_result := UTL_TCP.write_line(l_conn, 'PASS passord');
dbms_output.put_line('3 '||UTL_TCP.get_line(l_conn, TRUE));
l_result := UTL_TCP.write_line(l_conn, 'cwd /some/remote/path');
begin
loop
dbms_output.put_line('4 '||UTL_TCP.get_line(l_conn, TRUE));
end loop;
exception
when UTL_TCP.END_OF_INPUT then
null;
end;
l_result := UTL_TCP.write_line(l_conn, 'bye');
UTL_TCP.close_connection(l_conn);
END; in order to intercept all the returning messages ... but the loop never raise exception.
Any suggestion?
Thanks
AlexOraclePSP wrote:
Paul Horth wrote:
"I NEED to do ftp in a procedure, if possible"
What business function are you trying to satisfy?Hi Paul,
in a higher vision the all process have to :
- transfer files in directory and subdirectory (eg. /2012/Nov , /2012/Dec, /2013/Jan, etc)
- "check and make" the path
- the success or errors of the entire flow has to be reported to the user
I know that all this can be done with os command, and i already does, but i have an apex app that manage the files i would like to export.
Is easier to report os commands result in apex vs. reproducing the command in apex directly?
Any suggestion is welcome and appreciated.
AlexWithout knowing more it is difficult to say what would be the best fit to your requirements but two
approaches spring to mind.
One is to use Unix scripts to do the ftp and write the results to a table whcih can then be interrogated by an Apex
report as to what the result was. When you say the Apex app manages the files you want to export, do you mean
the user picks the files to transfer? One way of doing that would be for the Unix script to read a table to see what needs
ftping (the app would obviously set the required data up in the table).
The second approach might be to write a Java procedure that sits in the DB and invokes the OS commands required. This procedure
can be invoked like a normal PL/SQL procedure stored on the DB.
Others may have other ideas. -
File Deletion from PL/SQL using Java
I'm able to delete a file on the OS from PL/SQL by calling a java class. But there is a problem if I am trying to delete any file located on any machine othere than the one on which the Database is hosted.
Can anyone tell me how delete a file which is on anothere machin using PL/SQL with Java ?You can use a FTP Call: http://www.savarese.org/oro/software/NetComponents.html
-
Use evdre to query data from a SQL View
Hi all
I believe that it is possible to use evdre to query data from a SQL View. If this is possible then how does one go about setting it up in the evdre options (assuming that the view has already been created)?
Regards,
ByronByron, perhaps this is no longer supported, it might be worth opening up a case at service.sap.com on this. However, I did find the following on Page 11 of the "Usages and Considerations of EVDRE" pdf file. This doc is imbedded in the helpfile for BPC 7 SP5 (which was released in August of 2009, well after note 1315011 was last updated.
It looks like you are limited to one custom view per application, since you have to name the view in a parameter at the APPLICATION level. Go into BPC Administration, login to the application related to the custom view, choose "Set Application Parameters" and enter the name of the view to the Application Parameter called "EVDRE_QUERYVIEWNAME" If it is not listed, go ahead and create it at the bottom of the Application parameter screen.
Also: I interpreted the following info from Page 10 of the same doc:
In your EVDRE, set the following options:
QueryEngine: MANUAL
QueryType: enter either NEXJ OR TUPLE see below:
NEXJ - Use two-dimensional queries using the nonemptycrossjoin function
TUPLE - Use two-dimensional queries using tuples"
And I'm assuming you'd enter a Y for the following two parameters:
QueryViewName
"..to enforce the query engine to use a used-defined SQL view of the fact tables, when trying to read the values using SQL queries. This option is typically used in conjunction with the SQLOnly option (see below). "
Option SQLOnly
"..to enforce the query engine to only execute SQL queries, when reading data. This can be achieved using this option." -
Calling secured web service from Pl SQL
Hi
I am trying to call a secured web service from pl/sql using utl_http.
Is there a sample pl/sql program that i can refer to call a secured web service.
sample soap header that am trying to acheive.
<soap:Header>
<wsa:Action>http://myactaction</wsa:Action>
<wsa:MessageID>uuid:asdfadrewrwqr</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://myact</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="SecurityToken-321321">
<wsse:Username>mordfsafsdae</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">werwqrewrwe</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
Thanks a ton in advanceHi,
On way we can call a web service is to pass the Web Service URL to UTL_HTTP package:
Sample Web Service URL
===================
lv_url :=
'http://67.97.189.151:8888/plsqlsample/dbfunc?invoke=placeOrder'
|| '&'
|| 'param0=1'
|| '&'
|| 'param1=1'
|| '&'
|| 'param2=1';
Sample Call using UTL_HTTP
=====================
SELECT UTL_HTTP.request (lv_url)
INTO lv_result
FROM DUAL;
Thank you.
Regards,
Balu -
How to update or populate table in SQL server from PL/SQL
Hello - I am starting in the fascinating world of PL/SQL. One of the first task I've been assigned is to rewrite a code (basically I was thinking of creating a package with proc included) on PL/SQL that updates and/or populates tables in MS SQL Server. How should I do this? Is it doable? I guess it is. How can I call tables from others databases from PL/SQL? How to establish the conecction? where? It should be included in the package?
Any help will be appreciated
Thanks,
CCSee PL\SQL and SQL Server
-
Copying from Oracle SQL Developer Worksheet doesn't retain formatting (font,color etc...)in Microsoft Word but copying from other programs such as
visual studio, chrome browser etc works fine. This doesn't work even after changed the setting to Keep Source formatting of Options-> Copy and Paste SettingsHi,
I notice that you have cross posted in Answers forum and Oracle forum. Have you tried Mr. Peter's suggestion?
Then, I recommend we check the Word settings:
1. Go to: Options > Advanced > Cut, Copy and Paste
2. Make sure that Use smart cut and paste is ticked.
3. Click the Settings button next to this option
4. Make sure that Smart Style
Behavior is checked.
If the issue still exists, please upload a sample through One Drive, I want to test.
Regards,
George Zhao
TechNet Community Support -
Script fails when passing values from pl/sql to unix variable
Script fails when passing values from pl/sql to unix variable
Dear All,
I am Automating STATSPACK reporting by modifying the sprepins.sql script.
Using DBMS_JOB I take the snap of the database and at the end of the day the cron job creates the statspack report and emails it to me.
I am storing the snapshot ids in the database and when running the report picking up the recent ids(begin snap and end snap).
From the sprepins.sql script
variable bid number;
variable eid number;
begin
select begin_snap into :bid from db_snap;
select end_snap into :eid from db_snap;
end;
This fails with the following error:
DB Name DB Id Instance Inst Num Release Cluster Host
RDMDEVL 3576140228 RDMDEVL 1 9.2.0.4.0 NO ibm-rdm
:ela := ;
ERROR at line 4:
ORA-06550: line 4, column 17:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
( - + case mod new not null <an identifier>
<a double-quoted delimited-identifier> <a bind variable> avg
count current exists max min prior sql stddev sum variance
execute forall merge time timestamp interval date
<a string literal with character set specification>
<a number> <a single-quoted SQL string> pipe
The symbol "null" was substituted for ";" to continue.
ORA-06550: line 6, column 16:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
( - + case mod new not null <an identifier>
<a double-quoted delimited-identifier> <a bind variable> avg
count current exists max min prior sql stddev su
But when I change the select statements below the report runs successfully.
variable bid number;
variable eid number;
begin
select '46' into :bid from db_snap;
select '47' into :eid from db_snap;
end;
Even changing the select statements to:
select TO_CHAR(begin_snap) into :bid from db_snap;
select TO_CHAR(end_snap) into :eid from db_snap;
Does not help.
Please Help.
TIA,
NischalHi,
could it be the begin_ and end_ Colums of your query?
Seems SQL*PLUS hs parsing problems?
try to fetch another column from that table
and see if the error raises again.
Karl -
How to retrieve the values from PL/SQL table types.
Hi Every one,
I have the following procedure:
DECLARE
TYPE t1 IS TABLE OF emp%ROWTYPE
INDEX BY BINARY_INTEGER;
t t1;
BEGIN
SELECT *
BULK COLLECT INTO t
FROM emp;
END;
This procedure works perfectly fine to store the rows of employee in a table type. I am not able to retrieve the values from Pl/SQL table and display it using dbms_output.put_line command.
Can anybody help me please!!!!!
Thanks
Ahmed.You mean, you can't add this
for i in t.first..t.last loop
dbms_output.put_line(t(i).empno||' '||t(i).ename||' '||t(i).job);
end loop;or you can't add this
set serveroutput onor maybe, you are working in third party application where dbms_output is not applicable at all?
You see, not able like very similar it is not working - both are too vague...
Best regards
Maxim -
How to change the default directory from within SQL*Plus ?
Hello,
I want to change the default directory directly from within SQL*Plus to be able to launch my command files with simple instructions like :
@my_command.sql
If I haven't launched SQL*Plus from the directory containing the file my_command.sql, how do I change the default directory to the directory of my command files ?
I've searched through the Oracle documentation, there's no SET DEFAULTDIR or something like that. When I do a SHOW ALL, there's no variable containing the default directory.
If you have an idea...
regards,
Jérôme.Hi Jérôme (Jé²´me ?),
You cannot directly do that, but you can use @@ instead of @ to run subscripts in the same directory as the superscript
# head /tmp/tl30/xxx/[xyz].sql
==> /tmp/tl30/xxx/x.sql <==
prompt call y
@y
prompt call z
@@z
quit
==> /tmp/tl30/xxx/y.sql <==
prompt i am in y
==> /tmp/tl30/xxx/z.sql <==
prompt i am in z
# pwd
/root
# sqlplus -s scott/tiger@lsc62 @/tmp/tl30/xxx/x.sql
call y
SP2-0310: Datei "y.sql" konnte nicht geöffnet werden
call z
i am in zKind regards
Laurent Schneider
OCM DBA -
I have an Script mostly that is generated by SSMS which works with-out issue on SQL Server 2008, but when I attempt to run it on a new fresh install of SQL Server 2012 I get an Msg 8631. Internal error: Server stack limit has been reached. Please look for
potentially deep nesting in your query, and try to simplify it.
The script itself doesn't seem to be all that deep or nested. The script is large 2600 lines and when I remove the bulk of the 2600 lines, it does run on SQL Server 2012. I'm just really baffled why something that SQL Server generated with very
few additions/changes AND that WORKS without issue in SQL Server 2008 R2 would suddenly be invalid in SQL Server 2012
I need to know why my script which is working great on our current SQL Server 2008 R2 servers suddenly fails and won't run on an new SQL Server 2012 server. This script is used to create 'bulk' Replications on a large number of DBs saving a tremendous
amount of our time doing it the manual way.
Below is an 'condensed' version of the script which fails. I have removed around 2550 lines of specific sp_addarticle statements which are mostly just copy and pasted from what SQL Management Studio 'scripted' for me went I when through the Replication
Wizard and told it to save to script.
declare @dbname varchar(MAX), @SQL nvarchar(MAX)
declare c_dblist cursor for
select name from sys.databases WHERE name like 'dbone[_]%' order by name;
open c_dblist
fetch next from c_dblist into @dbname
while @@fetch_status = 0
begin
print @dbname
SET @SQL = 'DECLARE @dbname NVARCHAR(MAX); SET @dbname = ''' + @dbname + ''';
use ['+@dbname+']
exec sp_replicationdboption @dbname = N'''+@dbname+''', @optname = N''publish'', @value = N''true''
use ['+@dbname+']
exec ['+@dbname+'].sys.sp_addlogreader_agent @job_login = N''DOMAIN\DBServiceAccount'', @job_password = N''secret'', @publisher_security_mode = 1, @job_name = null
-- Adding the transactional publication
use ['+@dbname+']
exec sp_addpublication @publication = N'''+@dbname+' Replication'', @description = N''Transactional publication of database
'''''+@dbname+''''' from Publisher ''''MSSQLSRV\INSTANCE''''.'', @sync_method = N''concurrent'', @retention = 0, @allow_push = N''true'', @allow_pull = N''true'', @allow_anonymous = N''false'', @enabled_for_internet
= N''false'', @snapshot_in_defaultfolder = N''true'', @compress_snapshot = N''false'', @ftp_port = 21, @allow_subscription_copy = N''false'', @add_to_active_directory = N''false'', @repl_freq = N''continuous'', @status = N''active'', @independent_agent = N''true'',
@immediate_sync = N''true'', @allow_sync_tran = N''false'', @allow_queued_tran = N''false'', @allow_dts = N''false'', @replicate_ddl = 1, @allow_initialize_from_backup = N''true'', @enabled_for_p2p = N''false'', @enabled_for_het_sub = N''false''
exec sp_addpublication_snapshot @publication = N'''+@dbname+' Replication'', @frequency_type = 1, @frequency_interval = 1, @frequency_relative_interval = 1, @frequency_recurrence_factor = 0, @frequency_subday = 8,
@frequency_subday_interval = 1, @active_start_time_of_day = 0, @active_end_time_of_day = 235959, @active_start_date = 0, @active_end_date = 0, @job_login = N''DOMAIN\DBServiceAccount'', @job_password = N''secret'', @publisher_security_mode = 1
-- There are around 2400 lines roughly the same as this only difference is the tablename repeated below this one
use ['+@dbname+']
exec sp_addarticle @publication = N'''+@dbname+' Replication'', @article = N''TABLE_ONE'', @source_owner = N''dbo'', @source_object = N''TABLE_ONE'', @type = N''logbased'', @description = null, @creation_script =
null, @pre_creation_cmd = N''drop'', @schema_option = 0x000000000803509F, @identityrangemanagementoption = N''manual'', @destination_table = N''TABLE_ONE'', @destination_owner = N''dbo'', @vertical_partition = N''false'', @ins_cmd = N''CALL sp_MSins_dboTABLE_ONE'',
@del_cmd = N''CALL sp_MSdel_dboTABLE_ONE'', @upd_cmd = N''SCALL sp_MSupd_dboTABLE_ONE''
EXEC sp_executesql @SQL
SET @dbname = REPLACE(@dbname, 'dbone_', 'dbtwo_');
print @dbname
SET @SQL = 'DECLARE @dbname NVARCHAR(MAX); SET @dbname = ''' + @dbname + ''';
use ['+@dbname+']
exec sp_replicationdboption @dbname = N'''+@dbname+''', @optname = N''publish'', @value = N''true''
use ['+@dbname+']
exec ['+@dbname+'].sys.sp_addlogreader_agent @job_login = N''DOMAIN\DBServiceAccount'', @job_password = N''secret'', @publisher_security_mode = 1, @job_name = null
-- Adding the transactional publication
use ['+@dbname+']
exec sp_addpublication @publication = N'''+@dbname+' Replication'', @description = N''Transactional publication of database
'''''+@dbname+''''' from Publisher ''''MSSQLSRV\INSTANCE''''.'', @sync_method = N''concurrent'', @retention = 0, @allow_push = N''true'', @allow_pull = N''true'', @allow_anonymous = N''false'', @enabled_for_internet
= N''false'', @snapshot_in_defaultfolder = N''true'', @compress_snapshot = N''false'', @ftp_port = 21, @allow_subscription_copy = N''false'', @add_to_active_directory = N''false'', @repl_freq = N''continuous'', @status = N''active'', @independent_agent = N''true'',
@immediate_sync = N''true'', @allow_sync_tran = N''false'', @allow_queued_tran = N''false'', @allow_dts = N''false'', @replicate_ddl = 1, @allow_initialize_from_backup = N''true'', @enabled_for_p2p = N''false'', @enabled_for_het_sub = N''false''
exec sp_addpublication_snapshot @publication = N'''+@dbname+' Replication'', @frequency_type = 1, @frequency_interval = 1, @frequency_relative_interval = 1, @frequency_recurrence_factor = 0, @frequency_subday = 8,
@frequency_subday_interval = 1, @active_start_time_of_day = 0, @active_end_time_of_day = 235959, @active_start_date = 0, @active_end_date = 0, @job_login = N''DOMAIN\DBServiceAccount'', @job_password = N''secret'', @publisher_security_mode = 1
-- There are around 140 lines roughly the same as this only difference is the tablename repeated below this one
use ['+@dbname+']
exec sp_addarticle @publication = N'''+@dbname+' Replication'', @article = N''DB_TWO_TABLE_ONE'', @source_owner = N''dbo'', @source_object = N''DB_TWO_TABLE_ONE'', @type = N''logbased'', @description = null, @creation_script
= null, @pre_creation_cmd = N''drop'', @schema_option = 0x000000000803509D, @identityrangemanagementoption = N''manual'', @destination_table = N''DB_TWO_TABLE_ONE'', @destination_owner = N''dbo'', @vertical_partition = N''false''
EXEC sp_executesql @SQL
fetch next from c_dblist into @dbname
end
close c_dblist
deallocate c_dblist
George P Botuwell, ProgrammerHi George,
Thank you for your question.
I am trying to involve someone more familiar with this topic for a further look at this issue. Sometime delay might be expected from the job transferring. Your patience is greatly appreciated.
Thank you for your understanding and support.
If you have any feedback on our support, please click
here.
Allen Li
TechNet Community Support -
Invoking XML Publisher Report from PL/SQL
Hi,
I have a PL/SQL procecedure which invokes the Concurrent Request for the XML Publisher Report. This one finishes with a warning and I get 'Unable to publish output' when I try to view the output. Exact Error: "Unable to find the published output for this request "
But if I run the same report using Concurrent program, the Report executes with no error. Also the desired output is coming. I am not sure where I am doing wrong in code.
My code which invokes the XML Publisher Report (from PL/SQL package)is something like this:
declare
x boolean;
l_no_of_copies varchar2(45);
l_printername varchar2(45);
l_print_style varchar2(45);
l_result boolean;
y number;
begin
x := fnd_request.add_layout (template_appl_name => 'XXTMG', template_code => 'XXT_5001_BOL_T', template_language => 'EN', template_territory => 'US', output_format => 'XML');
l_no_of_copies := fnd_profile.VALUE ('XXTMG_SH_BOL_REPORT_NO_OF_COPIES');
l_printername := fnd_profile.VALUE ('XXTMG_SH_BOL_REPORT_PRINTER');
l_print_style := NULL; --For setting the Printer Options
l_result := fnd_request.set_print_options (l_printername, l_print_style, l_no_of_copies, TRUE, 'N');
l_result := fnd_request.add_printer (l_printername, 0);
y := fnd_request.submit_request ('XXTMG', 'XXT_5001_BOL', '', TO_CHAR (SYSDATE, 'YYYY/MM/DD HH24:MI:SS'), FALSE, 'CH-120079') ;
end;
Please let me know where I might be going wrong
ThanksHi Sumit,
You can try with this code:
exec dbms_application_info.set_client_info(org_id)
exec fnd_global.APPS_INITIALIZE(user_id,resp_id,appl_id);
declare
x boolean;
l_no_of_copies varchar2(45);
l_printername varchar2(45);
l_print_style varchar2(45);
l_result boolean;
y number;
begin
x := fnd_request.add_layout ('XXTMG' --template_appl_name
,'XXT_5001_BOL_T' --template_code
,'en' --template_language
,'US' --template_territory
,'XML' --output_format
if (x=TRUE) then
y := fnd_request.submit_request (
'XXTMG' --application
,'XXT_5001_BOL' --program
,SYSDATE --start_time
,FALSE --sub_request
,'CH-120079' --argument1 (Program's Parameter)
if y>0 then
dbms_output.put_line('Request ID '||y);
end if;
end if; --if (x=TRUE) then
end;
Make sure that program has only one input parameter. If program has other parameters then you can pass those parameters like argument1, argument2... etc.
Now you can see this submited request in your application using your user id. If you able to see your request in concurrent request then you can add printer parameters in this programs.
You don't need to pass all 100 parameters.
I am executing many programs/reports using this code.
Thanks
Ravi
[email protected]
Message was edited by:
Ravi Tripathi -
Call report from pl-sql routine
I just got started on the report-tool, so, this might be a very basic question, but, would appreciate your
response.
I have a pl-sql routine registered as a concurrent program. How do I make call to report-builder from this routine.
Thxs,Hello Chirag,
If you want to invoke and run a report from your PL/SQL code in the database, you can do so by installing and using the SRW package supplied with Reports. This package allows you to run parametrized report jobs on a specified Reports Server from your PL/SQL program and track the job status. Please take a look at the documentation for this package in the Publishing Reports manual on OTN at: http://download-west.oracle.com/docs/html/A92102_01/toc.htm.
On the other hand, if you just need to invoke the Reports Builder program from your PL/SQL routine, you would need to use the default builtin for calling external applications from PL/SQL.
Thanks,
The Oracle Reports Team. -
Hi,
I have a region built from PL/SQL(Anonymous Block). This report is one record with 90 fields formatted to display. Is there a way to do this so it opens in a pdf?
I am using APEX 3.1 on Oracle 11g. We do not have BI, so I need to find another solution to doing this.
Thanks
KevinYou can do this with Apache FOP. You may also want to look at http://www.plpdf.com/.
Mike
Maybe you are looking for
-
Im currently new to this iphone. Im using iphone 5s with a version iOS 7.1.1. My problem is what is wrong with my app store? It keep telling me to update my billing method. Was all apps in the app store is need to be purchase. Such as i wanted to upd
-
Upgradation steps for XI 3.0 to PI 7.0..?
Hi Folks, Can anyone give me the steps to upgrade the xi 3.0 with some suport pack to pi 7.0 with some support pack. What i need is what are the consideration should be taken and poceedure to do it. What about the interfaces in xi.? Thanks in ad
-
Oxd_db & db_diagram? db project project made in windows in mac
hi, I have a mac & windows machine on which respective Oracle Jdeveloper 11g have been installed. We have made db model diagram. When we open the same project in mac using jpr file then we are able to open the project but the class diagram file is no
-
DW 4 and 8 unused. Can I uninstall them?
Yahoo! I found it? Now basically I'll ask the question again since that other thread may be inactive. I have installed on my machine DW 4, DW 8, DW CS3 and now DW CS4. I'd like to clean up my unused software to make more room on my computer. I never
-
CS5 "Not enough scratch memory available" error when there's plenty of it.
Illustrator CS5 throws this error: Adobe Save For Web AI Error Could not complete this operation. There is not enough scratch memory available. OK when trying to save for web. The content has a lot of linked images, around 1000x1000 px each, used w