Persistent Procedures


Hi all,

Does anyone has a great example that should illustrate the pro / cons of using persistent procedures ?

Even if i've read the documentation about it, i still can't understand the whole concept, so if anyone could tell me in which typical cases are persistent procedures used, it would be great... :nahnah:
Here is one use for persistent procedures and also IMO one of the very few examples of global variable use! Think of those functions that nearly every program uses, their different from system to system, but some examples are base-name() (Removes leading path information from a file); date-to-string() (Date formatting program); dir-name() (Returns the path information from a file name).

These functions can be written in a system wide persistent procedure we call it usystem.p. The system wide include file (at bit of which is shown below) allows any program to call these functions just like the progress supplied functions without the code for them being compiled into every program.

def new global shared var gh_system    as handle no-undo.

function base-name returns character (ipc_path as char) in gh_system.

function date-to-string returns character
    (ipd_date as date, ipc_format as char) in gh_system.

function dir-name returns character (ipc_path as char) in gh_system.

if not valid-handle(gh_system)
then do:
    run usystem.p persistent set gh_system no-error.
    if error-status:error
    then do:
        message "Unable to execute System Procedure." skip
            "Please contact the system administrator," skip
            "NOW!!!" view-as alert-box error.
Procedures call also be called from usystem.p as shown below:

run get-system-printers in gh_system (output lc_printer-list).
You could think of persistent procedures as 'fire & forget' parts of your application. So you run the procedure and it then takes care of itself.

In Simon Sweetman's example, a procedure is created that contains a while lot of internal procedures, each doing very different things. Instead, you could create a load of separate .p's and run each one individually when required. The disadvantage is that there's an overhead for loading and executing r-code in memory and of course, you have to maintain many different .p files instead of just one.

Persistent procedures really allow you to create Objects. Simon's example is really a library object. Other examples are SmartObjects.

They can also be used to store run-time information. For example, if each routine in a persistent procedure needs to know say, the user ID, then it can store this information in a variable and because the procedure doesn't end, the value will remain intact.

If you adopt the approach of using several .p's run non-persistently, then you would need to pass the User ID as a parameter, which probably means shared variables will be in use somewhere and the whole thing becomes much less elegant.

So what happens when a persistent procedure is run? A persistent procedure usually contains functions, internal procedures and/or triggers. A persistent procedure without any of these things would be of little value. When the .p is run persistently, all code in the PROCEDURE-BLOCK is executed line-by-line regardless of where it physically exists in the source file. By PROCEDURE-BLOCK, I mean any 4GL code that isn't inside a function, internal procedure or trigger block.

For example, if you have 4GL code between lines 1-10 of your procedure. And an internal procedure between lines 11-20. And then more 4GL code between lines 21-30. Then the code between lines 1-10 AND 21-30 will be executed when the procedure starts.

When the procedure is run, all code in the definitions and procedure blocks will be executed from top to bottom. The code in the other blocks is only executed when triggered or when specifically called.

The procedure then exits back to the caller (either after the last 4GL statement in the PROCEDURE-BLOCK or on a RETURN at any point), but instead of being removed from memory as with non-persistent procedures, it remains. All of the triggers remain active, all of the local data (variables) remain in memory, and all of the functions and internal procedure are available to be run.

The main point to note here is that all code in the PROCEDURE-BLOCK can only ever be run once, when the persistent procedure is first started. So although all trigger code, functions and internal procedure can be run multiple times, the code in the PROCEDURE-BLOCK can only be run once per instance of the procedure.