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;
/