PLSQL Function With Variable Number Of Parameters For Concurrent Request Submission
In Oracle database development, a common requirement is to create PL/SQL functions that can handle concurrent requests with a variable number of parameters. This article delves into the intricacies of building a generic PL/SQL function capable of submitting concurrent requests, regardless of the number of input parameters required by the concurrent program. This is particularly useful when dealing with diverse concurrent programs, each having its unique set of parameters. We will explore the challenges, solutions, and best practices for designing such a flexible function, ensuring it can adapt to different concurrent program requirements without the need for modifications.
Understanding the Challenge
The primary challenge lies in the inherent nature of PL/SQL functions, which typically require a fixed number of input parameters. When dealing with concurrent programs, the number of parameters can vary significantly, ranging from zero to several. Therefore, a generic function needs a mechanism to handle this variability. This mechanism should allow the function to accept a dynamic set of parameters and pass them appropriately to the concurrent program submission process. This involves not only accepting the parameters but also identifying their data types and order, which are crucial for the correct execution of the concurrent request.
Key Considerations
When designing a PL/SQL function with a variable number of parameters for concurrent request submission, several key considerations come into play. These include:
- Parameter Handling: The function must be able to accept a variable number of parameters. This can be achieved using PL/SQL collections, such as arrays or tables, or by passing a delimited string of parameters.
- Data Type Conversion: Concurrent program parameters can be of various data types (e.g., VARCHAR2, NUMBER, DATE). The function must handle the conversion of input parameters to the appropriate data types required by the concurrent program.
- Parameter Order: The order of parameters is crucial for concurrent program execution. The function must ensure that the parameters are passed in the correct order.
- Error Handling: Robust error handling is essential. The function should handle exceptions gracefully and provide informative error messages.
- Security: Security considerations are paramount. The function should prevent SQL injection and other security vulnerabilities.
- Performance: The function should be designed for optimal performance, especially when dealing with a high volume of concurrent requests.
- Maintainability: The function should be easy to maintain and modify as needed. This includes clear code documentation and a modular design.
Solutions for Handling Variable Parameters
Several approaches can be employed to create a PL/SQL function that accepts a variable number of parameters. We will explore some common methods, including using PL/SQL collections, delimited strings, and XML parameters.
1. Using PL/SQL Collections
PL/SQL collections, such as arrays or tables, provide a structured way to pass a variable number of parameters to a function. This approach offers flexibility and type safety, as each element in the collection can have a specific data type.
Example
CREATE OR REPLACE PACKAGE pkg_concurrent_requests AS
TYPE param_array_t IS TABLE OF VARCHAR2(4000) INDEX BY PLS_INTEGER;
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_array param_array_t
) RETURN NUMBER;
END pkg_concurrent_requests;
/
CREATE OR REPLACE PACKAGE BODY pkg_concurrent_requests AS
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_array param_array_t
) RETURN NUMBER IS
l_request_id NUMBER;
BEGIN
-- Code to submit the concurrent request using FND_REQUEST.SUBMIT_REQUEST
-- Iterate through p_param_array and pass parameters to FND_REQUEST.SUBMIT_REQUEST
-- Example (replace with actual FND_REQUEST.SUBMIT_REQUEST call):
DBMS_OUTPUT.PUT_LINE('Submitting request for: ' || p_concurrent_program_name);
FOR i IN 1 .. p_param_array.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Parameter ' || i || ': ' || p_param_array(i));
END LOOP;
l_request_id := 1234; -- Placeholder request ID
RETURN l_request_id;
EXCEPTION
WHEN OTHERS THEN
-- Handle exceptions appropriately
RAISE;
END submit_concurrent_request;
END pkg_concurrent_requests;
/
-- Example usage:
DECLARE
l_param_array pkg_concurrent_requests.param_array_t;
l_request_id NUMBER;
BEGIN
l_param_array(1) := 'Value 1';
l_param_array(2) := 'Value 2';
l_param_array(3) := 'Value 3';
l_request_id := pkg_concurrent_requests.submit_concurrent_request(
'SQLGL',
'GLLEZL',
l_param_array
);
DBMS_OUTPUT.PUT_LINE('Request ID: ' || l_request_id);
END;
/
Advantages
- Type safety: Each element in the collection can have a specific data type.
- Structured approach: Collections provide a clear and organized way to pass parameters.
- Easy to iterate: PL/SQL provides built-in functions to iterate through collections.
Disadvantages
- Requires defining collection types: You need to define the collection type in the package specification.
- Can be verbose: The code can become verbose when dealing with a large number of parameters.
2. Using Delimited Strings
Another approach is to pass parameters as a delimited string. This involves concatenating the parameters into a single string, separated by a delimiter (e.g., comma, pipe).
Example
CREATE OR REPLACE PACKAGE pkg_concurrent_requests AS
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_string VARCHAR2,
p_delimiter VARCHAR2 := ','
) RETURN NUMBER;
END pkg_concurrent_requests;
/
CREATE OR REPLACE PACKAGE BODY pkg_concurrent_requests AS
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_string VARCHAR2,
p_delimiter VARCHAR2 := ','
) RETURN NUMBER IS
l_request_id NUMBER;
l_params DBMS_UTILITY.COMMA_TO_TABLE_TYPE;
l_param_count NUMBER;
BEGIN
DBMS_UTILITY.COMMA_TO_TABLE(p_param_string, l_param_count, l_params);
-- Code to submit the concurrent request using FND_REQUEST.SUBMIT_REQUEST
-- Iterate through l_params and pass parameters to FND_REQUEST.SUBMIT_REQUEST
-- Example (replace with actual FND_REQUEST.SUBMIT_REQUEST call):
DBMS_OUTPUT.PUT_LINE('Submitting request for: ' || p_concurrent_program_name);
FOR i IN 1 .. l_param_count LOOP
DBMS_OUTPUT.PUT_LINE('Parameter ' || i || ': ' || l_params(i));
END LOOP;
l_request_id := 1234; -- Placeholder request ID
RETURN l_request_id;
EXCEPTION
WHEN OTHERS THEN
-- Handle exceptions appropriately
RAISE;
END submit_concurrent_request;
END pkg_concurrent_requests;
/
-- Example usage:
DECLARE
l_request_id NUMBER;
BEGIN
l_request_id := pkg_concurrent_requests.submit_concurrent_request(
'SQLGL',
'GLLEZL',
'Value 1,Value 2,Value 3'
);
DBMS_OUTPUT.PUT_LINE('Request ID: ' || l_request_id);
END;
/
Advantages
- Simple to implement: This approach is relatively easy to implement.
- No need to define collection types: You don't need to define collection types in the package specification.
Disadvantages
- Type unsafety: All parameters are treated as strings, requiring manual type conversion.
- Error-prone: Incorrect delimiters or parameter order can lead to errors.
- Less structured: The string-based approach is less structured compared to collections.
3. Using XML Parameters
XML provides a flexible and structured way to pass parameters. This approach involves creating an XML document that contains the parameters and their values.
Example
CREATE OR REPLACE PACKAGE pkg_concurrent_requests AS
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_xml XMLType
) RETURN NUMBER;
END pkg_concurrent_requests;
/
CREATE OR REPLACE PACKAGE BODY pkg_concurrent_requests AS
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_xml XMLType
) RETURN NUMBER IS
l_request_id NUMBER;
l_param_count NUMBER;
BEGIN
-- Code to submit the concurrent request using FND_REQUEST.SUBMIT_REQUEST
-- Parse the XML and pass parameters to FND_REQUEST.SUBMIT_REQUEST
-- Example (replace with actual FND_REQUEST.SUBMIT_REQUEST call):
DBMS_OUTPUT.PUT_LINE('Submitting request for: ' || p_concurrent_program_name);
-- Example XML parsing (replace with actual XML parsing logic):
SELECT COUNT(*) INTO l_param_count FROM XMLTABLE('/parameters/parameter' PASSING p_param_xml COLUMNS name VARCHAR2(100) PATH '@name', value VARCHAR2(4000) PATH '.');
FOR i IN 1 .. l_param_count LOOP
DBMS_OUTPUT.PUT_LINE('Parameter ' || i || ': ' || p_param_xml.EXTRACT('/parameters/parameter[@name = "param' || i || '"]/text()').getStringVal());
END LOOP;
l_request_id := 1234; -- Placeholder request ID
RETURN l_request_id;
EXCEPTION
WHEN OTHERS THEN
-- Handle exceptions appropriately
RAISE;
END submit_concurrent_request;
END pkg_concurrent_requests;
/
-- Example usage:
DECLARE
l_param_xml XMLType;
l_request_id NUMBER;
BEGIN
l_param_xml := XMLType('<parameters><parameter name="param1">Value 1</parameter><parameter name="param2">Value 2</parameter><parameter name="param3">Value 3</parameter></parameters>');
l_request_id := pkg_concurrent_requests.submit_concurrent_request(
'SQLGL',
'GLLEZL',
l_param_xml
);
DBMS_OUTPUT.PUT_LINE('Request ID: ' || l_request_id);
END;
/
Advantages
- Highly structured: XML provides a well-defined structure for parameters.
- Type safety: XML allows specifying data types for parameters.
- Extensible: XML can easily accommodate additional parameters or complex data structures.
Disadvantages
- More complex to implement: XML parsing and handling can be more complex.
- Performance overhead: XML parsing can introduce some performance overhead.
Implementing the PL/SQL Function
Now, let's outline the steps involved in implementing the PL/SQL function. We will use the PL/SQL collections approach as an example.
- Define the Package Specification: Create a package specification that includes the function declaration and the collection type definition.
- Implement the Function Body: Implement the function body, which includes the logic for accepting the parameters, validating them, and submitting the concurrent request.
- Handle Data Type Conversion: Implement data type conversion logic to ensure that the parameters are passed in the correct format.
- Handle Errors: Implement robust error handling to catch exceptions and provide informative error messages.
- Test the Function: Thoroughly test the function with different concurrent programs and parameter sets.
Example Implementation (PL/SQL Collections Approach)
CREATE OR REPLACE PACKAGE pkg_concurrent_requests AS
TYPE param_array_t IS TABLE OF VARCHAR2(4000) INDEX BY PLS_INTEGER;
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_array param_array_t
) RETURN NUMBER;
-- New function to fetch parameter data types
FUNCTION get_program_parameters (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2
) RETURN SYS_REFCURSOR;
END pkg_concurrent_requests;
/
CREATE OR REPLACE PACKAGE BODY pkg_concurrent_requests AS
FUNCTION get_program_parameters (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2
) RETURN SYS_REFCURSOR IS
l_cursor SYS_REFCURSOR;
BEGIN
OPEN l_cursor FOR
SELECT
parameter_name,
description,
parameter_type,
maximum_size
FROM
fnd_conc_prog_parameters
WHERE
application_short_name = p_application_short_name
AND concurrent_program_name = p_concurrent_program_name
ORDER BY
parameter_sequence;
RETURN l_cursor;
END get_program_parameters;
FUNCTION submit_concurrent_request (
p_application_short_name VARCHAR2,
p_concurrent_program_name VARCHAR2,
p_param_array param_array_t
) RETURN NUMBER IS
l_request_id NUMBER;
l_user_id NUMBER := fnd_global.user_id;
l_resp_id NUMBER := fnd_global.resp_id;
l_resp_appl_id NUMBER := fnd_global.resp_appl_id;
l_conc_prog_id NUMBER;
l_cursor SYS_REFCURSOR;
l_parameter_name VARCHAR2(100);
l_description VARCHAR2(240);
l_parameter_type VARCHAR2(30);
l_maximum_size NUMBER;
l_param_value VARCHAR2(4000);
l_param_index PLS_INTEGER := 1;
BEGIN
-- Get the concurrent program ID
SELECT concurrent_program_id
INTO l_conc_prog_id
FROM fnd_concurrent_programs
WHERE application_short_name = p_application_short_name
AND concurrent_program_name = p_concurrent_program_name;
-- Open the cursor to fetch parameter details
l_cursor := get_program_parameters(p_application_short_name, p_concurrent_program_name);
-- Submit the concurrent request using FND_REQUEST.SUBMIT_REQUEST
l_request_id := FND_REQUEST.SUBMIT_REQUEST (
application => p_application_short_name,
program => p_concurrent_program_name,
description => 'Request submitted from PL/SQL function',
start_time => SYSDATE,
sub_request => FALSE,
argument1 => p_param_array(1),
argument2 => p_param_array(2),
argument3 => p_param_array(3),
argument4 => p_param_array(4),
argument5 => p_param_array(5),
argument6 => p_param_array(6),
argument7 => p_param_array(7),
argument8 => p_param_array(8),
argument9 => p_param_array(9),
argument10 => p_param_array(10),
argument11 => p_param_array(11),
argument12 => p_param_array(12),
argument13 => p_param_array(13),
argument14 => p_param_array(14),
argument15 => p_param_array(15),
argument16 => p_param_array(16),
argument17 => p_param_array(17),
argument18 => p_param_array(18),
argument19 => p_param_array(19),
argument20 => p_param_array(20),
argument21 => p_param_array(21),
argument22 => p_param_array(22),
argument23 => p_param_array(23),
argument24 => p_param_array(24),
argument25 => p_param_array(25),
argument26 => p_param_array(26),
argument27 => p_param_array(27),
argument28 => p_param_array(28),
argument29 => p_param_array(29),
argument30 => p_param_array(30),
-- Add more arguments as necessary up to argument100
print_output => FALSE
);
CLOSE l_cursor;
RETURN l_request_id;
EXCEPTION
WHEN OTHERS THEN
-- Handle exceptions appropriately
CLOSE l_cursor;
RAISE;
END submit_concurrent_request;
END pkg_concurrent_requests;
/
-- Example usage:
DECLARE
l_param_array pkg_concurrent_requests.param_array_t;
l_request_id NUMBER;
l_cursor SYS_REFCURSOR;
BEGIN
l_param_array(1) := 'Value 1';
l_param_array(2) := 'Value 2';
l_param_array(3) := 'Value 3';
-- Add more parameters as needed
l_request_id := pkg_concurrent_requests.submit_concurrent_request(
'SQLGL',
'GLLEZL',
l_param_array
);
DBMS_OUTPUT.PUT_LINE('Request ID: ' || l_request_id);
-- Fetch program parameters
l_cursor := pkg_concurrent_requests.get_program_parameters('SQLGL', 'GLLEZL');
-- Process cursor data here if needed
CLOSE l_cursor;
END;
/
This enhanced code provides a more robust and practical approach to submitting concurrent requests with a variable number of parameters. It includes the necessary logic to fetch parameter details and submit the request with up to 30 parameters, handling exceptions appropriately. This comprehensive example should serve as a solid foundation for building your generic PL/SQL function.
Best Practices
- Use PL/SQL Collections: PL/SQL collections provide a structured and type-safe way to pass parameters.
- Handle Data Type Conversion: Implement data type conversion logic to ensure that the parameters are passed in the correct format.
- Implement Error Handling: Implement robust error handling to catch exceptions and provide informative error messages.
- Validate Parameters: Validate the parameters before submitting the concurrent request.
- Secure the Function: Implement security measures to prevent SQL injection and other vulnerabilities.
- Document the Code: Provide clear and comprehensive documentation for the function.
Conclusion
Creating a PL/SQL function with a variable number of parameters for concurrent request submission requires careful planning and implementation. By using PL/SQL collections, delimited strings, or XML parameters, you can create a flexible and robust function that can handle different concurrent program requirements. Remember to follow best practices for error handling, security, and performance to ensure the function is reliable and efficient. This article has provided a comprehensive guide to designing and implementing such a function, enabling you to streamline your concurrent request submission process in Oracle databases.