G-4310: Never use GOTO statements in your code.
Major
Maintainability, Testability
Reason
Code containing gotos is hard to format. Indentation should be used to show logical structure, and gotos have an effect on logical structure. Using indentation to show the logical structure of a goto and its target, however, is difficult or impossible. (...)
Use of gotos is a matter of religion. My dogma is that in modern languages, you can easily replace nine out of ten gotos with equivalent sequential constructs. In these simple cases, you should replace gotos out of habit. In the hard cases, you can still exorcise the goto in nine out of ten cases: You can break the code into smaller routines, use try-finally, use nested ifs, test and retest a status variable, or restructure a conditional. Eliminating the goto is harder in these cases, but it’s good mental exercise (...).
-- McConnell, Steve C. (2004). Code Complete. Second Edition. Microsoft Press.
Example (bad)
CREATE OR REPLACE PACKAGE BODY my_package IS
PROCEDURE password_check (in_password IN VARCHAR2) IS
co_digitarray CONSTANT STRING(10 CHAR) := '0123456789';
co_lower_bound CONSTANT SIMPLE_INTEGER := 1;
co_errno CONSTANT SIMPLE_INTEGER := -20501;
co_errmsg CONSTANT STRING(100 CHAR) := 'Password must contain a digit.';
l_isdigit BOOLEAN := FALSE;
l_len_pw PLS_INTEGER;
l_len_array PLS_INTEGER;
BEGIN
l_len_pw := LENGTH(in_password);
l_len_array := LENGTH(co_digitarray);
<<check_digit>>
FOR i IN co_lower_bound .. l_len_array
LOOP
<<check_pw_char>>
FOR j IN co_lower_bound .. l_len_pw
LOOP
IF SUBSTR(in_password, j, 1) = SUBSTR(co_digitarray, i, 1) THEN
l_isdigit := TRUE;
GOTO check_other_things;
END IF;
END LOOP check_pw_char;
END LOOP check_digit;
<<check_other_things>>
NULL;
IF NOT l_isdigit THEN
raise_application_error(co_errno, co_errmsg);
END IF;
END password_check;
END my_package;
/
Example (better)
CREATE OR REPLACE PACKAGE BODY my_package IS
PROCEDURE password_check (in_password IN VARCHAR2) IS
co_digitarray CONSTANT STRING(10 CHAR) := '0123456789';
co_lower_bound CONSTANT SIMPLE_INTEGER := 1;
co_errno CONSTANT SIMPLE_INTEGER := -20501;
co_errmsg CONSTANT STRING(100 CHAR) := 'Password must contain a digit.';
l_isdigit BOOLEAN := FALSE;
l_len_pw PLS_INTEGER;
l_len_array PLS_INTEGER;
BEGIN
l_len_pw := LENGTH(in_password);
l_len_array := LENGTH(co_digitarray);
<<check_digit>>
FOR i IN co_lower_bound .. l_len_array
LOOP
<<check_pw_char>>
FOR j IN co_lower_bound .. l_len_pw
LOOP
IF SUBSTR(in_password, j, 1) = SUBSTR(co_digitarray, i, 1) THEN
l_isdigit := TRUE;
EXIT check_digit; -- early exit condition
END IF;
END LOOP check_pw_char;
END LOOP check_digit;
<<check_other_things>>
NULL;
IF NOT l_isdigit THEN
raise_application_error(co_errno, co_errmsg);
END IF;
END password_check;
END my_package;
/
Example (good)
CREATE OR REPLACE PACKAGE BODY my_package IS
PROCEDURE password_check (in_password IN VARCHAR2) IS
co_digitpattern CONSTANT STRING(10 CHAR) := '\d';
co_errno CONSTANT SIMPLE_INTEGER := -20501;
co_errmsg CONSTANT STRING(100 CHAR) := 'Password must contain a digit.';
BEGIN
IF NOT REGEXP_LIKE(in_password, co_digitpattern)
THEN
raise_application_error(co_errno, co_errmsg);
END IF;
END password_check;
END my_package;
/