S
Sometimes, when I'm working on a new package, it's difficult to keep the specification updated to reflect changes in arguments. Adding new procedures becomes tedious, and when I change the formatting, it's time-consuming to update the specification accordingly. When I reformat a legacy package, I usually copy the whole body into the specification and keep only the declarations. But that also takes some effort.
So I created this nifty script, which generates a specification from your package body. It keeps everything that exists in your current package spec before the first procedure or function (like constants, exceptions, comments...). Everything else is replaced. It does not support forward declarations, and your private procedures will get exposed (which is a good thing, because you should be using the ACCESSIBLE BY clause instead).
Here is the script:
DECLARE in_package_name CONSTANT VARCHAR2(30) := '&PACKAGE_NAME.'; -- v_header VARCHAR2(32767); v_package VARCHAR2(32767); v_function BOOLEAN; v_appending BOOLEAN; BEGIN -- go thru the package spec, to salvage internal constants, exceptions, leading comments... FOR s IN ( SELECT s.line, REGEXP_REPLACE(RTRIM(RTRIM(UPPER(LTRIM(s.text)), CHR(10))), '\s*--.*$', '') AS text_upper, s.text FROM user_source s WHERE 1 = 1 AND s.type = 'PACKAGE' AND s.name = UPPER(in_package_name) AND s.line > 1 ) LOOP -- hide second line if the first line is not complete IF s.line = 2 AND s.text_upper IN ('IS', 'AS') THEN CONTINUE; END IF; -- stop when first procedure or function found IF (s.text_upper LIKE 'PROCEDURE %' OR s.text_upper LIKE 'FUNCTION %') THEN EXIT; -- break END IF; -- v_package := v_package || s.text; END LOOP; -- v_package := LTRIM(v_package, CHR(10)); -- go thru the package body FOR s IN ( SELECT s.line, REGEXP_REPLACE(RTRIM(RTRIM(UPPER(LTRIM(s.text)), CHR(10))), '\s*--.*$', '') AS text_upper, s.text FROM user_source s WHERE 1 = 1 AND s.type = 'PACKAGE BODY' AND s.name = UPPER(in_package_name) AND s.line > 1 ORDER BY s.line ) LOOP -- find procedure or function start IF (s.text_upper LIKE 'PROCEDURE %' OR s.text_upper LIKE 'FUNCTION %') THEN v_function := s.text_upper LIKE 'FUNCTION %'; v_appending := TRUE; v_header := REGEXP_REPLACE(REGEXP_REPLACE(s.text, '(PROCEDURE)', 'PROCEDURE', 1, 1, 'i'), '(FUNCTION)', 'FUNCTION', 1, 1, 'i'); v_header := REGEXP_REPLACE(v_header, '(\s+AS|\s+IS)', '', 1, 1, 'i'); -- IF s.text_upper LIKE 'PROCEDURE %' AND s.text NOT LIKE '%(%' THEN v_package := v_package || RTRIM(RTRIM(v_header, CHR(10))) || ';' || CHR(10) || CHR(10) || CHR(10) || CHR(10); v_header := ''; -- shortloop next block END IF; CONTINUE; END IF; -- find arguments IF v_header IS NOT NULL THEN -- attach other statements before first AS/IS IF (s.text_upper LIKE '% AS' OR s.text_upper LIKE '% IS') THEN s.text := REGEXP_REPLACE(RTRIM(s.text, CHR(10)), '^(.*)(\s+AS|\s+IS)(\s*--.*)$', '\1'); v_header := v_header || s.text; -- append to the header s.text_upper := 'AS'; -- to trigger next block END IF; -- IF s.text_upper IN ('AS', 'IS') THEN v_header := REGEXP_REPLACE(RTRIM(RTRIM(v_header, CHR(10))), '(\s+AS|\s+IS)', '', 1, 1, 'i') || ';' || CHR(10); v_package := v_package || v_header || CHR(10) || CHR(10) || CHR(10); v_header := ''; v_appending := FALSE; CONTINUE; END IF; -- keep appending v_header := v_header || s.text; END IF; END LOOP; -- generate package spec IF v_package IS NOT NULL THEN v_package := 'CREATE OR REPLACE PACKAGE ' || LOWER(in_package_name) || ' AS' || CHR(10) || CHR(10) || RTRIM(v_package, CHR(10)) || CHR(10) || CHR(10) || 'END;'; -- DBMS_OUTPUT.PUT_LINE(v_package); EXECUTE IMMEDIATE v_package; END IF; END; /
Don't forget to recompile your invalid objects.
Comments
Post a Comment