Development Standards
Development Standards
Development Standards
OR Development Standards
1 Table of Contents
1. Table of Contents.......................................................................................................................... 2
2. Introduction .................................................................................................................................. 7
1 DDL Statements ............................................................................................................................ 8
2 DML Statements ........................................................................................................................... 9
2.1 Select....................................................................................................................................... 9
2.2 Insert ....................................................................................................................................... 9
2.3 Update .................................................................................................................................. 10
2.4 Delete .................................................................................................................................... 10
3 SQL*Loader ................................................................................................................................. 11
4 PL/SQL programs ........................................................................................................................ 12
4.1 General Standards ................................................................................................................ 12
4.1.1 Indentation..................................................................................................................... 12
4.1.2 Naming Conventions ...................................................................................................... 12
4.1.3 Fully Document the Specification .................................................................................. 12
4.1.4 Close All Cursors ............................................................................................................. 13
4.1.5 Calling SQL Packages Functions from a Form ................................................................ 13
4.1.6 No Commits, Posts or Savepoints in Functions ............................................................. 13
4.1.7 Calling a Function from Another Function ..................................................................... 13
4.1.8 No Reference to :block.item in Server Functions .......................................................... 14
4.1.9 Package Size ................................................................................................................... 14
4.1.10 User Separators .......................................................................................................... 14
4.1.11 Clarify your ENDs ........................................................................................................ 14
4.2 PL/SQL Objects...................................................................................................................... 14
4.2.1 Function ......................................................................................................................... 14
4.2.2 Procedures ..................................................................................................................... 15
4.2.3 Packages ......................................................................................................................... 15
4.2.4 Triggers ........................................................................................................................... 16
4.2.5 Types .............................................................................................................................. 16
4.3 Package Variables.................................................................................................................. 17
4.4 Defining Input, Output and In/Out Variables ....................................................................... 18
4.5 Exception and Error Handling ............................................................................................... 18
4.6 Conditional and Sequential Control...................................................................................... 20
4.7 Loops ..................................................................................................................................... 20
4.8 Cursor Standards and Use..................................................................................................... 21
4.9 Record Locking ...................................................................................................................... 23
4.10 Performing Tuning ............................................................................................................. 24
4.10.1 General Standards ...................................................................................................... 24
© 2006 Enabler Page 2
Development Standards
4.10.2 UNION vs UNION ALL .................................................................................................. 25
4.10.3 IN vs EXISTS ................................................................................................................. 25
4.10.4 NOT EXISTS vs NOT IN ................................................................................................. 25
5 Oracle Forms ............................................................................................................................... 27
5.1 General Standards ................................................................................................................ 27
5.2 Alerts & emessage ................................................................................................................ 28
5.3 Block Types and their Triggers .............................................................................................. 29
5.4 Form Level Triggers ............................................................................................................... 29
5.5 Single Record Blocks ............................................................................................................. 30
5.5.1 Block Level Triggers ........................................................................................................ 30
5.5.2 Item Level Triggers ......................................................................................................... 31
5.6 Multi Record Blocks .............................................................................................................. 31
5.6.1 Block Level Triggers ........................................................................................................ 32
5.6.2 Item Level Triggers ......................................................................................................... 33
5.6.3 Buttons in Multi-Record Blocks...................................................................................... 33
5.6.4 Date Push Buttons.......................................................................................................... 33
5.6.5 Multi-Language Considerations for Multi-Record Blocks .............................................. 34
5.7 Apply Blocks .......................................................................................................................... 35
5.8 Action Blocks ......................................................................................................................... 35
5.8.1 OK ................................................................................................................................... 36
5.8.2 OK + Repeat.................................................................................................................... 36
5.8.3 Delete ............................................................................................................................. 36
5.8.4 Cancel ............................................................................................................................. 36
5.8.5 LOV Buttons and Calendar Buttons ............................................................................... 36
5.9 Find Blocks ............................................................................................................................ 37
5.10 Boilerplate Text .................................................................................................................. 38
5.11 Busy Cursors ...................................................................................................................... 39
5.12 Calling a Function from a Form ......................................................................................... 39
5.13 Canvas Properties .............................................................................................................. 42
5.14 Capitalization Standards .................................................................................................... 42
5.15 Closing a Window .............................................................................................................. 43
5.16 Currency ............................................................................................................................ 44
5.16.1 About this Topic .......................................................................................................... 44
5.16.2 Technical Implementation .......................................................................................... 46
5.17 Delete Button Standard Code ............................................................................................ 48
5.18 Dynamic Hierarchy ............................................................................................................ 49
5.19 Editors ................................................................................................................................ 50
5.19.1 OG_editor Installation Instructions ............................................................................ 50
5.19.2 Multi-Record Blocks .................................................................................................... 53
2 Introduction
This document details the coding standards that should be followed when changing the existing
programs or creating new ones in Oracle Retail application.
It will ensure that code is written and maintained to specific standards.
It has been created as part of the Enabler’s project and is intended for use by internal Enabler,
contractors, and third parties developing or modifying code on behalf of Enabler.
All new objects developed for specifc clients must have their prefix “XX” to be easily identified as
customized objects. This rule is applied only to new objects, i.e. existing object cannot have the
name changed.
3 DDL Statements
DDL statements or events include ALTER, CREATE and DROP actions. The rules below
should be used:
• All DDL statements should be coded to ensure that indentation is aligned so that statements
are easily readable;
• DDL and DML statements should never be mixed in a script;
• A DDL script should only execute DDL against one table. If additional tables need to be
amended, additional scripts should be written;
• No needs for commit statements as these are implicit in DDL.
Alter table example:
alter table ADDR drop constraint <PREFIX>_ADR_STA_FK;
4 DML Statements
DML statements are common to both SQL, and PL/SQL modules, which include INSERT, UPDATE,
DELETE and SELECT commands. For the coding of these commands, the following standards should
be adhered to:
• DML and DDL statements should never be mixed in a script;
• A DML script should only INSERT/UPDATE/DELETE into one table. If additional tables need to be
amended, additional scripts should be written;
• At the end of the DML statement, it should be included the COMMIT statement.
4.1 Select
• Each column or field in the statement should occupy its own line, and when there is more than
one table in the FROM clause, prefixed with the table alias;
• Each table in the FROM clause should occupy its own line, and when there is more than one
table, an alias assigned to each;
• Ensure that the order of columns in the SELECT and FROM clause is similar;
• Ensure that each statement in the WHERE clause occupies its own line, unless wrapping is
required. In this case, indent to make the wrapping obvious;
• Ensure that both the SELECT, FROM, WHERE, ORDER BY and GROUP BY clauses are aligned to
improve readability.
Example:
select im.item,
im.dept,
il.location
from item_master im,
item_loc il
where im.item = il.item
and im.dept = 200
order by im.item,
im.dept;
4.2 Insert
• Any insert statement should have the column names specified after the table name, and the
alignment should be easily readable;
• In multi-line inserts, it may be more beneficial to include a number of columns on a line, which
should be at the discretion of the developer;
• Each column should occupy its own line after the initial column, and when more than one table
appears in any clause, the column should be prefixed with the table alias.
Example:
insert into chain (chain,
chain_name,
mgr_name,
4.3 Update
• Ensure that indentation is aligned so that statements are easily readable, as in the following
example;
• Ensure the where condition is added if applicable.
Example:
update ordloc
set qty_ordered = qty_ordered + L_value,
estimated_instock_date = TO_DATE(L_date)
where order_no = L_order_no
and item = L_item
and location = L_location;
4.4 Delete
• It is discretionary whether the developer specifies “FROM” before the table name in the DELETE
clause, but it is not required;
• Ensure that indentation is aligned so that statements are easily readable, as in the following
example;
• Ensure the where condition is added if applicable.
Example:
delete ord_temp
where ord_temp_seq_no = L_order_no
and item = L_item;
5 SQL*Loader
• For the coding of SQL*Loader scripts (.ctl), both the input file (data) and the bad (reject) file
should be specified;
• Positions of each field should be specified, where file format is fixed, where the field starts and
ends within the data file;
• Note that the log file created after executing the SQL*Loader script will be of the same name as
the data file, but suffixed with .log;
• Ensure that indentation is aligned so that statements are easily readable.
Example:
-- Header/Comments section (must be prefixed by –- for comment)
LOAD DATA
INFILE '<prefix>_store.dat'
BADFILE '<prefix>_store.bad'
APPEND
INTO TABLE store_grade_store
6 PL/SQL programs
Here you will find information on the technical aspects of using PL/SQL programs to store program
components of the Oracle Retail application on the database server.
6.1.1 Indentation
The code should be indented in 3 positions. Do not use TAB, instead of use 3 spaces. Code should
be indented so that it is easily readable and the columns should be aligned.
If you call a function that has an insert, update, or delete statement in it, additional code must be
added to handle rolling back to the appropriate spot if the function fails. To do this, a savepoint
should be created just prior to the call to the function, and a rollback added for failure conditions:
Issue_Savepoint('A');
if ORDER_VALIDATE.ORDER_NUMBER(L_error_message,
:block.item1, ...) = FALSE then
Issue_Rollback('A');
emessage(L_error_message);
raise FORM_TRIGGER_FAILURE;
end if;
Note that you should not use Savepoint A in place of Issue_Savepoint('A') or either Rollback to A or
Rollback Work to A in place of Issue_Rollback('A').
Also note that while the rollback will undo changes to the database, it will not revert variable
values passed into the function to their previous values. That is, if a function changes one of the
:block.item or other variable values passed into it as an IN OUT variable before raising an
exception and returning FALSE, those values already changed will retain their changed values (this
is standard behavior for functions in C as well).
This helps identify what procedure or function you are looking at.
6.2.2 Procedures
Procedures should be created in Oracle Retail only in particular cases. Whenever possible, it
should be used Functions.
Example:
PROCEDURE NEXT_MERCH_NUMBER(O_merch_number IN OUT NUMBER,
O_return_code IN OUT VARCHAR2,
O_error_message IN OUT VARCHAR2) IS
<variable/exception/cursor declarations>
BEGIN
<main body of code – cursor use etc>
EXCEPTION
WHEN OTHERS THEN
<assign error message, return code and context etc>
END NEXT_MERCH_NUMBER;
6.2.3 Packages
Avoid coding standalone functions or procedures, if they are related and can be included in a
package. Packages are a useful way of storing multiple functions, procedures, collections and
external procedures. Note that if the package is new, the function name inside should not start
with the <prefix>.
Example of package specification:
CREATE OR REPLACE PACKAGE <PREFIX>_DEAL_SQL AS
< Include here the comments using the standard defined for the client >
--------------------------------------------------------------------------
-- Function: GET_DEAL_DATES
-- Purpose : Get the deal active and close dates
--------------------------------------------------------------------------
FUNCTION GET_DEAL_DATES(O_error_message IN OUT VARCHAR2,
O_active_date IN DEAL_HEAD.ACTIVE_DATE%TYPE,
I_close_date IN DEAL_HEAD.CLOSE_DATE%TYPE
I_deal_id IN DEAL_HEAD.DEAL_ID%TYPE)
RETURN BOOLEAN;
--------------------------------------------------------------------------
END <PREFIX>_DEAL_SQL;
6.2.4 Triggers
• Avoid using INSTEAD OF triggers, unless absolutely necessary, as the logic can become
confusing and difficult to support;
• Ensure that a trigger is essential for the capturing of information, as they can have a
performance impact (especially if poorly-written!);
• If a trigger does not need to be continually enabled, ensure necessary steps are in place to
disable (for example during the overnight batch, if applicable).
Example:
CREATE OR REPLACE TRIGGER <PREFIX>_ITEM_LOC_AIUR
AFTER INSERT OR UPDATE
ON ITEM_LOC
FOR EACH ROW
DECLARE
<variable/exception/cursor declarations>
BEGIN
<main body of code – INSERTS etc>
EXCEPTION
WHEN OTHERS THEN
<assign error message and context etc>
END <PREFIX>_ITEM_LOC_AIUR;
6.2.5 Types
• For creation of a type within a package header, variables should be named and indentation
should be easily readable, as in the examples below;
• Where appropriate, anchor definitions to column datatypes, rather than hard coding.
Example of type creation within a package header:
CREATE OR REPLACE TYPE BOL_VIRTUAL_LOC IS RECORD
(
vir_from_loc ITEM_LOC.LOC%TYPE,
vir_from_loc_type ITEM_LOC.LOC%TYPE,
vir_qty ITEM_LOC_SOH.STOCK_ON_HAND%TYPE,
);
TYPE bol_virtual_loc_array IS TABLE of BOL_VIRTUAL_LOC
INDEX BY BINARY_INTEGER;
Finally, for collection types, the name should end in _TBL, for example an Oracle Retail collection
types such as DATE_TBL or COUNTRY_TBL.
Variables should be always anchored to a table column datatype, to ensure that the code is
“futureproof”. For example, the L_item variable would be anchored to item column of the
ITEM_MASTER table, such as:
L_item ITEM_MASTER.ITEM%TYPE;
Assuming the initial value of a variable is known, or is to remain static, the variable should be
assigned that value in the variable declaration section. For example:
L_program VARCHAR2(64) := 'ITEM_NUMBER_SQL.GET_NEXT';
When declaring multiple variables, ensure that each “column” is lined up, for ease of readability,
for example:
L_item ITEM_MASTER.ITEM%TYPE;
L_dept ITEM_MASTER.DEPT%TYPE;
L_class ITEM_MASTER.CLASS%TYPE;
The function SQL_LIB.CREATE_MSG takes four parameters and produces an encoded string that
emessage, wmessage, etc. will automatically decode and display properly. The first parameter is
the message code. The other 3 are parameter to the used in the error message. All available
messages are stored on the RTK_ERRORS table. Use the Message Search Tool to find existing
messages and to add new messages to the RTK_ERRORS table.
There are two ways to handle errors that happen in the code:
1. Return FALSE directly in the code. In some cases where the SQL fails (for example if a
NOTFOUND is true which indicates an essential piece of data is missing) all cursors should be
closed, O_error_message should be set to the error message, and a return code of FALSE
should be returned.
Example:
if C_MY_CURSOR%NOTFOUND then
close C_MY_CURSOR;
O_error_message := SQL_LIB.CREATE_MSG('INV_DATE',I_date);
return FALSE;
else
close C_MY_CURSOR;
end if;
Note that exceptions should always be named “e_<variable_name>”, unless they are standard
Oracle exceptions. Ensure that the exception section is easily readable and clear.
Sequential control statements include GOTO and NULL. The GOTO statement should NOT be used
as it can become very difficult to follow and support. The NULL statement is a viable executable
statement that does nothing, so it should be avoided as well.
6.7 Loops
There are a number of loops that can be used within PL/SQL, and there are benefits for using each.
Loop choice is at the developer’s discretion, but careful consideration should be given to ensure
optimum performance.
• WHILE LOOP
The while loop should be used when a cursor only requires execution if a given condition is
met, or may be occurring. The syntax is shown below, and the indentation should be followed:
WHILE <condition>
LOOP
<Executable statements>
END LOOP;
Example 2:
cursor C_ITEM_LOC (P_item ITEM_LOC.ITEM%TYPE) is
select loc,
loc_type
from item_loc
where item = P_item
for update nowait;
...
FOR rec in C_ITEM_LOC LOOP
if rec.loc_type = 'S' then
<Executable statement 1>
else
<Executable statement 2>
end if;
END LOOP;
Make certain that the where clause for this cursor is the same as the where clause in your update
or delete statement. Make sure to include the for update nowait; line. This is the command that
locks the record (the keyword nowait tells ORACLE not to wait if the table has been locked by
another user).
update item_supplier
set unit_cost = L_unit_cost
where item = I_item
and supplier = I_supplier;
Note: All rows are locked when you open the cursor, not as they are fetched. If you have
update/delete statements that affect multiple rows, you need to open and close the C_LOCK
cursor only once since the rows stay locked throughout the execution of your function/procedure.
Step 4: Declare a RECORD_LOCKED exception.
You may pass up to two of the key values from the where clause of your locking cursor into the
error message to help identify which record(s) is locked.
EXCEPTION
when RECORD_LOCKED then
O_error_message := SQL_LIB.CREATE_MSG('TABLE_LOCKED',
L_table,
TO_CHAR(I_item),
TO_CHAR(I_supplier));
To test whether the locking cursor is set up correctly you will need to open two sessions (Forms,
SQL*Plus, etc.). In the first session, update or delete the test records without committing. It is
critical that the records are in limbo, waiting to be either committed or rolled-back. It is at this
point that the records are locked. Now test your record locking code. If everything is functioning
correctly you should not be permitted to update or delete the records and should be given a
message indicating the records are locked by another user.
6.10.3 IN vs EXISTS
In certain circumstances, IN is more efficient than EXISTS, and if the selective predicate is in the
subquery, use IN. Alternatively, when the selective predicate is in the parent query, use EXISTS.
The example shows where the EXISTS would be more efficient:
SELECT im.item,
im.dept
FROM item_master im
WHERE im.dept = 200
AND im.item_level = im.tran_level
AND EXISTS (SELECT 1
FROM item_loc il
WHERE im.item = il.item);
Use of the IN statement instead would be more efficient in the following example:
SELECT im.item,
im.dept
FROM item_master im
WHERE im.item IN (SELECT il.item
FROM item_loc il
WHERE il.loc_type = 'W');
7 Oracle Forms
Here you will find information on how to properly code a Form to Oracle Retail standards. This
includes technical information on how to properly implement common functionality.
• Comments must use the -- format and be indented to the same level as the code. Do not use
multiple line comments with /* and */. Also, do not append comments to the end of code on
the same line as the code itself:
Wrong:
Next_Item; /* do not use these types of comments */
Right:
-- This type of comment should be used
• Use %TYPE when declaring a local variable that holds data which resides on a table, such as
ITEM:
DECLARE
L_item ITEM_MASTER.ITEM%TYPE;
• Hard coded values are never acceptable. This includes values, such as using the number 1000 in
your code to indicate corporate zone. This also includes any text, such as 'Retail Markup %'.
Codes or variables should be used to indicate or retrieve all such values.
• Use Parameters only for values being passed between forms. Use internal variables for form
level variables that are referred throughout the form (like system option fields). Always
question internal variables that seem suspect. These are usually quite easy to detect by name
and usually a sign of some bad coding going on. Some examples of poor Parameter use, just to
give you an idea what to look for, would be: PM_inserted, PM_record, PM_valid,
PM_deleted_only_rec1, PM_p_get_item_field.
The method for displaying alerts is simple. Use the following Oracle Retail library functions for
displaying messages to the user:
• emessage for errors (red circle with white X icon)
• wmessage for warnings (exclamation point yield sign icon)
• imessage for information (blue “i” in a white circle icon)
• F_YES_NO gives the user a choice of YES or NO buttons
• F_YES_NO_CANCEL gives the user a choice of YES, NO or CANCEL buttons
• F_OK_CANCEL gives the user a choice of OK or CANCEL buttons
• F_YES_NO_ALL gives the user a choice of YES, NO or ALL buttons
When using emessage, F_YES_NO, or any other alert-displaying function in which a message is
involved, it is imperative that message keys from RTK_ERRORS be used, not hard-coded messages.
WHEN-VALIDATE-RECORD should verify that each required item has a value entered for the
current record. All fields’ Required property should be set to NO on the Property Palette to permit
us to make this check for required fields ourselves. If REQUIRED = YES, then Forms takes over
these NULL checks for us. That would be fine except Forms gives a generic message which reads:
'Field Must Be Entered' – it doesn't tell the user which field must be entered. So, we bypass Forms'
check and create our own with specific error messages.
Because a Go_Item cannot be performed from within this trigger, if an item is NULL the message
that is displayed should state the name of the item that needs to have a value. The WVR trigger
should not contain any ‘if all fields are NULL’ or ‘if all fields are NOT NULL’ logic. That kind of logic
does not belong in a WVR trigger.
BEGIN
if :B_main.store is NULL then
emessage('STORE_REQ');
raise FORM_TRIGGER_FAILURE;
end if;
PRE-INSERT, which is also used for single record blocks, has an additional purpose for multi-record
blocks. This trigger should perform a check for duplicate records. This is in addition to the checking
done in the WHEN-VALIDATE-ITEM trigger. The only time the check in PRE-INSERT will be caught is
when the user enters two new records with the same key within the same session.
Apply blocks are generally used when the user needs to apply a group of items at one time (such
as an item list, or a region of stores). There are many different examples of apply block
implementation currently within the ORMS. The apply block should always mimic the multi-record
block. The apply block’s data should be populated in the multi-record block’s WHEN-NEW-
RECORD-INSTANCE trigger.
When you click the Add button, a new record should be available in the apply block for you to
enter.
When you click Delete, the existing record is deleted or cleared as appropriate.
When you click Apply, values are copied from the Apply block into the multi-record block and a
Post is issued (along with the command WINDOW_HANDLER.SET_FORM_CHANGED – See the
section on WINDOW_HANDLER for additional information).
When lists of things, such as an item list, are applied, the Apply button would call a function to
perform inserts into the table. Then it would perform a POST, set the window handler to changed,
and then re-query the block.
The common buttons used for a single record block are OK, OK+Repeat, Delete, and Cancel.
7.8.1 OK
This button should call the procedure P_EXIT.SAVE_FORM. This procedure will typically call
P_CHECK_REQUIRED to check for required fields. Usually, a Commit_Form is then issued.
In some cases, checks are made to see if the :system.form_status is anything other than 'QUERY'
before a commit is performed. This should NOT be done because of the possible use of posting
within the form.
In addition, because of posting within forms, the WINDOW_HANDLER.SET_FORM_UNCHANGED
should be set in P_EXIT.SAVE_FORM to verify that window closing logic will still work if the OK +
Repeat button has been clicked. See the section on WINDOW_HANDLER for additional
information.
7.8.2 OK + Repeat
This button should call the procedure P_EXIT.SAVE_FORM(FALSE), with the FALSE being passed to
stop the procedure from exiting the form. Then it should perform a Clear_Form, and usually will
call P_FORM_STARTUP again to reset the form appropriately.
For View/Edit modes, when this button is enabled, some special processing must occur. For
example, on a form such as department, when OK + Repeat is clicked, the dept field will be
enabled. Then, if a department is chosen via an LOV (or a department is entered in the field
manually) a query should be performed. That means, for this one instance only, that an
Execute_Query would be performed in a KEY-NEXT-ITEM trigger.
Note: This is the only code that really belongs in the KEY-NEXT-ITEM. Validation will still belong in
the WHEN-VALIDATE-ITEM trigger. Other than this one instance, only navigation belongs in
navigational triggers.
7.8.3 Delete
See “Delete Button Standard Code”.
7.8.4 Cancel
This button should call the P_EXIT.CANCEL_FORM procedure, which generally should just contain
an Exit_Form(No_Validate) command.
Any forms that have dependencies for parent/child relationships should ideally be in one form so
that this program unit does not need to deal with deleting incomplete records. This is not true in
all cases, however, so this may contain delete logic and committing as well.
A find form is different from other forms in that there should be no commit logic within the form.
It is used as a means to access other forms.
Find forms work by allowing users to enter in the values they wish to limit the where clause by and
then searching on those values. Technically, this is done by dynamically setting the where clause.
This is done in the P_SET_WHERE_CLAUSE program unit.
It is important to note that the busy cursor must be returned to DEFAULT in the exception
handling routine. If this code is not present, Windows will leave the busy mouse pointer as the
primary mouse pointer until the system is rebooted or a Set_Application_Property is encountered
that changes the cursor back to the default style. Any additional program units that are called
after the cursor style is set to busy must include the setting of the cursor style to default in their
exception handling as well.
Above, we see that the Function has one input, two outputs, and RETURNS a BOOLEAN variable.
To call this Function, then, we will need to pass in one input (a buyer number) and deal with two
outputs (the buyer name and the error message). In addition, we will need to evaluate the
contents of the BOOLEAN RETURN.
Another way to evaluate (and therefore execute) a Function is to assign its value to a variable:
L_some_variable := MY_FUNCTION(argument list);
In both cases, as the line of code is read, the Function will be executed. In both cases the value
that will be used in the expression is the value RETURNed by the Function. If MY_FUNCTION ends
up RETURNing the value TRUE, then the two expressions above would read like this:
if TRUE = TRUE then
perform some task;
end if;
and
L_some_variable := TRUE;
Again, Functions RETURN a value apart from the argument list (apart from the inputs and outputs).
At Oracle Retail, the RETURN is always a BOOLEAN variable holding the value TRUE if the Function
executed successfully and FALSE if the Function encountered an error.
When you call a Function, the Function call itself holds that RETURN value. For example, in the
code above, you write:
if BUYER_ATTRIB_SQL.GET_BUYER_NAME(argument list)..
Since TRUE does not equal FALSE, the error message isn’t printed and the Exception isn’t raised.
The Function still executes, however, taking the buyer number in and passing out the buyer name.
If the Function failed then the opposite would happen:
if FALSE(L_error_msg,
:B_buyer_info.TI_buyer_name,
:B_buyer_info.TI_buyer_no) = FALSE then
emessage(L_error_msg);
raise FORM_TRIGGER_FAILURE;
end if;
Since FALSE does equal FALSE, the error message is printed, and the exception is raised.
The odd aspect to all of this is that regardless of whether the Function returns TRUE or FALSE, the
inputs are passed into the Function and the outputs are returned from the Function. So, as seen
above, no matter what happens, the Buyer Number (which is held in the item
:B_your_block.TI_buyer_no) goes into the Function, and the Buyer Name comes back from the
Function and is placed in the item :B_your_block.TI_buyer_name.
At Oracle Retail we use the syntax illustrated above when calling a Function, checking for FALSE
and calling exception handling if needed. Checking for TRUE actually results in more lines of code
and an increase in nested logic:
Right:
if BUYER_ATTRIB_SQL.GET_BUYER_NAME(L_error_message,
:B_buyer_info.TI_buyer_name,
:B_buyer_info.buyer_no) = TRUE then
do some stuff;
else
emessage(L_error_message);
raise FORM_TRIGGER_FAILURE;
end if;
Wrong:
if BUYER_ATTRIB_SQL.GET_BUYER_NAME(L_error_message,
:B_buyer_info.TI_buyer_name,
:B_buyer_info.buyer_no) = FALSE then
emessage(L_error_message);
raise FORM_TRIGGER_FAILURE;
end if;
do some stuff;
• Objects defined within Oracle Forms should have the prefix to the object capitalized and the
rest of the name lowercase.
Example: B_shipment.TI_loc_name, W_shipment
• When referring to parameters, the word PARAMETER should appear in all capital letters.
Example: :PARAMETER.PM_mode
• The following PL/SQL reserved words should always be all capital letters:
• The following PL/SQL reserved words should always be all lowercase letters:
• Program units, packages, stored procedures, functions, and library program units should be
referred to in all capitals with the exception of emessage, wmessage, and imessage.
Example: P_FORM_STARTUP, GET_VDATE, F_SHOW_LOV
• When using the Set_xx_Property or Get_xx_Property built-ins, the property and the value
should both be all capitals.
Example: Set_Block_Property('B_shipment', UPDATEABLE, PROPERTY_TRUE)
• Local variables should begin with an “L_” and the rest should be lowercase.
Example: L_store_name
• Global variables should begin with “GV_” and the rest should be lowercase.
Example: GV_store_name, GV_promotion
7.16 Currency
7.16.1 About this Topic
This section describes design and technical implementation of currency in Oracle Retail.
Definitions
There are five different currency types used in Oracle Retail:
• Order currency: Associated with the Ordering dialog, the order currency will in many instances
be the same as the supplier’s preferred currency. The actual cost of an order will always be
stored in the order currency, implying payment in that currency.
• Contract currency: Associated with the contract. Serve as the default currency for an order
when the order is created from a contract.
• Supplier currency: Associated with the Ordering and Supplier Maintenance dialogs, this is the
supplier’s preferred currency and will be used as the default currency on an order placed with
the supplier. Order cost is stored in the supplier currency at the ITEM level.
• Primary currency: Referenced throughout the system, this is the corporate level preferred
currency and is stored on the System Options table. This is the currency in which the corporate
level reporting is done. Primary currency values will often be a calculated or converted value
from another currency. Use the currency package to get this value.
• Local currency: Referenced throughout the system at the location level, this is the currency
used at a specific location. Stores, for example, may each have their own preferred currency. All
location specific reporting can be done in the local currency. Sales and inventory are all valued
in the local currency, making this the most accurate financial measure at any specific location.
Primary currency values are converted from the local currency using the exchange rates and
rules stored in the ORMS. Note that currency is also stored on the Price Zone table and that all
stores within a zone must share the same currency.
General Currency Rules
If the form has an existing menu, add the Currency menu under the Options menu with the
appropriate currency types (Primary, Local, etc.) Then, in P_FORM_STARTUP, include the following
code:
if multi_currency_ind = 'N' then
Set_Menu_Item_Property('options_menu.currency', DISPLAYED,
PROPERTY_FALSE);
end if;
The menu options that remain enabled expect certain triggers to be present. For each option that
your Form will have, create its associated trigger at the Form level:
Option Expected Trigger Name
Primary T_primary
Local T_local
Supplier T_supplier
Order T_order
Order/Local T_orderlocal
Contract T_contract
Currency Fields
Delete buttons must have the Mouse Navigate property set to NO on the property palette to
prevent validation triggers from needlessly firing. See the section on Mouse Navigate in this
document for further details.
Merchandise:
Division (MH2)
Group (MH3)
Department (MH4)
Class (MH5)
Subclass (MH6)
The decoding of hierarchy names is done through the table DYNAMIC_HIER_CODE which contains
the following fields:
RMS_NAME VARCHAR2(30)
CLIENT_NAME VARCHAR2(30)
ABBR_NAME VARCHAR2(6)
This table is pre-populated and no rows can be added. There are values on the table for each level
of the hierarchy in its singular and plural form.
The package DYNAMIC_HIER_CODE_SQL should be called to retrieve values from this table. If you
needed to find the value for the word department, you would pass the word in the case as you
want it returned (uppercase, initcap, lowercase). If there is no client value on the table, the ORMS
name will automatically be returned.
In order to support the dynamic hierarchy there are a few steps that must be followed while
developing:
• Codes will be handled in the same way as messages. New codes should use the code for the
keyword rather than the actual word.
• Multiview: When a new column is added to multiview that is one of the levels of the hierarchy,
place the appropriate code into the column pointer field on the multiview edit Form for the
specific column.
Multiview Codes
Department @MH4
Department Name @MHN4
Class @MH5
Class Name @MHN5
Subclass @MH6
Subclass Name @MHN6
In multiview, the abbreviated name will be displayed for department, class, and subclass.
If the dynamic hierarchy table is updated, a database trigger exists to automatically update the
multiview pointer column appropriately.
7.19 Editors
There are two methods to call the editor: the simple method and the slightly more complicated
method. The simple method will place a title ‘Comments’ at the top of the editor and will be in the
default position. The slightly more complicated method lets you pass in a code type and code to
use as the title and an x and y position if you desire. Use the second option if your editor is for
anything other than comments so that it can be titled appropriately.
One additional new standard: Editors should now be able to be referenced using the key-listval
trigger in all fields that have an editor attached to them.
Example
I have a field called :B_head.TI_comments. :B_head.TI_comments should be enabled in 'NEW'
and 'EDIT' modes, but not in 'VIEW' mode. There is an iconic pushbutton next to
:B_head.TI_comments called :B_head.PB_comments. The KEY-LISTVAL trigger on
:B_head.TI_comments should look something like this:
if :PARAMETER.PM_mode = 'VIEW' then
P_CALL_EDITOR.CALL_EDITOR('B_head.TI_comments', 'N');
else
P_CALL_EDITOR.CALL_EDITOR('B_head.TI_comments', 'Y');
end if;
Example
I have a field called :B_head.TI_import_description. There is an iconic pushbutton next to
:B_head.TI_import_description called :B_head.PB_desc. I want the title of the editor window to
be ‘Import Description’, but I still want the editor window to be centered over the main
window. The KEY-LISTVAL trigger on :B_head.TI_import_description should look something like
this:
PROCEDURE CALL_EDITOR('B_head.TI_import_description',
'Y',
'LABL',
'IDLB',
NULL,
NULL);
If I also want the editor window to be elsewhere over the main window, the KEY-LISTVAL
trigger on :B_head.TI_import_description should look something like this:
PROCEDURE CALL_EDITOR('B_head.TI_import_description',
'Y',
'LABL',
'IDLB',
100,
200);
• You should now be able to save, generate and run your form with an editor. If you have any
other references to the old editor left in your form, delete them.
7.21 Form_Success
Form_Success is a Function provided by Oracle Forms to test the success or failure of a Forms
Built-In (“Built-Ins” meaning Forms subprograms like Go_Block, Set_Item_Property,
Execute_Query, etc.). When a Built-In fails, a runtime error occurs and Forms issues the
appropriate error message. However, no exception is raised – the code does not jump to the
Exception section, the trigger itself does not fail, and any subsequent statements in the code are
still executed. That is why sometimes you get a string of error messages in Forms when something
goes wrong instead of a single error message.
Because processing will not automatically stop if a Built-In fails, there will be occasions in which
we need to manually halt the processing. Form_Success reports on the outcome of the most
recently executed Built-In and can be used to determine whether processing should halt or
continue.
In the following example we perform validation on a block, causing any WHEN-VALIDATE-ITEM
and WHEN-VALIDATE-RECORD triggers on the block to fire. If an error occurs in one of those
triggers the processing will not halt, but the value for Form_Success will be FALSE. To get the
processing to halt we can do as follows:
Validate(Block_Scope);
if not Form_Success then
raise FORM_TRIGGER_FAILURE;
end if;
Go_Block('B_detail');
To get the correct results you must check the value of Form_Success immediately after the Built-
In is called. That is, another line of code cannot occur between the Built-In and the testing of
Form_Success value. If another action occurs, Form_Success will not reflect the status of the Built-
In you are testing. In the example above, any line of code between Validate(Block_Scope) and if
NOT Form_Success would interfere with Form_Success being able to return the correct result (this
is true even for something as innocuous as wmessage).
Let’s look at a more detailed example. Below are three PL/SQL blocks which are executed as the
result of a button being pressed. In this example a WHEN-BUTTON-PRESSED trigger issues the
Do_Key('Next_Item') Built-In. The KEY-NEXT-ITEM trigger makes a call to a procedure named
P_TEST. P_TEST evaluates some hypothetical condition, and ends-up raising
FORM_TRIGGER_FAILURE:
This manually raises an exception, causing processing to jump down to the Exception section
when FORM_TRIGGER_FAILURE then
raise;
In the Exception section, raise takes us back to the EXCEPTION section of the KEY-NEXT-ITEM
trigger (not the execution section). This means that the line P_DO_SOMETHING_ELSE in KEY-NEXT-
ITEM does not fire. Instead, we are stuck in its EXCEPTION section.
KEY-NEXT-ITEM
when FORM_TRIGGER_FAILURE then
raise;
When we raise to the WHEN-BUTTON-PRESSED trigger the unexpected happens: instead of going
to the EXCEPTION section, we stay in the execution section and actually perform the line
Go_Block('B_detail'). This is because everything began with Do_Key, which is a Forms Built-In. As
described at the beginning of this section, if a Forms Built-In causes an error to occur, processing
continues in that trigger/program unit. So, even though the subsequent blocks of code (KEY-NEXT-
ITEM and P_TEST) behaved as we would like by going into their exception-handling routines, the
WHEN-BUTTON-PRESSED trigger does not stop.
WHEN-BUTTON-PRESSED
Go_Block('B_detail');
It is here that Form_Success can be invaluable:
7.22 FORM_TRIGGER_FAILURE
FORM_TRIGGER_FAILURE is a built-in Oracle Forms Exception type that stops the processing in the
trigger. At Oracle Retail, whenever an anticipated Exception occurs in Forms, you print an error
message to the screen with emessage, then manually call FORM_TRIGGER_FAILURE:
if F_GET_BUYER_NAME(L_error_message,
:B_buyer_info.TI_buyer_no,
:B_buyer_info.TI_buyer_name) = FALSE then
emessage(L_error_message);
raise FORM_TRIGGER_FAILURE;
end if;
By raising FORM_TRIGGER_FAILURE, you jump out of the Execution section of your PL/SQL block
and go to the when FORM_TRIGGER_FAILURE portion of the Exception section:
EXCEPTION
when FORM_TRIGGER_FAILURE then
raise;
when OTHERS then
emessage (SQLERRM);
raise FORM_TRIGGER_FAILURE;
When you explicitly raise a FORM_TRIGGER_FAILURE exception, Forms halts the processing in the
trigger, and moves to the Exception section. In the Exception section, you then perform a
command called raise. RAISE looks to see if this trigger was fired as the result of some other
process. If it was, RAISE halts the calling process in a reverse-domino effect*:
TRIGGER
PROCEDURE
FUNCTION, etc
For unexpected errors (your WHEN OTHERS section), you print the error message generated by
the SQL server (SQLERRM) and then raise FORM_TRIGGER_FAILURE. This, in turn, stops the
processing, and stops calling processes.
* Technically, the line raise kicks the processing back out to the calling process Exception section
and executes the calling process Exception section. In the illustration below A, B, and C all have
Oracle Retail’s standard Forms Exception section code:
EXCEPTION
when FORM_TRIGGER_FAILURE then
raise;
when OTHERS then
emessage (SQLERRM);
raise FORM_TRIGGER_FAILURE;
7.23.2 Canvases
Canvases should be organized in a logical manner, grouping together related items in “sections".
When placing items on a canvas the goal is to conserve space. Group items tightly together and if
additional space exists on the canvas shrink the canvas size.
Attributes:
Bevel : None
Visual Attribute: VA_CANVAS
VA_EDIT_ITEM,
Black text on white background. Used for Enterable/Editable fields.
TEXT_ITEM_ON
VA_EDITOR,
Black text on white background. For the Editor canvases.
EDITOR
VA_HIGH,
Black text on khaki background. Used for "selected" fields and records.
HIGHLIGHT_ON
VA_LOV, LOV Black text on White background. Used for List of Values.
VA_WINDOW,
Camel foreground and background. Used for windows.
WINDOW
7.23.4 Windows
A window is the basic interface that is used to conduct a dialog with the user. Multiple windows
can be presented at the same time. This allows for greater flexibility in the system. Modal
windows force the user to perform a series of tasks in a certain order, but it is strongly
recommended that a modal window be avoided if at all possible (the only valid exception to this
are pop-up windows). A modeless window, on the other hand, allows the user a degree of
freedom in jumping between windows and/or other applications like a word processing or
spreadsheet application.
Neither vertical nor horizontal scroll bars should be used at the window level.
A window may contain two levels of push buttons -- one which affects the entire window and one
which affects a subset of controls.
The title bar is the top bar on every window. It should include the title of the window. The title
should capitalize the first letter of every word and the text should be centered. Window titles
should be in the following format:
Window Description (filename)
7.23.5 Blocks
Multi-Record Blocks
To achieve the new look, perform the following steps:
• Set all text fields in the block to Bevel = NONE
• All items in the multi-record block should have distance between records set to 1 (item-level
property)
• All items in the multi-record block should be set 1 pixel apart horizontally. This is done most
easily by opening the appropriate canvas and arranging the items using the left and right arrow
keys. (This is not required for multiview blocks) Checkboxes should be 6 pixels apart from other
items.
• Create a rectangle that encompasses the multi-record block. The bevel should be set to raised.
The rectangle should appear fully but should not extend far beyond the fields (just to be seen).
Set the rectangle’s visual attribute to RECTANGLE.
7.23.6 Input/Output
Check Boxes
Attributes:
Height : 10
Distance Between Records : 6 (multi-record blocks only)
Visual Attribute : VA_CHECK_BOX
Prompt Attachment Offset : 5 (multi-record blocks only)
A check box is used to display a setting choice. This control is a simple square with two, clearly
distinguishable states, checked and not checked. Checkboxes are used for values that have two
distinguishable values, yes and no, or checked and unchecked.
In most cases, Check Boxes should be grouped together in a Group Box with a title.
For single record blocks, use the Label field instead of a Prompt and adjust the checkbox’s width to
accommodate the text. Have the Label appear to the right of the checkbox.
For multi-record blocks, do not use the Label field – use the Prompt instead. The Prompt
Attachment Offset should be set to 5, and the Width of the checkbox should be 10.
Text Item
Attributes:
Height : 15
Visual Attribute : VA_EDIT_ITEM,VA_DISPLAY_ITEM,TEXT_ITEM_ON,TEXT_ITEM_OFF
Text items are used to display enterable or non-enterable dynamic fields, usually containing
information queried from the database.
Character Text Items are left justified.
Numeric Text Items are right justified. Amount and quantity fields must be left justified (and are
defined as such in their corresponding Property Classes) because fields that are right justified do
not allow scrolling through the field if the value is larger than the field displays.
Display Item
Attributes:
Height : 15
Bevel : None
Visual Attribute: BOILERPLATE_TEXT
With the usage of prompts, these should be virtually non-existent. One place they may still need
to be used is when a dynamic value will have multiple lines. Prompts do not support multiple lines
when setting a prompt dynamically.
Drop Down List Box (POPLIST)
Attributes:
Height : 15
Required : TRUE or FALSE
7.23.7 Buttons
Non-Iconic Push Buttons
Attributes:
Width :60
Height :16
Visual Attribute :TEXT_BUTTON_VAL_OFF
Cancel Closes the associated window and nullifies any user changes to the window.
Adds a new record in the multi-record block or calls an additional form to add
Add
another.
Delete Removes existing data from selected field, record, block or current form.
Next Accepts the current data on the window and transfers control to the next window.
Accepts the current data on the window and transfers control to the previous window
Prev
in the flow.
Print Prints a copy of the specified information.
Refresh Clears out fields and sets them back to the default values.
Search Opens a criteria window to allow the user to search for more data.
Buttons are placed on the bottom of the canvas and right-aligned. They should be placed 5 pixels
apart.
Put a KEY-LISTVAL trigger on the item which will be populated by the list and add the following
code:
BEGIN
-- replace lov_name with the name of your LOV
if F_SHOW_LOV('LOV_name') then
Do_Key('Next_Item');
end if;
END;
If the user is on the field and presses F9, the trigger KEY-LISTVAL is fired. That’s because Oracle
Retail has mapped the F9 key to that particular trigger.
If the user presses the button, we navigate to the field and then perform a Do_Key ('List_Values')
which fires the KEY-LISTVAL trigger.
A note on F_SHOW_LOV:
7.25 Menus
7.25.1 General Menu Standards
• The most common choices in a pull down menu should be near the top.
• If a menu item is not available it should be disabled, not hidden.
• Menu choices should be single words when possible.
Using the ampersand (&) in the Label works the same as defining an Access Key. In the example
above, the letter “y” will be displayed as underlined when the menu is run, and typing Alt + y at
run-time will choose that menu item.
If you do not specify the Access Key by using the ampersand method the menu will, by default,
underline the first letter in the first word of the Label for you. This can be a problem since it isn’t
intelligent-enough to ensure that the first letter isn’t already in use by another menu item. Use the
ampersand method even if the Access Key is to be the first letter in the first word.
© 2006 Enabler Page 66
Development Standards
NOTE ON KEYBOARD ACCELERATORS: An example of a Keyboard Accelerator is the combination
Ctrl + X for “Cut”. Keyboard Accelerators have to be created using Oracle Terminal, then mapped
to an item on its property palette.
Anytime you can execute an existing trigger or use a Do_Key command, it should definitely be
done.
A single installation of the application handles all supported languages. The language used on the
windows is varied at the individual level. For instance, a retailer could choose German as the
default language, but any individual user who prefers to use English, French, Japanese or any other
supported language can do so easily by setting his or her individual user language preferences.
Technical Implementation
These sections outline the technical implementation procedures for installing multiple language
support.
New:
select d.dept_name dept_name,
d.dept
from deps d
where GET_PRIMARY_LANG = GET_USER_LANG
union all
select NVL(tl.translated_value, d.dept_name) dept_name,
d.dept
from deps d,
tl_shadow tl
where GET_PRIMARY_LANG != GET_USER_LANG
and GET_USER_LANG = tl.lang (+)
and UPPER(d.dept_name) = tl.key (+)
order by dept_name
New:
select d.dept_name dept_name,
d.dept
from deps d
where GET_PRIMARY_LANG = GET_USER_LANG
and d.dept_name LIKE '%'||:block.item||'%'
union all
select NVL(tl.translated_value, d.dept_name) dept_name,
d.dept
from deps d,
tl_shadow tl
where GET_PRIMARY_LANG != GET_USER_LANG
and UPPER(d.dept_name) = tl.key (+)
and GET_USER_LANG = tl.lang (+)
and NVL(tl.translated_value, d.dept_name) LIKE '%'||:block.item||'%'
order by dept_name
7.28.4 Labels
All the labels shown on the screen are defined in database. There are two tables where the labels
and its translation are defined: FORM_ELEMENT and FORM_ELEMENT_LANG.
Parameter PM_xx
The name of the calling program with no prefix. For example,
Parameter List any Parameter Lists created in ordadd.fmb would be named
'ordadd' regardless of what Form they're calling.
Procedures P_xx
Push Button PB_xx
Radio Group RG_xx
Record Groups REC_(block where REC is used)_(item where REC is used)
Sending parameter to a
S_xx (e.g. 'cursor C_GET_DEPT(S_dept) is')
cursor
TI_xx (Base table items must be named after the associated
Text Item*
table.column, omitting the TI_prefix.)
User Area UA_xx
Visual Attributes VA_xx
Window W_xx
* Mirrored items are MI_xx when they are Text items, otherwise they should be MI_(item
type)_xx. For example, a mirrored push button would be called MI_PB_ok.
7.30 Navigation
Oracle Retail products always give the user the option of using either the keyboard or the mouse
to perform any function. The MS Windows standard of providing short cut keys for push buttons
and menu items will be followed. Users should be able to tab from enterable field to enterable
field and expect to go from left to right, top to bottom.
Things to remember:
• Tab order should only include navigable blocks.
• Tab order in a multi-record block should go from left to right through the enterable fields of a
single record. Upon reaching the last item of a single record, tabbing should proceed to the
Depending on what the user has done, the POST might cause triggers (such as PRE-INSERT, PRE-
DELETE, POST-UPDATE, etc.) to fire. If an exception is raised in an implicitly-fired trigger, the
trigger will halt but the lines of code following the POST will still be run! That means that even
though something went wrong while POSTing, the Form will still try to Go_Block('B_head') – not
what we would want to happen.
In situations such as this, it becomes necessary to check after the POST or COMMIT if everything
went as planned. We can use the :system.form_status to determine how the POST or COMMIT
went (see the information on :system.form_status in the section on :system Variables for details)
and either continue or halt as needed:
In P_EXIT.SAVE_FORM (the standard Form-exiting code in our template) you can see the following
example:
Commit_Form;
---
if :system.form_status != 'QUERY' then
emessage('COMMIT_FAILURE');
raise FORM_TRIGGER_FAILURE;
end if;
7.33 P_POPULATE_LIST
P_POPULATE_LIST is an Oracle Retail Function used to populate list items dynamically. List Items
can be populated manually but, since our products are used in many languages, we don’t want to
hard-code any values.
P_POPULATE_LIST draws information from the table CODE_DETAIL based on the CODE_TYPE fed
into it. It takes the values that correspond to the CODE_TYPE from CODE_DETAIL and puts them in
the list item you specify. This is the structure of the CODE_DETAIL table:
CODE_TYPE VARCHAR2(4)
CODE VARCHAR2(6)
CODE_DESC VARCHAR2(40)
REQUIRED_IND VARCHAR2(1)
CODE_SEQ NUMBER(4)
For any given CODE_TYPE used by P_POPULATE_LIST, the CODE_DESCs are what is displayed
to the user and the CODEs are the actual values that the list holds. These values (the information
from the CODE column) are what will be written to or read from the table.
An example of giving a list items values in P_FORM_STARTUP might look as follows:
P_POPULATE_LIST('B_lc_comp.comp_type', 'CCTP');
Here, the name of the list item is B_lc_comp.comp_type, and the information that will be put into
the list corresponds to the entries on the CODE_DETAIL table where the CODE_TYPE = 'CCTP'.
P_POPULATE_LIST executes the following query, putting the values into the list item:
select code,
code_desc
from code_detail
where code_type = 'CCTP'
order by code_seq;
P_POPULATE_LIST can also be used to give a list item a default value by using the following syntax:
P_POPULATE_LIST(block.list_item_name, code_type, default_value)
7.35 RWIDGET
RWIDGET is an Oracle Retail library package that sets an item’s properties, enabling and disabling
the item and causing the item to appear either enabled or disabled. A developer could issue a
series of Set_Item_Property commands instead of using RWIDGET, but RWIDGET saves effort and
applies a consistent look and feel. Because of these benefits, RWIDGET is required when enabling or
disabling fields. Never use Set_Item_Property to set the visual attribute, enabled, updateable,
navigable, etc.
When you first set up an item, set its initial attributes on the property sheet (e.g. if a field is display
only, its property sheet should be set to VA_DISPLAY_ITEM for the visual attribute, Update
Allowed = FALSE, Insert Allowed = FALSE, and Navigable = FALSE). Then, use RWIDGET any time it
is necessary to change the item’s attributes at run-time, such as turning the item on when it is off.
The different functions within the RWIDGET package are listed in the table below.
RWIDGET Function Use
TURN_ON Enables a field or button.
TURN_OFF Disables a field or button.
Enables the Cancel button, but leaves it as Mouse Navigate
TURN_ON_CANCEL_BUTTON NO, so that no validation is performed if users click the
button.
DISPLAY_ON Turns display on.
In the example above, our program unit now holds the current date (for Oracle Retail’s purposes)
in a local variable.
7.38 Synchronize
From Oracle Forms online help:
“Synchronizes the terminal screen with the internal state of the form. That is, synchronize updates
the screen display to reflect the information that Form Builder has in its internal representation of
the screen…. Without synchronize, the screen is typically only updated when Form Builder
completes all trigger execution and comes back for user input.”
What this means:
Oracle Forms has always kept the “terminal screen” processing separate from the “internal state”
of Forms. This allowed them to create a tool to build executables which could easily be ported to
Mac, Unix, Windows, etc. Part of this legacy is that what’s on screen and what’s going on behind
the scenes aren’t always in sync. Under certain circumstances, things become unglued and require
the line synchronize in Forms code.
When should I use synchronize?
Ideally?
NEVER.
In reality?
Here are a few examples of situations in which synchronize might cure what ails your Form:
• When you have an error, but once you put in emessage or wmessage lines to try to track
the error the error goes away. Solution: replace your emessage or wmessage lines with
synchronize.
• When queried, a radio group takes the default value rather than the queried value.
Solution: use synchronize immediately before Execute_Query.
• synchronize swapping canvases in and out of a Form the cursor gets lost. Solution: use
synchronize after the Hide_Canvas and/or Show_Canvas calls.
In the second (incorrect) example, the :system.current_block will never equal 'B_head' because
'B_head' contains lowercase letters (it might contain the value 'B_HEAD', which is different from
'B_head'). In this example, therefore, the line Go_Block('B_head') will always be executed,
defeating the purpose of having it inside a conditional statement.
7.39.1 :system.record_status
Forms keeps track of the record that is currently active in the Form and assigns one of four values
(QUERY, NEW, CHANGED, INSERT) to the :system.record_status based upon whether the data is
on the database and whether the data has been changed:
YES NO
Data HAS been modified
NO QUERY NEW
7.39.2 :system.form_status
The Form itself, when run, has a status value which varies based-upon the data’s state. There are
three possible values:
• NEW
NEW status occurs when the Form shows no base-table data. This happens before data is
queried into any base table blocks and if Execute_Query returns no rows.
• QUERY
The data in the base-table block(s) is identical to the data on the table(s). The Form will be in
QUERY status after an Execute_Query is performed if the query returns records. The Form will
stay in QUERY status until a change is made to base-table data in the Form.
• CHANGED
Data in the base-table block(s) has been updated, deleted, or inserted. The status will remain
CHANGED until a POST, ROLLBACK or COMMIT has been issued.
At Oracle Retail the :system.form_status is primarily used to determine the success or failure of a
POST or a COMMIT. If the user enters or modifies data in a Form the :system.form_status will
change from QUERY to CHANGED. This is because the base-table data in the Form no longer
matches the data on the table (the data has been changed). If, in the Form’s logic, a POST or
COMMIT is then executed, the status should return to QUERY since the base-table data will now
match the table’s data. If, following a POST or COMMIT, the status does not return to QUERY, then
something must have gone wrong.
For example:
POST;
if :system.form_status != 'QUERY' then
something went wrong so we must deal with the error…
else
WINDOW_HANDLER.SET_FORM_CHANGED;
end if;
In P_EXIT.SAVE_FORM (the standard Form-exiting code in our template) you can see the following:
Commit_Form;
---
if :system.form_status != 'QUERY' then
emessage('COMMIT_FAILURE');
raise FORM_TRIGGER_FAILURE;
end if;
Again, if the Commit_Form had executed successfully the status would be QUERY. Since the status
is not QUERY, something must have gone wrong with the COMMIT. In the case above we then
present an error message and stop the processing.
A Form-Level trigger is active and waiting to be triggered from any location in the Form. It doesn’t
matter what block the cursor is on or what item the cursor is on. The Form-Level trigger will fire if
the triggering condition arises.
A Block-Level trigger, on the other hand, will only fire if the appropriate circumstance arises while
the cursor is on an item in the block that has the trigger.
Likewise, an Item-Level trigger will only fire if the appropriate circumstance arises while the cursor
is on the specific item to which the trigger is attached.
If the same type of trigger exists at more than one level of scope then, given where your cursor is,
only the trigger that is most specific will fire: Item-Level is more specific than Block-Level, Block-
Level is more specific than Form-Level.
For example, in the Form below there is a KEY-UP trigger at the Form-Level, another KEY-UP
trigger at the block-level on the block B_MY_BLOCK, and still another KEY-UP trigger at the item
level on the item MY_ITEM.
7.42 Validation
All validation logic should reside in the FORM_VALIDATION local package. Each item that requires
validation should have a WHEN-VALIDATE-ITEM trigger that calls its respective procedure in the
FORM_VALIDATION package (ex. FORM_VALIDATION.ITEM).
Validation for each item should check to be sure that the validation code will only be executed if
the item is NOT NULL (if the item is NULL, there should be no need to validate):
BEGIN
if :B_head.dept is NOT NULL then
...
see if the value is valid
...
end if;
END;
NOTE: The only thing the WHEN-VALIDATE-RECORD trigger should do is check for NULLs. The
validation of the values the items have takes place in the WHEN-VALIDATE-ITEM trigger, not here.
Read the information on Multi-Record Blocks in the section Block Types and Their Triggers for
additional details.
7.43 WINDOW_HANDLER
If you open a document in any Windows application (e.g. Microsoft Word), make some changes,
then click on the in the upper-right corner, a pop-up window appears and asks "Do you want to
save the changes you have made?" The reason for the message is that we cannot be sure whether
the user intends to save or discard their changes when they choose to exit an application by
pressing the .
Oracle Forms operates the same way. If the user clicks on the in the upper-right corner of a
Form, Oracle checks the :system.form_status. If the status = 'CHANGED' (if the user has added,
changed, or deleted information) then Forms gives the user a "Do you want to save changes”
prompt.
There is, however, a catch. If in your code you perform a POST, then the :system.form_status will
not = 'CHANGED' even though the user may have made changes to the data – POSTing returns the
:system.form_status to 'QUERY'. For example, we always POST after we delete a record in a multi-
record block. In this case, if the user clicks on the Oracle would NOT pop-up the "Do you want
to save changes?" dialogue even though there have been changes. Instead, Oracle will just close
the window and throw away what the user has done. This is bad.
If the instructions above do not work for your Form, do the following:
Open the property sheet for the canvas C_TOOLBAR and change its Window property to be the
first window that is displayed when the Form is run.
8 Pro*C
The standards detailed below should be followed when designing and coding Pro*C programs:
Within this retek_2.h header file, the following additional header files are automatically defined
and referenced:
#include <stdio.h> standard include files (/usr/include)
#include <stdlib.h> standard library functions (/usr/include)
#include <string.h> standard C library string functions (/usr/include)
#include <ctype.h> standard C types (/usr/include)
#include <memory.h> standard C general library functions (/usr/include)
#include <math.h> maths functions (/usr/include)
#include <stdarg.h> argument header file (/usr/include)
#include <std_err.h> standard variable declarations for error handling
#include <std_len.h> standard column length definitions.
#include <limits.h> standard C limits (/usr/include)
#include <common.h> library for common functions, including return codes
#include <oracle.h> library for standard Oracle functions
#include <4_5_4.h> library for calendar/dates
#include <unistd.h> function definitions and declarations(/usr/include)
The above library routines that are not standard C are provided by Oracle Retail and stored at
$MMHOME/lib/src. In addition to these, there are a number of other libraries that have been
supplied with base ORMS.
If definitions include an additional character for nts (null terminate string), they should be named
accordingly, as NULL_<name>. Furthermore, definitions not including nts should be named
LEN_<name>, as per the Oracle Retail standards.
Wherever possible, definitions from the standard Oracle Retail header files should be used, to
avoid any hard-coding or incorrect variable lengths. By referring to the std_len.h header file, many
definitions can be found for commonly used columns.
Functions within these header files should be used, as should any standard variable declarations
contained within them. For example LEN_ITEM is declared as 25, and NULL_ITEM declared as
LEN_ITEM + 1 (26), within std_len.h.
8.4.1 main()
The main() function is the starting point of any C program. The only things that happen in the
main() function are:
• A connection is made to the database;
• init(), process() and final() are called to perform the work of the program;
• Messages are written to the daily log file in order to indicate the beginning and the end of the
program’s run.
8.4.2 init()
The init() function is where one-time tasks, which must happen before actual data processing, are
performed:
• Restart/Recovery is initialized and any outstanding bookmarks are retrieved;
• System-level variables and options are fetched from database;
• Input and output files are opened for reading and writing;
• Memory is allocated.
8.4.3 process()
The process() function is where the bulk of the work of a batch program is controlled:
• The driving cursor is opened and fetched from;
• Supporting functions are called to perform program-specific functions;
• Restart/Recovery is maintained by writing bookmarks to the database.
8.4.4 final()
The final() function is where loose ends in the program are tied up:
• Restart/Recovery is closed down;
• Input and output files are closed;
• Memory is released.
8.6 Variables
Variable names should clearly identify its purpose.
For example: instead of naming your variables as x, y, a, use ls_wh, ls_store, ls_item.
8.6.3 Date
Because C has no date type, Oracle dates should be always converted into string for use in batch
programs. Date string should be in the Oracle format ‘YYYYMMDD’. If a timestamp is part of the
date, the format should be ‘YYYYMMDDHH24MISS’. The ordering of fields (year, month, then day)
allows date comparison to be done in C using a simple strcmp(). Use the date mask when
retrieving the date from database and when using the date string (in a cursor, insert, update, etc).
Example:
EXEC SQL DECLARE c_init CURSOR FOR
SELECT TO_CHAR(vdate, 'YYYYMMDD')
FROM period
...
EXEC SQL FETCH c_init INTO :ps_vdate:pi_vdate_ora_ind;
...
EXEC SQL DECLARE c_unit_retail CURSOR FOR
SELECT unit_retail
FROM price_hist
WHERE item = :is_item
AND action_date < TO_DATE(:ps_vdate, 'YYYYMMDD');
8.9 Capitalization
8.9.1 Functions
Within a Pro*C program, functions should always be lowercase and have a meaningful name. In
parameters should be specified before out parameters, and in/out parameters specified after out
parameters.
Here are some examples of function declarations
int main(int argc, char* argv[])
int init()
8.9.2 Variables
C variables should be lowercase.
Example:
ls_item
Embedded PL/SQL variables should have their prefixed in uppercase and the description in
lowercase
Example:
L_error_message
8.10 Brackets
Brackets begin on the line following the statement triggering their use. Indent them to the same
depth as that statement. A line containing a bracket should not contain any other statement,
although comments are acceptable:
/* Improper bracketing */
int get_promotion() {
...
if(pi_multi_prom_ind)
{
...
}
...
if(SQL_ERROR_FOUND) { sprint(err_data,”SQL error”);
return(FATAL); }
...
return(OK);
} /* end get_promotion */
/* Proper bracketing */
int get_promotion()
{
...
if(pi_multi_prom_ind)
{
...
}
...
if(SQL_ERROR_FOUND)
{
sprint(err_data,”SQL error”);
return(FATAL);
}
...
return(OK);
} /* end get_promotion */
8.11 Comments
Source code is meant to explain a process in the clearest and most explicit manner possible.
However, situations often arise in which the action being performed by a program may be so
complex or subtle (from both a technical and business standpoint) that they may require
additional explanation. Comments in a program exist to help the reader of that program to
understand sections of code when the function or logic may not be obvious.
The following rules should be observed:
• Comments must be written clearly;
• Place the comments on the line before the code they describe;
• If a comment spans multiple lines, break it up and format it to keep it distinct from the code. Do
not use one set of comment characters to define multiple line comments;
• Do not include comments to reiterate the obvious, as in the next example:
/* Add the fetched quantity to the total quantity */
ld_total_qty += ld_fetched_qty;
• When maintaining code (fixing, customizing, enhancing) be careful to maintain all associated
comments as well. Incorrect or misleading comments can lead to serious misunderstanding and
errors. This is the primary reason why self-documenting code is desirable. Well named
variables, functions and constants as well as precise parameter passing all contribute greatly to
self-documenting code.
8.12.2 LOG_MESSAGE()
Messages are written to the log file by the LOG_MESSAGE() function. This function takes a single
string as a parameter and automatically adds the timestamp and program name.
sprintf(ls_log_message,"Thread %s - Terminated OK",ps_thread_val);
LOG_MESSAGE(ls_log_message);
8.12.4 WRITE_ERROR()
WRITE_ERROR (SQLCODE, function, table, err_data);
Parameters:
• Error code: SQLCODE (declared at the beginning of the code);
• Function: the name of the function in which the error occurred (declared at the beginning of
each function);
• Table: a list of the tables on which the error occurred;
• Error data: a formatted string describing the error conditions. This should provide reasonable
detail to users to help them figure out where the error occurred. This should not repeat any
information in the program, function or table parameters. This string should describe the action
taken, the cursor involved (if applicable) and any associated bind variable. The error message is
the only evidence on a problem and the only guide to finding that problem, so it should be as
helpful as possible.
sprintf(err_data, "UPDATE: active_date=%s", ps_vdate);
strcpy(table, "cost_susp_sup_head");
WRITE_ERROR(SQLCODE, function, table, err_data);
return(FATAL);
For function execution, the return code should be checked, and if failure, the program should
return a failure return code, as follows:
if (modify_indexes(ls_table_name, "disable") < 0)
{
return(FATAL);
}
'ITEM_ATTRIB_SQL.GET_PRIMARY_REF_ITEM',
SQLCODE), 1, 255);
END;
END-EXEC;
if (SQL_ERROR_FOUND)
{
sprintf(err_data, "STORED FUNCTION CALL FAILED: function =
ITEM_ATTRIB_SQL.GET_PRIMARY_REF_ITEM - item: %s ",is_item);
WRITE_ERROR(SQLCODE, function, "", err_data);
return(FATAL);
}
if (li_plsql_error || li_exception)
{
sprintf(err_data, "INTERNAL STORED FUNCTION ERROR: function =
ITEM_ATTRIB_SQL.GET_PRIMARY_REF_ITEM, error: %s", ls_error_msg);
WRITE_ERROR(RET_PROC_ERR, function, "", err_data);
return(FATAL);
}
return(OK);
} /* end of get_ref_item */
For multiple if statements within a block, indentation should be applied to each further statement,
as in the following example:
if (init() != NO_THREAD_AVAILABLE
{
/*true action */
if (init() == SUCCEEDED)
{
if (process() < 0)
{
gi_error_flag = 1;
}
}
}
8.13.2 GoTo
These should not be used within a Pro*C program.
Ensure that the break statement is used to end an otherwise infinite while loop.
All queries must be done using cursors. After a cursor is opened/fetched/closed, any errors should
be checked.
Example:
EXEC SQL DECLARE c_get_unit_retail CURSOR FOR
SELECT unit_retail
FROM item_loc
WHERE loc = :ls_store
AND item = :ls_item;
if(NO_DATA_FOUND)
{
ld_unit_retail = -1;
Note that no error check should be done after declaring the cursor. The cursor declaration does
not execute a SQL command and therefore, the SQL_ERROR_FOUND does not reflect the cursor
declaration command, but reflect the SQL executed before the cursor declaration.
Example:
char ps_vdate;
short pi_vdate_ora_ind;
...
FETCH c_init
INTO :ps_vdate:pi_vdate_ora_ind;
On Output:
After fetching the variable from database, the NULL indicator may have the following values:
Variable Description
-1 The column value is NULL, so the value of the host variable is indeterminate.
0 Oracle assigned an intact column value to the host variable.
>0 Oracle assigned a truncated column value to the host variable.
The integer returned by the indicator variable is the original length of the column
value, and SQLCODE in SQLCA is set to zero.
-2 Oracle assigned a truncated column variable to the host variable, but the original
column value could not be determined (a LONG column, for example).
Usually, it is used only 2 of these values:
-1 – is NULL
0 – is not NULL
long l_recs_returned;
long l_total_recs_processed;
long l_current_record;
} ta_ordloc;
ta_ordloc pa_ordloc;
...
while(1)
{
EXEC SQL FOR :pi_commit_max_ctr
FETCH c_ordloc
INTO :pa_ordloc.s_order_no:pa_ordloc.i_order_no_ora_ind,
:pa_ordloc.s_item:pa_ordloc.i_item_ora_ind,
:pa_ordloc.d_qty_rcv:pa_ordloc.i_qty_rcv_ora_ind;
...
for (pa_ordloc.l_current_record = 0;
pa_ordloc.l_current_record < pa_ordloc.l_recs_returned;
pa_ordloc.l_current_record++)
{
if (pa_ordloc.i_qty_rcv_ora_ind == -1)
if(receive_item() != OK)
return(FATAL);
INSERT example:
EXEC SQL FOR :pi_commit_max_ctr
INSERT INTO ordloc
(order_no,
item,
qty_received)
VALUES (:pa_ordloc.s_order_no:pa_ordloc.i_order_no_ora_ind,
:pa_ordloc.s_item:pa_ordloc.i_item_ora_ind,
:pa_ordloc.d_qty_rcv:pa_ordloc.i_qty_rcv_ora_ind);
ls_item_temp = &(s_ril_updates.s_item[ll_ins_ind]);
ls_location_temp = &(s_ril_updates.s_location[ll_ins_ind]);
ls_loc_type_temp = &(s_ril_updates.s_loc_type[ll_ins_ind]);
ls_change_type_temp = &(s_ril_updates.s_change_type[ll_ins_ind]);
if (SQL_ERROR_FOUND)
break;
}
if (SQL_ERROR_FOUND)
{
sprintf(err_data,"ARRAY INSERT - item: %s, loc: %s",
ls_item_temp[NUM_RECORDS_PROCESSED],
ls_location_temp[NUM_RECORDS_PROCESSED]);
strcpy(table,"REPL_ITEM_LOC_UPDATES");
WRITE_ERROR(SQLCODE,function,table,err_data);
return(FATAL);
}
When an array is used on insert/update/delete, all variables used in the statement must be within
the array, i.e. no single variable can be used.
Examples:
1) CALLOC_LONG(iot_size_this->l_ib_days_to_event, il_size);
2) CALLOC_STRING(iot_size_this->s_rr_rowid, il_size, NULL_ROWID);
3) CALLOC_SHORT(iot_size_this->i_rr_rowid_ind, il_size);
FREE may also be used, which de-allocates the space pointed to by the pointer to space previously
allocated by CALLOC, MALLOC or REALLOC.
For examples of restart recovery and multi-threading, refer to Oracle Retail batch programs, such
as supcnstr.pc or sitmain.pc.
An example of a driving cursor from a program containing restart recovery and multi-threading
can be seen below:
EXEC SQL DECLARE c_item_loc CURSOR FOR
SELECT ph.loc,
ph.item
FROM price_hist ph,
v_restart_store rv
WHERE ph.tran_type IN (4,11)
AND ph.loc_type = 'S'
AND rv.driver_value = ph.loc
AND rv.num_threads = TO_NUMBER(:ps_restart_num_threads)
AND rv.thread_val = TO_NUMBER(:ps_restart_thread_val)
AND ph.loc > NVL(:ps_restart_loc, -9999)
OR (ph.loc = :ps_restart_loc
AND (ph.item > :ps_restart_item))
ORDER BY 1, 2;
Restart/recovery can also be automatically implicit in the code in some situations, without using
restart variables. For example, when the driving cursor is based on a table whose records are in
status ‘A’ and the status is updated to status to ‘C’ after each commit. Thus, if the program fails,
when re-run it will only process records that remain in a status of ‘A’, and will not have to re-
process all data.
Number of threads per program are held within RESTART_CONTROL, and multi-threading is done
based on multiple files being processed in parallel. For example, if posupld receives 20 files, and 20
threads were configured, each thread would process one file. Note that multiple processes will not
process the same file, as they have to be split before.
Call Description
variables for start_string. If no start_string (as in file-based),
pass in NULL.
Force a commit.
int
retek_force_commit(int Pass in num_args, then variables for start_string first, and those
num_args, for image string (if needed) second. The num_args is the total
...
);
number of these two groups. All are string variables and are
passed in the same order as in retek_init();
Close restart/recovery at end of program.
int retek_close(
If errors, rollback all database changes, otherwise Commit
void
); work;
Close all opened file streams.
int is_new_start(
void Check if current run is a new start; if yes, return 1; otherwise 0.
);
int set_filename(
rtk_file* file_struct,
char* file_name, Set filename into rtk_file structure used by other calls.
int pad_flag
);
int rtk_print(
rtk_file* file_struct, Output data to a file described by an rtk_file structure. File
char* format, ... writes will be tracked to allow consistent restart.
);
For additional standard calls, see the supplier header files, such as intrface.h.
The following function may be used for general purpose:
Call Description
Call Description
(Macro) Matches two strings. Returns 1 if they match, 0 if not.
(wrapper around standard C function strncmp, for coding
int MATCH( clarity)
char * str,
example:
char *str
if(MATCH(ls_rec_type,"TDETL"))
);
{
null;
}
int valid_all_numeric( Validates string is numeric and at least N digits long. Decimal
char * str,
points are not supported.
int desired_len
); Returns 0 for success, 1 for reject , -1 for failure
int
valid_all_numeric_signed( Validates string is numeric and signed and at least N digits long.
char * str, Decimal points are not supported.
int desired_len
);
Returns 0 for success, 1 for reject, -1 for failure.
int left_shift( Shifts string into field, removes spaces.
char * str
); Returns 0 for success, -1 for failure.
void nullpad(
char * str,
int str_len Adds a C string terminator to end of field.
);
int valid_date( Validates input field for a date.
char * str)
; Returns 0 for success, 1 for reject, -1 for failure.
int field_has_value( Checks field is non-blank.
char * str
); Returns 0 for success, 1 for reject, -1 for failure.
int all_blank( Checks field is blank.
char * str
); Returns 0 for success, 1 for reject, -1 for failure.
void zero_pad(
int i_exp_len,
char * c_str Left Pads the number with 0.
);
Use of Data Blocks (B_head and B_action), Program Units (P_FORM_STARTUP, P_EXIT,
FORMS_VALIDATION, INTERNAL_VARIABLES) and PM_mode whenever applicable.
Correct use of the Visual Attributes for Items, LOV’s and Canvas, as well as the size of the items.
Correct use of button and LOV’s properties, regarding Functional Icon Filename (listval, calendar)
and Functional Access Keys.
List Items populated using P_POPULATE_LIST (data defined in CODE-HEAD and CODE_DETAIL
tables).
LOV’s functionality: returning data and LOV size (LOV by itself and column inside the LOV).
Navigation between the objects in the logic order, using TAB and SHIFT TAB (without navigating
in the LOVs).
Created script for List Items new values (CODE_HEAD and CODE_DETAIL).
Created script to insert the new form in the ORMS menu structure (NAV_*).
9.3 Pro*C
PROC
If new, the program is prefixed by <prefix>.
The program is indented in 3 positions.
The required function main(), init(), process() and final() are implemented.
Program returns 0 (OK), -1 (FATAL) or 1 (NONFATAL). Exceptions are acceptable only if defined in
the requirement.
All defined variables indicate clearly their purpose.
All variables are under the standard naming convention.
First letter – Scope
g – local variables declared out of the program.
p – local variables declared in the program.
l – local variables in a function.
i – input parameter for a function.
o – output parameter for a function.
io – input/output parameter for a function.
Second letter – Type
i – Integer or Short
l – Long
d – Double
c – Caracter
s – String
a – array or estrutura de array
sf – file pointer
Cursor name are prefixed with ‘c’ lowercase.
Internal variables in PL/SQL blocks start with ‘L’ capitalized.
PL/SQL variables have the first letter capitalized and the remaining letter in lowercase.
Package body:
CREATE OR REPLACE PACKAGE BODY <PREFIX>_BANNER_SQL AS
------------------------------------------------------------------------------
< Include here the comments using the standard defined for the client >
------------------------------------------------------------------------------
FUNCTION GET_BANNER_NAME(O_error_message IN OUT VARCHAR2,
O_banner_name IN OUT BANNER.BANNER_NAME%TYPE,
I_banner_id IN BANNER.BANNER_ID%TYPE)
RETURN BOOLEAN IS
cursor C_BANNER_NAME is
select banner_name
from banner
where banner_id = I_banner_id;
BEGIN
open C_BANNER_NAME;
fetch C_BANNER_NAME INTO O_banner_name;
---
if C_BANNER_NAME%NOTFOUND then
O_error_message := SQL_LIB.CREATE_MSG('INV_BANNER');
close C_BANNER_NAME;
return FALSE;
end if;
---
close C_BANNER_NAME;
EXCEPTION
when OTHERS then
O_error_message := SQL_LIB.CREATE_MSG('PACKAGE_ERROR',
SQLERRM,
L_program_name,
TO_CHAR(SQLCODE));
return FALSE;
END GET_BANNER_NAME;
------------------------------------------------------------------------------
FUNCTION DELETE_BANNER(O_error_message IN OUT VARCHAR2,
I_banner_id IN BANNER.BANNER_ID%TYPE)
RETURN BOOLEAN IS
cursor C_LOCK_BANNER is
select 'x'
from banner
where banner_id = I_banner_id
for update nowait;
BEGIN
open C_LOCK_BANNER;
close C_LOCK_BANNER;
---
delete from banner
where banner_id = I_banner_id;
return TRUE;
EXCEPTION
when RECORD_LOCKED then
O_error_message := SQL_LIB.CREATE_MSG('TABLE_LOCKED',
L_table,
TO_CHAR(I_banner),
NULL);
return FALSE;
when OTHERS then
O_error_message := SQL_LIB.CREATE_MSG('PACKAGE_ERROR',
SQLERRM,
L_program_name,
TO_CHAR(SQLCODE));
return FALSE;
END DELETE_BANNER;
------------------------------------------------------------------------------
END <PREFIX>_BANNER_SQL;
#include <retek_2.h>
/* Local Defines */
long SQLCODE;
#define NULL_LOC_DESC 151
init_parameter parameter[] =
{
/* NAME ----------- TYPE ------ SUB_TYPE */
"commit_max_ctr", "uint", "",
"thread_val", "string", "",
"num_threads", "string", "",
"restart_loc", "string", "S",
"restart_item", "string", "S"
};
#define NUM_COMMIT_PARAMETERS 2
#define NUM_INIT_PARAMETERS (sizeof(parameter) / sizeof(init_parameter))
/* Program Variables */
char ps_vdate[NULL_DATE];
short pi_vdate_ora_ind;
/*****************************************************************************\
|* Local typedefs *|
\*****************************************************************************/
struct item_array
{
char (*s_cycle_count)[NULL_CYCLE_COUNT];
short *i_cycle_count_ora_ind;
char (*s_loc_type)[NULL_IND];
short *i_loc_type_ora_ind;
char (*s_location)[NULL_LOC];
short *i_location_ora_ind;
char (*s_item)[NULL_ITEM];
short *i_item_ora_ind;
char (*s_qty)[NULL_QTY];
short *i_qty_ora_ind;
uint i_recs_returned;
int i_num_records_processed;
struct insert_array
{
char (*s_cycle_count)[NULL_CYCLE_COUNT];
char (*s_loc_type)[NULL_IND];
char (*s_location)[NULL_LOC];
char (*s_item)[NULL_ITEM];
char (*s_qty)[NULL_QTY];
char (*s_location_desc)[NULL_LOC_DESC];
int i_current_record;
} pa_insert;
struct update_array
{
char (*s_cycle_count)[NULL_CYCLE_COUNT];
char (*s_loc_type)[NULL_IND];
char (*s_location)[NULL_LOC];
char (*s_item)[NULL_ITEM];
char (*s_qty)[NULL_QTY];
int i_current_record;
} pa_update;
/*****************************************************************************\
|* Function prototypes *|
\*****************************************************************************/
int init();
int process();
int process_item();
int fill_loc_desc();
int post_insert();
int post_update();
int final();
int size_arrays();
int free_arrays();
/*****************************************************************************\
|* *|
|* PROGRAM IMPLEMENTATION *|
|* *|
\*****************************************************************************/
int main(int argc, char* argv[])
{
char *function = "main";
char ls_log_message[NULL_ERROR_MESSAGE] = "\0";
int li_init_results = 0;
if (argc < 2)
{
fprintf(stderr, "Usage: %s userid/passwd\n",argv[0]);
return(FAILED);
}
if (li_init_results != NO_THREAD_AVAILABLE)
if (final() < 0)
{
if (gi_error_flag == 0)
gi_error_flag = 3;
}
}
if (gi_error_flag == 2)
{
sprintf(ls_log_message, "Aborted in init");
LOG_MESSAGE(ls_log_message);
return(FAILED);
}
else if (gi_error_flag == 1)
{
sprintf(ls_log_message, "Thread %s - Aborted in process", ps_thread_val);
LOG_MESSAGE(ls_log_message);
return(FAILED);
}
else if (gi_error_flag == 3)
{
sprintf(ls_log_message, "Thread %s - Aborted in final", ps_thread_val);
LOG_MESSAGE(ls_log_message);
return(FAILED);
}
else if (li_init_results == NO_THREAD_AVAILABLE)
{
sprintf(ls_log_message, "Terminated - no threads available");
LOG_MESSAGE(ls_log_message);
return(NO_THREADS);
}
else
{
sprintf(ls_log_message,"Thread %s - Terminated
Successfully",ps_thread_val);
LOG_MESSAGE(ls_log_message);
}
return(SUCCEEDED);
} /* end of main() */
/*****************************************************************************\
|* INIT *|
\*****************************************************************************/
int init()
{
char *function = "init";
int li_init_return = 0;
if (li_init_return != 0)
return(li_init_return);
return(OK);
} /* end of init() */
/*****************************************************************************\
|* PROCESS *|
\*****************************************************************************/
int process()
{
char *function = "process";
if (NO_DATA_FOUND) li_ndf = 1;
if (!(pa_item.i_recs_returned = NUM_RECORDS_PROCESSED –
pa_item.i_num_records_processed)) break;
pa_item.i_num_records_processed = NUM_RECORDS_PROCESSED;
for (pa_item.i_current_record = 0;
pa_item.i_current_record < pa_item.i_recs_returned;
pa_item.i_current_record++)
if(process_item() != OK)
return (FATAL);
if(post_insert() != OK)
return (FATAL);
if(post_update() != OK)
return (FATAL);
if (retek_force_commit(NUM_COMMIT_PARAMETERS,
ls_location,
ls_item) < 0)
return(FATAL);
if (li_ndf) break;
} /* end while */
return(OK);
} /* end process */
/*****************************************************************************\
|* PROCESS_ITEM *|
\*****************************************************************************/
int process_item()
{
char *function = "process_item";
if(fill_loc_desc() != OK)
return (FATAL);
pa_insert.i_current_record++;
pa_update.i_current_record++;
}
return(OK);
} /* end process_item */
/*****************************************************************************\
|* FILL_LOC_DESC *|
\*****************************************************************************/
int fill_loc_desc()
{
char *function = "fill_loc_desc";
char ls_location[NULL_LOC] = "\0";
char ls_loc_type[NULL_LOC_TYPE] = "\0";
char ls_location_desc[NULL_LOC_DESC] = "\0";
int li_plsql_error = 0;
int li_exception = 0;
char ls_error_msg[NULL_ERROR_MESSAGE] = "\0";
strcpy(ls_location, pa_item.s_location[pa_item.i_current_record]);
strcpy(ls_loc_type, pa_item.s_loc_type[pa_item.i_current_record]);
'LOCATION_ATTRIB_SQL.GET_NAME',
SQLCODE), 1, 255);
END;
END-EXEC;
if (SQL_ERROR_FOUND)
{
sprintf(err_data, "STORED FUNCTION CALL FAILED: function =
strcpy(pa_insert.s_location_desc[pa_insert.i_current_record],
ls_location_desc);
return(OK);
} /* end post_insert */
/*****************************************************************************\
|* POST_INSERT *|
\*****************************************************************************/
int post_insert()
{
char *function = "post_insert";
if(!pa_insert.i_current_record)
return(OK);
if (SQL_ERROR_FOUND)
{
sprintf(err_data, "Inserting stake_qty - cycle_count: %s,
location: %s, item: %s",
pa_insert.s_cycle_count[pa_insert.i_current_record],
pa_insert.s_location[pa_insert.i_current_record],
pa_insert.s_item[pa_insert.i_current_record]);
strcpy(table, "stake_qty");
WRITE_ERROR(SQLCODE,function,table,err_data);
return (FATAL);
}
pa_insert.i_current_record = 0;
return(OK);
/*****************************************************************************\
|* POST_UPDATE *|
\*****************************************************************************/
int post_update()
{
char *function = "post_update";
if(!pa_update.i_current_record)
return(OK);
if (SQL_ERROR_FOUND)
{
sprintf(err_data, "Updating stake_qty - cycle_count: %s,
location: %s, item: %s",
pa_update.s_cycle_count[pa_update.i_current_record],
pa_update.s_location[pa_update.i_current_record],
pa_update.s_item[pa_update.i_current_record]);
strcpy(table, "stake_qty");
WRITE_ERROR(SQLCODE,function,table,err_data);
return (FATAL);
}
pa_update.i_current_record = 0;
return(OK);
} /* end post_update */
/*****************************************************************************\
|* FINAL *|
\*****************************************************************************/
int final()
{
char *function = "final";
int li_final_return = 0;
free_arrays();
li_final_return = retek_close();
return(li_final_return);
} /* end of final() */
/*****************************************************************************\
|* SIZE_ARRAYS *|
\*****************************************************************************/
int size_arrays()
{
char *function="size_arrays";
short li_no_mem = 0;
if (!li_no_mem&&(pa_insert.s_cycle_count = (char
(*)[NULL_CYCLE_COUNT])
calloc (pi_commit_max_ctr, NULL_CYCLE_COUNT)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_insert.s_loc_type = (char (*)[NULL_IND])
calloc (pi_commit_max_ctr, NULL_IND)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_insert.s_location = (char (*)[NULL_LOC])
calloc (pi_commit_max_ctr, NULL_LOC)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_insert.s_item = (char (*)[NULL_ITEM])
calloc (pi_commit_max_ctr, NULL_ITEM)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_insert.s_qty = (char (*)[NULL_QTY])
calloc (pi_commit_max_ctr, NULL_QTY)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_insert.s_location_desc = (char (*)[NULL_LOC_DESC])
calloc (pi_commit_max_ctr, NULL_QTY)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_update.s_cycle_count = (char
(*)[NULL_CYCLE_COUNT])
calloc (pi_commit_max_ctr, NULL_CYCLE_COUNT)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_update.s_loc_type = (char (*)[NULL_IND])
calloc (pi_commit_max_ctr, NULL_IND)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_update.s_location = (char (*)[NULL_LOC])
calloc (pi_commit_max_ctr, NULL_LOC)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_update.s_item = (char (*)[NULL_ITEM])
calloc (pi_commit_max_ctr, NULL_ITEM)) == NULL) li_no_mem = 1;
if (!li_no_mem&&(pa_update.s_qty = (char (*)[NULL_QTY])
calloc (pi_commit_max_ctr, NULL_QTY)) == NULL) li_no_mem = 1;
if (li_no_mem)
{
sprintf(err_data, "Unable to allocate memory!");
WRITE_ERROR(RET_FUNCTION_ERR, function, "", err_data);
return(FATAL);
}
pa_item.i_recs_returned = 0;
pa_item.i_num_records_processed = 0;
pa_item.i_current_record = 0;
pa_insert.i_current_record = 0;
pa_update.i_current_record = 0;
return(OK);
} /* end size_arrays */
/*****************************************************************************\
|* FREE_ARRAYS *|
\*****************************************************************************/
int free_arrays()
{
char *function = "free_arrays";
free(pa_item.s_cycle_count);
free(pa_item.i_cycle_count_ora_ind);
free(pa_item.s_loc_type);
free(pa_item.i_loc_type_ora_ind);
free(pa_item.s_location);
free(pa_item.i_location_ora_ind);
free(pa_item.s_item);
free(pa_item.i_item_ora_ind);
free(pa_item.s_qty);
free(pa_item.i_qty_ora_ind);
free(pa_insert.s_cycle_count);
free(pa_insert.s_loc_type);
free(pa_insert.s_location);
free(pa_insert.s_item);
free(pa_insert.s_qty);
free(pa_insert.s_location_desc);
free(pa_update.s_cycle_count);
free(pa_update.s_loc_type);
free(pa_update.s_location);
free(pa_update.s_item);
free(pa_update.s_qty);
return(OK);
} /* end free_arrays */