From d0e91a9e2d12559abc9333585f8474f2cd77b089 Mon Sep 17 00:00:00 2001 From: Philipp Salvisberg Date: Mon, 18 Mar 2024 10:47:21 +0100 Subject: [PATCH 1/2] add G-4387: Never use a FOR LOOP for a query that should return not more than one row. --- .../3-flow-control/g-4387.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 docs/4-language-usage/4-control-structures/3-flow-control/g-4387.md diff --git a/docs/4-language-usage/4-control-structures/3-flow-control/g-4387.md b/docs/4-language-usage/4-control-structures/3-flow-control/g-4387.md new file mode 100644 index 00000000..92820c79 --- /dev/null +++ b/docs/4-language-usage/4-control-structures/3-flow-control/g-4387.md @@ -0,0 +1,58 @@ +# G-4387: Never use a FOR LOOP for a query that should return not more than one row. + +!!! bug "Blocker" + Reliability, Efficiency, Readability + +!!! missing "Unsupported in db\* CODECOP Validators" + Without access to the Oracle Data Dictionary, we cannot determine the number of rows to be processed. + +## Reason + +A `for loop` can hide a `too_many_rows` exception. The more complex a query is, the higher is the risk that more than one row will be processed. +This affects performance and can lead to a wrong result. + +A `for loop` can also hide a `no_data_found` exception and the reader cannot determine whether this is intentional or not. + +## Example (bad) + +``` sql +create or replace package body employee_api is + function emp_name(in_empno in integer) return varchar2 is -- NOSONAR: non-deterministic + l_ename emp.ename%type; + begin + <> + for r in ( + select ename + from emp + where empno = in_empno + ) + loop + l_ename := r.ename; + end loop fetch_name; + return l_ename; + end emp_name; +end employee_api; +/ +``` + +## Example (good) + +``` sql +create or replace package body employee_api is + function emp_name(in_empno in integer) return varchar2 is -- NOSONAR: non-deterministic + l_ename emp.ename%type; + begin + select ename + into l_ename + from emp + where empno >= in_empno; + return l_ename; + exception + when no_data_found then + return null; + when too_many_rows then + raise; + end emp_name; +end employee_api; +/ +``` \ No newline at end of file From cd28542b6bdad39212898a3a28271d7d50f4e341 Mon Sep 17 00:00:00 2001 From: Philipp Salvisberg Date: Mon, 18 Mar 2024 10:47:42 +0100 Subject: [PATCH 2/2] add G-4387 to appendix --- docs/9-appendix/appendix.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/9-appendix/appendix.md b/docs/9-appendix/appendix.md index 953ec1d9..6b93d095 100644 --- a/docs/9-appendix/appendix.md +++ b/docs/9-appendix/appendix.md @@ -87,6 +87,7 @@ n/a | 4365 | Never use unconditional CONTINUE or EXIT in a loop. | Major | | | 46 | 4375 | Always use EXIT WHEN instead of an IF statement to exit from a loop. | Minor | | | ✘ | | | | | 47 | 4380 | Try to label your EXIT WHEN statements. | Minor | | | ✘ | | | | | 48 | 4385 | Never use a cursor for loop to check whether a cursor returns data. | Critical | | ✘ | | | | | | +n/a | 4387 | !!!CHARACTERISTIC ERROR!!! | Blocker | | ✘ | | | ✘ | | | 49 | 4390 | Avoid use of unreferenced FOR loop indexes. | Major | | ✘ | | | | | | 50 | 4395 | Avoid hard-coded upper or lower bound values with FOR loops. | Minor | ✘ | | ✘ | | | | | n/a | 5010 | Try to use a error/logging framework for your application. | Critical | | | | | ✘ | ✘ | | ✘