--rem {{ $Header: L:\\\\model\\jin\\RCS\\jriversn.jpb 40.25 1998/12/17 10:58:27 wstallar Exp $ --rem {{ -----------------------------------------------------------------------+ --rem File: $Workfile: jrpbvman.sql $ --rem Author: $Author: wstallar $ --rem Date: $Date: 1998/12/17 10:58:27 $ --rem Version: $Revision: 40.25 $ --rem Status: $State: Exp $ --rem Locked_by: $Locker: $ --rem Project: The Oracle Repository --rem Description: jr_version package --rem Notes: --rem RealAuth: Patrick Lorrimer --rem IncepDate: 17 Sept 1997 --rem Copyright: (c) Oracle Corporation 1997. All Rights Reserved. --rem Tagline: -- Repository: Managing *all* your data -- --rem --------------------------------------------------------------------------+ --rem Log of Changes from Source Control System --rem --------------------------------------------------------------------------+ --rem $Log: jriversn.jpb $ --rem --rem Revision 40.27 2000/11/02 sbhagava --rem Bug 1311500: Creating a table DUAL causes all objects to disappear --rem Changed the dual references to sys.dual --rem --rem --rem Revision 40.26 2000/10/30 sbhagava --rem Bug 1400964: Function GET_CHECKOUT() does not return the correct value --rem Changed the query to select ivid and removed max in the select --rem Max value was being selected to force a null. --rem Added an exception for no data found. --rem --rem Revision 40.25 1998/12/17 10:58:27 wstallar --rem o Renamed methods (in line with Amy's guidelines): --rem revoke_check_out() -> undo_checkout() --rem lock_check_out() -> lock_checkout() --rem unlock_check_out() -> unlock_checkout() --rem get_original_of_check_out() -> get_original_of_checkout() --rem 775696 BRANCH LABELS APPEAR TO BE FORCED TO UPPERCASE --rem 778725 JR_VERSION.GET_BRANCH_ID() SHOULD USE UPPER(BRANCH_NAME) --rem 780651 REQUIRE TO KNOW IF A CHECKOUT EXISTS FOR A SPECIFIC IN CONTEXT VERSION --rem --rem 781576 CHANGES OVERWRITTEN WHEN 2 USERS CHECK-IN AT SAME TIME --rem --rem Revision 40.24 1998/12/14 10:18:06 wstallar --rem Removed hard-coded ID for MAIN branch from get_associated_nodes. --rem --rem Revision 40.23 1998/12/11 17:28:24 wstallar --rem B774959 : Now allow check-out of configs when no wokrarea context set. --rem --rem Revision 40.22 1998/11/26 11:22:53 wstallar --rem Added methods lock_check_out() and unlock_check_out() --rem Modified package to use updated version labelling package. --rem Added tests to ensure version labels are unique. --rem --rem Revision 40.21 1998/11/13 17:46:05 wstallar --rem 763924 JR_VERSION.GET_STRICT_LOCK_POLICY() RETURNS NO VALUE FOR TRUE --rem Fixed missing return statement. --rem --rem Revision 40.20 1998/11/11 11:52:28 wstallar --rem ER 762589 : Can now specify notes on check-out. --rem Also, made new_verison function private, and modified it to allow check_out --rem to use it as a helper method. --rem --rem Revision 40.19 1998/11/09 12:51:20 wstallar --rem Bug 759686 - Check-in will check-in at tip if we are checking in a config. --rem --rem Revision 40.18 1998/10/27 11:22:34 wstallar --rem Added correct CDR error code to check_in() for branch error. --rem --rem Revision 40.17 1998/10/19 16:10:55 wstallar --rem Added delete_branch method --rem --rem Revision 40.16 1998/10/12 10:28:41 wstallar --rem Removed ampersands from comments. --rem --rem Revision 40.15 1998/10/12 10:18:57 wstallar --rem Modified is_checked_in for autobranching --rem Added wrappers for functions returning BOOLEANs. --rem --rem Revision 40.14 1998/09/18 15:25:02 wstallar --rem Added check_in_changes() to check-in then check-out maintaining any lock. --rem Fixed bug in auto version labeling on check-in (now gets next vlabel after --rem the branch tip, NOT the one following the checked out node). --rem --rem Revision 40.13 1998/09/17 10:26:05 wstallar --rem Changes for 404A. --rem Support automatic branching in workarea context. --rem Fixed revoke-checkout. --rem -- Revision 40.10 1998/08/25 15:03:00 wstallar -- Fixed BUG719451. No longer remove tip from merged branches - can still develop a branch that has been merged... -- -- Revision 40.9 1998/08/17 14:09:28 plorrime -- Test state of nodes during merge. -- -- Revision 40.8 1998/08/06 13:53:44 plorrime -- put back check_in_sub. It allows dense branching. -- -- Revision 40.7 1998/08/06 13:37:54 plorrime -- http://des2000.uk.oracle.com:8002/repos/specinfo/netdoc/version/release.htm#06_08_1998 -- -- Revision 40.6 1998/07/30 11:02:02 plorrime -- Added code for edge_sequence. -- Fixed spelling -- -- Revision 40.5 1998/07/23 10:51:19 plorrime -- see http://des2000.uk.oracle.com:8002/repos/specinfo/netdoc/version/release.htm -- Moved description to http://des2000.uk.oracle.com:8002/repos/specinfo/netdoc/version/versnAPIdesc.htm -- Same notes as for jriversn.jps -- ---- Revision 40.4 1998/06/11 16:01:29 cwilliam ---- disable constraints while calling on_element_checkout to prevent ---- violation of foreign key constraints preventing check out ---- ---- Revision 40.3 1998/05/28 15:46:50 rmolland ---- Fix bug where new_ivid is not returned on check_out ---- ---- Revision 40.2 1998/05/27 13:08:22 eharding ---- Moved reference JR_* PL/SQL from ClearCase ---- ---- ---- Rev 1.2 20 Jan 1998 16:37:40 jgitterm ---- rename file version procedure to generated name ---- ---- Rev 1.1 14 Jan 1998 18:07:40 kmchorto ---- Restructuring: Checkpoint for Jon DavidD ---- ---- Rev 1.0 12 Jan 1998 20:35:56 kmchorto ---- Checkpoint for Schema Cutover ---- ---- Rev 1.12 30 Dec 1997 14:46:38 plorrime ---- Corrected comment! ---- ---- Rev 1.11 18 Dec 1997 17:56:52 plorrime ---- gave exceptions numbers ---- ---- Rev 1.10 21 Nov 1997 17:27:00 jwetherb ---- Added check_out_file() as an optimization ---- Changed check_in() to not redundantly call get_tip() ---- Changed check_in_at() to allow -1 to be passed as tip_vseq, signalling ---- calc it inside the function. This way the caller doesn't have to get ---- this info beforehand. ---- ---- Rev 1.9 09 Oct 1997 10:08:06 plorrime ---- Added bug report. ---- ---- Rev 1.8 29 Sep 1997 15:47:32 plorrime ---- Changes to support 3 tier model of CICO. ---- ---- Rev 1.7 25 Sep 1997 15:12:54 dcaruana ---- Fix problem with checked-in tracking configurations ---- ---- Rev 1.6 24 Sep 1997 15:28:14 kmchorto ---- Minor compilation error ---- ---- Rev 1.5 24 Sep 1997 14:21:38 kmchorto ---- Mysterious funny characters ---- ---- Rev 1.4 23 Sep 1997 16:04:12 plorrime ---- Support fot tracking configurations. ---- ---- Rev 1.3 22 Sep 1997 16:30:22 kmchorto ---- drop table -> util package ---- ---- Rev 1.2 22 Sep 1997 14:24:58 plorrime ---- Merge little plus sign put on EOL into code. ---- ---- Rev 1.1 19 Sep 1997 17:06:24 jgitterm ---- Fix headers and extra column for folder ---- ---- Rev 1.0 17 Sep 1997 17:32:10 plorrime ---- Initial revision. ---- }} -----------------------------------------------------------------------+ ---- -- DESCRIPTION HAS BEEN MOVED TO THE WEB ON DES2000 prompt PACKAGE BODY: Jr_Version CREATE OR REPLACE PACKAGE BODY Jr_Version IS --------------------+ ---Private method declarations --------------------+ PROCEDURE get_associated_nodes(i_irid IN NUMBER, i_ivid IN NUMBER, o_tip_ivid OUT NUMBER, o_common_ivid OUT NUMBER, o_branch_id OUT NUMBER ); FUNCTION check_out_sub(i_irid NUMBER ,i_ivid NUMBER ,i_lock BOOLEAN ,in_dense_branch BOOLEAN ,i_notes VARCHAR2 DEFAULT NULL ,i_vlabel IN VARCHAR2 DEFAULT NULL ,i_version_dependencies BOOLEAN DEFAULT TRUE) RETURN NUMBER; procedure check_co_fol_orphans(co_ivid in number ,ci_ivid in number); PROCEDURE check_cfg_co(i_irid IN NUMBER ,i_ivid IN NUMBER); PROCEDURE check_co_ctxt_wa(i_ivid IN NUMBER); PROCEDURE get_checkin_db_locks(i_ci_ivid IN NUMBER ,i_tip_ivid IN NUMBER DEFAULT NULL); PROCEDURE get_checkout_db_locks(i_irid IN NUMBER ,i_ivid IN NUMBER ,i_branch IN NUMBER ,i_lock IN BOOLEAN); PROCEDURE tidy_fol_members_on_ci(ci_irid IN NUMBER ,ci_ivid IN NUMBER); PROCEDURE restore_fol_members(co_ivid IN NUMBER ,ci_ivid IN NUMBER); PROCEDURE remove_fol_members(co_ivid IN NUMBER ,ci_ivid IN NUMBER); PROCEDURE remove_sub_fol(fol_ivid in number); PROCEDURE undo_SACs(co_ivid IN NUMBER ,par_table IN NUMBER); FUNCTION new_version(i_irid NUMBER,i_ivid NUMBER, i_vlabel VARCHAR2 ,i_version_dependencies BOOLEAN) RETURN NUMBER; FUNCTION is_vlabel_unique(i_irid NUMBER, i_vlabel VARCHAR2) RETURN BOOLEAN; FUNCTION get_ivid(i_irid IN NUMBER) RETURN NUMBER; FUNCTION get_nls_type(i_ivid IN NUMBER) RETURN VARCHAR2; PROCEDURE check_version_ar(i_irid NUMBER); ---------------------------------------------------------+ -- PRIVATE HELPER FUNCTION -- Checks current user has sufficient access rights -- to perform versioning operation on object --------------------------------------------------------+ PROCEDURE check_version_ar(i_irid NUMBER) IS type_name VARCHAR2(30); supertype_name VARCHAR2(30); fol_irid NUMBER; nls_type_name RM_ELEMENT_TYPE_EXTENSIONS.NLS_NAME%TYPE; BEGIN --Ensure that the repository supports versioning IF NOT Jr_Sys_Privs.is_versioned_repos THEN --Cannot perform versioning operations in non-versioned repository Rmmes.post('CDR',1053); RAISE Jr_Acc_Rights.ACCESS_RIGHTS_ERROR; END IF; SELECT st.short_name ,et.short_name INTO supertype_name ,type_name FROM rm_element_types et ,rm_element_Types st WHERE st.id=et.supertypes AND et.id IN (SELECT MIN (ov.logical_type_id) FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.irid=i_irid); IF (type_name = 'CFG') OR (supertype_name = 'CEL') THEN --For configs and containers, check access rights on object itself fol_irid:=i_irid; ELSE --Otherwise, check access rights on owning container SELECT MIN(fm.folder_reference) INTO fol_irid FROM I$SDD_FOLDER_MEMBERS fm WHERE fm.member_object=i_irid AND fm.ownership_flag='Y'; END IF; IF NOT Jr_Acc_Rights.has_access(fol_irid,USER,'VER') THEN SELECT nls.nls_name INTO nls_type_name FROM rm_element_type_extensions nls ,rm_element_types et WHERE nls.for_type=et.id AND et.short_name=type_name; --Insufficient access rights to version %1!s %!0s Rmmes.post('CDR',2606,Jr_Name.get_path(i_irid,'NAME'),nls_type_name); RAISE Jr_Acc_Rights.ACCESS_RIGHTS_ERROR; END IF; END; ------------------------------------------------------ -- PRIVATE HELPER METHOD -- When performing an operation on a checked out -- element, confirm that it is checked out to the -- current workarea context. ------------------------------------------------------ PROCEDURE check_co_ctxt_wa(i_ivid in number) IS dummy number; obj_type RM_ELEMENT_TYPE_EXTENSIONS.NLS_NAME%TYPE; obj_table I$RM_SQL_TABLES.TABLE_NAME%TYPE; branch_name I$SDD_BRANCHES.NAME%TYPE; co_workarea_name I$SDD_WORKAREAS.NAME%TYPE; curr_workarea_name I$SDD_WORKAREAS.NAME%TYPE; in_ctxt boolean:=TRUE; BEGIN --No need to check configurations (never checked out to a workarea) SELECT tab.table_name INTO obj_table FROM I$SDD_OBJECT_VERSIONS ov ,I$RM_SQL_TABLES tab WHERE tab.IRID=ov.TABLE_IRID AND ov.IVID=i_ivid; if obj_table='SDD_CONFIGURATIONS' then return; end if; --Determine if element is checked out in current context if jr_context.workarea is null then in_ctxt:=FALSE; else BEGIN SELECT ctxt.workarea_irid INTO dummy FROM I$SDD_WA_CONTEXT ctxt WHERE ctxt.object_ivid=i_ivid AND ctxt.workarea_irid=jr_context.workarea; EXCEPTION WHEN NO_DATA_FOUND THEN --Object is not checked out to current workarea in_ctxt:=FALSE; END; end if; if not in_ctxt then --Get info for the message... SELECT nls.nls_name,NVL(br.name,'MAIN') INTO obj_type,branch_name FROM rm_element_type_extensions nls ,I$SDD_OBJECT_VERSIONS ov ,I$SDD_BRANCHES br WHERE ov.ivid=i_ivid AND nls.for_type=ov.logical_type_id AND br.branch_id(+)=ov.branch_id; SELECT wa.name INTO co_workarea_name FROM I$SDD_WORKAREAS wa ,I$SDD_WA_CONTEXT ctxt WHERE wa.irid=ctxt.workarea_irid and ctxt.object_ivid=i_ivid; IF Jr_Context.workarea IS NOT NULL THEN SELECT wa.name INTO curr_workarea_name FROM I$SDD_WORKAREAS wa WHERE wa.irid=Jr_Context.workarea; END IF; --1%!s 0%!s is checked out in workarea %2!s, not the current workarea (%3!s) Rmmes.post('CDR',1067,Jr_Name.get_path_no_context(i_ivid,branch_name,'NAME') ,obj_type ,co_workarea_name ,curr_workarea_name); RAISE CHECK_IN_ERROR; END IF; END; --------------------------------------------------------------------------- -- jr_version.CONTAINER_CHECKIN_REQD() -- Call before performing a checkin to determine if the objects container -- must also be checked in to make the object visible to other users. -- Returns TRUE if: -- o Object being checked in for first time -- o AND there are no checked-in container versions which own this object -- o AND object is not a root container or a configuration FUNCTION is_container_checkin_reqd(i_ivid IN NUMBER) RETURN BOOLEAN IS ob_irid NUMBER; fol_count PLS_INTEGER; ob_state VARCHAR2(1); root VARCHAR2(1); tab_name rm_sql_tables.name%TYPE; BEGIN SELECT ov.state,tab.name,ov.irid INTO ob_state,tab_name,ob_irid FROM I$SDD_OBJECT_VERSIONS ov ,RM_SQL_TABLES tab WHERE tab.id=ov.table_irid AND ov.ivid=i_ivid; --If object is a configuration, there's no problem IF tab_name='SDD_CONFIGURATIONS' THEN RETURN FALSE; END IF; --If object is a container, there's no problem if it's a root container IF tab_name='SDD_FOLDERS' THEN SELECT fol.root_flag INTO root FROM I$SDD_FOLDERS fol WHERE fol.ivid=i_ivid; IF root = 'Y' THEN RETURN FALSE; END IF; END IF; SELECT COUNT(1) INTO fol_count FROM I$SDD_FOLDERS fol ,I$SDD_FOLDER_MEMBERS fm ,I$SDD_OBJECT_VERSIONS ov WHERE fol.ivid=fm.parent_ivid AND fm.member_object=ob_irid AND ov.ivid=fol.ivid AND ov.state='I' AND ov.wastebasket='N'; --If object is present in a checked in container (that hasn't been deleted!) --there's no problem IF fol_count > 0 THEN RETURN FALSE; ELSE --Otherwise, we need to check the container in too.... RETURN TRUE; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN --Don't throw an exception if user supplies --invalid ivid RETURN FALSE; END; ---------------+ -- jr_version.is_merge_needed() -- Returns TRUE if check-in operation will result in the checking -- in of a node at the tip of the branch, when the checkout is -- from a non-tip node. -- Also returns common node, and the tip. -- If the check in operation is to be an explicit check-in at -- tip, then set i_at_tip to TRUE. ---------------+ FUNCTION is_merge_needed(i_irid IN NUMBER, i_ivid IN NUMBER, i_tip_ivid OUT NUMBER, i_common_ivid OUT NUMBER, i_at_tip IN BOOLEAN DEFAULT FALSE) RETURN BOOLEAN IS branch_id NUMBER; tip_ivid NUMBER; common_ivid NUMBER; branching BOOLEAN; wa_checkin_branch NUMBER; dummy PLS_INTEGER; BEGIN get_associated_nodes(i_irid,i_ivid, tip_ivid , common_ivid, branch_id ); i_tip_ivid :=tip_ivid ; -- stuff around because out parameters cant be written. i_common_ivid:=common_ivid ; --Work out if check-in will be at tip, or to branch IF (i_at_tip OR (Jr_Context.workarea IS NULL))THEN --If we are explicitly checking in at tip, or --we have no workarea context --then we can't be branching... branching:=FALSE; --Work out if autobranch policy will result in a branch ELSE IF (get_autobranch_policy = TRUE) THEN wa_checkin_branch:=Jr_Workarea.get_checkin_branch(Jr_Context.workarea); IF (wa_checkin_branch!=branch_id) THEN branching:=TRUE; ELSE branching:=FALSE; END IF; ELSE branching:=FALSE; END IF; END IF; --If we are checking in at tip, then merge --is needed if node isn't checked out from tip node. IF NOT branching THEN IF (tip_ivid!=common_ivid) THEN --Not checked out from tip --Ensure that we haven't already done a merge! SELECT COUNT(*) INTO dummy FROM I$SDD_VERSION_ASSOCIATIONS va WHERE va.to_ivid=i_ivid AND va.from_ivid=tip_ivid AND va.edge_kind='M'; --Ensure object has actually changed (no need for merge if it hasn't!) IF dummy = 0 AND (has_changed(i_ivid,common_ivid) OR has_changed(tip_ivid,common_ivid)) THEN --No merge, so we need to do one RETURN TRUE; ELSE --Already merged RETURN FALSE; END IF; ELSE --Checked out from tip - no need for merge RETURN FALSE; END IF; ELSE --If branching, merge is not needed... RETURN FALSE; END IF; EXCEPTION WHEN NOT_CHECKED_OUT THEN --%1!s %0!s not checked out Rmmes.post('CDR',1012,Jr_Name.get_path(i_ivid,'NAME') ,get_nls_type(i_ivid)); RAISE; END; --------------------------------------------------+ -- Funciton determines if there are any differences -- between the specified versions. -- If only the first version is specified, then -- it is compared with its predecessor. -- Returns TRUE if there are changes, false -- if no changes have been made -----------------------------------------------+ FUNCTION has_changed(i_ivid1 IN NUMBER ,i_ivid2 IN NUMBER := NULL) RETURN BOOLEAN IS this_notm PLS_INTEGER; other_notm PLS_INTEGER; other_ivid NUMBER; BEGIN IF i_ivid2 IS NULL THEN other_ivid:=get_predecessor(i_ivid1); ELSE other_ivid:=i_ivid2; END IF; SELECT this_ov.obj_notm,other_ov.obj_notm INTO this_notm,other_notm FROM I$SDD_OBJECT_VERSIONS this_ov ,I$SDD_OBJECT_VERSIONS other_ov WHERE this_ov.ivid = i_ivid1 AND other_ov.ivid= other_ivid; IF this_notm!=other_notm THEN --Versions have chagned RETURN TRUE; ELSE --Versions are the same RETURN FALSE; END IF; END; --------------------------+ -- PRIVATE HELPER METHOD -- Determines from the autobranch policy, -- and the current branch of the specified (checked out) -- element, whether a check-in will result in the -- object being automatically checked in on a branch. --------------------------+ FUNCTION CheckAutobranch(i_irid IN NUMBER ,i_ivid IN NUMBER) RETURN BOOLEAN IS dummy PLS_INTEGER; target_branch NUMBER; current_branch NUMBER; first_checkin BOOLEAN:=FALSE; BEGIN --If we're checking in a configuration, then check in at tip, --configurations are not in the context of a workarea, so workarea --default branches do not apply to them... SELECT COUNT(*) INTO dummy FROM I$SDD_OBJECT_VERSIONS ov ,RM_SQL_TABLES tab WHERE ov.ivid=i_ivid AND ov.table_irid=tab.irid AND tab.name='SDD_CONFIGURATIONS'; IF dummy > 0 THEN RETURN FALSE; END IF; --Can only autobranch in context of workarea - need workarea --to determine which branch to check-in to. IF Jr_Context.workarea IS NULL THEN RETURN FALSE; END IF; --Check autobranch policy IF (get_autobranch_policy = TRUE) THEN --We have autobranch - find current branch and target branch current_branch:=get_current_branch_id(i_irid,i_ivid); target_branch:=Jr_Workarea.get_checkin_branch(Jr_Context.workarea); IF current_branch=0 THEN first_checkin:=TRUE; END IF; --If current branch and target branch are the same, --or no target branch set, then check-in at tip... IF (target_branch IS NULL) OR (current_branch = target_branch) THEN RETURN FALSE; --If this is the first checkin, and default checkin branch is MAIN, --then this is just a checkin at the tip! ELSIF (first_checkin) AND (get_branch_name(target_branch)='MAIN') THEN RETURN FALSE; --Otherwise, autobranch. ELSE RETURN TRUE; END IF; END IF; --Autobranch policy not set, so check in at tip RETURN FALSE; END; ---------------------------------------------------------------+ --jr_version.GET_NEXT_VLABEL() --Function returns the next version label in the autogenerated --sequence for the specified object version. --CI_ACTION specifies, for checked out elems whether the --checkin will be to TIP or onto a BRANCH. --If left null, the autobranch policy for the current workarea --will be used to determine the likely checkin action. ----------------------------------------------------------------+ FUNCTION get_next_vlabel(i_irid IN NUMBER ,i_ivid IN NUMBER ,ci_action IN VARCHAR2 DEFAULT NULL) RETURN VARCHAR2 IS action VARCHAR2(10); new_vlabel VARCHAR2(32); old_vlabel VARCHAR2(32); root_vlabel VARCHAR2(32); curr_tip_ivid NUMBER; common_ivid NUMBER; curr_branch_id NUMBER; first_check_in BOOLEAN:=FALSE; l_edge_sequence NUMBER; n_branches NUMBER; BEGIN IF NOT Jr_Version_Label.get_automatic_vlabel_policy THEN RETURN NULL; END IF; action:=UPPER(ci_action); IF is_checked_in(i_irid,i_ivid) THEN action:='CO'; ELSIF action IS NULL THEN IF CheckAutobranch(i_irid,i_ivid) = TRUE THEN action:='BRANCH'; ELSE action:='TIP'; END IF; END IF; IF action!='CO' THEN get_associated_nodes(i_irid,i_ivid,curr_tip_ivid,common_ivid,curr_branch_id); IF common_ivid IS NULL THEN first_check_in := TRUE; END IF; END IF; IF action='TIP' THEN IF first_check_in THEN new_vlabel := Jr_Version_Label.first; ELSE --Generate new version label based on the label for first version on this branch. --Get the root_vlabel SELECT vlabel INTO root_vlabel FROM I$SDD_OBJECT_VERSIONS ov ,sdd_version_associations va WHERE ov.ivid = va.to_ivid AND va.irid = i_irid AND va.branch_id = curr_branch_id AND va.edge_kind IN ('R','B'); --Get the sequence of the current tip. SELECT edge_sequence INTO l_edge_sequence FROM sdd_version_associations WHERE to_ivid=curr_tip_ivid AND edge_kind='T'; l_edge_sequence := l_edge_sequence +1; new_vlabel:=Jr_Version_Label.next(root_vlabel,l_edge_sequence); END IF; ELSIF action='BRANCH' THEN IF first_check_in THEN --If branching on first check-in, version label will be --based on first vlabel with no other branches from the node new_vlabel:=Jr_Version_Label.branch(Jr_Version_Label.first,0); ELSE --New VLABEL is based on number of branches from source node. --We need to count branches for nodes in the wasetebasket, to prevent --problems with clashes on restore. SELECT COUNT(1) INTO n_branches FROM I$SDD_VERSION_ASSOCIATIONS WHERE irid=i_irid AND from_ivid=common_ivid AND edge_kind='B'; --New label will be based on label for node that we are currently checked out from. old_vlabel:=get_vlabel(i_irid,common_ivid); new_vlabel:=Jr_Version_Label.branch(old_vlabel,n_branches); END IF; ELSIF action='CO' THEN old_vlabel:=get_vlabel(i_irid,i_ivid); new_vlabel:=Jr_Version_Label.check_out(old_vlabel); --Ensure that the vlabel is unique... WHILE (is_vlabel_unique(i_irid,new_vlabel) = FALSE) LOOP new_vlabel:=Jr_Version_Label.next_check_out(new_vlabel); END LOOP; END IF; RETURN new_vlabel; END; ------------------------------------------------- -- PRIVATE HELPER FUNCTION -- Returns nls type name for given object version -- Used when posting errors ------------------------------------------------- FUNCTION get_nls_type(i_ivid IN NUMBER) RETURN VARCHAR2 IS type_name RM_ELEMENT_TYPE_EXTENSIONS.NLS_NAME%TYPE; BEGIN SELECT nls.nls_name INTO type_name FROM rm_element_type_extensions nls ,rm_element_types et ,I$SDD_OBJECT_VERSIONS ov WHERE nls.for_type=et.id AND et.id=ov.logical_type_id AND ov.ivid=i_ivid; RETURN type_name; END; ---------------+ -- -- Function uses current workarea to determine where check-in should be. -- Check-in occurs on the check-in branch defined for the current workarea -- If autobranch policy is not set, or no check-in branch is defined for -- the workarea, then we check at tip for the current branch. -- ---------------+ FUNCTION check_in(i_irid NUMBER ,i_ivid NUMBER ,i_notes VARCHAR2 ,i_vlabel VARCHAR2 ,i_tip_ivid NUMBER DEFAULT NULL) RETURN NUMBER IS current_branch NUMBER; target_branch NUMBER; l_ivid NUMBER; l_main_vlabel VARCHAR2(32); branch_name VARCHAR2(128); obj_name VARCHAR2(300); dummy NUMBER; BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; IF (CheckAutobranch(i_irid,i_ivid) = TRUE) THEN --We're performing an automatic branch operation -- find target branch target_branch:=Jr_Workarea.get_checkin_branch(Jr_Context.workarea); --If object never checked-in, then must first put it --onto MAIN branch, then branch it to the target branch IF never_checked_in(i_irid) THEN l_main_vlabel:=i_vlabel; IF l_main_vlabel IS NOT NULL THEN --Update user-defined VLABEL l_main_vlabel:=SUBSTR('MAIN:'||l_main_vlabel,1,32); END IF; --Check in onto main branch. l_ivid:=check_in_at_tip (i_irid, l_ivid, i_notes, l_main_vlabel); --Branch to target branch... l_ivid:=branch(i_irid,l_ivid,target_branch,i_notes,i_vlabel); --Otherwise, branch check in. ELSE --Cannot branch check-in for object that already exists on target branch IF is_already_on_branch(i_irid,target_branch) THEN branch_name:=get_branch_name(target_branch); obj_name:=Jr_Name.get_crn(l_ivid,'BRANCH'); --Cannot check in %0!s. Object already on branch %1!s. Rmmes.post('CDR',1032,obj_name,branch_name); RAISE CHECK_IN_ERROR; END IF; --Perform the check in l_ivid:=check_in_branch(i_irid,l_ivid,target_branch,i_notes,i_vlabel); END IF; --No autobranch, just check in at tip. ELSE l_ivid:=check_in_at_tip (i_irid, l_ivid, i_notes, i_vlabel,i_tip_ivid); END IF; RETURN l_ivid;--!!l_ivid MAY BE DIFFERENT TO i_ivid (if first check-in)!! EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE CHECK_IN_ERROR; END ; ---------------+ ------------------------------------------------------+ -- Check in object, and immeadiately check it out again -- in order to preserve lock. -- Returns ivid of new checked-out node. ------------------------------------------------------+ FUNCTION check_in_changes(i_irid NUMBER ,i_ivid NUMBER ,i_notes VARCHAR2 ,i_vlabel VARCHAR2 ,i_tip_ivid NUMBER DEFAULT NULL) RETURN NUMBER IS locked BOOLEAN; l_ivid NUMBER; checkout_notes VARCHAR2(2000); BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; --Determine lock status of current checked out node. locked:=is_locked(i_irid,l_ivid); --Get the current checkout notes checkout_notes := get_notes(i_irid,l_ivid); --Check-in current node. l_ivid:=check_in(i_irid,l_ivid,i_notes,i_vlabel,i_tip_ivid); --Check out node, maintaining the lock status and checkout_notes. l_ivid:=check_out(i_irid,l_ivid,locked,checkout_notes); RETURN l_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE CHECK_IN_ERROR; END; --------------+ -- return ivid of checked in node. It's the same as the original. -- tip null for 1st check in -- -- Check in at the tip -- --------------+ FUNCTION check_in_at_tip (i_irid NUMBER ,i_ivid NUMBER ,i_notes VARCHAR2 ,i_vlabel VARCHAR2 ,i_tip_ivid NUMBER DEFAULT NULL) RETURN NUMBER IS new_vlabel VARCHAR2(32); root_vlabel VARCHAR2(32); l_user VARCHAR2(30); locking_user VARCHAR2(30); locking_wa I$SDD_WORKAREAS.NAME%TYPE; curr_tip_ivid NUMBER; common_ivid NUMBER; curr_branch_id NUMBER; first_check_in BOOLEAN; auto_policy BOOLEAN; l_edge_sequence NUMBER; l_ivid NUMBER; l_is_merged NUMBER; BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; get_associated_nodes(i_irid,l_ivid, curr_tip_ivid , common_ivid, curr_branch_id ); --Lock the current node and the tip node to protect against a simultaneous --transaction checking in the same object at the tip get_checkin_db_locks(l_ivid,curr_tip_ivid); --Check object is checked out to current workarea check_co_ctxt_wa(l_ivid); --Check user has correct access rights to version object check_version_ar(i_irid); IF common_ivid IS NULL THEN first_check_in := TRUE; ELSE first_check_in := FALSE; END IF; --Check that the tip is still the same as the user thinks it is --(user should pass in the value of i_tip_ivid returned from IS_MERGE_NEEDED, -- i_tip_ivid is not passed in, then don't do this check...) -- IF (i_tip_ivid IS NOT NULL) AND (curr_tip_ivid != i_tip_ivid) THEN -- --Get hold of user that made the offending check-in -- SELECT va.created_by -- INTO l_user -- FROM SDD_VERSION_ASSOCIATIONS va -- WHERE va.from_ivid = curr_tip_ivid; -- --Check-in of %1!s %0!s by %2!s requires that merge of changes is performed -- Rmmes.post('CDR',1036,Jr_Name.get_path(l_ivid,'NAME') -- ,get_nls_type(l_ivid) -- ,l_user); -- RAISE MERGE_REQUIRED; -- END IF; -- -- SDP - Fix for bug 1494848 AUTOMATIC CHECKIN ON UPLOAD, DOES NOT DETECT FILE NEEDS MERGING -- -- In all cases except first checkin, check if current tip ivid is same as the predessor tip ivid -- if it is not check if a merge association has been recorded -- if it hasnt then we cannot allow the check in until a merge is performed -- if (first_check_in = FALSE) THEN if (common_ivid != curr_tip_ivid) THEN begin SELECT COUNT(*) INTO l_is_merged FROM I$SDD_VERSION_ASSOCIATIONS va WHERE va.to_ivid=l_ivid AND va.from_ivid=curr_tip_ivid AND va.edge_kind='M'; --Ensure object has actually changed (no need for merge if it hasn't!) IF l_is_merged = 0 AND (has_changed(i_ivid,common_ivid) OR has_changed(curr_tip_ivid,common_ivid)) THEN begin SELECT va.created_by INTO l_user FROM SDD_VERSION_ASSOCIATIONS va WHERE va.from_ivid = curr_tip_ivid; end; Rmmes.post('CDR',1036,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid) ,l_user); RAISE MERGE_REQUIRED; end if; end; end if; end if; -- if branch is locked, then only the locker may check in at the tip IF NOT first_check_in AND is_branch_locked(i_irid,curr_branch_id) AND NOT is_locked(i_irid,l_ivid) THEN locking_user:=get_lock_holder(i_irid,curr_branch_id); SELECT wa.name INTO locking_wa FROM I$SDD_WORKAREAS wa ,I$SDD_WA_CONTEXT ctxt WHERE wa.irid=ctxt.workarea_irid AND ctxt.object_ivid=get_locking_version(i_irid,curr_branch_id); --%0!s is locked on branch %1!s by user %2!s with checkout to workarea %3!s Rmmes.post('CDR',1011,Jr_Name.get_path(l_ivid,'NAME') ,get_branch_name(curr_branch_id) ,locking_user ,locking_wa); RAISE CHECK_IN_ERROR; END IF; auto_policy := Jr_Version_Label.get_automatic_vlabel_policy(); -- create label. This is child of the tip. IF i_vlabel IS NOT NULL THEN -- if specified, always use given label new_vlabel := SUBSTR(i_vlabel,1,32); --Make sure it fits! ELSIF auto_policy=TRUE THEN new_vlabel:=get_next_vlabel(i_irid,l_ivid,'TIP'); ELSE -- no label specified and not automatic either! new_vlabel := NULL; END IF; --Having derived a VLABEL, ensure it is unique IF (new_vlabel IS NOT NULL) AND NOT is_vlabel_unique(i_irid,new_vlabel) THEN --The version label %1!s is already in use for %2!s %0!s Rmmes.post('CDR',1033,Jr_Name.get_path(l_ivid,'NAME') ,new_vlabel ,get_nls_type(l_ivid)); RAISE CHECK_IN_ERROR; END IF; -- add root edge IF first_check_in THEN l_edge_sequence:=1; INSERT INTO I$SDD_VERSION_ASSOCIATIONS (irid,from_ivid,to_ivid,edge_kind,created_by,date_created,branch_id,edge_sequence,notes) VALUES (i_irid,l_ivid,l_ivid,'R',USER,SYSDATE,curr_branch_id,l_edge_sequence,i_notes); END IF; IF first_check_in = FALSE THEN IF l_edge_sequence IS NULL THEN -- get current the edge seq, if we don't already have it SELECT edge_sequence INTO l_edge_sequence FROM I$SDD_VERSION_ASSOCIATIONS WHERE irid=i_irid AND from_ivid=curr_tip_ivid AND edge_kind='T'; l_edge_sequence := l_edge_sequence +1; END IF; -- convert check out edge to next. UPDATE I$SDD_VERSION_ASSOCIATIONS SET edge_kind='N',from_ivid=curr_tip_ivid,notes=i_notes,edge_sequence=l_edge_sequence WHERE irid=i_irid AND to_ivid=l_ivid AND edge_kind!='M';--B1138540 - don't convert merge associtations! -- delete old tip DELETE FROM I$SDD_VERSION_ASSOCIATIONS WHERE irid=i_irid AND from_ivid=curr_tip_ivid AND edge_kind='T'; END IF; --update the info in I$SDD_OBJECT_VERSIONS UPDATE I$SDD_OBJECT_VERSIONS SET VLABEL=new_vlabel ,state='I' ,lock_flag=NULL ,branch_id=curr_branch_id ,sequence_in_branch=l_edge_sequence WHERE irid=i_irid AND ivid=l_ivid ; -- always create NEW tip edge on check-in INSERT INTO I$SDD_VERSION_ASSOCIATIONS (irid ,from_ivid ,to_ivid ,edge_kind ,created_by ,date_created ,branch_id ,edge_sequence,notes) VALUES (i_irid,l_ivid,l_ivid,'T',USER,SYSDATE,curr_branch_id,l_edge_sequence,i_notes); --If we checked in a folder, we need to tidy up any --folder members that were removed. tidy_fol_members_on_ci(i_irid,l_ivid); RETURN l_ivid; EXCEPTION WHEN NOT_CHECKED_OUT THEN --%1!s %0!s not checked out Rmmes.post('CDR',1012,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid)); RAISE NOT_CHECKED_OUT; WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE CHECK_IN_ERROR; END ; --------------+ ------------------------------------------------------+ -- Check in object at tip, and immeadiately -- check it out again in order to preserve lock. -- Returns ivid of new checked-out node. ------------------------------------------------------+ FUNCTION check_in_changes_at_tip(i_irid NUMBER ,i_ivid NUMBER ,i_notes VARCHAR2 ,i_vlabel VARCHAR2 ,i_tip_ivid NUMBER DEFAULT NULL) RETURN NUMBER IS locked BOOLEAN; l_ivid NUMBER; checkout_notes VARCHAR2(2000); BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; --Determine lock status of current checked out node. locked:=is_locked(i_irid,l_ivid); --Get the current checkout notes checkout_notes := get_notes(i_irid,l_ivid); --Check-in current node. l_ivid:=check_in_at_tip(i_irid,l_ivid,i_notes,i_vlabel,i_tip_ivid); --Check out node, maintaining the lock status and checkout_notes. l_ivid:=check_out(i_irid,l_ivid,locked,checkout_notes); RETURN l_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE CHECK_IN_ERROR; END; --------------+ -- -- Check in a node as a branch. -- --------------+ FUNCTION check_in_branch(i_irid NUMBER, i_ivid NUMBER,i_branch_id NUMBER,i_notes VARCHAR2, i_vlabel VARCHAR2) RETURN NUMBER IS new_vlabel VARCHAR2(32); auto_policy BOOLEAN; n_branches PLS_INTEGER; -- how many branches are there of this node? original NUMBER; l_ivid NUMBER; dummy PLS_INTEGER; BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; IF is_checked_in (i_IRID,l_ivid) THEN --%1!s %0!s not checked out Rmmes.post('CDR',1012,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid)); RAISE NOT_CHECKED_OUT; END IF; --Check object is checked out to current workarea check_co_ctxt_wa(l_ivid); --Check user has correct access rights to version object check_version_ar(i_irid); -- you can branch any checked out object except one that's never been checked in. IF never_checked_in(i_irid)=TRUE THEN --%1!s %0!s never checked in. Rmmes.post('CDR',1013,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid)); RAISE BRANCH_ERROR; END IF; IF valid_branch_id(i_branch_id)<>TRUE THEN --Branch with ID = %0!s does not exist. Rmmes.post('CDR',109,TO_CHAR(i_branch_id)); RAISE BRANCH_ERROR; END IF; IF is_already_on_branch(i_irid,i_branch_id)=TRUE THEN --%1!s %0!s already on branch %2!s Rmmes.post('CDR',1014,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid) ,get_branch_name(i_branch_id)); RAISE BRANCH_ERROR; END IF; --Get the ivid of the node that this node is checked out from. original := get_original_of_checkout(i_irid,l_ivid); -- lock the checked out node, to prevent two users performing a branch -- with the same check-out in two seperate transactions. get_checkin_db_locks(i_ivid); -- Also lock the original node to prevent version labelling inconsistencies get_checkin_db_locks(original); auto_policy := Jr_Version_Label.get_automatic_vlabel_policy(); -- create label. IF i_vlabel IS NOT NULL THEN --if specified always use given label new_vlabel := SUBSTR(i_vlabel,1,32); --Make sure it fits! ELSIF auto_policy=TRUE THEN new_vlabel := get_next_vlabel(i_irid,l_ivid,'BRANCH'); ELSE -- no label specified and not automatic either! new_vlabel := NULL; END IF; --Having derived a VLABEL, ensure it is unique IF (new_vlabel IS NOT NULL) AND NOT is_vlabel_unique(i_irid,new_vlabel) THEN ---The version label %!1s is already in use for %2!s %0!s Rmmes.post('CDR',1033,Jr_Name.get_path(l_ivid,'NAME') ,new_vlabel ,get_nls_type(l_ivid)); RAISE CHECK_IN_ERROR; END IF; -- convert check out edge to branch and ensure from_ivid is the old tip. UPDATE I$SDD_VERSION_ASSOCIATIONS SET edge_kind='B',from_ivid=original,notes=i_notes,branch_id=i_branch_id,edge_sequence=1 WHERE irid=i_irid AND to_ivid=l_ivid AND edge_kind!='M';--B1138540 - don't convert merge associtations! --update info in I$SDD_OBJECT_VERSIONS UPDATE I$SDD_OBJECT_VERSIONS SET VLABEL=new_vlabel ,state='I' ,lock_flag=NULL ,branch_id=i_branch_id ,sequence_in_branch=1 WHERE irid=i_irid AND ivid=l_ivid ; -- always create NEW tip edge on check-in INSERT INTO I$SDD_VERSION_ASSOCIATIONS (irid ,from_ivid ,to_ivid ,edge_kind ,created_by ,date_created ,branch_id,edge_sequence,notes) VALUES (i_irid,l_ivid,l_ivid,'T',USER,SYSDATE,i_branch_id,1,i_notes); -- Ensure that this is still the only occurance of this object on the branch. -- (another simultaneous transaction could have put this object onto the -- same branch, as we can't take out an appropriate database lock to prevent it). SELECT COUNT(1) INTO dummy FROM sdd_version_associations va WHERE va.irid=i_irid AND va.branch_id=i_branch_id AND va.to_ivid!=l_ivid; IF dummy > 0 THEN --%1!s %0!s already on branch %2!s Rmmes.post('CDR',1014,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid) ,get_branch_name(i_branch_id)); RAISE BRANCH_ERROR; END IF; --If we checked in a folder, we need to tidy up any --folder members that were removed. tidy_fol_members_on_ci(i_irid,l_ivid); RETURN l_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE CHECK_IN_ERROR; END; --------------+ ------------------------------------------------------ -- jr_version.TIDY_FOL_MEMBERS_ON_CI() -- PRIVATE HELPER METHOD -- Called by check-in methods. If we are checking -- in a container, we need to delete rows from -- I$SDD_WA_CONTEXT for any folder members that -- were removed ------------------------------------------------------ PROCEDURE tidy_fol_members_on_ci(ci_irid IN NUMBER ,ci_ivid IN NUMBER) IS dummy PLS_INTEGER; BEGIN --Check object is a container SELECT 1 INTO dummy FROM I$SDD_FOLDERS fol WHERE fol.ivid=ci_ivid; DELETE I$SDD_WA_CONTEXT ctxt WHERE ctxt.wastebasket='X' --wb flag set to X when folder membership removed AND ctxt.workarea_irid = Jr_Context.workarea AND EXISTS (SELECT ov.ivid FROM I$SDD_OBJECT_VERSIONS ov ,I$SDD_FOLDER_MEMBERS fm WHERE ov.irid=fm.member_object AND ov.ivid=ctxt.object_ivid AND fm.folder_reference=ci_irid); EXCEPTION WHEN NO_DATA_FOUND THEN RETURN; END; ------------------------------------------------------+ -- Check in object onto specified branch, -- and immeadiately check it out again -- in order to preserve lock. -- Returns ivid of new checked-out node. ------------------------------------------------------+ FUNCTION check_in_changes_on_branch(i_irid NUMBER ,i_ivid NUMBER ,i_branch_id NUMBER ,i_notes VARCHAR2 ,i_vlabel VARCHAR2) RETURN NUMBER IS locked BOOLEAN; l_ivid NUMBER; checkout_notes VARCHAR2(2000); BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; --Determine lock status of current checked out node. locked:=is_locked(i_irid,l_ivid); --Get the current checkout notes checkout_notes := get_notes(i_irid,l_ivid); --Check-in current node. l_ivid:=check_in_branch(i_irid,l_ivid,i_branch_id,i_notes,i_vlabel); --Check out node, maintaining the lock status and checkout_notes. l_ivid:=check_out(i_irid,l_ivid,locked,checkout_notes); RETURN l_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE CHECK_IN_ERROR; END; --------------+ -- jr_version.check_out() -- -- Checks out the specified element version. -- -- set i_lock = TRUE to get a lock -- Returns the ivid of the checked out object --------------+ FUNCTION check_out(i_irid NUMBER ,i_ivid NUMBER ,i_lock BOOLEAN ,i_notes VARCHAR2 DEFAULT NULL ,i_vlabel VARCHAR2 DEFAULT NULL ,i_version_dependencies BOOLEAN DEFAULT TRUE) RETURN NUMBER IS l_ivid NUMBER; BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; --Check user has correct access rights to version object check_version_ar(i_irid); RETURN check_out_sub(i_irid,l_ivid,i_lock,FALSE,i_notes,i_vlabel,i_version_dependencies); EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE CHECK_OUT_ERROR; -- -- Bug fix 2718043 -- WHEN OTHERS THEN Rmmes.post('ORA',SQLCODE,SQLERRM); RAISE; END; --------------+ --------------+ -- As for check-out. However in_dense_branch parameter allows check out even -- if strict locking is on and the branch is locked. --------------+ FUNCTION check_out_sub(i_irid NUMBER ,i_ivid NUMBER ,i_lock BOOLEAN ,in_dense_branch BOOLEAN ,i_notes IN VARCHAR2 DEFAULT NULL ,i_vlabel IN VARCHAR2 DEFAULT NULL ,i_version_dependencies BOOLEAN DEFAULT TRUE) RETURN NUMBER IS new_ivid NUMBER; func_name VARCHAR2(64); curs INTEGER; stmt VARCHAR2(200); table_name VARCHAR2(20); rows_proc INTEGER; in_track INTEGER; track_irid NUMBER; track_ivid NUMBER; ci_ivid NUMBER; is_config BOOLEAN := FALSE; l_lock BOOLEAN; l_lock_flag VARCHAR2(1); strict_locking BOOLEAN := FALSE; check_out_flag VARCHAR2(1); constraints_were_enabled BOOLEAN := Jr_Context.constraints_enabled; current_branch NUMBER; locking_user VARCHAR2(30); locking_wa I$SDD_WORKAREAS.NAME%TYPE; locking_version NUMBER; BEGIN --Are we checking out a configuration? SELECT tab.name INTO table_name FROM I$SDD_OBJECT_VERSIONS ov ,RM_SQL_TABLES tab WHERE tab.irid=ov.table_irid AND ov.ivid=i_ivid; IF table_name = 'SDD_CONFIGURATIONS' THEN is_config := TRUE; END IF; IF (Jr_Context.workarea IS NULL AND NOT is_config) THEN --Workarea context has not been set. Rmmes.post('CDR',100); RAISE CHECK_OUT_ERROR; END IF; IF is_checked_in(i_irid,i_ivid)=FALSE THEN -- %1!s %0!s not checked in Rmmes.post('CDR',1016,Jr_Name.get_path(i_ivid,'NAME') ,get_nls_type(i_ivid)); RAISE CHECK_OUT_ERROR; END IF; --For configurations, can only have a single checkout at any one time. IF is_config THEN check_cfg_co(i_irid,i_ivid); END IF; -- if strict locking then over-ride lock argument l_lock:=i_lock; IF get_strict_lock_policy() THEN l_lock:=TRUE; strict_locking:=TRUE; END IF; -- if intension is to branch then over-ride strict lock! IF in_dense_branch THEN l_lock:=FALSE; END IF; current_branch := get_current_branch_id(i_irid,i_ivid); -- take out appropriate database locks to ensure we don't get problems -- with simultaneous checkouts of the same object (B920148) get_checkout_db_locks(i_irid,i_ivid,current_branch,l_lock); IF (NOT is_config) AND (checked_out_in_workarea(i_irid) = 1) THEN --%1!s %0!s already checked out to this workarea Rmmes.post('CDR',1046,Jr_Name.get_path(i_ivid,'NAME') ,get_nls_type(i_ivid)); RAISE CHECK_OUT_ERROR; END IF; -- prevent locked check out if already locked... -- if strict locking is enabled, then prevent checkout if object -- is checked out at all on branch. IF (l_lock AND is_branch_locked(i_irid,current_branch)=TRUE) OR(l_lock AND strict_locking AND is_checked_out_on_branch(i_irid,current_branch)) THEN IF strict_locking THEN --With strict locking, 'lock' _might_ be held by normal checkout --(if we changed to strict locking whilst checkouts existed) SELECT MIN(va.to_ivid) INTO locking_version FROM SDD_VERSION_ASSOCIATIONS va WHERE va.irid=i_irid AND va.branch_id=current_branch AND va.edge_kind IN ('C','L'); SELECT va.created_by INTO locking_user FROM SDD_VERSION_ASSOCIATIONS va WHERE va.to_ivid=locking_version; ELSE locking_user:=get_lock_holder(i_irid,current_branch); locking_version:=get_locking_version(i_irid,current_branch); END IF; SELECT wa.name INTO locking_wa FROM I$SDD_WORKAREAS wa ,I$SDD_WA_CONTEXT ctxt WHERE wa.irid=ctxt.workarea_irid AND ctxt.object_ivid=locking_version; -- %0!s already locked by user %!2s in workarea %!3s Rmmes.post('CDR',1015,Jr_Name.get_path(i_ivid,'NAME') ,locking_user ,locking_wa); RAISE CHECK_OUT_ERROR; END IF; -- get edge_kind fr check out. IF l_lock IS NOT NULL AND l_lock=TRUE THEN check_out_flag:='L'; ELSE check_out_flag:='C'; END IF; --Get hold of checked-in version currently visible in workarea --and remove it from current context --(maybe different to version we are checking out from) --Don't do this for configurations IF NOT is_config THEN BEGIN SELECT ov.ivid INTO ci_ivid FROM SDD_OBJECT_VERSIONS ov WHERE ov.irid=i_irid; --Need to lock the workarea to prevent compilation whilst this --transaction is in progress. We take out a shared lock, so that --we don't block other checkout operations. Jr_Workarea.lock_for_update(Jr_Context.workarea ,JR_WORKAREA.S_MODE); --Remove the old, checked-in version from workarea context DELETE I$SDD_WA_CONTEXT ctxt WHERE ctxt.workarea_irid=Jr_Context.workarea AND ctxt.object_ivid=ci_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --There may not be a version currently in the workarea! NULL; END; END IF; --If checking out a folder, need to ensure checkout will not create --non-versioned or checkout orphans in the workarea (B1259954) if table_name = 'SDD_FOLDERS' and ci_ivid is not null then check_co_fol_orphans(i_ivid,ci_ivid); end if; --Create a new, checked out version of the object new_ivid := new_version(i_irid,i_ivid,i_vlabel,i_version_dependencies); IF new_ivid IS NULL THEN --Failed to create new version RAISE CHECK_OUT_ERROR; END IF; -- Update the version info I$SDD_OBJECT_VERSIONS to correct values IF l_lock THEN l_lock_flag:='Y'; ELSE l_lock_flag:='N'; END IF; UPDATE I$SDD_OBJECT_VERSIONS ov SET ov.lock_flag=l_lock_flag ,ov.branch_id=current_branch WHERE ov.ivid=new_ivid; -- depth is depth of previous node because intention of check out is to -- check in at this depth. INSERT INTO I$SDD_VERSION_ASSOCIATIONS (irid ,from_ivid ,to_ivid ,edge_kind ,created_by ,date_created ,branch_id ,notes) VALUES(i_irid, i_ivid, new_ivid, check_out_flag, USER,SYSDATE, current_branch, i_notes); RETURN new_ivid; END ; ---------------+ -------------------------------------------------------------------- -- check_co_fol_orphans() - Private helper method -- When chekcing out a container, if the checkout is from a -- different version to the one currently in the workarea, need -- to ensure that we don't end up with orphaned non-versioned -- or checked out objects in the workaera, which will prevent -- workarea compilation/refresh -------------------------------------------------------------------- procedure check_co_fol_orphans(co_ivid in number ,ci_ivid in number) is --Cursor lists checked-out or non-versioned elements on the ci element --that have no folder membership on the element being checked out. cursor get_orphans is select ov.name,ov.LOGICAL_TYPE_ID from SDD_OBJECT_VERSIONS ov ,I$SDD_FOLDER_MEMBERS ci_fm where ov.irid=ci_fm.member_object and ci_fm.parent_ivid=ci_ivid and ov.state in ('N','O') and ci_fm.member_object not in (select co_fm.member_object from I$SDD_FOLDER_MEMBERS co_fm where co_fm.parent_ivid=co_ivid); co_name varchar2(300); type_name RM_ELEMENT_TYPE_EXTENSIONS.NLS_NAME%TYPE; found_orphan boolean:=FALSE; begin --If the current ci version in the workarea is the same --as the version we're checking out, or there is currently --no version of the object in the workarea, then we can't --create orphans if (ci_ivid is null) or (ci_ivid=co_ivid) then return; end if; for orphan in get_orphans loop if not found_orphan then found_orphan:=TRUE; co_name:=jr_name.get_CRN_from_ivid(co_ivid,'BRANCH'); end if; select nls.nls_name into type_name from rm_element_type_extensions nls where nls.for_type=orphan.logical_type_id; --Checking out container %0!s will leave %1!s %2!s orphaned rmmes.post('CDR',1068,co_name,type_name,orphan.name); end loop; if found_orphan then RAISE CHECK_OUT_ERROR; end if; end;--check_co_fol_orphans -------------------------------------------------------------------- -- check_cfg_co() - Private helper method -- Only allow one checkout of a configutation at any one time. -- For other elements, only one version of the element can be -- checked out in the context of a workarea at any one time, for -- configurations, which do not exist in the context of workareas, -- only one version can be checked out across the entire repository -------------------------------------------------------------------- PROCEDURE check_cfg_co(i_irid IN NUMBER ,i_ivid IN NUMBER) IS co_vlabel I$SDD_OBJECT_VERSIONS.VLABEL%TYPE; co_user I$SDD_VERSION_ASSOCIATIONS.CREATED_BY%TYPE; co_from NUMBER; cfg_name I$SDD_CONFIGURATIONS.NAME%TYPE; BEGIN --Look for a checked out version BEGIN SELECT ov.vlabel,va.created_by,va.from_ivid INTO co_vlabel,co_user,co_from FROM I$SDD_OBJECT_VERSIONS ov ,I$SDD_VERSION_ASSOCIATIONS va WHERE ov.irid=i_irid AND ov.state='O' AND ov.wastebasket='N' AND va.to_ivid=ov.ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --Didn't find checkout - we're OK RETURN; END; SELECT cfg.name INTO cfg_name FROM I$SDD_CONFIGURATIONS cfg WHERE cfg.ivid=i_ivid; --Cannot checkout configuration %0!s, user %1!s has version %2!s checked out Rmmes.post('CDR',1064,cfg_name,co_user,NVL(co_vlabel ,Jr_Name.get_version_info(co_from,'BRANCH'))); RAISE CHECK_OUT_ERROR; END; -------------------------------------------------------------------- -- get_checkin_db_locks() - Private helper method -- When performing a checkout, takes out appropriate database locks -- to prevent problems with simultaneous transactions attempting -- to checkin the same object. Locks the node being checked in. -- If a tip ivid is also supplied, the tip node is also locked (used -- when checking in to the tip) -------------------------------------------------------------------- PROCEDURE get_checkin_db_locks(i_ci_ivid IN NUMBER ,i_tip_ivid IN NUMBER DEFAULT NULL) IS dummy PLS_INTEGER; BEGIN --Attempt to lock the node for check-in SELECT 1 INTO dummy FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.ivid=i_ci_ivid FOR UPDATE NOWAIT; IF i_tip_ivid IS NOT NULL THEN --Attempt to lock the tip node SELECT 1 INTO dummy FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.ivid=i_tip_ivid FOR UPDATE NOWAIT; END IF; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE; WHEN TOO_MANY_ROWS THEN RAISE; WHEN OTHERS THEN --Got resource busy error - another user --is performing a check-in of this object --in another transaction. --Another user is already performing a check-in on %1!s %0!s. Try again later. Rmmes.post('CDR',1047,Jr_Name.get_path(i_ci_ivid,'NAME') ,get_nls_type(i_ci_ivid)); RAISE CHECK_IN_ERROR; END; -------------------------------------------------------------------- -- get_checkout_db_locks() - Private helper method -- When performing a checkout, takes out appropriate database locks -- to prevent problems with simultaneous transactions attempting -- to checkout the same object. -------------------------------------------------------------------- PROCEDURE get_checkout_db_locks(i_irid IN NUMBER ,i_ivid IN NUMBER ,i_branch IN NUMBER ,i_lock IN BOOLEAN) IS dummy PLS_INTEGER; CURSOR lock_branch IS SELECT 1 FROM I$SDD_VERSION_ASSOCIATIONS va WHERE va.branch_id=i_branch AND va.irid=i_irid FOR UPDATE NOWAIT; BEGIN --We always lock the source version for the checkout - this ensures --a second checkout of the same version cannot be made whilst this --transaction is open. SELECT 1 INTO dummy FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.ivid=i_ivid FOR UPDATE NOWAIT; IF i_lock THEN --If we're checking out with a lock, we lock all versions associations --on this branch, to prevent another transaction checking out a different --version with a lock. --(By locking version associations, any uncommitted unlocked checkouts -- will not block the locked checkout). FOR lck IN lock_branch LOOP NULL; END LOOP; END IF; EXCEPTION WHEN OTHERS THEN --We couldn't acquire the necessary locks - another transaction --is performing a checkout. --Check-out currently in progress for %0!s on branch %1!s Rmmes.post('CDR',1050,Jr_Name.get_path(i_ivid,'NAME') ,get_branch_name(i_branch)); RAISE CHECK_OUT_ERROR; END; ----------------+ -- new_version() - Private helper function -- Creates a new, checked out copy of the specified object version. -- Calls the new_version function from the registration of version handlers. -- Creates a version label based on the current label. ----------------+ FUNCTION new_version(i_irid NUMBER,i_ivid NUMBER,i_vlabel VARCHAR2 ,i_version_dependencies BOOLEAN) RETURN NUMBER IS new_ivid NUMBER; o_vlabel VARCHAR2(32); func_name VARCHAR2(64); ver_deps VARCHAR2(5); curs INTEGER; stmt VARCHAR2(200); rows_proc INTEGER; constraints_were_enabled BOOLEAN := Jr_Context.constraints_enabled; BEGIN IF i_version_dependencies THEN ver_deps:='TRUE'; ELSE ver_deps:='FALSE'; END IF; --Get the current vlabel, and the function to use to create the new version BEGIN SELECT vr.new_version_function, ro.vlabel INTO func_name, o_vlabel FROM I$SDD_VERSION_REGISTRY vr , I$SDD_OBJECT_VERSIONS ro WHERE ro.irid=i_irid AND ro.ivid=i_ivid AND vr.table_irid=ro.table_irid; EXCEPTION WHEN NO_DATA_FOUND THEN --No registered handler for object with IRID = %0!s Rmmes.post('CDR',110,TO_CHAR(i_IRID)); RETURN NULL; END; IF i_vlabel IS NOT NULL THEN --Set the version label to the one specified by the user. o_vlabel := SUBSTR(i_vlabel,1,32); --Make sure it fits! IF (is_vlabel_unique(i_irid,o_vlabel) = FALSE) THEN ---The version label %!1s is already in use for %2!s %0!s Rmmes.post('CDR',1033,Jr_Name.get_path(i_ivid,'NAME') ,o_vlabel ,get_nls_type(i_ivid)); RETURN NULL; END IF; ELSIF Jr_Version_Label.get_automatic_vlabel_policy() THEN o_vlabel:=get_next_vlabel(i_irid,i_ivid); ELSE --No label specified, and we're not --auto-generating labels, so VLABEL is NULL o_vlabel:=NULL; END IF; --Build and execute the SQL to create the new version. stmt := ' begin :X:='||func_name||'('||i_irid||','||i_ivid||','||ver_deps||'); end;'; curs:= dbms_sql.open_cursor; dbms_sql.parse(curs,stmt,dbms_sql.v7); dbms_sql.bind_variable(curs,':X',new_ivid); IF constraints_were_enabled THEN Jr_Context.disable_constraints; END IF; -- CW 14-Oct-1999 -- Disable folder member creation during versioning -- Folder member creation used to be done (prior to 4.0.12) in the view 'instead-of' insert trigger -- The version functions insert into the i$ base tables and so bypass folder member creation. -- However, from 4.0.12, folder member creation is done from a trigger on the i$ base table which is -- therefore no longer bypassed. Jr_Context.disable(Jr_Context.FOLDER_MEMBERS); rows_proc := dbms_sql.execute(curs); Jr_Context.enable(Jr_Context.FOLDER_MEMBERS); IF constraints_were_enabled THEN Jr_Context.enable_constraints; END IF; dbms_sql.variable_value(curs,':X',new_ivid); dbms_sql.close_cursor(curs); --Update the new object version -- CW 27-Sep-1999 -- Fix bug 993715 Carry forward audit column values from previous version -- The bug is actually to copy obj_notm and, depending on policy, obj_notm_when_analyzed -- -- Note, when the policy for copying dependencies on versioning is implemented, the commented sql below -- (or something like it) needs to be uncommented UPDATE I$SDD_OBJECT_VERSIONS SET vlabel = o_vlabel , state = 'O' , (notm, obj_notm, date_created, created_by, date_changed, changed_by , obj_notm_when_analyzed ) = (SELECT OLD.notm , OLD.obj_notm , OLD.date_created , OLD.created_by , OLD.date_changed , OLD.changed_by , DECODE(ver_deps ,'TRUE', OLD.obj_notm_when_analyzed ,NULL ) FROM I$SDD_OBJECT_VERSIONS OLD WHERE OLD.ivid = i_ivid ) WHERE irid = i_irid AND ivid = new_ivid ; RETURN new_ivid; END; ----------------+ ---------------+ -- Simple as it may seem this function is a problem! Easy enough to remove the -- edge but how do you remove the object. ---------------+ PROCEDURE undo_checkout(i_irid NUMBER, i_ivid NUMBER) IS constraints_were_enabled BOOLEAN := Jr_Context.constraints_enabled; object_table VARCHAR2(30); object_tab_irid NUMBER; cid NUMBER; dummy NUMBER; delete_sql VARCHAR2(300); is_config BOOLEAN := FALSE; ci_version NUMBER; ci_cfg_ivid NUMBER; entry_seq NUMBER:=NULL; l_ivid NUMBER; el_type VARCHAR2(240); BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; IF is_checked_in(i_irid,l_ivid)=TRUE THEN --No check-out of %1! %0!s to undo Rmmes.post('CDR',1017,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid)); RAISE REVOKE_ERROR; END IF; --Get hold of the table the object is stored on SELECT tab.table_name ,ov.table_irid INTO object_table ,object_tab_irid FROM I$SDD_OBJECT_VERSIONS ov ,I$RM_SQL_TABLES tab WHERE tab.IRID=ov.TABLE_IRID AND ov.IVID=l_ivid; --Check if we're undoing checkout for config IF object_table='SDD_CONFIGURATIONS' THEN is_config:=TRUE; END IF; --Check object is checked out to current workarea if not is_config then check_co_ctxt_wa(l_ivid); end if; --Check user has sufficient access rights check_version_ar(i_irid); --Find the checked-in object SELECT from_ivid INTO ci_version FROM I$SDD_VERSION_ASSOCIATIONS WHERE to_ivid=l_ivid AND edge_kind!='M';--B1139752 Don't get any merge nodes! IF NOT is_config THEN --Need to lock the current workarea against update, as we're --changing the workarea contents, and need to prevent workarea --compilation whilst this transaction is in progress. Take out --a shared lock, as we don't want to block other checkout/undo operations. Jr_Workarea.lock_for_update(Jr_Context.workarea ,JR_WORKAREA.S_MODE); END IF; IF object_table='SDD_FOLDERS' THEN --If we're undoing the checkout of a folder --we need to restore to the workarea any --objects that had their memberships removed from --the folder when it was checked out restore_fol_members(l_ivid,ci_version); --We also need to remove from the workarea any objects --that had membership added when the folder was checked out remove_fol_members(l_ivid,ci_version); END IF; --Before phsically deleting the checked out PAC, we need --to tidy up any newly created SACs undo_SACs(l_ivid,object_tab_irid); --Build the delete statement --Use both irid and ivid as work around to trigger bug 902451 --Have to 'select' ivid from dual as workaround to database bug 754865 --in order to fix bug 917123 -- Changed dual to sys.dual to fix bug 1311500 -- delete_SQL:='DELETE I$'||object_table||' WHERE IVID in (SELECT '||TO_CHAR(l_ivid)||' FROM sys.DUAL)' ||' AND IRID = '||TO_CHAR(i_irid); --Execute a cursor to delete the object cid:=dbms_sql.open_cursor; dbms_sql.parse(cid,delete_SQL,DBMS_SQL.NATIVE); dummy:=dbms_sql.execute(cid); dbms_sql.close_cursor(cid); --Put the checked in object into the workarea, --so long as it's not a configuration IF (NOT is_config) THEN --Add checked-in object to I$SDD_WA_CONTEXT INSERT INTO I$SDD_WA_CONTEXT(workarea_irid,object_ivid,wastebasket,spec_entry) VALUES (Jr_Context.workarea,ci_version,'N',NULL); END IF; --Delete the checkout version associtaion (and any merge associations) DELETE FROM I$SDD_VERSION_ASSOCIATIONS WHERE irid=i_irid AND to_ivid=l_ivid; --Special case for entries in I$SDD_MUN - these are 'optional' primary objects --and have no real FKs in the schema, so do not get cascade deleted. --Other cases are handled in the triggers, but I$SDD_MUNs can refer to --other I$SDD_MUNs - this causes mutating table errors in triggers, and so --have to be handled here. IF object_table IN ('SDD_MOD','SDD_MUN') THEN --Could be undoing CO for primary MUN, or a MOD with a secondary MUN --Delete any MUNs which belonged to the undone CO delete_SQL:='delete I$SDD_MUN mun'|| ' where mun.parent_ivid='||TO_CHAR(l_ivid); cid:=dbms_sql.open_cursor; dbms_sql.parse(cid,delete_SQL,DBMS_SQL.NATIVE); dummy:=dbms_sql.execute(cid); dbms_sql.close_cursor(cid); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE REVOKE_ERROR; WHEN REVOKE_ERROR THEN --Don't trap a revoke error we've already reported RAISE; WHEN OTHERS THEN --Tell users problem has occured during checkout (may get delete blocking --errors) SELECT nls.nls_name INTO el_type FROM RM_ELEMENT_TYPE_EXTENSIONS nls ,I$SDD_OBJECT_VERSIONS ov WHERE nls.for_type=ov.logical_type_id AND ov.ivid=i_ivid; --Unable to undo checkout for %1!s %0!s Rmmes.post('CDR',1058,Jr_Name.get_path(i_ivid,'NAME'),el_type); RAISE; END; ---------------+ ----------------------------------------------------------------- -- jr_version.RESTORE_FOL_MEMBERS() PRIVATE HELPER METHOD -- This method is called by UNDO_CHECKOUT() for container elements -- It handles the restore of any object versions who had their -- foldermemberships removed from the checked out version of -- the folder. ----------------------------------------------------------------- PROCEDURE restore_fol_members(co_ivid IN NUMBER ,ci_ivid IN NUMBER) IS CURSOR get_removed_members IS SELECT ci_fm.member_object obj_irid FROM I$SDD_FOLDER_MEMBERS ci_fm WHERE ci_fm.parent_ivid=ci_ivid AND NOT EXISTS (SELECT 1 FROM I$SDD_FOLDER_MEMBERS co_fm WHERE co_fm.parent_ivid=co_ivid AND co_fm.member_object=ci_fm.member_object); BEGIN FOR fm IN get_removed_members LOOP -- BUG 1491529 psycamor - no data found exception thrown for non versioned object -- in the wastebasket - added block to catch exception BEGIN UPDATE I$SDD_WA_CONTEXT ctxt SET ctxt.wastebasket = 'N' WHERE ctxt.WORKAREA_IRID=Jr_Context.workarea AND ctxt.object_ivid IN (SELECT ov.ivid FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.irid=fm.obj_irid); EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; END LOOP; END; ----------------------------------------------------------------- -- jr_version.REMOVE_FOL_MEMBERS() PRIVATE HELPER METHOD -- This method is called by UNDO_CHECKOUT() for container elements -- It handles the removal of any object versions who had their -- foldermemberships created in the checked out version of -- the folder. ----------------------------------------------------------------- PROCEDURE remove_fol_members(co_ivid IN NUMBER ,ci_ivid IN NUMBER) IS CURSOR get_added_members IS SELECT co_fm.member_object obj_irid FROM I$SDD_FOLDER_MEMBERS co_fm WHERE co_fm.parent_ivid=co_ivid AND co_fm.ownership_flag='Y' --Not concerned about any references that were created. AND NOT EXISTS (SELECT 1 FROM I$SDD_FOLDER_MEMBERS ci_fm WHERE ci_fm.parent_ivid=ci_ivid AND ci_fm.member_object=co_fm.member_object); ov_ivid number; elem_type rm_element_types.short_name%TYPE; prod_code rm_element_types.product%TYPE; BEGIN --Delete the workarea entry for each object that was added --(i.e. created in this container - objects will go to LOST_AND_FOUND) FOR fm IN get_added_members LOOP -- BUG 1491529 psycamor - no data found exception thrown for non versioned object -- in the wastebasket - added block to catch exception BEGIN --Get hold of object ivid and type SELECT ov.ivid, st.short_name, st.product INTO ov_ivid, elem_type, prod_code FROM SDD_OBJECT_VERSIONS ov ,RM_ELEMENT_TYPES et ,RM_ELEMENT_TYPES st WHERE ov.irid=fm.obj_irid AND et.id=ov.logical_type_id AND st.id=et.supertypes; --If member is a container, we need to remove it and all its contents. if prod_code='CI' and elem_type='CEL' then remove_sub_fol(ov_ivid); else DELETE I$SDD_WA_CONTEXT ctxt WHERE ctxt.WORKAREA_IRID=Jr_Context.workarea AND ctxt.object_ivid =ov_ivid; end if; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; END; END LOOP; END; ----------------------------------------------------------------- -- jr_version.REMOVE_SUB_FOL() PRIVATE HELPER METHOD -- Called by REMOVE_FOL_CONTENTS to handle special case -- of removing a sub folder from a folder. ----------------------------------------------------------------- procedure remove_sub_fol(fol_ivid in number) is cursor fol_contents is SELECT ov.ivid ov_ivid ,st.short_name elem_type ,st.product prod_Code FROM I$SDD_FOLDER_MEMBERS fm ,SDD_OBJECT_VERSIONS ov ,RM_ELEMENT_TYPES et ,RM_ELEMENT_TYPES st WHERE ov.irid=fm.member_object AND et.id=ov.logical_type_id AND st.id=et.supertypes AND fm.parent_ivid=fol_ivid; begin for fm in fol_contents loop if fm.prod_code='CI' and fm.elem_type='CEL' then --Object is a sub folder, so recurse remove_sub_fol(fm.ov_ivid); else --Remove the object from the workarea. DELETE I$SDD_WA_CONTEXT ctxt WHERE ctxt.WORKAREA_IRID=Jr_Context.workarea AND ctxt.object_ivid =fm.ov_ivid; end if; --Remove the container itself DELETE I$SDD_WA_CONTEXT ctxt WHERE ctxt.WORKAREA_IRID=Jr_Context.workarea AND ctxt.object_ivid =fol_ivid; end loop; end; ----------------------------------------------------------------- -- jr_version.UNDO_SACS() PRIVATE HELPER METHOD -- This method is called by UNDO_CHECKOUT(). -- It handles the delete of any newly create SACs, ensuring -- referential integrity is maintained. ----------------------------------------------------------------- PROCEDURE undo_SACs(co_ivid IN NUMBER ,par_table IN NUMBER) IS CURSOR get_children IS SELECT DISTINCT tab.name tab_name, tab.irid tab_irid FROM RM_SQL_TABLES tab ,RM_SQL_CONSTRAINTS con1 ,RM_SQL_CONSTRAINTS con2 WHERE tab.irid=con2.table_irid AND con2.r_constraint_name=con1.constraint_name AND con2.is_owning_fk!='N' AND con2.table_irid!=con1.table_irid AND con1.table_irid=par_table; stmt VARCHAR2(1000); csr PLS_INTEGER; exec PLS_INTEGER; BEGIN --Loop over each table containing secondary elements of the current table csr:=DBMS_SQL.OPEN_CURSOR; FOR chld IN get_children LOOP --Delete any elements created for the checkout. --Delete through the views to ensure referential integrety checks are --carried out. if chld.tab_name = 'SDD_MTI' then -- bug 2189346 CDR-01058: UNABLE TO UNDO CHECKOUT -- FOR MODULES HAVING CHAINED LOOKUPS -- -- Iteratively removes newly added MTI SACs of the PAC -- (co_ivid). The MTIs are removed in reverse chain order, -- as you cannot remove a mid-chain MTI (trigger error - -- see bug). -- -- E.g. Given: ->MTI1(N)->MTI2(N) -- You cannot remove MTI1 until MTI2 has been removed. -- -- rjm 12-sep-2002 loop delete from SDD_MTI new_mti where new_mti.parent_ivid = co_ivid -- parent is PAC and not exists( -- end nodes only select null from SDD_MTI blocking_mti where blocking_mti.mti_ref = new_mti.irid ) and not exists( -- new MTIs only select 1 from I$SDD_MTI old_mti where old_mti.irid = new_mti.irid and old_mti.parent_ivid != new_mti.parent_ivid ); -- no rows deleted if all new (to co) refs removed exit when sql%rowcount = 0; end loop; else stmt:='DELETE '||chld.tab_name||' new_el'|| ' WHERE new_el.parent_ivid='||TO_CHAR(co_ivid)|| ' AND NOT EXISTS (SELECT 1 '|| ' FROM I$'||chld.tab_name||' old_el'|| ' WHERE old_el.irid=new_el.irid'|| ' AND old_el.parent_ivid!=new_el.parent_ivid)'; DBMS_SQL.PARSE(csr,stmt,DBMS_SQL.NATIVE); exec:=DBMS_SQL.EXECUTE(csr); end if; --now recurse to get any children of this element type undo_SACS(co_ivid,chld.tab_irid); END LOOP; IF DBMS_SQL.IS_OPEN(csr) THEN DBMS_SQL.CLOSE_CURSOR(csr); END IF; EXCEPTION WHEN OTHERS THEN IF DBMS_SQL.IS_OPEN(csr) THEN DBMS_SQL.CLOSE_CURSOR(csr); END IF; RAISE; END; ---------------+ -- Create a "dense branch". -- -- Normally, branches are only created on check in. However, using this method -- you can do it anytime (on a checked in node). -- It's a macro for check_out then check_in_branch -- returns the ivid of the branch object. ---------------+ FUNCTION branch(i_irid NUMBER, i_ivid NUMBER,i_branch_id NUMBER, i_notes VARCHAR2,i_vlabel VARCHAR2 ,i_version_dependencies BOOLEAN DEFAULT TRUE) RETURN NUMBER IS temp_ivid NUMBER; new_ivid NUMBER; check_in_state BOOLEAN; l_ivid NUMBER; BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; --Check user has correct access rights to version object check_version_ar(i_irid); -- check valid state first check_in_state := is_checked_in(i_irid,l_ivid); IF check_in_state <> TRUE THEN --%1!s %0!s not checked in Rmmes.post('CDR',1018,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid)); RAISE BRANCH_ERROR; END IF; IF valid_branch_id(i_branch_id)<>TRUE THEN --Branch with ID = %0!s does not exist. Rmmes.post('CDR',109,TO_CHAR(i_branch_id)); RAISE BRANCH_ERROR; END IF; IF is_already_on_branch(i_irid,i_branch_id)=TRUE THEN --%1!s %0!s already on branch %2!s Rmmes.post('CDR',1014,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid) ,get_branch_name(i_branch_id)); RAISE BRANCH_ERROR; END IF; temp_ivid := check_out_sub(i_irid,l_ivid,FALSE,TRUE,NULL,NULL ,i_version_dependencies); new_ivid := check_in_branch(i_irid,temp_ivid,i_branch_id,i_notes,i_vlabel); RETURN new_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid),TO_CHAR(i_ivid)); RAISE BRANCH_ERROR; END; ---------------- ---------------------------------------------------- -- Get hold of an ivid from an irid -- and the current workarea context --------------------------------------------------- FUNCTION get_ivid(i_irid IN NUMBER) RETURN NUMBER IS l_ivid NUMBER; wa_irid NUMBER; BEGIN wa_irid:=Jr_Context.workarea; --Can only get ivid if we have a workarea context IF wa_irid IS NULL THEN --Workarea context has not been set. Rmmes.post('CDR',100); RAISE CHECK_IN_ERROR; END IF; SELECT ivid INTO l_ivid FROM sdd_object_versions WHERE irid=i_irid; RETURN l_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid)); RAISE CHECK_IN_ERROR; END; ---------------- -- Merge 2 nodes. -- The second node must be checked out -- returns merge node (2nd arg). ---------------- FUNCTION merge(i_irid NUMBER ,i_ivid1 NUMBER --Source ,i_ivid2 NUMBER --Target ,i_notes VARCHAR2 ,i_vlabel VARCHAR2 ,i_do_check_in BOOLEAN DEFAULT TRUE) RETURN NUMBER IS temp NUMBER; l_branch_id NUMBER; BEGIN IF NOT is_checked_in(i_irid,i_ivid1) THEN --Source version %0!s not checked in. Rmmes.post('CDR',1019,Jr_Name.get_path(i_ivid1,'BRANCH')); RAISE MERGE_ERROR; END IF; --Enforce target is checked out IF NOT is_checked_out(i_irid,i_ivid2) THEN --Target version %0!s not checked out. Rmmes.post('CDR',1020,Jr_Name.get_path(i_ivid2,'BRANCH')); RAISE MERGE_ERROR; END IF; l_branch_id := get_current_branch_id(i_irid,i_ivid1); -- check in 2nd node IF i_do_check_in THEN temp := check_in_at_tip(i_irid,i_ivid2,i_notes,i_vlabel); END IF; -- insert the merging assoc INSERT INTO I$SDD_VERSION_ASSOCIATIONS (irid ,from_ivid ,to_ivid ,edge_kind ,created_by ,date_created ,branch_id ,notes) VALUES (i_irid,i_ivid1,i_ivid2,'M',USER,SYSDATE,l_branch_id,i_notes); RETURN i_ivid2; EXCEPTION WHEN NO_DATA_FOUND THEN --No versionable object with IRID: %0!s and IVID: 1!s Rmmes.post('CDR',113,TO_CHAR(i_irid)); RAISE MERGE_ERROR; END; ---------------+ ---------------+ -- Change a checkout to a locked checkout ---------------+ PROCEDURE lock_checkout(i_irid NUMBER, i_ivid NUMBER) IS branch_id NUMBER; l_ivid NUMBER; locking_user VARCHAR2(30); locking_wa I$SDD_WORKAREAS.NAME%TYPE; BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; branch_id:=get_current_branch_id(i_irid,l_ivid); --Get a database lock on all objects on this branch to --avoid problems with simultaneous transactions trying to lock --a checkout/perform checkout with lock... get_checkout_db_locks(i_irid,l_ivid,branch_id,TRUE); IF is_checked_in (i_IRID,l_ivid) THEN --%1!s %0!s not checked out Rmmes.post('CDR',1012,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid)); RAISE NOT_CHECKED_OUT; END IF; --Only update if check-out isn't locked already IF is_branch_locked(i_irid,branch_id) THEN locking_user:=get_lock_holder(i_irid,branch_id); SELECT wa.name INTO locking_wa FROM I$SDD_WORKAREAS wa ,I$SDD_WA_CONTEXT ctxt WHERE wa.irid=ctxt.workarea_irid AND ctxt.object_ivid=get_locking_version(i_irid,branch_id); -- %0!s already locked by user %!1s in workarea %!2s Rmmes.post('CDR',1015,Jr_Name.get_path(i_ivid,'NAME') ,locking_user ,locking_wa); RAISE CHECK_OUT_ERROR; ELSE -- CW 21-Jul-2000 Fix bug 1346680, this update was setting edge kind to 'L' -- for Merged associations to this object version UPDATE I$SDD_VERSION_ASSOCIATIONS SET edge_kind = 'L' WHERE to_ivid = l_ivid AND edge_kind != 'M' ; --Also update lock flag on I$SDD_OBJECT_VERSIONS UPDATE I$SDD_OBJECT_VERSIONS ov SET ov.lock_flag='Y' WHERE ov.ivid=l_ivid; END IF; END; ----------------+ --Unlock a locked checkout ----------------+ PROCEDURE unlock_checkout(i_irid NUMBER,i_ivid NUMBER) IS l_ivid NUMBER; BEGIN l_ivid:=i_ivid; --Get hold of ivid if it's null IF l_ivid IS NULL THEN l_ivid:=get_ivid(i_irid); END IF; IF is_checked_in (i_IRID,l_ivid) THEN --%1!s %0!s not checked out Rmmes.post('CDR',1012,Jr_Name.get_path(l_ivid,'NAME') ,get_nls_type(l_ivid)); RAISE NOT_CHECKED_OUT; END IF; --Only unlock if check-out is locked. IF is_locked(i_irid,l_ivid) THEN UPDATE I$SDD_VERSION_ASSOCIATIONS SET edge_kind='C' WHERE to_ivid = l_ivid AND edge_kind='L'; --Also update lock flag on I$SDD_OBJECT_VERSIONS UPDATE I$SDD_OBJECT_VERSIONS ov SET ov.lock_flag='N' WHERE ov.ivid=l_ivid AND ov.lock_flag='Y'; END IF; END; ---------------+ -- just get the label. Handy but non-optimal ---------------+ FUNCTION get_vlabel(i_irid NUMBER , i_ivid NUMBER ) RETURN VARCHAR2 IS o_vlabel VARCHAR2(32); BEGIN SELECT vlabel INTO o_vlabel FROM I$SDD_OBJECT_VERSIONS WHERE ivid=i_ivid; RETURN o_vlabel; END; ---------------+ ---------------+ --PRIVATE FUNCTION --Checks for uniqueness of version labels ---------------+ FUNCTION is_vlabel_unique(i_irid NUMBER, i_vlabel VARCHAR2) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.irid = i_irid AND ov.vlabel = i_vlabel; IF cnt > 0 THEN RETURN FALSE; ELSE RETURN TRUE; END IF; END; ------------------------------+ -- Get the notes for this node ------------------------------+ FUNCTION get_notes(i_irid NUMBER, i_ivid NUMBER ) RETURN VARCHAR2 IS o_notes VARCHAR2(2000); BEGIN SELECT notes INTO o_notes FROM SDD_VERSION_ASSOCIATIONS WHERE irid = i_irid AND to_ivid = i_ivid AND edge_kind NOT IN ('M','T'); --Avoid getting multiple notes from merge/tip edges RETURN o_notes; EXCEPTION WHEN NO_DATA_FOUND THEN --Could not find Repository Object with IVID=%0!s. Rmmes.post('CDR',101,TO_CHAR(i_ivid)); RETURN NULL; END; ---------------+ -- find first node in a tree. ---------------- FUNCTION get_root (i_irid IN NUMBER) RETURN NUMBER IS root_ivid NUMBER; BEGIN SELECT to_ivid INTO root_ivid FROM sdd_version_associations va WHERE va.irid=i_irid AND va.edge_kind='R'; RETURN root_ivid; END; ---------------+ -- find most recent version of object. -- corresponding to the current branch -- of the specified node ----------------+ FUNCTION get_tip_on_current_branch (i_irid IN NUMBER, i_ivid IN NUMBER) RETURN NUMBER IS tip_ivid NUMBER; this_branch_id NUMBER; tip_identifier VARCHAR2(1); BEGIN -- is it the tip itself ?? IF never_checked_in(i_irid) THEN RETURN i_ivid; END IF; this_branch_id:=get_current_branch_id(i_irid,i_ivid); SELECT from_ivid INTO tip_ivid FROM sdd_version_associations va WHERE va.irid=i_irid AND va.edge_kind='T' AND branch_id=this_branch_id; RETURN tip_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN -- (ie never checked in) RETURN i_ivid; END; ---------------+ ----------------------------------------+ -- Returns TRUE if the specified version -- if the latest version on a branch ----------------------------------------+ FUNCTION is_latest(i_ivid IN NUMBER) RETURN BOOLEAN IS dummy NUMBER; BEGIN SELECT va.to_ivid INTO dummy FROM SDD_VERSION_ASSOCIATIONS va WHERE va.to_ivid=i_ivid AND va.edge_kind='T'; --If we found a row, this is the latest RETURN TRUE; EXCEPTION WHEN NO_DATA_FOUND THEN --We didn't find a row, it's not the latest RETURN FALSE; END; ----------------------------------------+ -- Get the predecessor for this version -- Returns null if no predecessor exists ----------------------------------------+ FUNCTION get_predecessor(i_ivid NUMBER) RETURN NUMBER IS pred_ivid NUMBER; BEGIN SELECT va.from_ivid INTO pred_ivid FROM sdd_version_associations va WHERE va.to_ivid=i_ivid AND va.edge_kind NOT IN ('R','T','M'); -- Ignore roots, tips and merges RETURN pred_ivid; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; ---------------+ -- test if a branch has been merged. ---------------+ FUNCTION branch_has_merged(i_irid NUMBER,i_branch_id NUMBER) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM sdd_version_associations WHERE i_irid=i_irid AND branch_id=i_branch_id AND edge_kind='M'; IF cnt<>0 THEN RETURN TRUE; END IF; RETURN FALSE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; ---------------+ ---------------+ -- find salient nodes in a tree. -- find tip, common_node, current branch_id also -- ONLY apply to checked out nodes! It won't find common node correctly otherwise. -- however the other output parameters are correct. ---------------- PROCEDURE get_associated_nodes(i_irid IN NUMBER , i_ivid IN NUMBER , o_tip_ivid OUT NUMBER, o_common_ivid OUT NUMBER, o_branch_id OUT NUMBER ) IS o_edge_kind VARCHAR2(1); BEGIN -- get previous edge to give common node - don't get merges SELECT from_ivid, branch_id, edge_kind INTO o_common_ivid, o_branch_id , o_edge_kind FROM sdd_version_associations WHERE to_ivid=i_ivid AND edge_kind!='M'; -- if too many rows it's not checked out IF o_edge_kind<>'L' AND o_edge_kind<>'C' THEN RAISE NOT_CHECKED_OUT; END IF; SELECT from_ivid INTO o_tip_ivid FROM sdd_version_associations WHERE irid=i_irid AND branch_id=o_branch_id AND edge_kind='T'; EXCEPTION WHEN NO_DATA_FOUND THEN -- that is there are no previous nodes (ie never checked in) o_tip_ivid := i_ivid; --Branch will be the MAIN branch. SELECT BRANCH_ID INTO o_branch_id FROM I$SDD_BRANCHES WHERE NAME = 'MAIN'; WHEN TOO_MANY_ROWS THEN RAISE NOT_CHECKED_OUT; WHEN NOT_CHECKED_OUT THEN RAISE NOT_CHECKED_OUT; END; ---------------+ ---------------+ -- Looks at the edges before this node and gets the branch id. -- If there is more than one, then its the min. -- note the count(*) because min() always returns rows; ---------------+ FUNCTION get_current_branch_id (i_irid NUMBER , i_ivid NUMBER ) RETURN NUMBER IS branch_id NUMBER; row_c NUMBER; BEGIN SELECT MIN(branch_id) , COUNT(*) INTO branch_id , row_c FROM sdd_version_associations WHERE irid=i_irid AND to_ivid=i_ivid AND edge_kind!='M'; --Merge associations will always point at a different branch! IF row_c = 0 THEN branch_id := 0; END IF; RETURN branch_id; END; ---------------- ---------------+ -- How many child branches are there of this node ? ---------------+ FUNCTION get_number_of_branches (i_irid NUMBER , i_ivid NUMBER ) RETURN NUMBER IS n_branches NUMBER; BEGIN SELECT COUNT(*) INTO n_branches FROM sdd_version_associations WHERE irid=i_irid AND from_ivid=i_ivid AND edge_kind='B'; RETURN n_branches; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN 0; END; ---------------+ ----------------+ -- tests if a node is in the checked_in state. -- can specify just irid if workarea context is set ----------------+ FUNCTION is_checked_in(i_irid NUMBER,i_ivid NUMBER ) RETURN BOOLEAN IS ov_state VARCHAR2(1); BEGIN IF i_ivid IS NULL AND Jr_Context.workarea IS NULL THEN --Can't locate object with just irid oustide of workarea context RAISE NO_DATA_FOUND; END IF; IF i_ivid IS NULL THEN SELECT ov.state INTO ov_state FROM sdd_object_Versions ov WHERE ov.irid=i_irid; ELSE SELECT ov.state INTO ov_state FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.ivid=i_ivid; END IF; IF ov_state='I' THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END; ---------------- ----------------+ -- tests if a node is in the checked_out state. -- can specify just irid if workarea context is set -- returns FALSE if node is non-versioned or checked-in ----------------+ FUNCTION is_checked_out(i_irid NUMBER,i_ivid NUMBER ) RETURN BOOLEAN IS ov_state VARCHAR2(1); BEGIN IF i_ivid IS NULL AND Jr_Context.workarea IS NULL THEN --Can't locate object with just irid oustide of workarea context RAISE NO_DATA_FOUND; END IF; IF i_ivid IS NULL THEN SELECT ov.state INTO ov_state FROM sdd_object_Versions ov WHERE ov.irid=i_irid; ELSE SELECT ov.state INTO ov_state FROM I$SDD_OBJECT_VERSIONS ov WHERE ov.ivid=i_ivid; END IF; IF ov_state='O' THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END; ---------------- -- tests if a node has never be checked_in (not part of graph yet). ---------------- FUNCTION never_checked_in(i_irid NUMBER ) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM I$SDD_VERSION_ASSOCIATIONS WHERE irid=i_irid ; IF cnt=0 THEN RETURN TRUE; END IF; RETURN FALSE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN TRUE; END; ---------------- ---------------- -- tests if an object is locked. That is checked out with a lock. ---------------- FUNCTION is_locked(i_irid NUMBER,i_ivid NUMBER) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM sdd_version_associations WHERE irid=i_irid AND to_ivid=i_ivid AND edge_kind='L'; IF cnt<>0 THEN RETURN TRUE; END IF; RETURN FALSE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; ---------------- -----------------+ -- returns the user (if any) holding the lock on the same -- branch and object as the specified version -----------------+ FUNCTION get_lock_holder(i_ivid NUMBER) RETURN VARCHAR2 IS l_irid NUMBER; l_branch_id NUMBER; BEGIN SELECT va.irid,va.branch_id INTO l_irid,l_branch_id FROM SDD_VERSION_ASSOCIATIONS va WHERE va.to_ivid=i_ivid AND va.edge_kind NOT IN ('T','M'); RETURN get_lock_holder(l_irid,l_branch_id); EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; -----------------+ -- returns the user (if any) holding the lock on the specified -- branch and object. -----------------+ FUNCTION get_lock_holder(i_irid NUMBER,i_branch_id NUMBER) RETURN VARCHAR2 IS locking_user VARCHAR2(30); BEGIN --Get changed_by if it's there, as user --may have changed checkout to be locked SELECT NVL(va.changed_by,va.created_by) INTO locking_user FROM SDD_VERSION_ASSOCIATIONS va WHERE va.branch_id=i_branch_id AND va.irid=i_irid AND va.edge_kind='L'; RETURN locking_user; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; -----------------+ -- returns the ivid of the version (if any) holding the lock on the same -- branch and object as the specified version -----------------+ FUNCTION get_locking_version(i_ivid NUMBER) RETURN NUMBER IS l_irid NUMBER; l_branch_id NUMBER; BEGIN SELECT va.irid,va.branch_id INTO l_irid,l_branch_id FROM SDD_VERSION_ASSOCIATIONS va WHERE va.to_ivid=i_ivid AND va.edge_kind NOT IN ('T','M'); RETURN get_locking_version(l_irid,l_branch_id); EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; -----------------+ -- returns the ivid of the version (if any) holding the lock on the specified -- branch and object. -----------------+ FUNCTION get_locking_version(i_irid NUMBER,i_branch_id NUMBER) RETURN NUMBER IS locking_version NUMBER; BEGIN SELECT va.to_ivid INTO locking_version FROM SDD_VERSION_ASSOCIATIONS va WHERE va.irid=i_irid AND va.branch_id=i_branch_id AND va.edge_kind='L'; RETURN locking_version; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; ---------------- -- tests if a branch is locked ---------------- FUNCTION is_branch_locked(i_irid NUMBER,i_branch_id NUMBER) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM sdd_version_associations WHERE irid=i_irid AND edge_kind='L' AND branch_id=i_branch_id; IF cnt<>0 THEN RETURN TRUE; END IF; RETURN FALSE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; ---------------- ---------------- -- tests if object has any versions that are checked out ---------------- FUNCTION is_any_version_checked_out(i_irid NUMBER) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM sdd_version_associations WHERE (irid=i_irid AND edge_kind='L') OR (irid=i_irid AND edge_kind='C'); IF cnt<>0 THEN RETURN TRUE; END IF; RETURN FALSE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; ---------------- ----------------------------------------------- -- returns the ivid of the node checked out from -- the specified node, in the current workarea. -- If no node is checked out from this node -- in the current workarea, returns null. -- If no data is found it returns null. -- ----------------------------------------------- FUNCTION get_checkout(i_ivid NUMBER) RETURN NUMBER IS co_ivid NUMBER; BEGIN --If no workarea context set, return null. IF Jr_Context.workarea IS NULL THEN RETURN NULL; END IF; Begin SELECT ov.ivid INTO co_ivid FROM SDD_OBJECT_VERSIONS ov ,I$SDD_VERSION_ASSOCIATIONS va WHERE va.from_ivid = i_ivid AND ov.ivid = va.to_ivid AND va.edge_kind IN ('C','L'); EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; end; RETURN co_ivid; END; ---------------- -- Test check out status of branch. -- TRUE is anything on branch is checked out. FUNCTION is_checked_out_on_branch(i_irid NUMBER,i_branch_id NUMBER) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM sdd_version_associations WHERE (irid=i_irid AND branch_id=i_branch_id AND edge_kind='L') OR (irid=i_irid AND branch_id=i_branch_id AND edge_kind='C') ; IF cnt<>0 THEN RETURN TRUE; END IF; RETURN FALSE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; ---------------- ----------------------------------------------------- -- Test if object is checked out in current workarea ----------------------------------------------------- FUNCTION checked_out_in_workarea(i_irid NUMBER) RETURN NUMBER IS cnt NUMBER; BEGIN IF Jr_Context.workarea IS NULL THEN --No workarea context set, return 'FALSE' RETURN 0; END IF; SELECT COUNT(*) INTO cnt FROM SDD_OBJECT_VERSIONS WHERE irid=i_irid AND state = 'O'; IF cnt = 0 THEN RETURN 0; ELSE RETURN 1; END IF; END; ---------------- -- If a node is checked out, find where it came from. -- no test that object is checked out. It really just finds the previous node -- so don't try it on a merge node ! ---------------- FUNCTION get_original_of_checkout(i_irid NUMBER ,i_ivid NUMBER ) RETURN NUMBER IS o_ivid NUMBER; l_edge_kind VARCHAR2(1); BEGIN SELECT from_ivid, edge_kind INTO o_ivid,l_edge_kind FROM sdd_version_associations WHERE irid=i_irid AND to_ivid=i_ivid AND edge_kind NOT IN('T','M'); IF l_edge_kind<>'L' AND l_edge_kind<>'C' THEN RAISE NOT_CHECKED_OUT; END IF; RETURN o_ivid; END; ---------------- ---------------- -- use to create a branch. branches must exist before use. -- Check that branch name is unique. ---------------- FUNCTION mk_branch (i_name VARCHAR2,i_notes VARCHAR2) RETURN NUMBER IS new_branch_id NUMBER; existing_branch_label I$SDD_BRANCHES.NAME%TYPE; BEGIN --Check repos supports versioning IF NOT Jr_Sys_Privs.is_versioned_repos THEN --Cannot perform versioning operations in non-versioned repository Rmmes.post('CDR',1053); RAISE Jr_Acc_Rights.ACCESS_RIGHTS_ERROR; END IF; --Check user can manage branches Jr_Sys_Privs.check_manage(USER,'BR'); SELECT MIN(br.name) INTO existing_branch_label FROM I$SDD_BRANCHES br WHERE NLS_UPPER(br.NAME) = NLS_UPPER(i_name); IF existing_branch_label IS NOT NULL THEN --Branch with name %0!s already exists. Rmmes.post('CDR',1021,existing_branch_label); RAISE BRANCH_ERROR; END IF; new_branch_id := Jr_System_Util.get_new_branch_id(); INSERT INTO I$SDD_BRANCHES (BRANCH_ID,NAME,DATE_CREATED,CREATED_BY,NOTES) VALUES (new_branch_id,i_name,SYSDATE,USER,i_notes); RETURN new_branch_id; END; ---------------- ---------------- -- use to delete a branch. -- cannot delete a brach that is being used ---------------- PROCEDURE delete_branch(i_branch_id IN NUMBER) IS dummy NUMBER; BEGIN --Check user can manage branches Jr_Sys_Privs.check_manage(USER,'BR'); --Check if branch is being used - a branch is used if an object --version exists on the branch - even if it is the wastebasket (it --may be restored back onto the branch!) SELECT COUNT(*) INTO dummy FROM I$SDD_VERSION_ASSOCIATIONS va ,I$SDD_OBJECT_VERSIONS ov WHERE va.branch_id = i_branch_id AND va.to_ivid=ov.ivid; IF dummy > 0 THEN --Cannot delete a branch label (%0!s) that is being used Rmmes.post('CDR',1030,get_branch_name(i_branch_id)); RAISE BRANCH_ERROR; ELSE DELETE I$SDD_BRANCHES WHERE BRANCH_ID = i_branch_id; END IF; END; ---------------- -- use to change a branch label ---------------- PROCEDURE change_branch_label(i_branch_id IN NUMBER, i_new_name IN VARCHAR2) IS dummy PLS_INTEGER; other_branch I$SDD_BRANCHES.NAME%TYPE; BEGIN --Check user can manage branches Jr_Sys_Privs.check_manage(USER,'BR'); --Check that the branch id is valid IF NOT valid_branch_id(i_branch_id) THEN --Branch with ID = %0!s does not exist. Rmmes.post('CDR',109,TO_CHAR(i_branch_id)); RAISE BRANCH_ERROR; END IF; --Check that new name is unique SELECT MIN(br.name) INTO other_branch FROM I$SDD_BRANCHES br WHERE NLS_UPPER(br.NAME) = NLS_UPPER(i_new_name) AND br.branch_id!=i_branch_id; IF other_branch IS NOT NULL THEN --Branch with name %0!s already exists. Rmmes.post('CDR',1021,i_new_name); RAISE BRANCH_ERROR; END IF; --Check if branch label is being used SELECT COUNT(va.irid) INTO dummy FROM I$SDD_VERSION_ASSOCIATIONS va ,I$SDD_OBJECT_VERSIONS ov WHERE va.branch_id = i_branch_id AND va.to_ivid = ov.ivid; IF dummy > 0 THEN --Cannot rename a branch label (%0!s) that is being used Rmmes.post('CDR',1037,get_branch_name(i_branch_id)); RAISE BRANCH_ERROR; END IF; UPDATE I$SDD_BRANCHES SET NAME = i_new_name WHERE BRANCH_ID = i_branch_id; END; ---------------- -- use to change a branch label notes ---------------- PROCEDURE change_branch_label_notes(i_branch_id IN NUMBER, i_new_notes IN VARCHAR2) IS BEGIN --Check user can manage branches Jr_Sys_Privs.check_manage(USER,'BR'); --Check that the branch id is valid IF NOT valid_branch_id(i_branch_id) THEN --Branch with ID = %0!s does not exist. Rmmes.post('CDR',109,TO_CHAR(i_branch_id)); RAISE BRANCH_ERROR; END IF; UPDATE I$SDD_BRANCHES SET NOTES = i_new_notes WHERE BRANCH_ID = i_branch_id; END; ---------------- -- test if given number really is a branch ---------------- FUNCTION valid_branch_id( i_branch_id NUMBER ) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM I$SDD_BRANCHES WHERE branch_id=i_branch_id; IF cnt=0 THEN RETURN FALSE; END IF; RETURN TRUE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; ---------------- ---------------- -- take branch name and get ID. Ret null if not valid. ---------------- FUNCTION get_branch_id( i_branch_name VARCHAR2) RETURN NUMBER IS o_branch_id NUMBER; BEGIN SELECT branch_id INTO o_branch_id FROM I$SDD_BRANCHES WHERE name=i_branch_name; RETURN o_branch_id; EXCEPTION WHEN NO_DATA_FOUND THEN --No branch exists with name %0!s. Rmmes.post('CDR',1022,i_branch_name); RETURN NULL; END; ---------------- ---------------- -- take branch ID and get name. Ret null if not valid. ---------------- FUNCTION get_branch_name( i_branch_id NUMBER) RETURN VARCHAR2 IS o_branch_name VARCHAR2(128); BEGIN SELECT name INTO o_branch_name FROM I$SDD_BRANCHES WHERE branch_id=i_branch_id; RETURN o_branch_name; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; ---------------- ---------------- -- is the object already branched on the branch? -- A graph for a single object can only use a branch ID once. This checks for that error! ---------------- FUNCTION is_already_on_branch(i_irid NUMBER,i_branch_id NUMBER ) RETURN BOOLEAN IS cnt NUMBER; BEGIN SELECT COUNT(*) INTO cnt FROM sdd_version_associations va WHERE va.irid=i_irid AND va.branch_id=i_branch_id; IF cnt=0 THEN RETURN FALSE; END IF; RETURN TRUE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END; ---------------- ---------------- -- Use to enable or diable strict locking. ---------------- PROCEDURE strict_lock_policy (i_enable BOOLEAN) IS BEGIN IF i_enable THEN Jr_Policy.set_policy('strict_lock','TRUE'); ELSE Jr_Policy.set_policy('strict_lock','FALSE'); END IF; END; ---------------- ---------------- -- Use to find out if strict locking is enabled ---------------- FUNCTION get_strict_lock_policy RETURN BOOLEAN IS val VARCHAR2(30); BEGIN val := Jr_Policy.get_policy('strict_lock'); IF val IS NULL OR val='FALSE' THEN RETURN FALSE; ELSE RETURN TRUE; END IF; END; ---------------- -- Use to enable or disable automatic branching. ---------------- PROCEDURE autobranch_policy (i_enable BOOLEAN) IS BEGIN IF i_enable THEN Jr_Policy.set_policy('autobranch','TRUE'); ELSE Jr_Policy.set_policy('autobranch','FALSE'); END IF; END; ---------------- ---------------- -- Use to find out if automatic branching is enabled ---------------- FUNCTION get_autobranch_policy RETURN BOOLEAN IS val VARCHAR2(30); BEGIN val := Jr_Policy.get_policy('autobranch'); IF val IS NULL OR val='FALSE' THEN RETURN FALSE; END IF; RETURN TRUE; END; ---------------- -------------------------------------------------------------------------- -- Wrappers for BRANCH methods to provide more consistent names -- (we wrapper rather than rename methods to avoid breaking existing code) -------------------------------------------------------------------------- --Wrappers delete_branch PROCEDURE delete_branch_label(i_branch_id IN NUMBER) IS BEGIN delete_branch(i_branch_id); END; --Wrappers get_branch_name FUNCTION get_branch_label(i_branch_id NUMBER) RETURN VARCHAR2 IS BEGIN RETURN get_branch_name(i_branch_id); END; --Wrappers mk_branch FUNCTION create_branch_label(i_name VARCHAR2,i_notes VARCHAR2) RETURN NUMBER IS BEGIN RETURN mk_branch(i_name,i_notes); END; ------------------------------------------------ -- Wrappers for functions that return BOOLEAN -- values (for JDBC and OCI). -- Wrappers return NUMBER instead : -- 1 == TRUE -- 0 == FALSE ------------------------------------------------ FUNCTION merge_needed(i_irid IN NUMBER, i_ivid IN NUMBER, i_tip_ivid OUT NUMBER, i_common_ivid OUT NUMBER, i_at_tip IN NUMBER DEFAULT 0)RETURN NUMBER IS b_at_tip BOOLEAN; BEGIN IF i_at_tip = 0 THEN b_at_tip := FALSE; ELSE b_at_tip := TRUE; END IF; IF is_merge_needed(i_irid,i_ivid,i_tip_ivid,i_common_ivid,b_at_tip) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION has_version_changed(i_ivid1 IN NUMBER ,i_ivid2 IN NUMBER:=NULL) RETURN NUMBER IS BEGIN IF has_changed(i_ivid1,i_ivid2) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION container_checkin_reqd(i_ivid IN NUMBER) RETURN NUMBER IS BEGIN IF is_container_checkin_reqd(i_ivid) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION autobranch_policy RETURN NUMBER IS BEGIN IF get_autobranch_policy THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION strict_lock_policy RETURN NUMBER IS BEGIN IF get_strict_lock_policy THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION already_on_branch(i_irid NUMBER,i_branch_id NUMBER ) RETURN NUMBER IS BEGIN IF is_already_on_branch(i_irid, i_branch_id) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION is_latest_version(i_ivid IN NUMBER) RETURN NUMBER IS BEGIN IF is_latest(i_ivid) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION checked_out_on_branch(i_irid NUMBER,i_branch_id NUMBER) RETURN NUMBER IS BEGIN IF is_checked_out_on_branch(i_irid, i_branch_id) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION any_version_checked_out(i_irid NUMBER) RETURN NUMBER IS BEGIN IF is_any_version_checked_out(i_irid) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION branch_locked(i_irid NUMBER,i_branch_id NUMBER) RETURN NUMBER IS BEGIN IF is_branch_locked(i_irid, i_branch_id) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION locked(i_irid NUMBER,i_ivid NUMBER) RETURN NUMBER IS BEGIN IF is_locked(i_irid, i_ivid) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION never_been_checked_in(i_irid NUMBER ) RETURN NUMBER IS BEGIN IF never_checked_in(i_irid) THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION is_version_checked_in (i_irid NUMBER, i_ivid NUMBER) RETURN NUMBER IS BEGIN IF is_checked_in (i_irid, i_ivid)= TRUE THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION is_version_checked_out(i_irid NUMBER, i_ivid NUMBER) RETURN NUMBER IS BEGIN IF is_checked_out(i_irid, i_ivid)= TRUE THEN RETURN 1; ELSE RETURN 0; END IF; END; FUNCTION has_branch_merged(i_irid NUMBER,i_branch_id NUMBER) RETURN NUMBER IS BEGIN IF branch_has_merged (i_irid, i_branch_id)= TRUE THEN RETURN 1; ELSE RETURN 0; END IF; END; ---------------------------------------------------------- --- get_common_ancestor() --- Get the IVID of the common version --- Given two versions of the same object ---------------------------------------------------------- FUNCTION get_common_ancestor(obj_irid IN NUMBER ,merge_from_ivid NUMBER ,merge_to_ivid NUMBER) RETURN NUMBER IS common_ivid NUMBER := NULL; --get the ivids of the nodes in the from branch CURSOR from_branch_ivids(obj_irid NUMBER, merge_from_ivid NUMBER) IS SELECT merge_from_ivid from_ivid, 0 xx FROM sys.dual UNION ALL SELECT x.from_ivid, x.xx FROM ( SELECT from_ivid , LEVEL xx FROM ( SELECT to_ivid , from_ivid FROM I$SDD_VERSION_ASSOCIATIONS WHERE irid = obj_irid AND edge_kind != 'T' AND edge_kind != 'R' ) CONNECT BY PRIOR from_ivid = to_ivid START WITH to_ivid = merge_from_ivid ) x ORDER BY 2; --get the ivids of the nodes in the to (main) branch CURSOR to_branch_ivids(obj_irid NUMBER, merge_to_ivid NUMBER) IS SELECT merge_to_ivid from_ivid, 0 xx FROM sys.dual UNION ALL SELECT y.from_ivid, y.xx FROM ( SELECT from_ivid , LEVEL xx FROM ( SELECT to_ivid ,from_ivid FROM I$SDD_VERSION_ASSOCIATIONS WHERE irid = obj_irid AND edge_kind != 'T' AND edge_kind != 'R' ) CONNECT BY PRIOR from_ivid = to_ivid START WITH to_ivid = merge_to_ivid) y ORDER BY 2; BEGIN --If we are merging from the root node, then this will itself by the common --ancestor.... begin select va.to_ivid into common_ivid from SDD_VERSION_ASSOCIATIONS va where va.to_ivid=merge_from_ivid and va.edge_kind='R'; --If we get this far, we're merging from the root return common_ivid; exception when NO_DATA_FOUND then --From node is not the root, so carry on... NULL; end; --loop through both branches until the common ancestor is found FOR to_rec IN to_branch_ivids(obj_irid, merge_to_ivid) LOOP FOR from_rec IN from_branch_ivids(obj_irid, merge_from_ivid) LOOP IF to_rec.from_ivid = from_rec.from_ivid THEN --found common ancestor, set the return value and exit out of loop common_ivid := to_rec.from_ivid; EXIT; END IF; END LOOP; IF common_ivid IS NOT NULL THEN --common ancestor has been found in inner loop so also exit from outer loop EXIT; END IF; END LOOP; RETURN common_ivid; END get_common_ancestor; END Jr_Version; / -- -- ---------------------------------------------------------------------------+ -- }} End of file $Workfile: jrpbvman.sql $ $$Header_is_done -- ---------------------------------------------------------------------------+