• Aucun résultat trouvé

BIP-05: Encapsulate UTL_FILE.GET_LINE to avoid propagating the NO_DATA_FOUND

Dans le document Preface Oracle PL/SQL Best Practices (Page 185-189)

exception.

UTL_FILE.GET_LINE raises the NO_DATA_FOUND exception when it reads past the end of a file (a common and even necessary "error" when you are reading the full contents of a file).

This reliance on an exception to signal EOF results in poorly structured code.

Consider the following:

BEGIN LOOP

UTL_FILE.GET_LINE (file_id, l_line);

process_line (l_line);

END LOOP;

... lots of code EXCEPTION

WHEN NO_DATA_FOUND THEN

UTL_FILE.FCLOSE (file_id);

END;

The problem with this code is that the simple loop looks, for all intents and purpose, like an infinite loop. It's impossible to tell by looking at the code what makes the loop terminate. Upon termination, be sure to close the file. This logic is implemented in the exception section, which may be far away from the loop. This physical separation of logically related code can lead to a maintenance nightmare.

Instead of using UTL_FILE.GET_LINE directly, build your own "get next line"

procedure and have it return a Boolean flag indicating whether the EOF was reached.

Example

Here's a simple substitution for UTL_FILE.GET_LINE:

CREATE OR REPLACE PROCEDURE get_next_line ( file_in IN UTL_FILE.file_type, line_out OUT VARCHAR2,

eof_out OUT BOOLEAN )

IS BEGIN

UTL_FILE.GET_LINE (file_in, line_out);

eof_out := FALSE;

EXCEPTION

WHEN NO_DATA_FOUND THEN

line_out := NULL;

eof_out := TRUE;

END;

Using this program, the earlier block of code becomes:

BEGIN LOOP

get_next_line (file_id, l_line, l_eof);

EXIT WHEN l_eof;

process_line (l_line);

END LOOP;

UTL_FILE.FCLOSE (file_id);

... lots of code END;

Now, any developer can easily see under what criteria the loop will terminate, and the file is closed immediately afterwards.

Benefits

Your code is easier to understand and maintain. Logically related code is kept close together physically.

Resources

getnext.sp : The get_next_line procedure replaces UTL_FILE.GET_LINE.

BIP-06: Soft-code directory names in your calls to UTL_FILE.FOPEN.

Oracle requires that you pass the directory name along with the filename when you open a file. The tendency among developers is to place these directory names directly in the call to UTL_FILE.FOPEN, thinking that the locations of files will not change or not really envisioning an alternative. A directory name is just one example of an operating system dependency within PL/SQL code, and you should make every effort to isolate such dependencies from your business logic.

There are several distinct approaches to avoiding such hard-coding:

• Store directory names in a database table. Instead of calling UTL_FILE.FOPEN directly, call your own file open function that obtains the directory from the table, based on various characteristics, such as instance name, development phase, application component, etc.

• Obtain the current settings for UTL_FILE_DIR (the allowable directories for read/write activity) and then extract your directory from that string. This is possible if you can identify the needed directory from its name.

• Add support for a path in UTL_FILE, in which you define a list of directories from which a file may be read. Again, provide your own encapsulation of UTL_FILE.FOPEN that reads from the path list instead of a static, single directory.

The following example demonstrates each technique.

Example

First, let's take a look at "soft coding" directory names in a database table (I will not show all the code here; see Resources for the relevant file references). I want to change directories according to phase of development and the application with which I am working. I create a table:

CREATE TABLE dir ( phase INTEGER, app VARCHAR2(100), name VARCHAR2(100));

and then within my fdir package create an encapsulation of UTL_FILE.FOPEN as follows:

FUNCTION fopen (

app_in IN dir.app%TYPE, file_in IN VARCHAR2,

mode_in IN VARCHAR2 := 'R' )

RETURN UTL_FILE.file_type IS

retval UTL_FILE.file_type;

l_name dir.name%TYPE;

BEGIN

l_name := fdir.name (app_in);

IF l_name IS NOT NULL THEN

retval := UTL_FILE.fopen (l_name, file_in, mode_in);

END IF;

RETURN retval;

END fopen;

where fdir.name retrieves the name for an application. The phase of development is set as a global variable, since I don't want my actual code to contain references to the phase.

With the package in place, I can set the phase to "development" in my current session as follows:

EXEC fdir.setphase (fdir.c_dev);

And then all calls to fdir.open will automatically use the development directory for whatever application I specify. Here's an example:

DECLARE

fid UTL_FILE.file_type;

l_line VARCHAR2 (100);

BEGIN

fid := fdir.fopen ('LIBMEM', 'fdir.txt');

UTL_FILE.get_line (fid, l_line);

pl (l_line);

UTL_FILE.fclose (fid);

END;

/

You can also obtain the value for the UTL_FILE_DIR parameter used in your database initialization file either by querying from the V$PARAMETER file:

SELECT value

FROM v$parameter

WHERE name = 'utl_file_dir';

or by calling DBMS_UTILITY.GET_PARAMETER_VALUE (available in Oracle8i and above). See dbparm.pkg for an example of how to use this built-in.

Finally, if you want to explore adding a path to your UTL_FILE open operation, check out the filepath package (see Resources).

Benefits

You can more easily port your code from development to test to production, or to different operation systems/hardware platforms.

You can change the locations of files in your application without having to change your code.

Challenges

Set the rules for opening files before you start building your application code. Create a package that implements the rules, and make sure everyone uses that package.

You can check for compliance with the rule by using the valstd.pkg package listed in Resources, along with the following call:

SQL> exec valstd.progwith ('UTL_FILE.FOPEN')

Resources

1. fdir.pkg and fdir.tst : A package that allows you to define directories in a table based on development phase and application, and then open files without hardcoding the directory location. There is also an accompanying test script.

2. filepath.pkg : An encapsulation of UTL_FILE.FOPEN that adds support for a user-specified path (it can only be used to open files in Read mode).

3. valstd.pkg : A general (and simple) standards validation package that

searches ALL_SOURCE for the specified string and reports on those programs that contain the string.

9.3 DBMS_PIPE

Use the DBMS_PIPE built-in package to create, write to, and read from database

Dans le document Preface Oracle PL/SQL Best Practices (Page 185-189)

Documents relatifs