If you ever wish to have a custom rules engine in plsql, here is one simple version of it.
- Rules are configured as simple SQL queries. These queries may use
- Bundle the rules and call it Rule Set and give an ID to it
- The caller will execute the rules engine proc by passing rule-set id as in parameter
- The rules engine proc will excute the rule queries one by one and record the result in the rule_output table
- The caller then can query the rule_output table to get the results and further understand the meaning of the result
I have used it in multiple projects
- to fire sequence of queries to introspect a domain object and signal other processes
- to generate reporting data
- to enrich a domin object
- To implement purge requirements (Ruleset triggered to Quartz scheduler)
Thought it might of some help to others.
Features
-User configures list of SQL select queries - Called rule queries.
- Rule Queries are bundled under the name RulesSet
- Caller provides the RuleSet name as input and execute the rules engine - to fire all the rules configured under the ruleset
- The rules engine prepares / parses the rule queries
Rules queries may use bind variables
- User configures bind-queries against each bind-variable, in the bind-query tables.
- Engine will execute the bind SQL queries, get the value and replace it to the Rule query to prepare the SQL Query
- Parsed SQL Queries are executed by the engine and results are collected in a rule-output tables
Controlling the execution of the rules under a RuleSet
- While the engine always executes the list of the Rules for a given RuleSet, user may wish to abort after a certain condition is met.
- In other words, the engine executes the list of Rule Queries for a given RuleSet one by one say in a while loop.
- The caller is provided with an option to control the while-loop's condition.
- The while loop's condition can be configured as another Rule-Query - breakConditionQuery rule
Passing values to the rules-engine
- A oracle global session table is used to pass values to the rule engine procedure
- Caller will insert the input parameters as key value pairs in input_param session table and then invoke the rules-engine proc in the same session
- Bind variable queries & Rule queries will use these values.
Dependency:
- Uses a database logger component similar to apache log4j, it is included as well.
CODE
/****
DDLs
****/
CREATE TABLE MY_log_properties
(
logger VARCHAR2 (200) PRIMARY KEY,
loglevel VARCHAR2 (100),
createdby VARCHAR2 (100),
createddt DATE,
updateddt DATE,
updatedby VARCHAR2 (100)
);
CREATE TABLE MY_log
(
logid NUMBER,
code VARCHAR2 (100),
msg CLOB,
logger VARCHAR2 (200),
loglevel VARCHAR2 (10),
iden1 VARCHAR2 (100),
iden2 VARCHAR2 (100),
iden3 VARCHAR2 (100),
iden4 VARCHAR2 (100),
iden5 VARCHAR2 (100),
createdby VARCHAR2 (100),
sys_timestamp TIMESTAMP
);
CREATE INDEX MY_log_logid_idx
ON MY_log (logid);
CREATE INDEX MY_log_time_idx
ON MY_log (sys_timestamp);
CREATE INDEX MY_log_iden_idx
ON MY_log (iden1,
iden2,
iden3,
iden4,
iden5);
CREATE SEQUENCE MY_log_seq
MINVALUE 1
MAXVALUE 999999999999999
CYCLE;
--------------------------
CREATE TABLE MY_RULE
(
ID NUMBER NOT NULL,
APPID VARCHAR2 (300 BYTE) NOT NULL,
RULE_ID VARCHAR2 (100 BYTE),
RULESET_ID VARCHAR2 (100 BYTE) NOT NULL,
RULE_QUERY CLOB NOT NULL,
UPDDT TIMESTAMP (6) DEFAULT SYSTIMESTAMP
);
ALTER TABLE MY_RULE ADD (
PRIMARY KEY
(RULE_ID));
CREATE TABLE MY_RULE_BINDVARIABLES
(
APPID VARCHAR2 (300 BYTE) NOT NULL,
VARIABLENAME VARCHAR2 (64 BYTE),
BIND_QUERY CLOB,
INC_TYPE VARCHAR2 (2 BYTE) DEFAULT 'EQ' NOT NULL,
CACHE_WITHIN_EXEC VARCHAR2 (1 BYTE) DEFAULT 'Y' NOT NULL,
UPDDT TIMESTAMP (6) DEFAULT SYSTIMESTAMP
);
ALTER TABLE MY_RULE_BINDVARIABLES ADD (
PRIMARY KEY
(VARIABLENAME));
CREATE TABLE MY_RULE_EXE_CONFIG
(
ID NUMBER,
APPID VARCHAR2 (300 BYTE) NOT NULL,
RULESET_ID VARCHAR2 (100 BYTE) NOT NULL,
BREAK_CONDN_BEF_AFT VARCHAR2 (3 BYTE) DEFAULT 'BEF',
BREAKING_RULEID VARCHAR2 (100 BYTE) NOT NULL,
UPDDT TIMESTAMP (6) DEFAULT SYSTIMESTAMP
);
ALTER TABLE MY_RULE_EXE_CONFIG ADD (
PRIMARY KEY
(ID));
CREATE GLOBAL TEMPORARY TABLE MY_RE_INPUT_PARAM
(
EXEID VARCHAR2 (500),
KEY VARCHAR2 (500),
VALUE VARCHAR2 (500)
)
ON COMMIT DELETE ROWS;
CREATE TABLE MY_RULE_OUTPUT
(
APPID VARCHAR2 (300 BYTE) NOT NULL,
OUTPUTID VARCHAR2 (300 BYTE),
RULE_ID VARCHAR2 (100 BYTE),
RULESET_ID VARCHAR2 (100 BYTE),
EXEID VARCHAR2 (200 BYTE),
RECID NUMBER (19),
COLIDX NUMBER (10),
COLNAME VARCHAR2 (100 BYTE),
COLTYPE VARCHAR2 (4000 BYTE),
COLVAL VARCHAR2 (4000 BYTE),
COLVAL_DT DATE,
COLVAL_TS TIMESTAMP (6),
COLVAL_CL CLOB,
UPDDT TIMESTAMP (6) DEFAULT SYSTIMESTAMP
);
CREATE INDEX MY_RULE_OUTPUT_IDX1
ON MY_RULE_OUTPUT (APPID, RULESET_ID, EXEID);
CREATE INDEX MY_RULE_OUTPUT_IDX2
ON MY_RULE_OUTPUT (APPID, RULE_ID, EXEID);
CREATE UNIQUE INDEX MY_RULE_OUTPUT_PK
ON MY_RULE_OUTPUT (OUTPUTID, COLIDX);
ALTER TABLE MY_RULE_OUTPUT ADD (
CONSTRAINT MY_RULE_OUTPUT_PK
PRIMARY KEY
(OUTPUTID, COLIDX)
USING INDEX MY_RULE_OUTPUT_PK
ENABLE VALIDATE);
CREATE SEQUENCE MY_RULE_OUTPUT_SEQ
START WITH 81
MAXVALUE 99999999999999
MINVALUE 1
CYCLE
CACHE 20
NOORDER;
CREATE OR REPLACE TYPE RowData IS TABLE OF VARCHAR2 (4000);
CREATE OR REPLACE TYPE ResultSet IS TABLE OF RowData;
-----------------------------------------------------
CREATE OR REPLACE PACKAGE MY_logger
AS
PROCEDURE LOG (pLogger MY_log.logger%TYPE,
pLogLevel MY_log.loglevel%TYPE,
pCode MY_log.code%TYPE,
pMsg MY_log.msg%TYPE,
pIden1 MY_log.iden1%TYPE DEFAULT NULL,
pIden2 MY_log.iden2%TYPE DEFAULT NULL,
pIden3 MY_log.iden3%TYPE DEFAULT NULL,
pIden4 MY_log.iden4%TYPE DEFAULT NULL,
pIden5 MY_log.iden5%TYPE DEFAULT NULL);
gv_logging_status VARCHAR2 (100);
END MY_logger;
/
/********************************************************************************************
* INPUT : pLogger --type of logger
* pLogLevel -- log level,
* pCode -- error code Passed,
* pMsg -- Error Message Passed
* pIden1 --Identifier 1
* pIden2 --Identifier 2
* pIden3 --Identifier 3
* pIden4 --Identifier 4
* pIden5 --Identifier 5
----------------------------------------------------
* Description :IF logging status ("*" in MY_LOG_PROPERTIES table is OFF then return )
*
* Based On Logging level set in MY_log_properties , logging will be saved
* If the Passed Level is less than the level of DB , then logs will not be
* Stored. (If logging status not matched or passed null thenby default
* logs will be saved)
**********************************************************************************************/
CREATE OR REPLACE PACKAGE BODY MY_logger
AS
PROCEDURE LOG (pLogger MY_log.logger%TYPE,
pLogLevel MY_log.loglevel%TYPE,
pCode MY_log.code%TYPE,
pMsg MY_log.msg%TYPE,
pIden1 MY_log.iden1%TYPE DEFAULT NULL,
pIden2 MY_log.iden2%TYPE DEFAULT NULL,
pIden3 MY_log.iden3%TYPE DEFAULT NULL,
pIden4 MY_log.iden4%TYPE DEFAULT NULL,
pIden5 MY_log.iden5%TYPE DEFAULT NULL)
AS
PRAGMA AUTONOMOUS_TRANSACTION;
lnpLogLevel NUMBER := 0;
lnDBLogLevel NUMBER := 0;
generated_logid MY_log.logid%TYPE;
lDBLogLevel MY_log.loglevel%TYPE := 'ERR';
npLogLevel NUMBER := 0;
nDBLogLevel NUMBER := 0;
BEGIN
BEGIN
SELECT UPPER (LOGLEVEL)
INTO gv_logging_status
FROM MY_log_properties
WHERE logger = '*' AND ROWNUM < 2;
-- Returning the call from procedure , logging status is OFF
IF gv_logging_status = 'OFF'
THEN
RETURN;
END IF;
EXCEPTION
WHEN OTHERS
THEN
gv_logging_status := 'OFF';
END;
-- Checking the DB Log Level for the input logger
BEGIN
SELECT LOGLEVEL
INTO lDBLogLevel
FROM MY_log_properties
WHERE UPPER (pLogger) = UPPER (logger) AND ROWNUM < 2;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
BEGIN
-- IF exact match is not found then checking for the wild card search based on the maximum defined log level
SELECT loglevel
INTO lDBLogLevel
FROM ( SELECT DISTINCT LOGLEVEL, LENGTH (logger), ROWNUM rn
FROM MY_log_properties
WHERE UPPER (pLogger) LIKE (UPPER (logger) || '%')
ORDER BY LENGTH (logger) DESC)
WHERE rn = 1;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
lDBLogLevel := 'ERR';
WHEN OTHERS
THEN
-- when any error in Query , raise the error back to environment
RAISE;
END;
END;
-- Making the Level for passed logger
SELECT DECODE (pLogLevel,
'ON', 2,
'ERR', 2,
'WAR', 1,
'DEB', 0,
-1)
INTO lnpLogLevel
FROM DUAL;
-- Fetching the DB Level for passed logger
SELECT DECODE (lDBLogLevel, 'ERR', 2, 'WAR', 1, 'DEB', 0, 2, -1)
INTO lnDBLogLevel
FROM DUAL;
IF gv_logging_status = 'ON' AND (lnpLogLevel = -1 OR lnDBLogLevel = -1)
THEN
-- Overriding the LOG logic , if logging status is ON and logging indicators are not passed then
-- based on this flag logging will be done
lnpLogLevel := 2;
lnDBLogLevel := 2;
END IF;
IF lnDBLogLevel <= lnpLogLevel
THEN
-- creating the ID for LOGS
SELECT LPAD (MY_log_seq.NEXTVAL, 5, 0)
INTO generated_logid
FROM DUAL;
-- If all validations passes then we have to insert into the log table
INSERT INTO PMTS_PSH_OWNER.MY_LOG (logger,
loglevel,
logid,
code,
msg,
iden1,
iden2,
iden3,
iden4,
iden5,
sys_timestamp)
VALUES (pLogger,
ploglevel,
generated_logid,
pCode,
pMsg,
pIden1,
pIden2,
pIden3,
pIden4,
pIden5,
CURRENT_TIMESTAMP);
END IF;
COMMIT;
END LOG;
END MY_logger;
/
------------------------------------------------------------------------------
create or replace
PACKAGE "MY_RULESENGINE"
AS
TYPE BindQueryCacheType IS TABLE OF CLOB
INDEX BY VARCHAR2 (100);
aLOGID MY_LOG.logid%TYPE;
PROCEDURE pub_fireRules (pAppId MY_RULE.APPID%TYPE,
pRuleSetId MY_RULE.RULESET_ID%TYPE,
pExecId MY_RULE_OUTPUT.EXEID%TYPE);
FUNCTION parseQuery (pAppId MY_RULE.APPID%TYPE,
pQuery CLOB,
pExecId MY_RULE_OUTPUT.EXEID%TYPE)
RETURN CLOB;
FUNCTION exeRuleBindQuery (pAppId MY_RULE.APPID%TYPE,
pVariableName VARCHAR2,
pExecId MY_RULE_OUTPUT.EXEID%TYPE)
RETURN CLOB;
FUNCTION exeAnyQuery (pQuery CLOB)
RETURN ResultSet
PIPELINED;
FUNCTION getRuleBindQueryResult (pQuery CLOB, incType VARCHAR2)
RETURN CLOB;
PROCEDURE getColumnDesc (pQuery CLOB,
oColCnt IN OUT NUMBER,
oDescQry IN OUT NOCOPY DBMS_SQL.DESC_TAB);
PROCEDURE exeRuleQryAndGenOutput (
pAppId MY_RULE.APPID%TYPE,
exeId MY_RULE_OUTPUT.EXEID%TYPE,
ruleId MY_RULE_OUTPUT.RULE_ID%TYPE,
ruleSetId MY_RULE_OUTPUT.RULESET_ID%TYPE,
parsedRuleQry CLOB);
FUNCTION shouldBreak (pAppId MY_RULE.APPID%TYPE,
pQuery CLOB,
pExecId MY_RULE_OUTPUT.EXEID%TYPE)
RETURN BOOLEAN;
FUNCTION isSelectQuery (pParsedQuery CLOB)
RETURN BOOLEAN;
PROCEDURE exeAnyNonSelectQuery (
pAppId MY_RULE.APPID%TYPE,
exeId MY_RULE_OUTPUT.EXEID%TYPE,
ruleId MY_RULE_OUTPUT.RULE_ID%TYPE,
ruleSetId MY_RULE_OUTPUT.RULESET_ID%TYPE,
parsedRuleQry CLOB);
END MY_RULESENGINE;
/**
BODY
****/
create or replace
PACKAGE BODY MY_RULESENGINE
AS
aBindQryCache BindQueryCacheType;
/***************************************************************************
* TYPE : PROCEDURE
* PURPOSE : Gateway Proc to Fire Rules
* INPUT : pAppId <ApplicationID>
* : pRuleSetId <RuleID>
* : pExecId
* PROCESS : This Proc Will be called directly from the mapper to fire the
* Rules , process is as below
* 1: Check Breaking Condition for APPLICATION and RULE SETID
* 2: Fetch The Breaking Query from rule table
* 3: Check the Breaking Condition AFTER or BEFORE
*****************************************************************************/
PROCEDURE pub_fireRules (pAppId MY_RULE.APPID%TYPE,
pRuleSetId MY_RULE.RULESET_ID%TYPE,
pExecId MY_RULE_OUTPUT.EXEID%TYPE)
AS
CURSOR rules_sql
IS
SELECT rule_query, rule_id, ruleset_id
FROM MY_RULE
WHERE appid = pAppId
AND ruleset_id = pRulesetId
AND rule_id NOT IN
(SELECT BREAKING_RULEID
FROM MY_RULE_EXE_CONFIG
WHERE appid = pAppId AND ruleset_id = pRulesetId)
ORDER BY id;
currParsedRuleQry CLOB := NULL;
breakConditionQry CLOB := NULL;
whenToBreak MY_RULE_EXE_CONFIG.BREAK_CONDN_BEF_AFT%TYPE;
chkBreakCondition VARCHAR (1) := 'N';
BEGIN
SELECT MY_LOG_SEQ.NEXTVAL INTO aLOGID FROM DUAL;
MY_LOGGER.LOG ('MY_RULESENGINE.pub_fireRules',
'DEB',
'0',
'ENTER pub_fireRules',
pExecId,
pRuleSetId,
NULL);
FOR i IN (SELECT BREAK_CONDN_BEF_AFT, BREAKING_RULEID
FROM MY_RULE_EXE_CONFIG
WHERE appid = pAppId AND ruleset_id = pRulesetId)
LOOP
-- Bypassing the BreakingRule Condition if conditionid =0
IF (i.BREAK_CONDN_BEF_AFT IS NOT NULL AND i.BREAKING_RULEID <> 0)
THEN
SELECT rule_query
INTO breakConditionQry
FROM MY_RULE
WHERE appid = pAppId
AND ruleset_id = pRulesetId
AND rule_id = i.BREAKING_RULEID;
chkBreakCondition := 'Y';
END IF;
whenToBreak := i.BREAK_CONDN_BEF_AFT;
MY_LOGGER.LOG (
'MY_RULESENGINE.pub_fireRules',
'DEB',
'0',
'BreakingCondition:'
|| chkBreakCondition
|| '-'
|| whenToBreak
|| '-',
pExecId,
pRuleSetId,
NULL,
aLOGID,
'Normal');
END LOOP;
aBindQryCache.delete;
FOR ruleQueries IN rules_sql
LOOP
IF ( (chkBreakCondition = 'Y') AND (whenToBreak = 'BEF'))
THEN
IF shouldBreak (pAppId,
parseQuery (pAppId, breakConditionQry, pExecId),
pExecId)
THEN
aBindQryCache.delete;
MY_LOGGER.LOG ('MY_RULESENGINE.pub_fireRules',
'DEB',
'0',
'EXIT pub_fireRules (breaking-BEF)',
pExecId,
pRuleSetId,
ruleQueries.rule_id,
aLOGID,
'Normal');
EXIT;
END IF;
END IF;
currParsedRuleQry :=
parseQuery (pAppId, ruleQueries.rule_query, pExecId);
MY_LOGGER.LOG ('MY_RULESENGINE.pub_fireRules',
'DEB',
'0',
'CurrentParsedRuleQuery:' || currParsedRuleQry,
pExecId,
pRuleSetId,
ruleQueries.rule_id,
aLOGID,
'Normal');
IF (isSelectQuery (currParsedRuleQry))
THEN
exeRuleQryAndGenOutput (pAppId,
pExecId,
ruleQueries.rule_id,
pRuleSetId,
currParsedRuleQry);
ELSE
exeAnyNonSelectQuery (pAppId,
pExecId,
ruleQueries.rule_id,
pRuleSetId,
currParsedRuleQry);
END IF;
MY_LOGGER.LOG (
'MY_RULESENGINE.pub_fireRules',
'DEB',
'0',
'exeRuleQryAndGenOutput - Completed for ' || ruleQueries.rule_id,
pExecId,
pRuleSetId,
ruleQueries.rule_id,
aLOGID,
'Normal');
IF ( (chkBreakCondition = 'Y') AND (whenToBreak = 'AFT'))
THEN
IF shouldBreak (pAppId,
parseQuery (pAppId, breakConditionQry, pExecId),
pExecId)
THEN
MY_LOGGER.LOG ('MY_RULESENGINE.pub_fireRules',
'DEB',
'0',
'EXIT pub_fireRules (breaking-AFT)',
pExecId,
pRuleSetId,
ruleQueries.rule_id,
aLOGID,
'Normal');
EXIT;
END IF;
END IF;
END LOOP;
aBindQryCache.delete;
MY_LOGGER.LOG ('MY_RULESENGINE.pub_fireRules',
'DEB',
'0',
'EXIT pub_fireRules',
pExecId,
pRuleSetId,
NULL,
aLOGID,
'Normal');
END pub_fireRules;
/***************************************************************************
* TYPE : FUNCTION
* PURPOSE : Create a Parsed Query
* INPUT : pAppId <ApplicationID>
* : pQuery <Query String>
* : pExecId
* PROCESS : This Proc Will be called to create the Query
* Setting of parameter will be done and query will be properly
* Formed <Query will be picked with a bind variable in it>
*****************************************************************************/
FUNCTION parseQuery (pAppId MY_RULE.APPID%TYPE,
pQuery CLOB,
pExecId MY_RULE_OUTPUT.EXEID%TYPE)
RETURN CLOB
AS
parsedQuery CLOB := pQuery;
vPattern VARCHAR2 (10) := '\$[^\$]+\$';
vVariableName VARCHAR2 (65) := NULL;
i NUMBER := 0;
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :parseQuery');
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:pAppId=' || pAppId);
--DBMS_OUTPUT.PUT_LINE ('Parameter 2:pQuery=' || pQuery);
--DBMS_OUTPUT.PUT_LINE ('Parameter 3:pExecId=' || pExecId);
-- Parse all the variables in the rule query
vVariableName :=
REGEXP_SUBSTR (parsedQuery,
vPattern,
1,
1,
'm');
WHILE ( (LENGTH (vVariableName) > 0) AND (vVariableName IS NOT NULL))
LOOP
IF (vVariableName IS NOT NULL)
THEN
IF (vVariableName = '$p.pExecId$')
THEN
parsedQuery :=
REGEXP_REPLACE (parsedQuery,
vPattern,
pExecId,
1,
1,
'm');
ELSIF (vVariableName LIKE '$v.%')
THEN
parsedQuery :=
REGEXP_REPLACE (
parsedQuery,
vPattern,
exeRuleBindQuery (pAppId, vVariableName, pExecId),
1,
1,
'm');
ELSIF (vVariableName LIKE '$p.%')
THEN
MY_LOGGER.LOG (
'MY_RULESENGINE.parseQuery',
'ERR',
'-20501',
'RE-Variable Name '
|| vVariableName
|| ' is not supported in the query('
|| pQuery
|| ') ExecId('
|| pExecId
|| ')',
pExecId,
aLOGID,
NULL,
NULL,
'Error');
raise_application_error (
-20501,
'RE-Variable Name '
|| vVariableName
|| ' is not supported in the query('
|| pQuery
|| ') ExecId('
|| pExecId
|| ')',
TRUE);
ELSE
MY_LOGGER.LOG (
'MY_RULESENGINE.parseQuery',
'ERR',
'-20500',
'RE-Variable Name '
|| vVariableName
|| ' is not supported in the query('
|| pQuery
|| ') ExecId('
|| pExecId
|| ')',
pExecId,
aLOGID,
NULL,
NULL,
'Error');
raise_application_error (
-20500,
'RE-Variable Name '
|| vVariableName
|| ' is not supported in the query('
|| pQuery
|| ') ExecId('
|| pExecId
|| ')',
TRUE);
END IF;
END IF;
vVariableName :=
REGEXP_SUBSTR (parsedQuery,
vPattern,
1,
1,
'm');
END LOOP;
RETURN parsedQuery;
END parseQuery;
/**********************************************************************************************
* TYPE : FUNCTION
*
* PURPOSE : Create a Query with Bind Variables
*
* INPUT : pAppId <ApplicationID>
* : pVariableName <Variable Names>
* : pExecId
*
* PROCESS : This Proc Will be called to create Bind Variable Query
* and return the output for that BIND variable to the calling
* Process <Bind Variable needs to be configured in MY_rule_bindvariables table>
*
**********************************************************************************************/
FUNCTION exeRuleBindQuery (pAppId MY_RULE.APPID%TYPE,
pVariableName VARCHAR2,
pExecId MY_RULE_OUTPUT.EXEID%TYPE)
RETURN CLOB
AS
vRuleBindQuery CLOB := NULL;
vPattern VARCHAR2 (10) := '\$[^\$]+\$';
localVariableName VARCHAR2 (65) := NULL;
incType VARCHAR2 (2) := 'IN';
cache MY_RULE_BINDVARIABLES.CACHE_WITHIN_EXEC%TYPE;
cResult CLOB := NULL;
BEGIN
BEGIN
-- Return the result if already cached
IF aBindQryCache.EXISTS (pVariableName)
THEN
cResult := aBindQryCache (pVariableName);
MY_LOGGER.LOG ('MY_RULESENGINE.exeRuleBindQuery',
'DEB',
'0',
'Result from CACHE:' || cResult,
pExecId,
NULL,
NULL,
aLOGID,
'Normal');
RETURN cResult;
END IF;
SELECT BIND_QUERY, INC_TYPE, CACHE_WITHIN_EXEC
INTO vRuleBindQuery, incType, cache
FROM MY_RULE_BINDVARIABLES
WHERE variablename = pVariableName AND appid = pAppId;
localVariableName :=
REGEXP_SUBSTR (vRuleBindQuery,
vPattern,
1,
1,
'm');
MY_LOGGER.LOG (
'MY_RULESENGINE.exeRuleBindQuery',
'DEB',
'0',
'Unparsed SQL/Inc_type:' || vRuleBindQuery || '/' || incType,
pExecId,
NULL,
NULL,
aLOGID,
'Normal');
IF ( (LENGTH (localVariableName) > 0)
AND (localVariableName IS NOT NULL))
THEN
vRuleBindQuery := parseQuery (pAppId, vRuleBindQuery, pExecId);
MY_LOGGER.LOG (
'MY_RULESENGINE.exeRuleBindQuery',
'DEB',
'0',
'Parsed SQL/Inc_type:' || vRuleBindQuery || '/' || incType,
pExecId,
NULL,
NULL,
aLOGID,
'Normal');
END IF;
cResult := getRuleBindQueryResult (vRuleBindQuery, incType);
-- Add to cache
IF (UPPER (cache) = 'Y')
THEN
aBindQryCache (pVariableName) := cResult;
END IF;
MY_LOGGER.LOG ('MY_RULESENGINE.exeRuleBindQuery',
'DEB',
'0',
'Result:' || cResult,
pExecId,
NULL,
NULL,
aLOGID,
'Normal');
EXCEPTION
WHEN NO_DATA_FOUND
THEN
MY_LOGGER.LOG (
'MY_RULESENGINE.exeRuleBindQuery',
'ERR',
SQLCODE,
'RE- Rule Bind query for the Variable Name '
|| pVariableName
|| ' is not found for ExecId('
|| pExecId
|| ')',
pExecId,
'-20502',
aLOGID,
'Error');
raise_application_error (
-20502,
'RE- Rule Bind query for the Variable Name '
|| pVariableName
|| ' is not found for ExecId('
|| pExecId
|| ')',
TRUE);
END;
RETURN cResult;
END exeRuleBindQuery;
/***************************************************************************
* TYPE : FUNCTION
* PURPOSE : Create a Query with Bind Variables
* INPUT : pAppId <ApplicationID>
* : pVariableName <Variable Names>
* : pExecId
* PROCESS : This Proc Will be called to create the Query
* Setting of parameter will be done and query will be properly
* Formed
*****************************************************************************/
FUNCTION getRuleBindQueryResult (pQuery CLOB, incType VARCHAR2)
RETURN CLOB
AS
colCount NUMBER := 0;
ctxQryResult CLOB := NULL;
CURSOR c1
IS
SELECT * FROM TABLE (exeAnyQuery (pQuery));
currRow RowData;
currRowStr CLOB;
recordFound BOOLEAN := FALSE;
desctab DBMS_SQL.DESC_TAB;
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :getRuleBindQueryResult');
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:pQuery=' || pQuery);
--DBMS_OUTPUT.PUT_LINE ('Parameter 2:incType=' || incType);
IF incType = 'IN'
THEN
OPEN c1;
LOOP
FETCH c1 INTO currRow;
EXIT WHEN c1%NOTFOUND;
colCount := currRow.COUNT;
currRowStr := NULL;
recordFound := TRUE;
FOR i IN 1 .. currRow.COUNT
LOOP
currRowStr :=
currRowStr
|| ''''
|| REPLACE (currRow (i), '''', '''''')
|| '''';
IF (i < currRow.COUNT)
THEN
currRowStr := currRowStr || ',';
END IF;
END LOOP;
IF (colCount > 1)
THEN
currRowStr := '(' || currRowStr || ')';
END IF;
ctxQryResult := ctxQryResult || currRowStr || ',';
END LOOP;
CLOSE c1;
-- Remove the extra ,
ctxQryResult := SUBSTR (ctxQryResult, 0, LENGTH (ctxQryResult) - 1);
IF (NOT recordFound)
THEN
getColumnDesc (pQuery, colCount, desctab);
currRowStr := NULL;
FOR i IN 1 .. colCount
LOOP
currRowStr := currRowStr || '''' || '''';
IF (i < currRow.COUNT)
THEN
currRowStr := currRowStr || ',';
END IF;
END LOOP;
IF (colCount > 1)
THEN
currRowStr := '(' || currRowStr || ')';
END IF;
ctxQryResult := ctxQryResult || currRowStr || ',';
END IF;
ELSIF incType = 'EQ'
THEN
BEGIN
SELECT * INTO currRow FROM TABLE (exeAnyQuery (pQuery));
ctxQryResult :=
'''' || REPLACE (currRow (1), '''', '''''') || '''';
EXCEPTION
WHEN NO_DATA_FOUND
THEN
ctxQryResult := '''''';
WHEN OTHERS
THEN
MY_LOGGER.LOG ('MY_RULESENGINE.getRuleBindQueryResult',
'ERR',
SQLCODE,
SQLERRM,
'-',
aLOGID,
'Error');
RAISE;
END;
END IF;
RETURN ctxQryResult;
EXCEPTION
WHEN OTHERS
THEN
IF c1%ISOPEN
THEN
CLOSE c1;
END IF;
MY_LOGGER.LOG ('MY_RULESENGINE.getRuleBindQueryResult',
'ERR',
SQLCODE,
SQLERRM,
'-',
aLOGID,
'Error');
RAISE;
END getRuleBindQueryResult;
/***************************************************************************
* TYPE : FUNCTION
* PURPOSE : upper level to execute the Query
* INPUT : pAppId <ApplicationID>
* : pVariableName <Variable Names>
* : pExecId
* PROCESS : This Proc Will be called to create the Query
* Setting of parameter will be done and query will be properly
* Formed
*****************************************************************************/
FUNCTION exeAnyQuery (pQuery CLOB)
RETURN ResultSet
PIPELINED
AS
currRow RowData := NULL;
v_cur_hdl INT;
ret NUMBER;
desctab DBMS_SQL.DESC_TAB;
colcnt NUMBER;
refDate DATE;
refNum NUMBER;
refVarchar VARCHAR2 (4000);
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :exeAnyQuery');
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:pQuery=' || pQuery);
v_cur_hdl := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE (v_cur_hdl, pQuery, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS (v_cur_hdl, colcnt, desctab);
FOR i IN 1 .. colcnt
LOOP
IF desctab (i).col_type = DBMS_TYPES.NO_DATA
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl,
i,
refVarchar,
4000);
ELSIF desctab (i).col_type IN
(181,
DBMS_TYPES.TYPECODE_DATE,
DBMS_TYPES.TYPECODE_TIMESTAMP,
DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ,
DBMS_TYPES.TYPECODE_TIMESTAMP_TZ)
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl, i, refDate);
ELSIF desctab (i).col_type = DBMS_TYPES.TYPECODE_NUMBER
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl, i, refNum);
ELSE
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl,
i,
refVarchar,
4000);
END IF;
END LOOP;
ret := DBMS_SQL.EXECUTE (v_cur_hdl);
LOOP
IF DBMS_SQL.FETCH_ROWS (v_cur_hdl) > 0
THEN
currRow := NEW RowData ();
FOR i IN 1 .. colcnt
LOOP
IF desctab (i).col_type = DBMS_TYPES.NO_DATA
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refVarchar);
currRow.EXTEND;
currRow (i) := refVarchar;
ELSIF desctab (i).col_type IN
(181,
DBMS_TYPES.TYPECODE_DATE,
DBMS_TYPES.TYPECODE_TIMESTAMP,
DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ,
DBMS_TYPES.TYPECODE_TIMESTAMP_TZ)
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refDate);
currRow.EXTEND;
currRow (i) :=
TO_CHAR (refDate, 'DD-MON-YYYY HH12.MI.SS AM');
ELSIF desctab (i).col_type = DBMS_TYPES.TYPECODE_NUMBER
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refNum);
currRow.EXTEND;
currRow (i) := TO_CHAR (refNum);
ELSE
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refVarchar);
currRow.EXTEND;
currRow (i) := refVarchar;
END IF;
END LOOP;
PIPE ROW (currRow);
ELSE
EXIT;
END IF;
END LOOP;
DBMS_SQL.CLOSE_CURSOR (v_cur_hdl);
EXCEPTION
WHEN OTHERS
THEN
IF DBMS_SQL.IS_OPEN (v_cur_hdl)
THEN
DBMS_SQL.CLOSE_CURSOR (v_cur_hdl);
END IF;
MY_LOGGER.LOG ('MY_RULESENGINE.exeAnyQuery',
'ERR',
SQLCODE,
SQLERRM,
'-',
aLOGID,
'Error');
RAISE;
END exeAnyQuery;
PROCEDURE getColumnDesc (pQuery CLOB,
oColCnt IN OUT NUMBER,
oDescQry IN OUT NOCOPY DBMS_SQL.DESC_TAB)
AS
v_cur_hdl INT;
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :getColumnDesc ');
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:pQuery=' || pQuery);
--DBMS_OUTPUT.PUT_LINE ('Parameter 2:oColCnt=' || oColCnt);
----DBMS_OUTPUT.PUT_LINE ('Parameter 3:oDescQry=' || oDescQry);
v_cur_hdl := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE (v_cur_hdl, pQuery, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS (v_cur_hdl, oColCnt, oDescQry);
DBMS_SQL.CLOSE_CURSOR (v_cur_hdl);
EXCEPTION
WHEN OTHERS
THEN
IF DBMS_SQL.IS_OPEN (v_cur_hdl)
THEN
DBMS_SQL.CLOSE_CURSOR (v_cur_hdl);
END IF;
MY_LOGGER.LOG ('MY_RULESENGINE.getColumnDesc',
'ERR',
SQLCODE,
'Exception in getColumnDesc Query : ' || pQuery,
'',
aLOGID,
'Exception');
MY_LOGGER.LOG ('MY_RULESENGINE.getColumnDesc',
'ERR',
SQLCODE,
'Exception in getColumnDesc Error : ' || SQLERRM,
'',
aLOGID,
'Exception');
RAISE;
END getColumnDesc;
PROCEDURE exeRuleQryAndGenOutput (
pAppId MY_RULE.APPID%TYPE,
exeId MY_RULE_OUTPUT.EXEID%TYPE,
ruleId MY_RULE_OUTPUT.RULE_ID%TYPE,
ruleSetId MY_RULE_OUTPUT.RULESET_ID%TYPE,
parsedRuleQry CLOB)
AS
v_cur_hdl INT;
ret NUMBER;
desctab DBMS_SQL.DESC_TAB;
colcnt NUMBER;
refDate DATE;
refTimeStamp TIMESTAMP;
refVarchar VARCHAR2 (4000);
refClob CLOB;
refNum NUMBER;
outputRow MY_RULE_OUTPUT%ROWTYPE;
rowCount NUMBER := 1;
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :exeRuleQryAndGenOutput');
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:pAppId=' || pAppId);
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:exeId=' || exeId);
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:ruleId=' || ruleId);
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:ruleSetId=' || ruleSetId);
--DBMS_OUTPUT.PUT_LINE ('Parameter 1:parsedRuleQry=' || parsedRuleQry);
v_cur_hdl := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE (v_cur_hdl, parsedRuleQry, DBMS_SQL.NATIVE);
DBMS_SQL.DESCRIBE_COLUMNS (v_cur_hdl, colcnt, desctab);
FOR i IN 1 .. colcnt
LOOP
IF desctab (i).col_type = DBMS_TYPES.NO_DATA
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl,
i,
refVarchar,
4000);
ELSIF desctab (i).col_type IN (DBMS_TYPES.TYPECODE_DATE)
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl, i, refDate);
ELSIF desctab (i).col_type IN
(181,
DBMS_TYPES.TYPECODE_TIMESTAMP,
DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ,
DBMS_TYPES.TYPECODE_TIMESTAMP_TZ)
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl, i, refTimeStamp);
ELSIF desctab (i).col_type = DBMS_TYPES.TYPECODE_NUMBER
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl, i, refNum);
ELSIF desctab (i).col_type = DBMS_TYPES.TYPECODE_CLOB
THEN
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl, i, refClob);
ELSE
DBMS_SQL.DEFINE_COLUMN (v_cur_hdl,
i,
refVarchar,
4000);
END IF;
END LOOP;
ret := DBMS_SQL.EXECUTE (v_cur_hdl);
LOOP
IF DBMS_SQL.FETCH_ROWS (v_cur_hdl) > 0
THEN
SELECT MY_RULE_OUTPUT_SEQ.NEXTVAL
INTO outputRow.outputid
FROM DUAL;
outputRow.appid := pAppId;
outputRow.rule_id := ruleId;
outputRow.RULESET_ID := ruleSetId;
outputRow.exeid := exeId;
outputRow.recid := rowCount;
FOR i IN 1 .. colcnt
LOOP
outputRow.colIdx := i;
outputRow.colName := desctab (i).col_name;
outputRow.colType := TO_CHAR (desctab (i).col_type);
outputRow.colval := NULL;
outputRow.colval_dt := NULL;
outputRow.colval_ts := NULL;
outputRow.colval_cl := NULL;
IF desctab (i).col_type = DBMS_TYPES.NO_DATA
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refVarchar);
outputRow.COLVAL := NULL;
ELSIF desctab (i).col_type IN (DBMS_TYPES.TYPECODE_DATE)
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refDate);
outputRow.COLVAL :=
TO_CHAR (refDate, 'DD-MON-YYYY HH12.MI.SS AM');
outputRow.COLVAL_DT := refDate;
ELSIF desctab (i).col_type IN
(181,
DBMS_TYPES.TYPECODE_TIMESTAMP,
DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ,
DBMS_TYPES.TYPECODE_TIMESTAMP_TZ)
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refTimeStamp);
outputRow.COLVAL :=
TO_CHAR (refTimeStamp, 'DD-MON-YYYY HH12.MI.SS AM');
outputRow.COLVAL_TS := refTimeStamp;
ELSIF desctab (i).col_type = DBMS_TYPES.TYPECODE_NUMBER
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refNum);
outputRow.COLVAL := TO_CHAR (refNum);
ELSIF desctab (i).col_type = DBMS_TYPES.TYPECODE_CLOB
THEN
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refClob);
outputRow.COLVAL := TO_CHAR (refClob);
outputRow.COLVAL_CL := refClob;
ELSE
DBMS_SQL.COLUMN_VALUE (v_cur_hdl, i, refVarchar);
outputRow.COLVAL := refVarchar;
END IF;
INSERT INTO MY_RULE_OUTPUT
VALUES outputRow;
END LOOP;
ELSE
EXIT;
END IF;
rowCount := rowCount + 1;
END LOOP;
DBMS_SQL.CLOSE_CURSOR (v_cur_hdl);
EXCEPTION
WHEN OTHERS
THEN
IF DBMS_SQL.IS_OPEN (v_cur_hdl)
THEN
DBMS_SQL.CLOSE_CURSOR (v_cur_hdl);
END IF;
MY_LOGGER.LOG ('MY_RULESENGINE.exeRuleQryAndGenOutput',
'ERR',
SQLCODE,
SQLERRM,
'-',
aLOGID,
'Error');
RAISE;
END exeRuleQryAndGenOutput;
FUNCTION shouldBreak (pAppId MY_RULE.APPID%TYPE,
pQuery CLOB,
pExecId MY_RULE_OUTPUT.EXEID%TYPE)
RETURN BOOLEAN
AS
currParsedRuleQry CLOB := NULL;
currRow RowData;
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :shouldBreak');
MY_LOGGER.LOG ('MY_RULESENGINE.shouldBreak',
'DEB',
'0',
'Executing breakingConditionQuery:' || pQuery,
'ExecId',
pExecId,
aLOGID,
'Normal');
currParsedRuleQry := parseQuery (pAppId, pQuery, pExecId);
BEGIN
SELECT * INTO currRow FROM TABLE (exeAnyQuery (currParsedRuleQry));
IF (UPPER (TRIM (currRow (1))) IN ('Y', 'YES', 'T', 'TRUE', '1'))
THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
RETURN FALSE;
WHEN OTHERS
THEN
MY_LOGGER.LOG ('MY_RULESENGINE.shouldBreak',
'ERR',
SQLCODE,
SQLERRM,
pExecId,
aLOGID,
'Error');
RAISE;
END;
END shouldBreak;
FUNCTION isSelectQuery (pParsedQuery CLOB)
RETURN BOOLEAN
AS
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :isSelectQuery');
IF (REGEXP_INSTR (TRIM (pParsedQuery),
'select',
1,
1,
0,
'im')) = 1
THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END isSelectQuery;
PROCEDURE exeAnyNonSelectQuery (
pAppId MY_RULE.APPID%TYPE,
exeId MY_RULE_OUTPUT.EXEID%TYPE,
ruleId MY_RULE_OUTPUT.RULE_ID%TYPE,
ruleSetId MY_RULE_OUTPUT.RULESET_ID%TYPE,
parsedRuleQry CLOB)
AS
BEGIN
--DBMS_OUTPUT.PUT_LINE ('********* PROC :exeAnyNonSelectQuery');
MY_LOGGER.LOG ('MY_RULESENGINE.exeAnyNonSelectQuery',
'ERR',
'0',
'Executing exeAnyNonSelectQuery:' || parsedRuleQry,
exeId,
ruleSetId,
ruleId,
aLOGID,
'Normal');
EXECUTE IMMEDIATE TO_CHAR (parsedRuleQry);
EXCEPTION
WHEN OTHERS
THEN
MY_LOGGER.LOG ('MY_RULESENGINE.exeAnyNonSelectQuery',
'ERR',
SQLCODE,
SQLERRM,
exeId,
ruleSetId,
ruleId,
aLOGID,
'Error');
RAISE;
END exeAnyNonSelectQuery;
END MY_RULESENGINE;
--------------------------------------------------------------------
Test Run / Sample Program
/**
DMLs
***/
INSERT INTO MY_LOG_PROPERTIES (LOGGER,
LOGLEVEL,
CREATEDBY,
CREATEDDT,
UPDATEDT,
UPDATEDBY)
VALUES ('*',
'ON',
'Agilan',
SYSDATE,
SYSDATE,
'Agilan');
INSERT INTO MY_LOG_PROPERTIES (LOGGER,
LOGLEVEL,
CREATEDBY,
CREATEDDT,
UPDATEDT,
UPDATEDBY)
VALUES ('MY_RULESENGINE',
'DEB',
'Agilan',
SYSDATE,
SYSDATE,
'Agilan');
---------------
INSERT INTO MY_RULE_EXE_CONFIG (ID,
APPID,
RULESET_ID,
BREAK_CONDN_BEF_AFT,
BREAKING_RULEID,
UPDDT)
VALUES (MY_exe_rule_config_seq,
'YourApplicationID',
'YourRuleSetId',
'AFT',
'breakOnFailure',
SYSDATE);
INSERT INTO MY_RULE_BINDVARIABLES (APPID,
VARIABLENAME,
BIND_QUERY,
INC_TYPE,
CACHE_WITHIN_EXEC,
UPDDT)
VALUES (
'YourApplicationID',
'$v.input_param1_from_caller$',
'SELECT value FROM MY_RE_INPUT_PARAM WHERE EXEID=$p.pExecId$ AND upper(KEY)=''PARAMETERNAME_1''',
'EQ',
'N',
SYSDATE);
INSERT INTO MY_RULE_BINDVARIABLES (APPID,
VARIABLENAME,
BIND_QUERY,
INC_TYPE,
CACHE_WITHIN_EXEC,
UPDDT)
VALUES (
'YourApplicationID',
'$v.input_param2_list_from_caller$',
'SELECT value FROM MY_RE_INPUT_PARAM WHERE EXEID=$p.pExecId$ AND upper(KEY)=''PARAMETERNAME_2_LIST''',
'IN',
'N',
SYSDATE);
INSERT INTO MY_RULE_BINDVARIABLES (APPID,
VARIABLENAME,
BIND_QUERY,
INC_TYPE,
CACHE_WITHIN_EXEC,
UPDDT)
VALUES (
'YourApplicationID',
'$v.current_date$',
'SELECT sysdate from dual',
'EQ',
'Y',
SYSDATE);
INSERT INTO MY_RULE_BINDVARIABLES (APPID,
VARIABLENAME,
BIND_QUERY,
INC_TYPE,
CACHE_WITHIN_EXEC,
UPDDT)
VALUES (
'YourApplicationID',
'$v.somevariable1$',
'SELECT ''data'' from dual',
'EQ',
'N',
SYSDATE);
INSERT INTO MY_RULE (ID,
APPID,
RULE_ID,
RULESET_ID,
RULE_QUERY,
UPDATEDDT)
VALUES (MY_rule_seq.NEXTVAL,
'YourApplicationID',
'breakOnFailure',
'YourRuleSetId',
'select ''1'' neverbreak from dual where 1=2 ',
SYSDATE);
INSERT INTO MY_RULE (ID,
APPID,
RULE_ID,
RULESET_ID,
RULE_QUERY,
UPDATEDDT)
VALUES (
MY_rule_seq.NEXTVAL,
'YourApplicationID',
'MyRuleQuery1',
'YourRuleSetId',
' select col1,col2,col3 from some_table where some_col = $v.somevariable1$',
SYSDATE);
INSERT INTO MY_RULE (ID,
APPID,
RULE_ID,
RULESET_ID,
RULE_QUERY,
UPDATEDDT)
VALUES (
MY_rule_seq.NEXTVAL,
'YourApplicationID',
'MyRuleQuery2',
'YourRuleSetId',
' select col1,col2,col3 from some_other_table where some_date_col = $v.current_date$ ',
SYSDATE);
INSERT INTO MY_RULE (ID,
APPID,
RULE_ID,
RULESET_ID,
RULE_QUERY,
UPDATEDDT)
VALUES (
MY_rule_seq.NEXTVAL,
'YourApplicationID',
'MyRuleQuery3',
'YourRuleSetId',
' select col1,col2,col3 from some_other_table where some_col = $v.input_param1_from_caller$ ',
SYSDATE);
INSERT INTO MY_RULE (ID,
APPID,
RULE_ID,
RULESET_ID,
RULE_QUERY,
UPDATEDDT)
VALUES (
MY_rule_seq.NEXTVAL,
'YourApplicationID',
'MyRuleQuery4',
'YourRuleSetId',
' select col1,col2,col3 from some_other_table where some_col in $v.input_param2_list_from_caller$ ',
SYSDATE);
-----------------------
/** Testing the proc **/
begin
/** Insert input_param and execute the proc in the same session **/
insert into MY_RE_INPUT_PARAM (exeid,key,value) values ( 1, 'PARAMETERNAME_1','apple');
insert into MY_RE_INPUT_PARAM (exeid,key,value) values ( 1, 'PARAMETERNAME_2_LIST','apple');
insert into MY_RE_INPUT_PARAM (exeid,key,value) values ( 1, 'PARAMETERNAME_2_LIST','mango');
insert into MY_RE_INPUT_PARAM (exeid,key,value) values ( 1, 'PARAMETERNAME_2_LIST','banana');
insert into MY_RE_INPUT_PARAM (exeid,key,value) values ( 1, 'PARAMETERNAME_2_LIST','avacado');
MY_RULESENGINE.pub_fireRules('YourApplicationId','YourRuleSetId',1);
end;