From 0a7e283d1b00d649806d829c13697c61a9ee5c86 Mon Sep 17 00:00:00 2001 From: Sebastien Marichal Date: Wed, 27 Nov 2024 14:11:29 +0100 Subject: [PATCH] SONARPLSQL-805 S3921: Update RSPEC to mention data dictionary (#4544) --- rules/S3618/plsql/rule.adoc | 60 ++++++++++++--------- rules/S3641/compliant.adoc | 15 ------ rules/S3641/how.adoc | 30 +++++++++++ rules/S3641/noncompliant.adoc | 8 --- rules/S3641/plsql/rule.adoc | 13 ++--- rules/S3641/tsql/rule.adoc | 4 +- rules/S3651/plsql/rule.adoc | 48 +++++++++++------ rules/S3921/plsql/rule.adoc | 64 +++++++++++++++-------- shared_content/plsql/data_dictionary.adoc | 3 ++ 9 files changed, 149 insertions(+), 96 deletions(-) delete mode 100644 rules/S3641/compliant.adoc create mode 100644 rules/S3641/how.adoc delete mode 100644 rules/S3641/noncompliant.adoc create mode 100644 shared_content/plsql/data_dictionary.adoc diff --git a/rules/S3618/plsql/rule.adoc b/rules/S3618/plsql/rule.adoc index ab39b36bfe5..7fbc25b1f26 100644 --- a/rules/S3618/plsql/rule.adoc +++ b/rules/S3618/plsql/rule.adoc @@ -1,42 +1,54 @@ == Why is this an issue? -Any insert which omits a value for a ``++NOT NULL++`` column in a database table will be automatically rejected by the database unless a default value has been specified for the column. +Any insert which omits a value for a `NOT NULL` column in a database table will be automatically rejected by the database unless a default value has been specified for the column. +include::../../../shared_content/plsql/data_dictionary.adoc[] -*Noteworthy* +== How to fix it -This rule raises issues only when a *Data Dictionary* is provided during the analysis. See https://docs.sonarqube.org/latest/analysis/languages/plsql/ +Ensure that all `NOT NULL` columns have a value specified in the `INSERT` statement. -=== Noncompliant code example +=== Code examples -With the table ``++MY_TABLE++`` having a ``++NOT NULL++`` column ``++N2++`` without default value and a ``++NOT NULL++`` column ``++N3++`` with default value: +Given the following table: [source,sql] ---- - INSERT INTO MY_TABLE -- Noncompliant; N2 value omitted - ( - N1 - ) - VALUES - ( - 1 - ) +CREATE TABLE MY_TABLE ( + N1 NUMBER NOT NULL, + N2 VARCHAR2(50) NOT NULL, + N3 VARCHAR2(50) DEFAULT 'Default Value' +); +---- + +==== Noncompliant code example + +[source,sql,diff-id=1,diff-type=noncompliant] +---- +INSERT INTO MY_TABLE -- Noncompliant; N2 value omitted +( + N1 +) +VALUES +( + 1 +) ---- === Compliant solution -[source,sql] +[source,sql,diff-id=1,diff-type=compliant] ---- - INSERT INTO MY_TABLE -- Compliant even though N3 value not supplied - ( - N1, - N2 - ) - VALUES - ( - 1, - 'Paul' - ) +INSERT INTO MY_TABLE -- Compliant; N3 has a default value +( + N1, + N2 +) +VALUES +( + 1, + 'Paul' +) ---- ifdef::env-github,rspecator-view[] diff --git a/rules/S3641/compliant.adoc b/rules/S3641/compliant.adoc deleted file mode 100644 index 0a265a13177..00000000000 --- a/rules/S3641/compliant.adoc +++ /dev/null @@ -1,15 +0,0 @@ -=== Compliant solution - -[source,text] ----- -SELECT * -FROM my_table -WHERE NOT EXISTS (SELECT 1 FROM another_table WHERE nullable_column = my_table.my_column) ----- - -[source,text] ----- -SELECT * -FROM my_table -WHERE my_column NOT IN (SELECT nullable_column FROM another_table WHERE nullable_column IS NOT NULL) ----- diff --git a/rules/S3641/how.adoc b/rules/S3641/how.adoc new file mode 100644 index 00000000000..312705eac0d --- /dev/null +++ b/rules/S3641/how.adoc @@ -0,0 +1,30 @@ +== How to fix it + +Use `NOT EXISTS` or `IS NOT NULL` instead of `NOT IN` when the subquery may return `NULL` values. + +=== Code examples + +==== Noncompliant code example + +[source,sql] +---- +SELECT * +FROM my_table +WHERE my_column NOT IN (SELECT nullable_column FROM another_table) -- Noncompliant; "nullable_column" may contain 'NULL' value and the whole SELECT query will return nothing +---- + +==== Compliant solution + +[source,sql] +---- +SELECT * +FROM my_table +WHERE NOT EXISTS (SELECT 1 FROM another_table WHERE nullable_column = my_table.my_column) +---- + +[source,sql] +---- +SELECT * +FROM my_table +WHERE my_column NOT IN (SELECT nullable_column FROM another_table WHERE nullable_column IS NOT NULL) +---- diff --git a/rules/S3641/noncompliant.adoc b/rules/S3641/noncompliant.adoc deleted file mode 100644 index 076c91338cc..00000000000 --- a/rules/S3641/noncompliant.adoc +++ /dev/null @@ -1,8 +0,0 @@ -=== Noncompliant code example - -[source,text] ----- -SELECT * -FROM my_table -WHERE my_column NOT IN (SELECT nullable_column FROM another_table) -- Noncompliant; "nullable_column" may contain 'NULL' value and the whole SELECT query will return nothing ----- diff --git a/rules/S3641/plsql/rule.adoc b/rules/S3641/plsql/rule.adoc index 842d96f01fa..6592876ee32 100644 --- a/rules/S3641/plsql/rule.adoc +++ b/rules/S3641/plsql/rule.adoc @@ -1,17 +1,12 @@ == Why is this an issue? -A WHERE clause condition that uses NOT IN with a subquery will have unexpected results if that subquery returns NULL. On the other hand NOT EXISTS subqueries work reliably under the same conditions. +A `WHERE` clause condition that uses `NOT IN` with a subquery will have unexpected results if that subquery returns `NULL`. On the other hand `NOT EXISTS` subqueries work reliably under the same conditions. -This rule raises an issue when NOT IN is used with a subquery where the selected column is nullable. +This rule raises an issue when `NOT IN` is used with a subquery where the selected column is nullable. +include::../../../shared_content/plsql/data_dictionary.adoc[] -*Noteworthy* - -This rule raises issues only when a *Data Dictionary* is provided during the analysis. See https://docs.sonarqube.org/latest/analysis/languages/plsql/ - -include::../noncompliant.adoc[] - -include::../compliant.adoc[] +include::../how.adoc[] ifdef::env-github,rspecator-view[] diff --git a/rules/S3641/tsql/rule.adoc b/rules/S3641/tsql/rule.adoc index aa53b9d710f..2806952316c 100644 --- a/rules/S3641/tsql/rule.adoc +++ b/rules/S3641/tsql/rule.adoc @@ -5,9 +5,7 @@ A ``++WHERE++`` clause condition that uses ``++NOT IN++`` with a subquery will h This rule raises an issue when ``++NOT IN++`` is used with a subquery. This rule doesn't check if the selected column is a nullable column because the rules engine has no information about the table definition. It's up to the developer to review manually if the column is nullable. -include::../noncompliant.adoc[] - -include::../compliant.adoc[] +include::../how.adoc[] ifdef::env-github,rspecator-view[] diff --git a/rules/S3651/plsql/rule.adoc b/rules/S3651/plsql/rule.adoc index a9e9d3bb85a..fea874b9885 100644 --- a/rules/S3651/plsql/rule.adoc +++ b/rules/S3651/plsql/rule.adoc @@ -1,27 +1,45 @@ == Why is this an issue? -``++WHERE++`` clause conditions that reinforce or contradict the definitions of their columns are useless; they are always either unconditionally true or unconditionally false. For instance, there's no point in including ``++AND column IS NOT NULL++`` if the column is defined as non-null. +Conditions in the `WHERE` clause that either reinforce or contradict the definitions of their columns are redundant, as they are always either unconditionally true or unconditionally false. For example, including `AND column IS NOT NULL` is unnecessary if the column is already defined as non-null. +include::../../../shared_content/plsql/data_dictionary.adoc[] -*Noteworthy* +== How to fix it -This rule raises issues only when a *Data Dictionary* is provided during the analysis. See https://docs.sonarqube.org/latest/analysis/languages/plsql/ +Ensure that the conditions in the `WHERE` clause are not always true or false. -=== Noncompliant code example +=== Code examples + +Given the following table: [source,sql] ---- -CREATE TABLE product -(id INT, - name VARCHAR(6) NOT NULL, - mfg_name VARCHAR(6), - mfg_id INT - ... - -SELECT name, price -FROM product -WHERE name is not null -- Noncompliant; always true. This column is NOT NULL - AND mfg_name = 'Too long name' -- Noncompliant; always false. This column can contain only 6 characters +CREATE TABLE Product +( + Id INT, + Name VARCHAR(6), + Price INT NOT NULL +) +---- + + +==== Noncompliant code example + +[source,sql,diff-id=1,diff-type=noncompliant] +---- +SELECT Name, Price FROM Product +WHERE + Name = 'Too long name' -- Noncompliant; always false. This column can contain only 6 characters + AND Price IS NOT NULL -- Noncompliant; always true. This column is NOT NULL +---- + +==== Compliant solution + +[source,sql,diff-id=1,diff-type=compliant] +---- +SELECT Name, Price FROM Product +WHERE + Name = 'Name' ---- ifdef::env-github,rspecator-view[] diff --git a/rules/S3921/plsql/rule.adoc b/rules/S3921/plsql/rule.adoc index c9f93abadff..6b6f01f59d3 100644 --- a/rules/S3921/plsql/rule.adoc +++ b/rules/S3921/plsql/rule.adoc @@ -2,41 +2,61 @@ Trying to assign a large character value to a smaller variable or column will raise an error. -=== Noncompliant code example +include::../../../shared_content/plsql/data_dictionary.adoc[] -[source,sql] ----- -create table persons (id number, name varchar2(4)); +== How to fix it -insert into persons (id, name) values (1, 'Alice'); -- Noncompliant, raises ORA-12899 +Ensure that the size of the variable or column is large enough to hold the value. -create or replace procedure sp1 -is - foo varchar2(2); -begin - select name into foo from persons where id = 1; -- Noncompliant, may raise ORA-06502 -end; ----- +=== Code examples -=== Compliant solution +==== Noncompliant code example -[source,sql] +[source,sql,diff-id=1,diff-type=noncompliant] +---- +CREATE TABLE Persons +( + Id NUMBER, + Name VARCHAR2(4) +); + +INSERT INTO Persons (Id, Name) VALUES (1, 'Alice'); -- Noncompliant, raises ORA-12899 + +CREATE OR REPLACE PROCEDURE sp1 +IS + foo VARCHAR2(2); +BEGIN + SELECT Name INTO foo FROM Persons WHERE Id = 1; -- Noncompliant, may raise ORA-06502 +END; ---- -create table persons (id number, name varchar2(8)); -insert into persons (id, name) values (1, 'Alice'); +==== Compliant solution -create or replace procedure sp1 -is - foo varchar2(8); -begin - select name into foo from persons where id = 1; -end; +[source,sql,diff-id=1,diff-type=compliant] +---- +CREATE TABLE Persons +( + Id NUMBER, + Name VARCHAR2(8) +); + +INSERT INTO Persons (Id, Name) VALUES (1, 'Alice'); + +CREATE OR REPLACE PROCEDURE sp1 +IS + foo VARCHAR2(8); +BEGIN + SELECT Name INTO foo FROM Persons WHERE Id = 1; +END; ---- == Resources +=== Documentation + * CWE - https://cwe.mitre.org/data/definitions/704[CWE-704 - Incorrect Type Conversion or Cast] +* Oracle Database - https://docs.oracle.com/en/error-help/db/ora-12899[ORA-12899] +* Oracle Database - https://docs.oracle.com/en/error-help/db/ora-06502[ORA-06502] ifdef::env-github,rspecator-view[] diff --git a/shared_content/plsql/data_dictionary.adoc b/shared_content/plsql/data_dictionary.adoc new file mode 100644 index 00000000000..4dc06b2f835 --- /dev/null +++ b/shared_content/plsql/data_dictionary.adoc @@ -0,0 +1,3 @@ +=== Noteworthy + +This rule raises issues only when a *Data Dictionary* is provided during the analysis. See{nbsp}https://docs.sonarqube.org/latest/analysis/languages/plsql/