G-4350: Always use 1 as lower and COUNT() as upper bound when looping through a dense array.

Major

Reliability

Reason

Doing so will not raise a VALUE_ERROR if the array you are looping through is empty. If you want to use FIRST()..LAST() you need to check the array for emptiness beforehand to avoid the raise of VALUE_ERROR.

Example (bad)

DECLARE
   TYPE t_employee_type IS TABLE OF employees.employee_id%TYPE;
   t_employees t_employee_type := t_employee_type();
BEGIN
   <<process_employees>>
   FOR i IN t_employees.FIRST()..t_employees.LAST()
   LOOP
      sys.dbms_output.put_line(t_employees(i)); -- some processing
   END LOOP process_employees;
END;
/

Example (better)

Raise an unitialized collection error if t_employees is not initialized.

DECLARE
   TYPE t_employee_type IS TABLE OF employees.employee_id%TYPE;
   t_employees t_employee_type := t_employee_type();
BEGIN
   <<process_employees>>
   FOR i IN 1..t_employees.COUNT()
   LOOP
      sys.dbms_output.put_line(t_employees(i)); -- some processing
   END LOOP process_employees;
END;
/

Example (good)

Raises neither an error nor checking whether the array is empty. t_employees.COUNT() always returns a NUMBER (unless the array is not initialized). If the array is empty COUNT() returns 0 and therefore the loop will not be entered.

DECLARE
   TYPE t_employee_type IS TABLE OF employees.employee_id%TYPE;
   t_employees t_employee_type := t_employee_type();
BEGIN
   IF t_employees IS NOT NULL THEN
      <<process_employees>>
      FOR i IN 1..t_employees.COUNT()
      LOOP
         sys.dbms_output.put_line(t_employees(i)); -- some processing
      END LOOP process_employees;
   END IF;
END;
/