From 6032969025db3bd5e220eb15f2e4af23c9bee9e7 Mon Sep 17 00:00:00 2001 From: Denielig Date: Thu, 30 May 2024 09:30:54 +0200 Subject: [PATCH 01/25] feat: add test files --- .../UseEveryColumnQueriedCompliant1.java | 50 ++++++++++++++ .../UseEveryColumnQueriedCompliant2.java | 51 ++++++++++++++ .../UseEveryColumnQueriedCompliant3.java | 50 ++++++++++++++ .../UseEveryColumnQueriedCompliant4.java | 50 ++++++++++++++ .../UseEveryColumnQueriedCompliant5.java | 66 +++++++++++++++++++ .../UseEveryColumnQueriedCompliant6.java | 53 +++++++++++++++ .../UseEveryColumnQueriedNonCompliant1.java | 49 ++++++++++++++ .../UseEveryColumnQueriedNonCompliant2.java | 50 ++++++++++++++ .../UseEveryColumnQueriedNonCompliant3.java | 49 ++++++++++++++ .../UseEveryColumnQueriedNonCompliant4.java | 49 ++++++++++++++ .../UseEveryColumnQueriedNonCompliant5.java | 64 ++++++++++++++++++ .../UseEveryColumnQueriedNonCompliant6.java | 52 +++++++++++++++ 12 files changed, 633 insertions(+) create mode 100644 src/test/files/UseEveryColumnQueriedCompliant1.java create mode 100644 src/test/files/UseEveryColumnQueriedCompliant2.java create mode 100644 src/test/files/UseEveryColumnQueriedCompliant3.java create mode 100644 src/test/files/UseEveryColumnQueriedCompliant4.java create mode 100644 src/test/files/UseEveryColumnQueriedCompliant5.java create mode 100644 src/test/files/UseEveryColumnQueriedCompliant6.java create mode 100644 src/test/files/UseEveryColumnQueriedNonCompliant1.java create mode 100644 src/test/files/UseEveryColumnQueriedNonCompliant2.java create mode 100644 src/test/files/UseEveryColumnQueriedNonCompliant3.java create mode 100644 src/test/files/UseEveryColumnQueriedNonCompliant4.java create mode 100644 src/test/files/UseEveryColumnQueriedNonCompliant5.java create mode 100644 src/test/files/UseEveryColumnQueriedNonCompliant6.java diff --git a/src/test/files/UseEveryColumnQueriedCompliant1.java b/src/test/files/UseEveryColumnQueriedCompliant1.java new file mode 100644 index 00000000..e81a11cd --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedCompliant1.java @@ -0,0 +1,50 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedCompliant1 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", Age: " + rs.getInt("age")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedCompliant2.java b/src/test/files/UseEveryColumnQueriedCompliant2.java new file mode 100644 index 00000000..3b45c2fe --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedCompliant2.java @@ -0,0 +1,51 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedCompliant2 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", Age: " + rs.getInt("age")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + +} diff --git a/src/test/files/UseEveryColumnQueriedCompliant3.java b/src/test/files/UseEveryColumnQueriedCompliant3.java new file mode 100644 index 00000000..a6f0fe7a --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedCompliant3.java @@ -0,0 +1,50 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedCompliant3 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("1")); + System.out.print(", Age: " + rs.getInt("4")); + System.out.print(", First: " + rs.getString("2")); + System.out.println(", Last: " + rs.getString("3")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedCompliant4.java b/src/test/files/UseEveryColumnQueriedCompliant4.java new file mode 100644 index 00000000..350b23cc --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedCompliant4.java @@ -0,0 +1,50 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedCompliant4 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", Age: " + rs.getInt(4)); + System.out.print(", First: " + rs.getString(2)); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedCompliant5.java b/src/test/files/UseEveryColumnQueriedCompliant5.java new file mode 100644 index 00000000..7fedb2af --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedCompliant5.java @@ -0,0 +1,66 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedCompliant5 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + private static final String QUERY2 = "SELECT id, first, last, age FROM Registration2"; + + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ) { + + ResultSet rs = stmt.executeQuery(QUERY); + while (rs.next()) { + // Display values + System.out.print("Age: " + rs.getInt("age")); + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + rs = stmt.executeQuery(QUERY2); + + + while (rs.next()) { + // Display values + System.out.print("Age: " + rs.getInt("age")); + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + + + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedCompliant6.java b/src/test/files/UseEveryColumnQueriedCompliant6.java new file mode 100644 index 00000000..a3b28e8e --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedCompliant6.java @@ -0,0 +1,53 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedCompliant6 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { + extractGet(rs); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + private void extractGet(ResultSet rs) throws SQLException { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", Age: " + rs.getInt("age")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } +} diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant1.java b/src/test/files/UseEveryColumnQueriedNonCompliant1.java new file mode 100644 index 00000000..0daf184b --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedNonCompliant1.java @@ -0,0 +1,49 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedNonCompliant1 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant2.java b/src/test/files/UseEveryColumnQueriedNonCompliant2.java new file mode 100644 index 00000000..8da60f05 --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedNonCompliant2.java @@ -0,0 +1,50 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedNonCompliant2 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { // Noncompliant {{Avoid querying SQL columns that are not used}} + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + +} \ No newline at end of file diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant3.java b/src/test/files/UseEveryColumnQueriedNonCompliant3.java new file mode 100644 index 00000000..775c6387 --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedNonCompliant3.java @@ -0,0 +1,49 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedNonCompliant3 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt(1)); + System.out.print(", First: " + rs.getString(2)); + System.out.println(", Last: " + rs.getString(3)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant4.java b/src/test/files/UseEveryColumnQueriedNonCompliant4.java new file mode 100644 index 00000000..86ac6e40 --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedNonCompliant4.java @@ -0,0 +1,49 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedNonCompliant4 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString(2)); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant5.java b/src/test/files/UseEveryColumnQueriedNonCompliant5.java new file mode 100644 index 00000000..5c922e50 --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedNonCompliant5.java @@ -0,0 +1,64 @@ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +public class UseEveryColumnQueriedNonCompliant5 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + private static final String QUERY2 = "SELECT id, first, last, age FROM Registration2"; + + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ) { + + ResultSet rs = stmt.executeQuery(QUERY); + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + rs = stmt.executeQuery(QUERY2); + + + while (rs.next()) { + // Display values + System.out.print("Age: " + rs.getInt("age")); + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + + + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant6.java b/src/test/files/UseEveryColumnQueriedNonCompliant6.java new file mode 100644 index 00000000..86d6a863 --- /dev/null +++ b/src/test/files/UseEveryColumnQueriedNonCompliant6.java @@ -0,0 +1,52 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class UseEveryColumnQueriedNonCompliant6 { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { // Noncompliant {{Avoid querying SQL columns that are not used}} + extractGet(rs); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + private void extractGet(ResultSet rs) throws SQLException { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } +} From 9419960f80feb76a3ca193fe01a775ba8e96da0f Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Thu, 30 May 2024 10:21:59 +0200 Subject: [PATCH 02/25] feat: :construction: EC1024 skeleton --- .../java/checks/UseEveryColumnQueried.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java new file mode 100644 index 00000000..73f7dd97 --- /dev/null +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -0,0 +1,38 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2024 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.util.List; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.Tree.Kind; + +@Rule(key = "1044") +public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { + + protected static final String MESSAGERULE = "Avoid SQL request in loop"; + private static final String JAVA_SQL_STATEMENT = "java.sql.Statement"; + + @Override + public List nodesToVisit() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'nodesToVisit'"); + } + +} From f7905699b2a390e28d026041626698a2670caa14 Mon Sep 17 00:00:00 2001 From: Denielig Date: Thu, 30 May 2024 10:33:51 +0200 Subject: [PATCH 03/25] feat: add test class --- .../checks/UserEveryColumnQueriedTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/test/java/fr/greencodeinitiative/java/checks/UserEveryColumnQueriedTest.java diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UserEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UserEveryColumnQueriedTest.java new file mode 100644 index 00000000..035194df --- /dev/null +++ b/src/test/java/fr/greencodeinitiative/java/checks/UserEveryColumnQueriedTest.java @@ -0,0 +1,52 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +class UserEveryColumnQueriedTest { + + @Test + void testHasIssues() { + CheckVerifier.newVerifier() + .onFiles("src/test/files/UseEveryColumnQueriedNonCompliant1.java", + "src/test/files/UseEveryColumnQueriedNonCompliant2.java", + "src/test/files/UseEveryColumnQueriedNonCompliant3.java", + "src/test/files/UseEveryColumnQueriedNonCompliant4.java", + "src/test/files/UseEveryColumnQueriedNonCompliant5.java", + "src/test/files/UseEveryColumnQueriedNonCompliant6.java") + .withCheck(new UseEveryColumnQueried()) + .verifyIssues(); + } + + @Test + void testHasNoIssues() { + CheckVerifier.newVerifier() + .onFiles( + "src/test/files/UseEveryColumnQueriedCompliant1.java", + "src/test/files/UseEveryColumnQueriedCompliant2.java", + "src/test/files/UseEveryColumnQueriedCompliant3.java", + "src/test/files/UseEveryColumnQueriedCompliant4.java", + "src/test/files/UseEveryColumnQueriedCompliant5.java", + "src/test/files/UseEveryColumnQueriedCompliant6.java" + ) + .withCheck(new UseEveryColumnQueried()) + .verifyNoIssues(); + } +} From 2facdf253f689c780366ea2a60b031f3a757ee47 Mon Sep 17 00:00:00 2001 From: Denielig Date: Thu, 30 May 2024 11:46:01 +0200 Subject: [PATCH 04/25] Fix: fix class and file name --- ...eryColumnQueriedTest.java => UseEveryColumnQueriedTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/fr/greencodeinitiative/java/checks/{UserEveryColumnQueriedTest.java => UseEveryColumnQueriedTest.java} (98%) diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UserEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java similarity index 98% rename from src/test/java/fr/greencodeinitiative/java/checks/UserEveryColumnQueriedTest.java rename to src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java index 035194df..6c4bc8da 100644 --- a/src/test/java/fr/greencodeinitiative/java/checks/UserEveryColumnQueriedTest.java +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test; import org.sonar.java.checks.verifier.CheckVerifier; -class UserEveryColumnQueriedTest { +class UseEveryColumnQueriedTest { @Test void testHasIssues() { From a9160183263c808c6b94850ff2c2b18be6ff9c24 Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Thu, 30 May 2024 12:11:31 +0200 Subject: [PATCH 05/25] feat: :construction: WIP : create rule EC10044 --- .../java/checks/UseEveryColumnQueried.java | 133 +++++++++++++++++- 1 file changed, 128 insertions(+), 5 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 73f7dd97..4a9914d5 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -1,6 +1,6 @@ /* * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs - * Copyright © 2024 Green Code Initiative (https://www.ecocode.io) + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,22 +17,145 @@ */ package fr.greencodeinitiative.java.checks; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.sonar.check.Rule; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.semantic.MethodMatchers; +import org.sonar.plugins.java.api.semantic.Symbol.VariableSymbol; +import org.sonar.plugins.java.api.tree.Arguments; +import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; +import org.sonar.plugins.java.api.tree.ExpressionTree; +import org.sonar.plugins.java.api.tree.IdentifierTree; +import org.sonar.plugins.java.api.tree.LiteralTree; +import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree; +import org.sonar.plugins.java.api.tree.MethodInvocationTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.VariableTree; import org.sonar.plugins.java.api.tree.Tree.Kind; @Rule(key = "1044") public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { - protected static final String MESSAGERULE = "Avoid SQL request in loop"; + protected static final String MESSAGERULE = "Do not request columns that are not used in the query"; private static final String JAVA_SQL_STATEMENT = "java.sql.Statement"; - + private static final String JAVA_SQL_RESULTSET = "java.sql.ResultSet"; + private static final MethodMatchers SQL_STATEMENT_DECLARE_SQL = MethodMatchers + .create() + .ofSubTypes(JAVA_SQL_STATEMENT) + //TODO : also take into account addBatch and executeBatch + .names("executeQuery", "execute", "executeUpdate", "executeLargeUpdate") + .withAnyParameters() + .build(); + private static MethodMatchers SQL_STATEMENT_RETRIEVE_RESULTSET = MethodMatchers + .create() + .ofSubTypes(JAVA_SQL_STATEMENT) + .names("executeQuery", "getResultSet") + // TODO : take into account variable instead of just a string as parameters + .addParametersMatcher("java.lang.String") + .build(); + private static final MethodMatchers SQL_RESULTSET_GET = MethodMatchers + .create() + .ofSubTypes(JAVA_SQL_RESULTSET) + .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", "getBigDecimal", + "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", "getClob", "getRef", "getRowId", + "getNClob", "getSQLXML", "getURL", "getNString", "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") + .addParametersMatcher("java.lang.String") + .build(); + private static final Pattern SELECTED_COLUMNS_PATTERN = Pattern.compile("SELECT\\s+(.*)\\s+FROM\\s+.*"); + @Override public List nodesToVisit() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'nodesToVisit'"); + return Arrays.asList(Kind.METHOD_INVOCATION); + } + + @Override + public void visitNode(Tree tree) { + + MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; + if(!SQL_STATEMENT_DECLARE_SQL.matches(methodInvocationTree)) { + return; + } + VariableSymbol statement = (VariableSymbol) methodInvocationTree.methodSymbol().owner(); + if(statement == null) { + return; + } + + // STEP 1 : retrieve the selected columns in the query + + Arguments arguments = methodInvocationTree.arguments(); + if(arguments.isEmpty()) { + return; + } + ExpressionTree argument = arguments.get(0); + if(!argument.is(Tree.Kind.STRING_LITERAL)){ + return; + } + String query = ((LiteralTree) argument).value(); + List selectedColumns = extractSelectedSQLColumns(query); + + // STEP 2 : retrieve the resultSet object + + List usages = statement.usages(); + VariableSymbol resultSet = null; + for(IdentifierTree usage : usages) { + Tree parent = usage.parent(); + if(!parent.is(Tree.Kind.MEMBER_SELECT)){ + continue; + } + MemberSelectExpressionTree memberSelect = (MemberSelectExpressionTree) parent; + IdentifierTree identifier = memberSelect.identifier(); + if(!identifier.is(Tree.Kind.METHOD_INVOCATION)){ + continue; + } + MethodInvocationTree methodInvocation = (MethodInvocationTree) identifier; + if(!SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)) { + continue; + } + + while(!parent.is(Tree.Kind.ASSIGNMENT)){ + parent = parent.parent(); + if(parent == null){ + continue; + } + } + ExpressionTree variable = ((AssignmentExpressionTree)parent).variable(); + if(!variable.is(Tree.Kind.VARIABLE)){ + continue; + } + resultSet = ((VariableSymbol)((VariableTree)variable).symbol()); + break; + } + + // STEP 3 : retrieve the columns used from the resultSet object + + if(resultSet == null) { + return; + } + + List usedColumns = new ArrayList<>(); + List resultSetUsages = resultSet.usages(); + for(IdentifierTree usage : resultSetUsages) { + } + + // STEP 4 : compare selected and used columns, report issues + } + + List extractSelectedSQLColumns(String query){ + List columns = new ArrayList<>(); + Matcher matcher = SELECTED_COLUMNS_PATTERN.matcher(query); + if (matcher.matches()) { + String columnString = matcher.group(1); + columns = Arrays.asList(columnString.split(",")); + columns.replaceAll(String::toUpperCase); + columns.replaceAll(column -> column.replaceAll("\\s+", " ")); + columns.replaceAll(column -> column.contains(" AS ") ? column.split(" AS ")[1].trim() : column.trim()); + } + return columns; } } From 5a8eb15f3d4db5b957d22e8a02a5f164f69373ce Mon Sep 17 00:00:00 2001 From: Denielig Date: Thu, 30 May 2024 12:51:46 +0200 Subject: [PATCH 06/25] fix: improve test --- .../checks/UseEveryColumnQueriedTest.java | 102 +++++++++++++++--- 1 file changed, 86 insertions(+), 16 deletions(-) diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java index 6c4bc8da..b73ffc09 100644 --- a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java @@ -23,29 +23,99 @@ class UseEveryColumnQueriedTest { @Test - void testHasIssues() { + void testHasIssues1() { CheckVerifier.newVerifier() - .onFiles("src/test/files/UseEveryColumnQueriedNonCompliant1.java", - "src/test/files/UseEveryColumnQueriedNonCompliant2.java", - "src/test/files/UseEveryColumnQueriedNonCompliant3.java", - "src/test/files/UseEveryColumnQueriedNonCompliant4.java", - "src/test/files/UseEveryColumnQueriedNonCompliant5.java", - "src/test/files/UseEveryColumnQueriedNonCompliant6.java") + .onFile("src/test/files/UseEveryColumnQueriedNonCompliant1.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test - void testHasNoIssues() { + void testHasIssues2() { CheckVerifier.newVerifier() - .onFiles( - "src/test/files/UseEveryColumnQueriedCompliant1.java", - "src/test/files/UseEveryColumnQueriedCompliant2.java", - "src/test/files/UseEveryColumnQueriedCompliant3.java", - "src/test/files/UseEveryColumnQueriedCompliant4.java", - "src/test/files/UseEveryColumnQueriedCompliant5.java", - "src/test/files/UseEveryColumnQueriedCompliant6.java" - ) + .onFile("src/test/files/UseEveryColumnQueriedNonCompliant2.java") + .withCheck(new UseEveryColumnQueried()) + .verifyIssues(); + } + + @Test + void testHasIssues3() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedNonCompliant3.java") + .withCheck(new UseEveryColumnQueried()) + .verifyIssues(); + } + + @Test + void testHasIssues4() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedNonCompliant4.java") + .withCheck(new UseEveryColumnQueried()) + .verifyIssues(); + } + + @Test + void testHasIssues5() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedNonCompliant5.java") + .withCheck(new UseEveryColumnQueried()) + .verifyIssues(); + } + + @Test + void testHasIssues6() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedNonCompliant6.java") + .withCheck(new UseEveryColumnQueried()) + .verifyIssues(); + } + + + + @Test + void testHasNoIssues1() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedCompliant1.java") + .withCheck(new UseEveryColumnQueried()) + .verifyNoIssues(); + } + + @Test + void testHasNoIssues2() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedCompliant2.java") + .withCheck(new UseEveryColumnQueried()) + .verifyNoIssues(); + } + + @Test + void testHasNoIssues3() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedCompliant3.java") + .withCheck(new UseEveryColumnQueried()) + .verifyNoIssues(); + } + + @Test + void testHasNoIssues4() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedCompliant4.java") + .withCheck(new UseEveryColumnQueried()) + .verifyNoIssues(); + } + + @Test + void testHasNoIssues5() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedCompliant5.java") + .withCheck(new UseEveryColumnQueried()) + .verifyNoIssues(); + } + + @Test + void testHasNoIssues6() { + CheckVerifier.newVerifier() + .onFile("src/test/files/UseEveryColumnQueriedCompliant6.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } From de7897753539925ed1cddddf95a217d4575cade1 Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Thu, 30 May 2024 12:56:40 +0200 Subject: [PATCH 07/25] feat: :construction: WIP finish rule creation for EC1044 rule --- .../java/checks/UseEveryColumnQueried.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 4a9914d5..2292359e 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -26,6 +26,8 @@ import org.sonar.check.Rule; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.semantic.Symbol.MethodSymbol; import org.sonar.plugins.java.api.semantic.Symbol.VariableSymbol; import org.sonar.plugins.java.api.tree.Arguments; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; @@ -140,9 +142,30 @@ public void visitNode(Tree tree) { List usedColumns = new ArrayList<>(); List resultSetUsages = resultSet.usages(); for(IdentifierTree usage : resultSetUsages) { + Tree parent = usage.parent(); + if(!parent.is(Tree.Kind.MEMBER_SELECT)){ + continue; + } + MemberSelectExpressionTree memberSelect = (MemberSelectExpressionTree) parent; + IdentifierTree identifier = memberSelect.identifier(); + if(!identifier.is(Tree.Kind.METHOD_INVOCATION)){ + continue; + } + MethodInvocationTree methodInvocation = (MethodInvocationTree) identifier; + if(!SQL_RESULTSET_GET.matches(methodInvocation)) { + continue; + } + String column = methodInvocation.arguments().get(0).toString(); + usedColumns.add(column); } // STEP 4 : compare selected and used columns, report issues + + selectedColumns.removeAll(usedColumns); + if(!selectedColumns.isEmpty()) { + reportIssue(methodInvocationTree, MESSAGERULE); + } + } List extractSelectedSQLColumns(String query){ From c6eaa43e993e51890888fbbe780f7fc3f6559229 Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Thu, 30 May 2024 14:25:52 +0200 Subject: [PATCH 08/25] feat: :construction: WIP : create rule 1044 --- .../java/checks/UseEveryColumnQueried.java | 36 +++++++++++-------- .../checks/UseEveryColumnQueriedTest.java | 15 ++++++++ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 2292359e..5ba15fdb 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -23,12 +23,14 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jdt.internal.core.Member; import org.sonar.check.Rule; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.Symbol.MethodSymbol; import org.sonar.plugins.java.api.semantic.Symbol.VariableSymbol; +import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.Arguments; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; import org.sonar.plugins.java.api.tree.ExpressionTree; @@ -82,7 +84,18 @@ public void visitNode(Tree tree) { if(!SQL_STATEMENT_DECLARE_SQL.matches(methodInvocationTree)) { return; } - VariableSymbol statement = (VariableSymbol) methodInvocationTree.methodSymbol().owner(); + ExpressionTree et = methodInvocationTree.methodSelect(); + if(!et.is(Tree.Kind.MEMBER_SELECT)) { + return; + } + MemberSelectExpressionTree mset = (MemberSelectExpressionTree) et; + ExpressionTree expression = mset.expression(); + if(!expression.is(Tree.Kind.IDENTIFIER)) { + return; + } + IdentifierTree id = (IdentifierTree) expression; + Symbol statement = id.symbol(); + //Symbol statement = methodInvocationTree.methodSymbol().owner(); if(statement == null) { return; } @@ -103,7 +116,7 @@ public void visitNode(Tree tree) { // STEP 2 : retrieve the resultSet object List usages = statement.usages(); - VariableSymbol resultSet = null; + Symbol resultSet = null; for(IdentifierTree usage : usages) { Tree parent = usage.parent(); if(!parent.is(Tree.Kind.MEMBER_SELECT)){ @@ -119,17 +132,8 @@ public void visitNode(Tree tree) { continue; } - while(!parent.is(Tree.Kind.ASSIGNMENT)){ - parent = parent.parent(); - if(parent == null){ - continue; - } - } - ExpressionTree variable = ((AssignmentExpressionTree)parent).variable(); - if(!variable.is(Tree.Kind.VARIABLE)){ - continue; - } - resultSet = ((VariableSymbol)((VariableTree)variable).symbol()); + VariableTree resultSetVariable = (VariableTree) methodInvocationTree.parent(); + resultSet = resultSetVariable.symbol(); break; } @@ -168,13 +172,15 @@ public void visitNode(Tree tree) { } - List extractSelectedSQLColumns(String query){ + static List extractSelectedSQLColumns(String query){ + query = query.toUpperCase(); + query = query.replaceAll("^['\"]", ""); + query = query.replaceAll("['\"]$", ""); List columns = new ArrayList<>(); Matcher matcher = SELECTED_COLUMNS_PATTERN.matcher(query); if (matcher.matches()) { String columnString = matcher.group(1); columns = Arrays.asList(columnString.split(",")); - columns.replaceAll(String::toUpperCase); columns.replaceAll(column -> column.replaceAll("\\s+", " ")); columns.replaceAll(column -> column.contains(" AS ") ? column.split(" AS ")[1].trim() : column.trim()); } diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java index b73ffc09..78c8d58d 100644 --- a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java @@ -17,11 +17,26 @@ */ package fr.greencodeinitiative.java.checks; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + import org.junit.jupiter.api.Test; import org.sonar.java.checks.verifier.CheckVerifier; class UseEveryColumnQueriedTest { + @Test + void testExtractSelectedSQLColumns(){ + String query = "\"SELECT id AS registration_id,\tfirst, last as Final, AGE FROM Registration\""; + List columns = UseEveryColumnQueried.extractSelectedSQLColumns(query); + assertEquals(4, columns.size()); + assertEquals("REGISTRATION_ID", columns.get(0)); + assertEquals("FIRST", columns.get(1)); + assertEquals("FINAL", columns.get(2)); + assertEquals("AGE", columns.get(3)); + } + @Test void testHasIssues1() { CheckVerifier.newVerifier() From 75196b8601fefbafca514c5ea03bc79aa5691afc Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Thu, 30 May 2024 15:17:48 +0200 Subject: [PATCH 09/25] feat: :sparkles: created rule 1044 --- .../java/checks/UseEveryColumnQueried.java | 68 ++++++++++--------- .../checks/UseEveryColumnQueriedTest.java | 7 ++ 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 5ba15fdb..483dc150 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.eclipse.jdt.internal.core.Member; import org.sonar.check.Rule; @@ -45,7 +46,7 @@ @Rule(key = "1044") public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { - protected static final String MESSAGERULE = "Do not request columns that are not used in the query"; + protected static final String MESSAGERULE = "Avoid querying SQL columns that are not used"; private static final String JAVA_SQL_STATEMENT = "java.sql.Statement"; private static final String JAVA_SQL_RESULTSET = "java.sql.ResultSet"; private static final MethodMatchers SQL_STATEMENT_DECLARE_SQL = MethodMatchers @@ -53,14 +54,14 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { .ofSubTypes(JAVA_SQL_STATEMENT) //TODO : also take into account addBatch and executeBatch .names("executeQuery", "execute", "executeUpdate", "executeLargeUpdate") - .withAnyParameters() + // TODO : take into account variable instead of just a string as parameters + .addParametersMatcher("java.lang.String") .build(); private static MethodMatchers SQL_STATEMENT_RETRIEVE_RESULTSET = MethodMatchers .create() .ofSubTypes(JAVA_SQL_STATEMENT) .names("executeQuery", "getResultSet") - // TODO : take into account variable instead of just a string as parameters - .addParametersMatcher("java.lang.String") + .withAnyParameters() .build(); private static final MethodMatchers SQL_RESULTSET_GET = MethodMatchers .create() @@ -95,7 +96,6 @@ public void visitNode(Tree tree) { } IdentifierTree id = (IdentifierTree) expression; Symbol statement = id.symbol(); - //Symbol statement = methodInvocationTree.methodSymbol().owner(); if(statement == null) { return; } @@ -118,22 +118,11 @@ public void visitNode(Tree tree) { List usages = statement.usages(); Symbol resultSet = null; for(IdentifierTree usage : usages) { - Tree parent = usage.parent(); - if(!parent.is(Tree.Kind.MEMBER_SELECT)){ + Tree methodInvocation = getMethodInvocationFromTree(usage); + if(methodInvocation == null){ continue; } - MemberSelectExpressionTree memberSelect = (MemberSelectExpressionTree) parent; - IdentifierTree identifier = memberSelect.identifier(); - if(!identifier.is(Tree.Kind.METHOD_INVOCATION)){ - continue; - } - MethodInvocationTree methodInvocation = (MethodInvocationTree) identifier; - if(!SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)) { - continue; - } - - VariableTree resultSetVariable = (VariableTree) methodInvocationTree.parent(); - resultSet = resultSetVariable.symbol(); + resultSet = ((VariableTree) methodInvocationTree.parent()).symbol(); break; } @@ -146,32 +135,47 @@ public void visitNode(Tree tree) { List usedColumns = new ArrayList<>(); List resultSetUsages = resultSet.usages(); for(IdentifierTree usage : resultSetUsages) { - Tree parent = usage.parent(); - if(!parent.is(Tree.Kind.MEMBER_SELECT)){ - continue; - } - MemberSelectExpressionTree memberSelect = (MemberSelectExpressionTree) parent; - IdentifierTree identifier = memberSelect.identifier(); - if(!identifier.is(Tree.Kind.METHOD_INVOCATION)){ + MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); + if(!SQL_RESULTSET_GET.matches(methodInvocation)) { continue; } - MethodInvocationTree methodInvocation = (MethodInvocationTree) identifier; - if(!SQL_RESULTSET_GET.matches(methodInvocation)) { + ExpressionTree columnET = methodInvocation.arguments().get(0); + if(!columnET.is(Tree.Kind.STRING_LITERAL)) { continue; } - String column = methodInvocation.arguments().get(0).toString(); + String column = ((LiteralTree) columnET).value(); + + column = column.toUpperCase(); + column = column.replaceAll("^['\"]", ""); + column = column.replaceAll("['\"]$", ""); usedColumns.add(column); } // STEP 4 : compare selected and used columns, report issues - - selectedColumns.removeAll(usedColumns); - if(!selectedColumns.isEmpty()) { + List differences = selectedColumns.stream() + .filter(element -> !usedColumns.contains(element)) + .collect(Collectors.toList()); + if(!differences.isEmpty()) { reportIssue(methodInvocationTree, MESSAGERULE); } } + private static MethodInvocationTree getMethodInvocationFromTree(Tree tree) { + Tree parent = tree.parent(); + if(!parent.is(Tree.Kind.MEMBER_SELECT)){ + return null; + } + while(!parent.is(Tree.Kind.METHOD_INVOCATION) && parent != null){ + parent = parent.parent(); + } + if(parent == null){ + return null; + } + + return (MethodInvocationTree) parent; + } + static List extractSelectedSQLColumns(String query){ query = query.toUpperCase(); query = query.replaceAll("^['\"]", ""); diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java index 78c8d58d..8ec3fa70 100644 --- a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java @@ -21,6 +21,7 @@ import java.util.List; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.sonar.java.checks.verifier.CheckVerifier; @@ -38,6 +39,7 @@ void testExtractSelectedSQLColumns(){ } @Test + @Disabled void testHasIssues1() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant1.java") @@ -54,6 +56,7 @@ void testHasIssues2() { } @Test + @Disabled void testHasIssues3() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant3.java") @@ -62,6 +65,7 @@ void testHasIssues3() { } @Test + @Disabled void testHasIssues4() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant4.java") @@ -70,6 +74,7 @@ void testHasIssues4() { } @Test + @Disabled void testHasIssues5() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant5.java") @@ -78,6 +83,7 @@ void testHasIssues5() { } @Test + @Disabled void testHasIssues6() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant6.java") @@ -128,6 +134,7 @@ void testHasNoIssues5() { } @Test + @Disabled void testHasNoIssues6() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedCompliant6.java") From e82572d555ed7178370c50654ade3aa7668073e7 Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Mon, 10 Jun 2024 16:46:43 +0200 Subject: [PATCH 10/25] fix: :bug: improve false positives detection --- .../java/checks/UseEveryColumnQueried.java | 146 ++++++++++++++---- .../UseEveryColumnQueriedCompliant3.java | 8 +- .../UseEveryColumnQueriedCompliant5.java | 3 +- .../UseEveryColumnQueriedNonCompliant1.java | 4 +- .../UseEveryColumnQueriedNonCompliant3.java | 4 +- .../UseEveryColumnQueriedNonCompliant4.java | 4 +- .../UseEveryColumnQueriedNonCompliant5.java | 5 +- .../checks/UseEveryColumnQueriedTest.java | 7 +- 8 files changed, 130 insertions(+), 51 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 483dc150..ac12fe45 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -24,12 +24,10 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.eclipse.jdt.internal.core.Member; import org.sonar.check.Rule; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.semantic.Symbol; -import org.sonar.plugins.java.api.semantic.Symbol.MethodSymbol; import org.sonar.plugins.java.api.semantic.Symbol.VariableSymbol; import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.Arguments; @@ -54,7 +52,6 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { .ofSubTypes(JAVA_SQL_STATEMENT) //TODO : also take into account addBatch and executeBatch .names("executeQuery", "execute", "executeUpdate", "executeLargeUpdate") - // TODO : take into account variable instead of just a string as parameters .addParametersMatcher("java.lang.String") .build(); private static MethodMatchers SQL_STATEMENT_RETRIEVE_RESULTSET = MethodMatchers @@ -63,7 +60,7 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { .names("executeQuery", "getResultSet") .withAnyParameters() .build(); - private static final MethodMatchers SQL_RESULTSET_GET = MethodMatchers + private static final MethodMatchers SQL_RESULTSET_GET_COLNAME = MethodMatchers .create() .ofSubTypes(JAVA_SQL_RESULTSET) .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", "getBigDecimal", @@ -71,6 +68,14 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { "getNClob", "getSQLXML", "getURL", "getNString", "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") .addParametersMatcher("java.lang.String") .build(); + private static final MethodMatchers SQL_RESULTSET_GET_COLID = MethodMatchers + .create() + .ofSubTypes(JAVA_SQL_RESULTSET) + .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", "getBigDecimal", + "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", "getClob", "getRef", "getRowId", + "getNClob", "getSQLXML", "getURL", "getNString", "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") + .addParametersMatcher("int") + .build(); private static final Pattern SELECTED_COLUMNS_PATTERN = Pattern.compile("SELECT\\s+(.*)\\s+FROM\\s+.*"); @Override @@ -107,10 +112,17 @@ public void visitNode(Tree tree) { return; } ExpressionTree argument = arguments.get(0); - if(!argument.is(Tree.Kind.STRING_LITERAL)){ + String query; + if(argument.is(Tree.Kind.STRING_LITERAL)){ + query = ((LiteralTree) argument).value(); + } else if(argument.is(Tree.Kind.IDENTIFIER)){ + query = extractValueFromVariable((IdentifierTree) argument); + } else { + return; + } + if(query == null){ return; } - String query = ((LiteralTree) argument).value(); List selectedColumns = extractSelectedSQLColumns(query); // STEP 2 : retrieve the resultSet object @@ -118,37 +130,79 @@ public void visitNode(Tree tree) { List usages = statement.usages(); Symbol resultSet = null; for(IdentifierTree usage : usages) { - Tree methodInvocation = getMethodInvocationFromTree(usage); - if(methodInvocation == null){ + MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); + if(methodInvocation == null ){ continue; } - resultSet = ((VariableTree) methodInvocationTree.parent()).symbol(); - break; + if(SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)){ + Tree parent = methodInvocation.parent(); + if(parent.is(Tree.Kind.VARIABLE)){ + resultSet = ((VariableTree) parent).symbol(); + break; + } + } } - // STEP 3 : retrieve the columns used from the resultSet object - + // STEP 2.1 check if the resultSet is used as a parameter of a method + // if it is, this check this check cannot be applied if(resultSet == null) { return; } + List resultSetUsages = resultSet.usages(); + for(IdentifierTree usage : resultSetUsages) { + Tree parent = usage.parent(); + if(parent.is(Tree.Kind.ARGUMENTS)){ + return; + } + } + + // STEP 2.2 check if the resultSet is reassigned + for(IdentifierTree usage : resultSetUsages) { + Tree parent = usage.parent(); + if(parent.is(Tree.Kind.ASSIGNMENT)){ + AssignmentExpressionTree assignment = (AssignmentExpressionTree) parent; + ExpressionTree expressionTree = assignment.variable(); + if(expressionTree.is(Tree.Kind.IDENTIFIER)){ + if(resultSet.equals(((IdentifierTree) expressionTree).symbol())) { + return; + } + } + } + } + + // STEP 3 : retrieve the columns used from the resultSet object List usedColumns = new ArrayList<>(); - List resultSetUsages = resultSet.usages(); for(IdentifierTree usage : resultSetUsages) { + if(usage.parent().is(Tree.Kind.ASSIGNMENT)){ + break; + } MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); - if(!SQL_RESULTSET_GET.matches(methodInvocation)) { + if(methodInvocation == null ){ continue; } - ExpressionTree columnET = methodInvocation.arguments().get(0); - if(!columnET.is(Tree.Kind.STRING_LITERAL)) { - continue; + if(SQL_RESULTSET_GET_COLNAME.matches(methodInvocation)) { + ExpressionTree columnET = methodInvocation.arguments().get(0); + if(!columnET.is(Tree.Kind.STRING_LITERAL)) { + continue; + } + String column = ((LiteralTree) columnET).value(); + + column = column.toUpperCase(); + column = column.replaceAll("^['\"]", ""); + column = column.replaceAll("['\"]$", ""); + usedColumns.add(column); + } else if(SQL_RESULTSET_GET_COLID.matches(methodInvocation)) { + ExpressionTree columnET = methodInvocation.arguments().get(0); + if(!columnET.is(Tree.Kind.INT_LITERAL)) { + continue; + } + int column = Integer.parseInt(((LiteralTree) columnET).value()); + if(column > selectedColumns.size()) { + continue; + } + usedColumns.add(selectedColumns.get(column - 1)); } - String column = ((LiteralTree) columnET).value(); - - column = column.toUpperCase(); - column = column.replaceAll("^['\"]", ""); - column = column.replaceAll("['\"]$", ""); - usedColumns.add(column); } // STEP 4 : compare selected and used columns, report issues @@ -161,21 +215,53 @@ public void visitNode(Tree tree) { } - private static MethodInvocationTree getMethodInvocationFromTree(Tree tree) { - Tree parent = tree.parent(); - if(!parent.is(Tree.Kind.MEMBER_SELECT)){ - return null; - } - while(!parent.is(Tree.Kind.METHOD_INVOCATION) && parent != null){ + private static MethodInvocationTree getMethodInvocationFromTree(IdentifierTree tree) { + Tree parent = tree; + while(parent != null && !parent.is(Tree.Kind.METHOD_INVOCATION) ){ parent = parent.parent(); } if(parent == null){ return null; } - return (MethodInvocationTree) parent; } + private static String extractValueFromVariable(IdentifierTree tree){ + Symbol symbol = tree.symbol(); + if(symbol == null) { + return null; + } + //accept this value if it's a final variable or it's a variable that is not reassigned + if(!symbol.isFinal()){ + List usages = symbol.usages(); + int assignementCount = 0; + for(IdentifierTree usage : usages) { + Tree parent = usage.parent(); + if(parent.is(Tree.Kind.ASSIGNMENT)){ + assignementCount++; + } + } + if(assignementCount > 1){ + return null; + } + } + if(symbol.isVariableSymbol()) { + VariableSymbol variableSymbol = (VariableSymbol) symbol; + Type type = variableSymbol.type(); + if(type.is("java.lang.String")) { + Tree assignement = variableSymbol.declaration(); + if(assignement.is(Tree.Kind.VARIABLE)){ + VariableTree variableTree = (VariableTree) assignement; + ExpressionTree initializer = variableTree.initializer(); + if(initializer.is(Tree.Kind.STRING_LITERAL)){ + return ((LiteralTree) initializer).value(); + } + } + } + } + return null; + } + static List extractSelectedSQLColumns(String query){ query = query.toUpperCase(); query = query.replaceAll("^['\"]", ""); diff --git a/src/test/files/UseEveryColumnQueriedCompliant3.java b/src/test/files/UseEveryColumnQueriedCompliant3.java index a6f0fe7a..e2a50822 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant3.java +++ b/src/test/files/UseEveryColumnQueriedCompliant3.java @@ -37,10 +37,10 @@ public void callJdbc() { ResultSet rs = stmt.executeQuery(QUERY);) { while (rs.next()) { // Display values - System.out.print("ID: " + rs.getInt("1")); - System.out.print(", Age: " + rs.getInt("4")); - System.out.print(", First: " + rs.getString("2")); - System.out.println(", Last: " + rs.getString("3")); + System.out.print("ID: " + rs.getInt(1)); + System.out.print(", Age: " + rs.getInt(4)); + System.out.print(", First: " + rs.getString(2)); + System.out.println(", Last: " + rs.getString(3)); } } catch (SQLException e) { e.printStackTrace(); diff --git a/src/test/files/UseEveryColumnQueriedCompliant5.java b/src/test/files/UseEveryColumnQueriedCompliant5.java index 7fedb2af..4f5f68ca 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant5.java +++ b/src/test/files/UseEveryColumnQueriedCompliant5.java @@ -29,7 +29,7 @@ public class UseEveryColumnQueriedCompliant5 { private static final String USER = "guest"; private static final String PASS = "guest123"; private static final String QUERY = "SELECT id, first, last, age FROM Registration"; - private static final String QUERY2 = "SELECT id, first, last, age FROM Registration2"; + private static final String QUERY2 = "SELECT id, first, last FROM Registration2"; public void callJdbc() { @@ -51,7 +51,6 @@ public void callJdbc() { while (rs.next()) { // Display values - System.out.print("Age: " + rs.getInt("age")); System.out.print("ID: " + rs.getInt("id")); System.out.print(", First: " + rs.getString("first")); System.out.println(", Last: " + rs.getString("last")); diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant1.java b/src/test/files/UseEveryColumnQueriedNonCompliant1.java index 0daf184b..1c40dbbb 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant1.java +++ b/src/test/files/UseEveryColumnQueriedNonCompliant1.java @@ -28,13 +28,13 @@ public class UseEveryColumnQueriedNonCompliant1 { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; public void callJdbc() { try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(QUERY);) { + ResultSet rs = stmt.executeQuery(QUERY);) { // Noncompliant {{Avoid querying SQL columns that are not used}} while (rs.next()) { // Display values System.out.print("ID: " + rs.getInt("id")); diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant3.java b/src/test/files/UseEveryColumnQueriedNonCompliant3.java index 775c6387..50afcc5f 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant3.java +++ b/src/test/files/UseEveryColumnQueriedNonCompliant3.java @@ -28,13 +28,13 @@ public class UseEveryColumnQueriedNonCompliant3 { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; public void callJdbc() { try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(QUERY);) { + ResultSet rs = stmt.executeQuery(QUERY);) { // Noncompliant {{Avoid querying SQL columns that are not used}} while (rs.next()) { // Display values System.out.print("ID: " + rs.getInt(1)); diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant4.java b/src/test/files/UseEveryColumnQueriedNonCompliant4.java index 86ac6e40..00235237 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant4.java +++ b/src/test/files/UseEveryColumnQueriedNonCompliant4.java @@ -28,13 +28,13 @@ public class UseEveryColumnQueriedNonCompliant4 { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; public void callJdbc() { try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(QUERY);) { + ResultSet rs = stmt.executeQuery(QUERY);) { // Noncompliant {{Avoid querying SQL columns that are not used}} while (rs.next()) { // Display values System.out.print("ID: " + rs.getInt("id")); diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant5.java b/src/test/files/UseEveryColumnQueriedNonCompliant5.java index 5c922e50..959bc80c 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant5.java +++ b/src/test/files/UseEveryColumnQueriedNonCompliant5.java @@ -27,7 +27,7 @@ public class UseEveryColumnQueriedNonCompliant5 { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; // Noncompliant {{Avoid querying SQL columns that are not used}} + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; private static final String QUERY2 = "SELECT id, first, last, age FROM Registration2"; @@ -37,7 +37,7 @@ public void callJdbc() { Statement stmt = conn.createStatement(); ) { - ResultSet rs = stmt.executeQuery(QUERY); + ResultSet rs = stmt.executeQuery(QUERY); // Noncompliant {{Avoid querying SQL columns that are not used}} while (rs.next()) { // Display values System.out.print("ID: " + rs.getInt("id")); @@ -46,7 +46,6 @@ public void callJdbc() { } rs = stmt.executeQuery(QUERY2); - while (rs.next()) { // Display values System.out.print("Age: " + rs.getInt("age")); diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java index 8ec3fa70..df1ab3be 100644 --- a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java @@ -39,7 +39,6 @@ void testExtractSelectedSQLColumns(){ } @Test - @Disabled void testHasIssues1() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant1.java") @@ -56,7 +55,6 @@ void testHasIssues2() { } @Test - @Disabled void testHasIssues3() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant3.java") @@ -65,7 +63,6 @@ void testHasIssues3() { } @Test - @Disabled void testHasIssues4() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant4.java") @@ -74,7 +71,6 @@ void testHasIssues4() { } @Test - @Disabled void testHasIssues5() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant5.java") @@ -83,7 +79,7 @@ void testHasIssues5() { } @Test - @Disabled + @Disabled // case not handled yet void testHasIssues6() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant6.java") @@ -134,7 +130,6 @@ void testHasNoIssues5() { } @Test - @Disabled void testHasNoIssues6() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedCompliant6.java") From 759b8ad7701c5e49f9bb36ebf61884162d2c41b9 Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Tue, 11 Jun 2024 14:40:46 +0200 Subject: [PATCH 11/25] refactor: :recycle: quick refactor --- .../java/checks/UseEveryColumnQueried.java | 245 ++++++++++-------- .../UseEveryColumnQueriedCompliant4.java | 6 +- .../UseEveryColumnQueriedNonCompliant4.java | 3 +- .../checks/UseEveryColumnQueriedTest.java | 3 +- 4 files changed, 138 insertions(+), 119 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index ac12fe45..f45fd80e 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -24,12 +24,13 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.annotation.Nullable; + import org.sonar.check.Rule; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; import org.sonar.plugins.java.api.semantic.MethodMatchers; import org.sonar.plugins.java.api.semantic.Symbol; import org.sonar.plugins.java.api.semantic.Symbol.VariableSymbol; -import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.Arguments; import org.sonar.plugins.java.api.tree.AssignmentExpressionTree; import org.sonar.plugins.java.api.tree.ExpressionTree; @@ -90,131 +91,117 @@ public void visitNode(Tree tree) { if(!SQL_STATEMENT_DECLARE_SQL.matches(methodInvocationTree)) { return; } - ExpressionTree et = methodInvocationTree.methodSelect(); - if(!et.is(Tree.Kind.MEMBER_SELECT)) { + List selectedColumns = getSelectedColumns(methodInvocationTree); + if(selectedColumns.isEmpty()) { return; } - MemberSelectExpressionTree mset = (MemberSelectExpressionTree) et; - ExpressionTree expression = mset.expression(); - if(!expression.is(Tree.Kind.IDENTIFIER)) { + List usedColumns = getUsedColumns(methodInvocationTree, selectedColumns); + if(usedColumns == null) { return; } - IdentifierTree id = (IdentifierTree) expression; - Symbol statement = id.symbol(); - if(statement == null) { - return; + List differences = selectedColumns.stream() + .filter(element -> !usedColumns.contains(element)) + .collect(Collectors.toList()); + if(!differences.isEmpty()) { + reportIssue(methodInvocationTree, MESSAGERULE); } + } - // STEP 1 : retrieve the selected columns in the query - + private static List getSelectedColumns(MethodInvocationTree methodInvocationTree) { Arguments arguments = methodInvocationTree.arguments(); if(arguments.isEmpty()) { - return; + return new ArrayList<>(); } ExpressionTree argument = arguments.get(0); - String query; - if(argument.is(Tree.Kind.STRING_LITERAL)){ - query = ((LiteralTree) argument).value(); - } else if(argument.is(Tree.Kind.IDENTIFIER)){ - query = extractValueFromVariable((IdentifierTree) argument); - } else { - return; - } - if(query == null){ - return; + LiteralTree litteral = extractLiteralFromVariable(argument); + if(litteral == null) { + return new ArrayList<>(); } + String query = litteral.value(); List selectedColumns = extractSelectedSQLColumns(query); + return selectedColumns; + } - // STEP 2 : retrieve the resultSet object + @Nullable + private static List getUsedColumns(MethodInvocationTree methodInvocationTree, List selectedColumns){ + Symbol resultSet = getResultSetNode(methodInvocationTree); - List usages = statement.usages(); - Symbol resultSet = null; - for(IdentifierTree usage : usages) { + if(resultSet == null || isResultSetInvalid(resultSet)) { + return null; + } + + List usedColumns = new ArrayList<>(); + List resultSetUsages = resultSet.usages(); + for(IdentifierTree usage : resultSetUsages) { + if(usage.parent().is(Tree.Kind.ASSIGNMENT)){ + break; + } MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); if(methodInvocation == null ){ continue; } - if(SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)){ - Tree parent = methodInvocation.parent(); - if(parent.is(Tree.Kind.VARIABLE)){ - resultSet = ((VariableTree) parent).symbol(); + if(methodInvocation.arguments().isEmpty()){ + continue; + } + ExpressionTree parameter = methodInvocation.arguments().get(0); + LiteralTree columnGot = extractLiteralFromVariable(parameter); + if(columnGot == null){ + continue; + } + String column; + String value = columnGot.value(); + if(SQL_RESULTSET_GET_COLNAME.matches(methodInvocation) && columnGot.is(Tree.Kind.STRING_LITERAL)) { + column = value.toUpperCase(); + column = column.replaceAll("^['\"]", ""); + column = column.replaceAll("['\"]$", ""); + } else if(SQL_RESULTSET_GET_COLID.matches(methodInvocation) && columnGot.is(Tree.Kind.INT_LITERAL)) { + int columnId = Integer.parseInt(value); + if(columnId > selectedColumns.size()) { break; } + column = selectedColumns.get(columnId - 1); + } else { + continue; } + usedColumns.add(column); } + return usedColumns; + } - // STEP 2.1 check if the resultSet is used as a parameter of a method - // if it is, this check this check cannot be applied - if(resultSet == null) { - return; + @Nullable + private static Symbol getResultSetNode(MethodInvocationTree methodInvocationTree) { + ExpressionTree et = methodInvocationTree.methodSelect(); + if(!et.is(Tree.Kind.MEMBER_SELECT)) { + return null; } - List resultSetUsages = resultSet.usages(); - for(IdentifierTree usage : resultSetUsages) { - Tree parent = usage.parent(); - if(parent.is(Tree.Kind.ARGUMENTS)){ - return; - } + MemberSelectExpressionTree mset = (MemberSelectExpressionTree) et; + ExpressionTree expression = mset.expression(); + if(!expression.is(Tree.Kind.IDENTIFIER)) { + return null; } - - // STEP 2.2 check if the resultSet is reassigned - for(IdentifierTree usage : resultSetUsages) { - Tree parent = usage.parent(); - if(parent.is(Tree.Kind.ASSIGNMENT)){ - AssignmentExpressionTree assignment = (AssignmentExpressionTree) parent; - ExpressionTree expressionTree = assignment.variable(); - if(expressionTree.is(Tree.Kind.IDENTIFIER)){ - if(resultSet.equals(((IdentifierTree) expressionTree).symbol())) { - return; - } - } - } + IdentifierTree id = (IdentifierTree) expression; + Symbol statement = id.symbol(); + if(statement == null) { + return null; } - - // STEP 3 : retrieve the columns used from the resultSet object - - List usedColumns = new ArrayList<>(); - for(IdentifierTree usage : resultSetUsages) { - if(usage.parent().is(Tree.Kind.ASSIGNMENT)){ - break; - } + List usages = statement.usages(); + Symbol resultSet = null; + for(IdentifierTree usage : usages) { MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); if(methodInvocation == null ){ continue; } - if(SQL_RESULTSET_GET_COLNAME.matches(methodInvocation)) { - ExpressionTree columnET = methodInvocation.arguments().get(0); - if(!columnET.is(Tree.Kind.STRING_LITERAL)) { - continue; - } - String column = ((LiteralTree) columnET).value(); - - column = column.toUpperCase(); - column = column.replaceAll("^['\"]", ""); - column = column.replaceAll("['\"]$", ""); - usedColumns.add(column); - } else if(SQL_RESULTSET_GET_COLID.matches(methodInvocation)) { - ExpressionTree columnET = methodInvocation.arguments().get(0); - if(!columnET.is(Tree.Kind.INT_LITERAL)) { - continue; - } - int column = Integer.parseInt(((LiteralTree) columnET).value()); - if(column > selectedColumns.size()) { - continue; + if(SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)){ + Tree parent = methodInvocation.parent(); + if(parent.is(Tree.Kind.VARIABLE)){ + resultSet = ((VariableTree) parent).symbol(); + break; } - usedColumns.add(selectedColumns.get(column - 1)); } } - - // STEP 4 : compare selected and used columns, report issues - List differences = selectedColumns.stream() - .filter(element -> !usedColumns.contains(element)) - .collect(Collectors.toList()); - if(!differences.isEmpty()) { - reportIssue(methodInvocationTree, MESSAGERULE); - } - + return resultSet; } - + @Nullable private static MethodInvocationTree getMethodInvocationFromTree(IdentifierTree tree) { Tree parent = tree; while(parent != null && !parent.is(Tree.Kind.METHOD_INVOCATION) ){ @@ -226,43 +213,71 @@ private static MethodInvocationTree getMethodInvocationFromTree(IdentifierTree t return (MethodInvocationTree) parent; } - private static String extractValueFromVariable(IdentifierTree tree){ - Symbol symbol = tree.symbol(); + @Nullable + private static LiteralTree extractLiteralFromVariable(ExpressionTree tree){ + if(tree == null || tree instanceof LiteralTree){ + return (LiteralTree) tree; + } + if (!tree.is(Tree.Kind.IDENTIFIER)) { + return null; + } + IdentifierTree identifierTree = (IdentifierTree) tree; + Symbol symbol = identifierTree.symbol(); if(symbol == null) { return null; } - //accept this value if it's a final variable or it's a variable that is not reassigned if(!symbol.isFinal()){ - List usages = symbol.usages(); - int assignementCount = 0; - for(IdentifierTree usage : usages) { - Tree parent = usage.parent(); - if(parent.is(Tree.Kind.ASSIGNMENT)){ - assignementCount++; - } - } - if(assignementCount > 1){ - return null; - } + return null; } if(symbol.isVariableSymbol()) { VariableSymbol variableSymbol = (VariableSymbol) symbol; - Type type = variableSymbol.type(); - if(type.is("java.lang.String")) { - Tree assignement = variableSymbol.declaration(); - if(assignement.is(Tree.Kind.VARIABLE)){ - VariableTree variableTree = (VariableTree) assignement; - ExpressionTree initializer = variableTree.initializer(); - if(initializer.is(Tree.Kind.STRING_LITERAL)){ - return ((LiteralTree) initializer).value(); + Tree assignement = variableSymbol.declaration(); + if(assignement.is(Tree.Kind.VARIABLE)){ + VariableTree variableTree = (VariableTree) assignement; + ExpressionTree initializer = variableTree.initializer(); + return extractLiteralFromVariable(initializer); + } + } + return null; + } + + private static boolean isResultSetInvalid(Symbol resultSet) { + return isResultSetUsedInMethod(resultSet) + || isResultSetReassigned(resultSet); + } + + private static boolean isResultSetUsedInMethod(Symbol resultSet) { + List resultSetUsages = resultSet.usages(); + for(IdentifierTree usage : resultSetUsages) { + Tree parent = usage.parent(); + if(parent.is(Tree.Kind.ARGUMENTS)){ + return true; + } + } + return false; + } + + private static boolean isResultSetReassigned(Symbol resultSet) { + List resultSetUsages = resultSet.usages(); + for(IdentifierTree usage : resultSetUsages) { + Tree parent = usage.parent(); + if(parent.is(Tree.Kind.ASSIGNMENT)){ + AssignmentExpressionTree assignment = (AssignmentExpressionTree) parent; + ExpressionTree expressionTree = assignment.variable(); + if(expressionTree.is(Tree.Kind.IDENTIFIER)){ + if(resultSet.equals(((IdentifierTree) expressionTree).symbol())) { + return true; } } } } - return null; + return false; } static List extractSelectedSQLColumns(String query){ + if(query == null){ + return new ArrayList<>(); + } query = query.toUpperCase(); query = query.replaceAll("^['\"]", ""); query = query.replaceAll("['\"]$", ""); diff --git a/src/test/files/UseEveryColumnQueriedCompliant4.java b/src/test/files/UseEveryColumnQueriedCompliant4.java index 350b23cc..05731213 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant4.java +++ b/src/test/files/UseEveryColumnQueriedCompliant4.java @@ -29,6 +29,8 @@ public class UseEveryColumnQueriedCompliant4 { private static final String USER = "guest"; private static final String PASS = "guest123"; private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + private static final String ID = "id"; + private static final int AGE = 4; public void callJdbc() { @@ -37,8 +39,8 @@ public void callJdbc() { ResultSet rs = stmt.executeQuery(QUERY);) { while (rs.next()) { // Display values - System.out.print("ID: " + rs.getInt("id")); - System.out.print(", Age: " + rs.getInt(4)); + System.out.print("ID: " + rs.getInt(ID)); + System.out.print(", Age: " + rs.getInt(AGE)); System.out.print(", First: " + rs.getString(2)); System.out.println(", Last: " + rs.getString("last")); } diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant4.java b/src/test/files/UseEveryColumnQueriedNonCompliant4.java index 00235237..ffe73558 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant4.java +++ b/src/test/files/UseEveryColumnQueriedNonCompliant4.java @@ -29,6 +29,7 @@ public class UseEveryColumnQueriedNonCompliant4 { private static final String USER = "guest"; private static final String PASS = "guest123"; private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + private static final String ID = "id"; public void callJdbc() { @@ -37,7 +38,7 @@ public void callJdbc() { ResultSet rs = stmt.executeQuery(QUERY);) { // Noncompliant {{Avoid querying SQL columns that are not used}} while (rs.next()) { // Display values - System.out.print("ID: " + rs.getInt("id")); + System.out.print("ID: " + rs.getInt(ID)); System.out.print(", First: " + rs.getString(2)); System.out.println(", Last: " + rs.getString("last")); } diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java index df1ab3be..61c664f8 100644 --- a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java @@ -71,6 +71,7 @@ void testHasIssues4() { } @Test + @Disabled // case not handled (multiple queries with the same ResultSet object) void testHasIssues5() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant5.java") @@ -79,7 +80,7 @@ void testHasIssues5() { } @Test - @Disabled // case not handled yet + @Disabled // case not handled (usage of a method) void testHasIssues6() { CheckVerifier.newVerifier() .onFile("src/test/files/UseEveryColumnQueriedNonCompliant6.java") From 2b7f600c58a1bc58e835fde3ee7a1b53b92ee700 Mon Sep 17 00:00:00 2001 From: Maxime Daniel Date: Wed, 12 Jun 2024 09:09:50 +0200 Subject: [PATCH 12/25] refactor: :recycle: fix formating --- .../java/checks/UseEveryColumnQueried.java | 188 ++++++++---------- 1 file changed, 84 insertions(+), 104 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index f45fd80e..2d0b6608 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -48,35 +48,33 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { protected static final String MESSAGERULE = "Avoid querying SQL columns that are not used"; private static final String JAVA_SQL_STATEMENT = "java.sql.Statement"; private static final String JAVA_SQL_RESULTSET = "java.sql.ResultSet"; - private static final MethodMatchers SQL_STATEMENT_DECLARE_SQL = MethodMatchers - .create() - .ofSubTypes(JAVA_SQL_STATEMENT) - //TODO : also take into account addBatch and executeBatch - .names("executeQuery", "execute", "executeUpdate", "executeLargeUpdate") - .addParametersMatcher("java.lang.String") - .build(); - private static MethodMatchers SQL_STATEMENT_RETRIEVE_RESULTSET = MethodMatchers - .create() - .ofSubTypes(JAVA_SQL_STATEMENT) - .names("executeQuery", "getResultSet") - .withAnyParameters() - .build(); - private static final MethodMatchers SQL_RESULTSET_GET_COLNAME = MethodMatchers - .create() - .ofSubTypes(JAVA_SQL_RESULTSET) - .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", "getBigDecimal", - "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", "getClob", "getRef", "getRowId", - "getNClob", "getSQLXML", "getURL", "getNString", "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") - .addParametersMatcher("java.lang.String") - .build(); - private static final MethodMatchers SQL_RESULTSET_GET_COLID = MethodMatchers - .create() - .ofSubTypes(JAVA_SQL_RESULTSET) - .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", "getBigDecimal", - "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", "getClob", "getRef", "getRowId", - "getNClob", "getSQLXML", "getURL", "getNString", "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") - .addParametersMatcher("int") - .build(); + private static final MethodMatchers SQL_STATEMENT_DECLARE_SQL = MethodMatchers.create() + .ofSubTypes(JAVA_SQL_STATEMENT) + // TODO : also take into account addBatch and executeBatch + .names("executeQuery", "execute", "executeUpdate", "executeLargeUpdate") + .addParametersMatcher("java.lang.String") + .build(); + private static final MethodMatchers SQL_STATEMENT_RETRIEVE_RESULTSET = MethodMatchers.create() + .ofSubTypes(JAVA_SQL_STATEMENT) + .names("executeQuery", "getResultSet") + .withAnyParameters() + .build(); + private static final MethodMatchers SQL_RESULTSET_GET_COLNAME = MethodMatchers.create() + .ofSubTypes(JAVA_SQL_RESULTSET) + .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", + "getBigDecimal", "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", + "getClob", "getRef", "getRowId", "getNClob", "getSQLXML", "getURL", "getNString", + "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") + .addParametersMatcher("java.lang.String") + .build(); + private static final MethodMatchers SQL_RESULTSET_GET_COLID = MethodMatchers.create() + .ofSubTypes(JAVA_SQL_RESULTSET) + .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", + "getBigDecimal", "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", + "getClob", "getRef", "getRowId", "getNClob", "getSQLXML", "getURL", "getNString", + "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") + .addParametersMatcher("int") + .build(); private static final Pattern SELECTED_COLUMNS_PATTERN = Pattern.compile("SELECT\\s+(.*)\\s+FROM\\s+.*"); @Override @@ -86,77 +84,70 @@ public List nodesToVisit() { @Override public void visitNode(Tree tree) { - MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; - if(!SQL_STATEMENT_DECLARE_SQL.matches(methodInvocationTree)) { + if (!SQL_STATEMENT_DECLARE_SQL.matches(methodInvocationTree)) { return; } List selectedColumns = getSelectedColumns(methodInvocationTree); - if(selectedColumns.isEmpty()) { + if (selectedColumns.isEmpty()) { return; } List usedColumns = getUsedColumns(methodInvocationTree, selectedColumns); - if(usedColumns == null) { + if (usedColumns == null) { return; } List differences = selectedColumns.stream() - .filter(element -> !usedColumns.contains(element)) - .collect(Collectors.toList()); - if(!differences.isEmpty()) { + .filter(element -> !usedColumns.contains(element)) + .collect(Collectors.toList()); + if (!differences.isEmpty()) { reportIssue(methodInvocationTree, MESSAGERULE); } } private static List getSelectedColumns(MethodInvocationTree methodInvocationTree) { Arguments arguments = methodInvocationTree.arguments(); - if(arguments.isEmpty()) { + if (arguments.isEmpty()) { return new ArrayList<>(); } ExpressionTree argument = arguments.get(0); - LiteralTree litteral = extractLiteralFromVariable(argument); - if(litteral == null) { + LiteralTree literal = extractLiteralFromVariable(argument); + if (literal == null) { return new ArrayList<>(); } - String query = litteral.value(); - List selectedColumns = extractSelectedSQLColumns(query); - return selectedColumns; + String query = literal.value(); + return extractSelectedSQLColumns(query); } @Nullable - private static List getUsedColumns(MethodInvocationTree methodInvocationTree, List selectedColumns){ + private static List getUsedColumns(MethodInvocationTree methodInvocationTree, List selectedColumns) { Symbol resultSet = getResultSetNode(methodInvocationTree); - - if(resultSet == null || isResultSetInvalid(resultSet)) { + if (resultSet == null || isResultSetInvalid(resultSet)) { return null; } - List usedColumns = new ArrayList<>(); List resultSetUsages = resultSet.usages(); - for(IdentifierTree usage : resultSetUsages) { - if(usage.parent().is(Tree.Kind.ASSIGNMENT)){ + for (IdentifierTree usage : resultSetUsages) { + if (usage.parent().is(Tree.Kind.ASSIGNMENT)) { break; } MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); - if(methodInvocation == null ){ - continue; - } - if(methodInvocation.arguments().isEmpty()){ + if (methodInvocation == null || methodInvocation.arguments().isEmpty()) { continue; } ExpressionTree parameter = methodInvocation.arguments().get(0); LiteralTree columnGot = extractLiteralFromVariable(parameter); - if(columnGot == null){ + if (columnGot == null) { continue; } String column; String value = columnGot.value(); - if(SQL_RESULTSET_GET_COLNAME.matches(methodInvocation) && columnGot.is(Tree.Kind.STRING_LITERAL)) { - column = value.toUpperCase(); - column = column.replaceAll("^['\"]", ""); - column = column.replaceAll("['\"]$", ""); - } else if(SQL_RESULTSET_GET_COLID.matches(methodInvocation) && columnGot.is(Tree.Kind.INT_LITERAL)) { + if (SQL_RESULTSET_GET_COLNAME.matches(methodInvocation) && columnGot.is(Tree.Kind.STRING_LITERAL)) { + column = value.toUpperCase() + .replaceAll("^['\"]", "") + .replaceAll("['\"]$", ""); + } else if (SQL_RESULTSET_GET_COLID.matches(methodInvocation) && columnGot.is(Tree.Kind.INT_LITERAL)) { int columnId = Integer.parseInt(value); - if(columnId > selectedColumns.size()) { + if (columnId > selectedColumns.size()) { break; } column = selectedColumns.get(columnId - 1); @@ -171,51 +162,47 @@ private static List getUsedColumns(MethodInvocationTree methodInvocation @Nullable private static Symbol getResultSetNode(MethodInvocationTree methodInvocationTree) { ExpressionTree et = methodInvocationTree.methodSelect(); - if(!et.is(Tree.Kind.MEMBER_SELECT)) { + if (!et.is(Tree.Kind.MEMBER_SELECT)) { return null; } MemberSelectExpressionTree mset = (MemberSelectExpressionTree) et; ExpressionTree expression = mset.expression(); - if(!expression.is(Tree.Kind.IDENTIFIER)) { + if (!expression.is(Tree.Kind.IDENTIFIER)) { return null; } IdentifierTree id = (IdentifierTree) expression; Symbol statement = id.symbol(); - if(statement == null) { + if (statement == null) { return null; } List usages = statement.usages(); Symbol resultSet = null; - for(IdentifierTree usage : usages) { + for (IdentifierTree usage : usages) { MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); - if(methodInvocation == null ){ + if (methodInvocation == null || !SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)) { continue; } - if(SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)){ - Tree parent = methodInvocation.parent(); - if(parent.is(Tree.Kind.VARIABLE)){ - resultSet = ((VariableTree) parent).symbol(); - break; - } + Tree parent = methodInvocation.parent(); + if (parent.is(Tree.Kind.VARIABLE)) { + resultSet = ((VariableTree) parent).symbol(); + break; } } return resultSet; } + @Nullable private static MethodInvocationTree getMethodInvocationFromTree(IdentifierTree tree) { Tree parent = tree; - while(parent != null && !parent.is(Tree.Kind.METHOD_INVOCATION) ){ + while (parent != null && !parent.is(Tree.Kind.METHOD_INVOCATION)) { parent = parent.parent(); } - if(parent == null){ - return null; - } return (MethodInvocationTree) parent; } @Nullable - private static LiteralTree extractLiteralFromVariable(ExpressionTree tree){ - if(tree == null || tree instanceof LiteralTree){ + private static LiteralTree extractLiteralFromVariable(ExpressionTree tree) { + if (tree == null || tree instanceof LiteralTree) { return (LiteralTree) tree; } if (!tree.is(Tree.Kind.IDENTIFIER)) { @@ -223,34 +210,29 @@ private static LiteralTree extractLiteralFromVariable(ExpressionTree tree){ } IdentifierTree identifierTree = (IdentifierTree) tree; Symbol symbol = identifierTree.symbol(); - if(symbol == null) { + if (symbol == null || !symbol.isFinal() || !symbol.isVariableSymbol()) { return null; } - if(!symbol.isFinal()){ + VariableSymbol variableSymbol = (VariableSymbol) symbol; + Tree assignment = variableSymbol.declaration(); + if (!assignment.is(Tree.Kind.VARIABLE)) { return null; } - if(symbol.isVariableSymbol()) { - VariableSymbol variableSymbol = (VariableSymbol) symbol; - Tree assignement = variableSymbol.declaration(); - if(assignement.is(Tree.Kind.VARIABLE)){ - VariableTree variableTree = (VariableTree) assignement; - ExpressionTree initializer = variableTree.initializer(); - return extractLiteralFromVariable(initializer); - } - } - return null; + VariableTree variableTree = (VariableTree) assignment; + ExpressionTree initializer = variableTree.initializer(); + return extractLiteralFromVariable(initializer); } private static boolean isResultSetInvalid(Symbol resultSet) { - return isResultSetUsedInMethod(resultSet) - || isResultSetReassigned(resultSet); + return isResultSetUsedInMethod(resultSet) + || isResultSetReassigned(resultSet); } private static boolean isResultSetUsedInMethod(Symbol resultSet) { List resultSetUsages = resultSet.usages(); - for(IdentifierTree usage : resultSetUsages) { + for (IdentifierTree usage : resultSetUsages) { Tree parent = usage.parent(); - if(parent.is(Tree.Kind.ARGUMENTS)){ + if (parent.is(Tree.Kind.ARGUMENTS)) { return true; } } @@ -259,28 +241,27 @@ private static boolean isResultSetUsedInMethod(Symbol resultSet) { private static boolean isResultSetReassigned(Symbol resultSet) { List resultSetUsages = resultSet.usages(); - for(IdentifierTree usage : resultSetUsages) { + for (IdentifierTree usage : resultSetUsages) { Tree parent = usage.parent(); - if(parent.is(Tree.Kind.ASSIGNMENT)){ + if (parent.is(Tree.Kind.ASSIGNMENT)) { AssignmentExpressionTree assignment = (AssignmentExpressionTree) parent; ExpressionTree expressionTree = assignment.variable(); - if(expressionTree.is(Tree.Kind.IDENTIFIER)){ - if(resultSet.equals(((IdentifierTree) expressionTree).symbol())) { - return true; - } + if (expressionTree.is(Tree.Kind.IDENTIFIER) + && resultSet.equals(((IdentifierTree) expressionTree).symbol())) { + return true; } } } return false; } - static List extractSelectedSQLColumns(String query){ - if(query == null){ + static List extractSelectedSQLColumns(String query) { + if (query == null) { return new ArrayList<>(); } - query = query.toUpperCase(); - query = query.replaceAll("^['\"]", ""); - query = query.replaceAll("['\"]$", ""); + query = query.toUpperCase() + .replaceAll("^['\"]", "") + .replaceAll("['\"]$", ""); List columns = new ArrayList<>(); Matcher matcher = SELECTED_COLUMNS_PATTERN.matcher(query); if (matcher.matches()) { @@ -291,5 +272,4 @@ static List extractSelectedSQLColumns(String query){ } return columns; } - } From 30b131f5892b6774d58a9fc438d0e8d51a29b0c8 Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Wed, 7 Aug 2024 16:04:24 +0200 Subject: [PATCH 13/25] remove executeUpdate --- .../greencodeinitiative/java/checks/UseEveryColumnQueried.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 2d0b6608..24d084fd 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -51,7 +51,7 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { private static final MethodMatchers SQL_STATEMENT_DECLARE_SQL = MethodMatchers.create() .ofSubTypes(JAVA_SQL_STATEMENT) // TODO : also take into account addBatch and executeBatch - .names("executeQuery", "execute", "executeUpdate", "executeLargeUpdate") + .names("executeQuery", "execute") .addParametersMatcher("java.lang.String") .build(); private static final MethodMatchers SQL_STATEMENT_RETRIEVE_RESULTSET = MethodMatchers.create() From 647df32ddeabc4770f7a96c1a4b6f9c98e9f466a Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Wed, 7 Aug 2024 16:57:03 +0200 Subject: [PATCH 14/25] add some comments --- .../java/checks/UseEveryColumnQueried.java | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 24d084fd..c3a16b1f 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -50,7 +50,6 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { private static final String JAVA_SQL_RESULTSET = "java.sql.ResultSet"; private static final MethodMatchers SQL_STATEMENT_DECLARE_SQL = MethodMatchers.create() .ofSubTypes(JAVA_SQL_STATEMENT) - // TODO : also take into account addBatch and executeBatch .names("executeQuery", "execute") .addParametersMatcher("java.lang.String") .build(); @@ -82,20 +81,56 @@ public List nodesToVisit() { return Arrays.asList(Kind.METHOD_INVOCATION); } + /** + * How this rule works : + * We start from the method invocation that declares the SQL query ( stmt.executeQuery("SELECT ... FROM ...") ) + * the selected columns are directly extracted from the parameters of this method invocation + * We explore the stmt object to find where the method invocation that returns the ResultSet object + * finally we explore all invocations of this ResultSet object to list all the column used. + * the selected and used columns are compared, and an issue is reported if columns are selected but not used. + * + * + * stmt.execute("SELECT ... FROM ...") or stmt.executeQuery(...) + * | | + * | ----> Selected Columns + * [Statement Object] + * | + * | + * v + * res = stmt.getResultSet() or stmt.executeQuery(...) + * | + * | + * [ResultSet Object] + * | + * | + * v + * res.getInt(...) or any column extraction method + * | + * ----> Used Column + * + */ @Override public void visitNode(Tree tree) { MethodInvocationTree methodInvocationTree = (MethodInvocationTree) tree; if (!SQL_STATEMENT_DECLARE_SQL.matches(methodInvocationTree)) { return; } + + // extraction of the selected columns List selectedColumns = getSelectedColumns(methodInvocationTree); if (selectedColumns.isEmpty()) { return; } + + // extraction of the used columns List usedColumns = getUsedColumns(methodInvocationTree, selectedColumns); if (usedColumns == null) { + // usedColumns is an empty List when there is no ResultSet or it is never read from + // usedColumns is null when the ResultSet was redefined or passed in a method. return; } + + // if there are selected columns that are not used in the code, report the issue List differences = selectedColumns.stream() .filter(element -> !usedColumns.contains(element)) .collect(Collectors.toList()); @@ -121,7 +156,10 @@ private static List getSelectedColumns(MethodInvocationTree methodInvoca @Nullable private static List getUsedColumns(MethodInvocationTree methodInvocationTree, List selectedColumns) { Symbol resultSet = getResultSetNode(methodInvocationTree); - if (resultSet == null || isResultSetInvalid(resultSet)) { + if (resultSet == null) { + return new ArrayList<>() + } + if(isResultSetInvalid(resultSet)){ return null; } List usedColumns = new ArrayList<>(); From 50259b0d44d6299746d4511256b10ca06e80e145 Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Mon, 19 Aug 2024 10:27:33 +0200 Subject: [PATCH 15/25] small refactor and add comments --- .../java/checks/UseEveryColumnQueried.java | 137 ++++++++++++------ 1 file changed, 91 insertions(+), 46 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index c3a16b1f..316e4607 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -122,14 +122,18 @@ public void visitNode(Tree tree) { return; } - // extraction of the used columns - List usedColumns = getUsedColumns(methodInvocationTree, selectedColumns); - if (usedColumns == null) { - // usedColumns is an empty List when there is no ResultSet or it is never read from - // usedColumns is null when the ResultSet was redefined or passed in a method. + // get the ResultSet object and check it's validity + Symbol resultSet = getResultSetNode(methodInvocationTree); + if (resultSet == null) { + return; + } + if(isResultSetInvalid(resultSet)){ return; } + // extraction of the used columns + List usedColumns = getUsedColumns(resultSet, selectedColumns); + // if there are selected columns that are not used in the code, report the issue List differences = selectedColumns.stream() .filter(element -> !usedColumns.contains(element)) @@ -140,31 +144,31 @@ public void visitNode(Tree tree) { } private static List getSelectedColumns(MethodInvocationTree methodInvocationTree) { + // get the first argument of the query definition method Arguments arguments = methodInvocationTree.arguments(); if (arguments.isEmpty()) { return new ArrayList<>(); } ExpressionTree argument = arguments.get(0); + // get the contents of the string in this first parameters LiteralTree literal = extractLiteralFromVariable(argument); if (literal == null) { return new ArrayList<>(); } String query = literal.value(); + //get the list of selected columns from this string return extractSelectedSQLColumns(query); } - @Nullable - private static List getUsedColumns(MethodInvocationTree methodInvocationTree, List selectedColumns) { - Symbol resultSet = getResultSetNode(methodInvocationTree); - if (resultSet == null) { - return new ArrayList<>() - } - if(isResultSetInvalid(resultSet)){ - return null; - } + /** + * returns a list of used columns from a resultset object and a list of the selected columns + */ + private static List getUsedColumns(Symbol resultSet, List selectedColumns) { + // iterate across all usages of the ResultSet List usedColumns = new ArrayList<>(); List resultSetUsages = resultSet.usages(); for (IdentifierTree usage : resultSetUsages) { + // check this usage is an assignement, and the method parameters if (usage.parent().is(Tree.Kind.ASSIGNMENT)) { break; } @@ -172,6 +176,7 @@ private static List getUsedColumns(MethodInvocationTree methodInvocation if (methodInvocation == null || methodInvocation.arguments().isEmpty()) { continue; } + // get the value of the first parameter ExpressionTree parameter = methodInvocation.arguments().get(0); LiteralTree columnGot = extractLiteralFromVariable(parameter); if (columnGot == null) { @@ -179,10 +184,12 @@ private static List getUsedColumns(MethodInvocationTree methodInvocation } String column; String value = columnGot.value(); + // if this first parameter is a string, clean up and use as is for used column name if (SQL_RESULTSET_GET_COLNAME.matches(methodInvocation) && columnGot.is(Tree.Kind.STRING_LITERAL)) { column = value.toUpperCase() .replaceAll("^['\"]", "") .replaceAll("['\"]$", ""); + // if this first parameter is an int, use as and id for used column name } else if (SQL_RESULTSET_GET_COLID.matches(methodInvocation) && columnGot.is(Tree.Kind.INT_LITERAL)) { int columnId = Integer.parseInt(value); if (columnId > selectedColumns.size()) { @@ -197,8 +204,46 @@ private static List getUsedColumns(MethodInvocationTree methodInvocation return usedColumns; } + /** + * returns the litteral assigned to a variable var + * var has to be either a litteral itself or a final variable + * returns null if the ExpressionTree is not a variable, + * if the variable is not final, or if the variable has not been initialized + */ + @Nullable + private static LiteralTree extractLiteralFromVariable(ExpressionTree tree) { + if (tree instanceof LiteralTree) { + return (LiteralTree) tree; + } + if (!tree.is(Tree.Kind.IDENTIFIER)) { + return null; + } + IdentifierTree identifierTree = (IdentifierTree) tree; + Symbol symbol = identifierTree.symbol(); + if (symbol == null || !symbol.isFinal() || !symbol.isVariableSymbol()) { + return null; + } + VariableSymbol variableSymbol = (VariableSymbol) symbol; + Tree assignment = variableSymbol.declaration(); + if (!assignment.is(Tree.Kind.VARIABLE)) { + return null; + } + VariableTree variableTree = (VariableTree) assignment; + ExpressionTree initializer = variableTree.initializer(); + if (initializer instanceof LiteralTree) { + return (LiteralTree) initializer; + } + return null; + } + + /** + * get the ResultSet Object assigned from the result of the retrieve resultset method + * from the sql declaration method (via the shared stmt object) + * stmt.execute(...) -> rs = stmt.getResultSet() + */ @Nullable private static Symbol getResultSetNode(MethodInvocationTree methodInvocationTree) { + // get the Statement object on witch the method is called ExpressionTree et = methodInvocationTree.methodSelect(); if (!et.is(Tree.Kind.MEMBER_SELECT)) { return null; @@ -213,13 +258,16 @@ private static Symbol getResultSetNode(MethodInvocationTree methodInvocationTree if (statement == null) { return null; } + // iterate over all usages of this Statement object List usages = statement.usages(); Symbol resultSet = null; for (IdentifierTree usage : usages) { + // does this usage of the Statement object match SQL_STATEMENT_RETRIEVE_RESULTSET ? MethodInvocationTree methodInvocation = getMethodInvocationFromTree(usage); if (methodInvocation == null || !SQL_STATEMENT_RETRIEVE_RESULTSET.matches(methodInvocation)) { continue; } + // if so end the search, we have found our resultSet object Tree parent = methodInvocation.parent(); if (parent.is(Tree.Kind.VARIABLE)) { resultSet = ((VariableTree) parent).symbol(); @@ -229,6 +277,10 @@ private static Symbol getResultSetNode(MethodInvocationTree methodInvocationTree return resultSet; } + /** + * unpacks a chain call to get the method invocation node + * example : this.object.chain.method() -> method() + */ @Nullable private static MethodInvocationTree getMethodInvocationFromTree(IdentifierTree tree) { Tree parent = tree; @@ -238,37 +290,23 @@ private static MethodInvocationTree getMethodInvocationFromTree(IdentifierTree t return (MethodInvocationTree) parent; } - @Nullable - private static LiteralTree extractLiteralFromVariable(ExpressionTree tree) { - if (tree == null || tree instanceof LiteralTree) { - return (LiteralTree) tree; - } - if (!tree.is(Tree.Kind.IDENTIFIER)) { - return null; - } - IdentifierTree identifierTree = (IdentifierTree) tree; - Symbol symbol = identifierTree.symbol(); - if (symbol == null || !symbol.isFinal() || !symbol.isVariableSymbol()) { - return null; - } - VariableSymbol variableSymbol = (VariableSymbol) symbol; - Tree assignment = variableSymbol.declaration(); - if (!assignment.is(Tree.Kind.VARIABLE)) { - return null; - } - VariableTree variableTree = (VariableTree) assignment; - ExpressionTree initializer = variableTree.initializer(); - return extractLiteralFromVariable(initializer); - } - + /** + * checks the two conditions that make a ResultSet object invalid, + * and would stop the search for used columns because of side effects + * - the ResultSet object being passed in a method + * - the ResultSet object being reassigned + */ private static boolean isResultSetInvalid(Symbol resultSet) { - return isResultSetUsedInMethod(resultSet) - || isResultSetReassigned(resultSet); + return isObjectUsedInMethodParameters(resultSet) + || isObjectReassigned(resultSet); } - private static boolean isResultSetUsedInMethod(Symbol resultSet) { - List resultSetUsages = resultSet.usages(); - for (IdentifierTree usage : resultSetUsages) { + /** + * checks if an object is used as a parameter a method + */ + private static boolean isObjectUsedInMethodParameters(Symbol obj) { + List usages = obj.usages(); + for (IdentifierTree usage : usages) { Tree parent = usage.parent(); if (parent.is(Tree.Kind.ARGUMENTS)) { return true; @@ -277,15 +315,18 @@ private static boolean isResultSetUsedInMethod(Symbol resultSet) { return false; } - private static boolean isResultSetReassigned(Symbol resultSet) { - List resultSetUsages = resultSet.usages(); - for (IdentifierTree usage : resultSetUsages) { + /** + * checks if an object is reassigned + */ + private static boolean isObjectReassigned(Symbol obj) { + List usages = obj.usages(); + for (IdentifierTree usage : usages) { Tree parent = usage.parent(); if (parent.is(Tree.Kind.ASSIGNMENT)) { AssignmentExpressionTree assignment = (AssignmentExpressionTree) parent; ExpressionTree expressionTree = assignment.variable(); if (expressionTree.is(Tree.Kind.IDENTIFIER) - && resultSet.equals(((IdentifierTree) expressionTree).symbol())) { + && obj.equals(((IdentifierTree) expressionTree).symbol())) { return true; } } @@ -293,6 +334,10 @@ private static boolean isResultSetReassigned(Symbol resultSet) { return false; } + /** + * extract from a SQL query in the form of "SELECT X, Y AS Z FROM TABLE ..." + * a list of all the column names and aliases (X and Z) without whitespace and in uppercase + */ static List extractSelectedSQLColumns(String query) { if (query == null) { return new ArrayList<>(); From d0fe4d2bc3547cf9ffc661562e9a44c4e10704c8 Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Mon, 9 Sep 2024 09:11:02 +0000 Subject: [PATCH 16/25] Add rule to registrar and correct ID --- .../java/fr/greencodeinitiative/java/JavaCheckRegistrar.java | 4 +++- .../java/checks/UseEveryColumnQueried.java | 2 +- .../fr/greencodeinitiative/java/ecoCode_way_profile.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java index 50ada183..b90a262d 100644 --- a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java +++ b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java @@ -35,6 +35,7 @@ import fr.greencodeinitiative.java.checks.InitializeBufferWithAppropriateSize; import fr.greencodeinitiative.java.checks.NoFunctionCallWhenDeclaringForLoop; import fr.greencodeinitiative.java.checks.OptimizeReadFileExceptions; +import fr.greencodeinitiative.java.checks.UseEveryColumnQueried; import org.sonar.plugins.java.api.CheckRegistrar; import org.sonar.plugins.java.api.JavaCheck; import org.sonarsource.api.sonarlint.SonarLintSide; @@ -62,7 +63,8 @@ public class JavaCheckRegistrar implements CheckRegistrar { InitializeBufferWithAppropriateSize.class, AvoidSetConstantInBatchUpdate.class, FreeResourcesOfAutoCloseableInterface.class, - AvoidMultipleIfElseStatement.class + AvoidMultipleIfElseStatement.class, + UseEveryColumnQueried.class ); /** diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 316e4607..57d78d5c 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -42,7 +42,7 @@ import org.sonar.plugins.java.api.tree.VariableTree; import org.sonar.plugins.java.api.tree.Tree.Kind; -@Rule(key = "1044") +@Rule(key = "EC1044") public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { protected static final String MESSAGERULE = "Avoid querying SQL columns that are not used"; diff --git a/src/main/resources/fr/greencodeinitiative/java/ecoCode_way_profile.json b/src/main/resources/fr/greencodeinitiative/java/ecoCode_way_profile.json index 88e381ae..b2f127c7 100644 --- a/src/main/resources/fr/greencodeinitiative/java/ecoCode_way_profile.json +++ b/src/main/resources/fr/greencodeinitiative/java/ecoCode_way_profile.json @@ -16,6 +16,7 @@ "EC76", "EC77", "EC78", - "EC79" + "EC79", + "EC1044" ] } From 0e76371d0a8b524ea018bea35e784d8fb040df1d Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Mon, 9 Sep 2024 09:12:25 +0000 Subject: [PATCH 17/25] Improve test names and add select * case --- .../java/checks/UseEveryColumnQueried.java | 5 ++ .../AttributeQueryCompliant.java} | 6 +- .../AttributeQueryNonCompliant.java} | 6 +- .../LitteralQueryCompliant.java} | 6 +- .../LitteralQueryNonCompliant.java} | 6 +- .../MultipleQueriesCompliant.java} | 6 +- .../MultipleQueriesNonCompliant.java} | 21 ++++--- .../UseEveryColumnQueried/SelectStar.java | 55 +++++++++++++++++++ ...eColumnIdsAndNameAttributesCompliant.java} | 6 +- ...lumnIdsAndNameAttributesNonCompliant.java} | 6 +- .../UseMethodCompliant.java} | 6 +- .../UseMethodNonCompliant.java} | 6 +- .../UseEveryColumnQueriedCompliant3.java | 50 ----------------- .../UseEveryColumnQueriedNonCompliant3.java | 49 ----------------- .../checks/UseEveryColumnQueriedTest.java | 35 +++++------- 15 files changed, 131 insertions(+), 138 deletions(-) rename src/test/files/{UseEveryColumnQueriedCompliant1.java => UseEveryColumnQueried/AttributeQueryCompliant.java} (90%) rename src/test/files/{UseEveryColumnQueriedNonCompliant1.java => UseEveryColumnQueried/AttributeQueryNonCompliant.java} (90%) rename src/test/files/{UseEveryColumnQueriedCompliant2.java => UseEveryColumnQueried/LitteralQueryCompliant.java} (90%) rename src/test/files/{UseEveryColumnQueriedNonCompliant2.java => UseEveryColumnQueried/LitteralQueryNonCompliant.java} (90%) rename src/test/files/{UseEveryColumnQueriedCompliant5.java => UseEveryColumnQueried/MultipleQueriesCompliant.java} (92%) rename src/test/files/{UseEveryColumnQueriedNonCompliant5.java => UseEveryColumnQueried/MultipleQueriesNonCompliant.java} (92%) create mode 100644 src/test/files/UseEveryColumnQueried/SelectStar.java rename src/test/files/{UseEveryColumnQueriedCompliant4.java => UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java} (89%) rename src/test/files/{UseEveryColumnQueriedNonCompliant4.java => UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java} (89%) rename src/test/files/{UseEveryColumnQueriedCompliant6.java => UseEveryColumnQueried/UseMethodCompliant.java} (92%) rename src/test/files/{UseEveryColumnQueriedNonCompliant6.java => UseEveryColumnQueried/UseMethodNonCompliant.java} (92%) delete mode 100644 src/test/files/UseEveryColumnQueriedCompliant3.java delete mode 100644 src/test/files/UseEveryColumnQueriedNonCompliant3.java diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 57d78d5c..9eac5135 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -122,6 +122,11 @@ public void visitNode(Tree tree) { return; } + //if selected columns includes "*", stop the search + if (selectedColumns.contains("*")) { + return; + } + // get the ResultSet object and check it's validity Symbol resultSet = getResultSetNode(methodInvocationTree); if (resultSet == null) { diff --git a/src/test/files/UseEveryColumnQueriedCompliant1.java b/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java similarity index 90% rename from src/test/files/UseEveryColumnQueriedCompliant1.java rename to src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java index e81a11cd..c12e375e 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant1.java +++ b/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedCompliant1 { +/** + * This is the nominal test case, where the SQL query is an attribute of the class. + * All Fields are accessed, so no issue is raised + */ +public class AttributeQueryCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant1.java b/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java similarity index 90% rename from src/test/files/UseEveryColumnQueriedNonCompliant1.java rename to src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java index 1c40dbbb..e42ff485 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant1.java +++ b/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedNonCompliant1 { +/** + * This is the nominal test case, where the SQL query is an attribute of the class. + * One field is not accesed, so an issue is raised + */ +public class AttributeQueryNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedCompliant2.java b/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java similarity index 90% rename from src/test/files/UseEveryColumnQueriedCompliant2.java rename to src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java index 3b45c2fe..6146dfa3 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant2.java +++ b/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedCompliant2 { +/** + * In this test case, the query is a litteral string directly inserted in the method + * All Fields are accessed, so no issue is raised + */ +public class LitteralQueryCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant2.java b/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java similarity index 90% rename from src/test/files/UseEveryColumnQueriedNonCompliant2.java rename to src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java index 8da60f05..476233d9 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant2.java +++ b/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedNonCompliant2 { +/** + * In this test case, the query is a litteral string directly inserted in the method + * One field is not accesed, so an issue is raised + */ +public class LitteralQueryNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedCompliant5.java b/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java similarity index 92% rename from src/test/files/UseEveryColumnQueriedCompliant5.java rename to src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java index 4f5f68ca..94f7a296 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant5.java +++ b/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedCompliant5 { +/** + * In this test case, multiple queries are done using the same Statement Object. + * All Fields are accessed, so no issue is raised + */ +public class MultipleQueriesCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant5.java b/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java similarity index 92% rename from src/test/files/UseEveryColumnQueriedNonCompliant5.java rename to src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java index 959bc80c..f7cf12c6 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant5.java +++ b/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java @@ -1,10 +1,3 @@ -package fr.greencodeinitiative.java.checks; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; /* * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) @@ -22,7 +15,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -public class UseEveryColumnQueriedNonCompliant5 { +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * In this test case, multiple queries are done using the same Statement Object. + * One field is not accesed, so an issue is raised + */ +public class MultipleQueriesNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/SelectStar.java b/src/test/files/UseEveryColumnQueried/SelectStar.java new file mode 100644 index 00000000..84fe3489 --- /dev/null +++ b/src/test/files/UseEveryColumnQueried/SelectStar.java @@ -0,0 +1,55 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package fr.greencodeinitiative.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * In this test case, we use a "select * from" statement + * Since we can't know what columns exist, we can't know if all columns are being used, no issue is raised + * "select * from" is bad practice but is already covered by EC74 + */ +public class SelectStar { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT * FROM Registration"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", Age: " + rs.getInt("age")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/test/files/UseEveryColumnQueriedCompliant4.java b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java similarity index 89% rename from src/test/files/UseEveryColumnQueriedCompliant4.java rename to src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java index 05731213..03ac224c 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant4.java +++ b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedCompliant4 { +/** + * In this test case, columns are accesed by IDs and names, some of them being in final variables + * All Fields are accessed, so no issue is raised + */ +public class UseColumnIdsAndNameAttributesCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant4.java b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java similarity index 89% rename from src/test/files/UseEveryColumnQueriedNonCompliant4.java rename to src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java index ffe73558..c7972d44 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant4.java +++ b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedNonCompliant4 { +/** + * In this test case, columns are accesed by IDs and names, some of them being in final variables + * One field is not accesed, so an issue is raised + */ +public class UseColumnIdsAndNameAttributesNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedCompliant6.java b/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java similarity index 92% rename from src/test/files/UseEveryColumnQueriedCompliant6.java rename to src/test/files/UseEveryColumnQueried/UseMethodCompliant.java index a3b28e8e..db7ae3e3 100644 --- a/src/test/files/UseEveryColumnQueriedCompliant6.java +++ b/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedCompliant6 { +/** + * In this test case, the ResultSet is passed through a method + * All Fields are accessed, so no issue is raised + */ +public class UseMethodCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant6.java b/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java similarity index 92% rename from src/test/files/UseEveryColumnQueriedNonCompliant6.java rename to src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java index 86d6a863..559c06b8 100644 --- a/src/test/files/UseEveryColumnQueriedNonCompliant6.java +++ b/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java @@ -23,7 +23,11 @@ import java.sql.SQLException; import java.sql.Statement; -public class UseEveryColumnQueriedNonCompliant6 { +/** + * In this test case, the ResultSet is passed through a method + * One field is not accesed, so an issue is raised + */ +public class UseMethodNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueriedCompliant3.java b/src/test/files/UseEveryColumnQueriedCompliant3.java deleted file mode 100644 index e2a50822..00000000 --- a/src/test/files/UseEveryColumnQueriedCompliant3.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs - * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.greencodeinitiative.java.checks; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -public class UseEveryColumnQueriedCompliant3 { - - private static final String DB_URL = "jdbc:mysql://localhost/TEST"; - private static final String USER = "guest"; - private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; - - public void callJdbc() { - - try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(QUERY);) { - while (rs.next()) { - // Display values - System.out.print("ID: " + rs.getInt(1)); - System.out.print(", Age: " + rs.getInt(4)); - System.out.print(", First: " + rs.getString(2)); - System.out.println(", Last: " + rs.getString(3)); - } - } catch (SQLException e) { - e.printStackTrace(); - } - - } -} diff --git a/src/test/files/UseEveryColumnQueriedNonCompliant3.java b/src/test/files/UseEveryColumnQueriedNonCompliant3.java deleted file mode 100644 index 50afcc5f..00000000 --- a/src/test/files/UseEveryColumnQueriedNonCompliant3.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs - * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package fr.greencodeinitiative.java.checks; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -public class UseEveryColumnQueriedNonCompliant3 { - - private static final String DB_URL = "jdbc:mysql://localhost/TEST"; - private static final String USER = "guest"; - private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; - - public void callJdbc() { - - try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(QUERY);) { // Noncompliant {{Avoid querying SQL columns that are not used}} - while (rs.next()) { - // Display values - System.out.print("ID: " + rs.getInt(1)); - System.out.print(", First: " + rs.getString(2)); - System.out.println(", Last: " + rs.getString(3)); - } - } catch (SQLException e) { - e.printStackTrace(); - } - - } -} diff --git a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java index 61c664f8..edf2b762 100644 --- a/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueriedTest.java @@ -41,7 +41,7 @@ void testExtractSelectedSQLColumns(){ @Test void testHasIssues1() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedNonCompliant1.java") + .onFile("src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @@ -49,7 +49,7 @@ void testHasIssues1() { @Test void testHasIssues2() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedNonCompliant2.java") + .onFile("src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @@ -57,43 +57,34 @@ void testHasIssues2() { @Test void testHasIssues3() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedNonCompliant3.java") - .withCheck(new UseEveryColumnQueried()) - .verifyIssues(); - } - - @Test - void testHasIssues4() { - CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedNonCompliant4.java") + .onFile("src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test @Disabled // case not handled (multiple queries with the same ResultSet object) - void testHasIssues5() { + void testHasIssues4() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedNonCompliant5.java") + .onFile("src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test @Disabled // case not handled (usage of a method) - void testHasIssues6() { + void testHasIssues5() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedNonCompliant6.java") + .onFile("src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } - @Test void testHasNoIssues1() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedCompliant1.java") + .onFile("src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @@ -101,7 +92,7 @@ void testHasNoIssues1() { @Test void testHasNoIssues2() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedCompliant2.java") + .onFile("src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @@ -109,7 +100,7 @@ void testHasNoIssues2() { @Test void testHasNoIssues3() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedCompliant3.java") + .onFile("src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @@ -117,7 +108,7 @@ void testHasNoIssues3() { @Test void testHasNoIssues4() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedCompliant4.java") + .onFile("src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @@ -125,7 +116,7 @@ void testHasNoIssues4() { @Test void testHasNoIssues5() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedCompliant5.java") + .onFile("src/test/files/UseEveryColumnQueried/UseMethodCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @@ -133,7 +124,7 @@ void testHasNoIssues5() { @Test void testHasNoIssues6() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueriedCompliant6.java") + .onFile("src/test/files/UseEveryColumnQueried/SelectStar.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } From b8a7654b6c49b5ffab05fae3f49f0e28e08011a4 Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Mon, 9 Sep 2024 09:22:55 +0000 Subject: [PATCH 18/25] improve ResultSet get method detection --- .../java/checks/UseEveryColumnQueried.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java index 9eac5135..7e85e11a 100644 --- a/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/fr/greencodeinitiative/java/checks/UseEveryColumnQueried.java @@ -60,18 +60,12 @@ public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { .build(); private static final MethodMatchers SQL_RESULTSET_GET_COLNAME = MethodMatchers.create() .ofSubTypes(JAVA_SQL_RESULTSET) - .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", - "getBigDecimal", "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", - "getClob", "getRef", "getRowId", "getNClob", "getSQLXML", "getURL", "getNString", - "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") + .name(n -> n.startsWith("get")) .addParametersMatcher("java.lang.String") .build(); private static final MethodMatchers SQL_RESULTSET_GET_COLID = MethodMatchers.create() .ofSubTypes(JAVA_SQL_RESULTSET) - .names("getInt", "getString", "getLong", "getDouble", "getFloat", "getBoolean", "getByte", "getShort", - "getBigDecimal", "getTimestamp", "getDate", "getTime", "getObject", "getArray", "getBlob", - "getClob", "getRef", "getRowId", "getNClob", "getSQLXML", "getURL", "getNString", - "getNCharacterStream", "getCharacterStream", "getAsciiStream", "getBinaryStream") + .name(n -> n.startsWith("get")) .addParametersMatcher("int") .build(); private static final Pattern SELECTED_COLUMNS_PATTERN = Pattern.compile("SELECT\\s+(.*)\\s+FROM\\s+.*"); From 3d879a69c73c431364234fd16cd369ff147ca03b Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Tue, 20 May 2025 09:25:45 +0200 Subject: [PATCH 19/25] change package --- .../files/UseEveryColumnQueried/AttributeQueryCompliant.java | 2 +- .../files/UseEveryColumnQueried/AttributeQueryNonCompliant.java | 2 +- .../files/UseEveryColumnQueried/LitteralQueryCompliant.java | 2 +- .../files/UseEveryColumnQueried/LitteralQueryNonCompliant.java | 2 +- .../files/UseEveryColumnQueried/MultipleQueriesCompliant.java | 2 +- .../UseEveryColumnQueried/MultipleQueriesNonCompliant.java | 2 +- src/test/files/UseEveryColumnQueried/SelectStar.java | 2 +- .../UseColumnIdsAndNameAttributesCompliant.java | 2 +- .../UseColumnIdsAndNameAttributesNonCompliant.java | 2 +- src/test/files/UseEveryColumnQueried/UseMethodCompliant.java | 2 +- src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java b/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java index c12e375e..8f948f07 100644 --- a/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java +++ b/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java b/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java index e42ff485..5aa34f74 100644 --- a/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java b/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java index 6146dfa3..e0d4a767 100644 --- a/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java +++ b/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java b/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java index 476233d9..565cf2ad 100644 --- a/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java b/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java index 94f7a296..4f0bc885 100644 --- a/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java +++ b/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java b/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java index f7cf12c6..337b20c4 100644 --- a/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/SelectStar.java b/src/test/files/UseEveryColumnQueried/SelectStar.java index 84fe3489..df607bfc 100644 --- a/src/test/files/UseEveryColumnQueried/SelectStar.java +++ b/src/test/files/UseEveryColumnQueried/SelectStar.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java index 03ac224c..01589e9e 100644 --- a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java index c7972d44..7ea85086 100644 --- a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java b/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java index db7ae3e3..d65418e8 100644 --- a/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java index 559c06b8..8888fbd3 100644 --- a/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package fr.greencodeinitiative.java.checks; +package org.greencodeinitiative.creedengo.java.checks; import java.sql.Connection; import java.sql.DriverManager; From 6a1484c81ca9edf6eccd2a316c4b45743c7498ab Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Tue, 20 May 2025 11:43:28 +0200 Subject: [PATCH 20/25] add integration test --- .../java/integration/tests/GCIRulesIT.java | 44 ++++++++++++ .../checks/AttributeQueryNonCompliant.java | 53 +++++++++++++++ .../checks/LitteralQueryNonCompliant.java | 54 +++++++++++++++ .../checks/MultipleQueriesNonCompliant.java | 68 +++++++++++++++++++ ...olumnIdsAndNameAttributesNonCompliant.java | 54 +++++++++++++++ .../java/checks/UseMethodNonCompliant.java | 56 +++++++++++++++ .../java/checks/UseEveryColumnQueried.java | 2 +- .../creedengo/java/creedengo_way_profile.json | 3 +- 8 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java create mode 100644 src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java create mode 100644 src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java create mode 100644 src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java create mode 100644 src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java diff --git a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java index dfd7957c..c42b4a7a 100644 --- a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java +++ b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java @@ -548,4 +548,48 @@ void testGCI94() { checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_1MIN); } + @Test + void testGCI1044_1() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java"; + String ruleId= "creedengo-java:GCI1044"; + String ruleMsg = "Avoid querying SQL columns that are not used"; + int[] startLines = new int[]{41}; + int[] endLines = new int[]{41}; + } + + @Test + void testGCI1044_2() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java"; + String ruleId= "creedengo-java:GCI1044"; + String ruleMsg = "Avoid querying SQL columns that are not used"; + int[] startLines = new int[]{40}; + int[] endLines = new int[]{40}; + } + + @Test + void testGCI1044_3() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java"; + String ruleId= "creedengo-java:GCI1044"; + String ruleMsg = "Avoid querying SQL columns that are not used"; + int[] startLines = new int[]{45}; + int[] endLines = new int[]{45}; + } + + @Test + void testGCI1044_4() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java"; + String ruleId= "creedengo-java:GCI1044"; + String ruleMsg = "Avoid querying SQL columns that are not used"; + int[] startLines = new int[]{42}; + int[] endLines = new int[]{42}; + } + + @Test + void testGCI1044_5() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java"; + String ruleId= "creedengo-java:GCI1044"; + String ruleMsg = "Avoid querying SQL columns that are not used"; + int[] startLines = new int[]{40}; + int[] endLines = new int[]{40}; + } } diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java new file mode 100644 index 00000000..5aa34f74 --- /dev/null +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java @@ -0,0 +1,53 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * This is the nominal test case, where the SQL query is an attribute of the class. + * One field is not accesed, so an issue is raised + */ +public class AttributeQueryNonCompliant { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { // Noncompliant {{Avoid querying SQL columns that are not used}} + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java new file mode 100644 index 00000000..565cf2ad --- /dev/null +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java @@ -0,0 +1,54 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * In this test case, the query is a litteral string directly inserted in the method + * One field is not accesed, so an issue is raised + */ +public class LitteralQueryNonCompliant { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { // Noncompliant {{Avoid querying SQL columns that are not used}} + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + +} \ No newline at end of file diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java new file mode 100644 index 00000000..337b20c4 --- /dev/null +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java @@ -0,0 +1,68 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * In this test case, multiple queries are done using the same Statement Object. + * One field is not accesed, so an issue is raised + */ +public class MultipleQueriesNonCompliant { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + private static final String QUERY2 = "SELECT id, first, last, age FROM Registration2"; + + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ) { + + ResultSet rs = stmt.executeQuery(QUERY); // Noncompliant {{Avoid querying SQL columns that are not used}} + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + rs = stmt.executeQuery(QUERY2); + + while (rs.next()) { + // Display values + System.out.print("Age: " + rs.getInt("age")); + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + + + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java new file mode 100644 index 00000000..7ea85086 --- /dev/null +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java @@ -0,0 +1,54 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * In this test case, columns are accesed by IDs and names, some of them being in final variables + * One field is not accesed, so an issue is raised + */ +public class UseColumnIdsAndNameAttributesNonCompliant { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + private static final String QUERY = "SELECT id, first, last, age FROM Registration"; + private static final String ID = "id"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(QUERY);) { // Noncompliant {{Avoid querying SQL columns that are not used}} + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt(ID)); + System.out.print(", First: " + rs.getString(2)); + System.out.println(", Last: " + rs.getString("last")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java new file mode 100644 index 00000000..8888fbd3 --- /dev/null +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java @@ -0,0 +1,56 @@ +/* + * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * In this test case, the ResultSet is passed through a method + * One field is not accesed, so an issue is raised + */ +public class UseMethodNonCompliant { + + private static final String DB_URL = "jdbc:mysql://localhost/TEST"; + private static final String USER = "guest"; + private static final String PASS = "guest123"; + + public void callJdbc() { + + try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { // Noncompliant {{Avoid querying SQL columns that are not used}} + extractGet(rs); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + private void extractGet(ResultSet rs) throws SQLException { + while (rs.next()) { + // Display values + System.out.print("ID: " + rs.getInt("id")); + System.out.print(", First: " + rs.getString("first")); + System.out.println(", Last: " + rs.getString("last")); + } + } +} diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java b/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java index 3409f0ea..66dfb67f 100644 --- a/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java @@ -42,7 +42,7 @@ import org.sonar.plugins.java.api.tree.VariableTree; import org.sonar.plugins.java.api.tree.Tree.Kind; -@Rule(key = "EC1044") +@Rule(key = "GCI1044") public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { protected static final String MESSAGERULE = "Avoid querying SQL columns that are not used"; diff --git a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json index 059bf0f5..3cc33d32 100644 --- a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json +++ b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json @@ -18,6 +18,7 @@ "GCI78", "GCI79", "GCI82", - "GCI94" + "GCI94", + "GCI1044" ] } From ba71712f802fb2124829ef92a77c416eac12427f Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Tue, 20 May 2025 16:50:21 +0200 Subject: [PATCH 21/25] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23418684..615d0e4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- [#63](https://github.com/green-code-initiative/ecoCode-java/pull/63) [EC1044] [Java] Avoid querying SQL columns that are not used ### Changed From 482f2f888bb49d81b846b92d7848b42fea91321c Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Tue, 20 May 2025 17:57:23 +0200 Subject: [PATCH 22/25] fix pr following feedback --- CHANGELOG.md | 2 +- .../java/integration/tests/GCIRulesIT.java | 10 ++-- ...umnQueriedAttributeQueryNonCompliant.java} | 2 +- ...lumnQueriedLitteralQueryNonCompliant.java} | 2 +- ...mnQueriedMultipleQueriesNonCompliant.java} | 2 +- ...lumnIdsAndNameAttributesNonCompliant.java} | 2 +- ...ryColumnQueriedUseMethodNonCompliant.java} | 2 +- .../java/checks/UseEveryColumnQueried.java | 5 +- ...ColumnQueriedAttributeQueryCompliant.java} | 2 +- ...umnQueriedAttributeQueryNonCompliant.java} | 2 +- ...yColumnQueriedLitteralQueryCompliant.java} | 2 +- ...lumnQueriedLitteralQueryNonCompliant.java} | 2 +- ...olumnQueriedMultipleQueriesCompliant.java} | 2 +- ...mnQueriedMultipleQueriesNonCompliant.java} | 2 +- ...a => UseEveryColumnQueriedSelectStar.java} | 2 +- ...eColumnIdsAndNameAttributesCompliant.java} | 2 +- ...lumnIdsAndNameAttributesNonCompliant.java} | 2 +- ...EveryColumnQueriedUseMethodCompliant.java} | 2 +- ...ryColumnQueriedUseMethodNonCompliant.java} | 2 +- .../checks/UseEveryColumnQueriedTest.java | 46 +++++++++---------- 20 files changed, 46 insertions(+), 49 deletions(-) rename src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/{AttributeQueryNonCompliant.java => UseEveryColumnQueriedAttributeQueryNonCompliant.java} (96%) rename src/{test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java => it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedLitteralQueryNonCompliant.java} (96%) rename src/{test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java => it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java} (97%) rename src/{test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java => it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java} (96%) rename src/{test/files/UseEveryColumnQueried/UseMethodNonCompliant.java => it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java} (97%) rename src/test/files/UseEveryColumnQueried/{AttributeQueryCompliant.java => UseEveryColumnQueriedAttributeQueryCompliant.java} (97%) rename src/test/files/UseEveryColumnQueried/{AttributeQueryNonCompliant.java => UseEveryColumnQueriedAttributeQueryNonCompliant.java} (96%) rename src/test/files/UseEveryColumnQueried/{LitteralQueryCompliant.java => UseEveryColumnQueriedLitteralQueryCompliant.java} (96%) rename src/{it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java => test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryNonCompliant.java} (96%) rename src/test/files/UseEveryColumnQueried/{MultipleQueriesCompliant.java => UseEveryColumnQueriedMultipleQueriesCompliant.java} (97%) rename src/{it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java => test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java} (97%) rename src/test/files/UseEveryColumnQueried/{SelectStar.java => UseEveryColumnQueriedSelectStar.java} (97%) rename src/test/files/UseEveryColumnQueried/{UseColumnIdsAndNameAttributesCompliant.java => UseEveryColumnQueriedUseColumnIdsAndNameAttributesCompliant.java} (96%) rename src/{it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java => test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java} (96%) rename src/test/files/UseEveryColumnQueried/{UseMethodCompliant.java => UseEveryColumnQueriedUseMethodCompliant.java} (97%) rename src/{it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java => test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 615d0e4f..68dcd399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- [#63](https://github.com/green-code-initiative/ecoCode-java/pull/63) [EC1044] [Java] Avoid querying SQL columns that are not used +- [#63](https://github.com/green-code-initiative/ecoCode-java/pull/63) [GCI1044] [Java] Avoid querying SQL columns that are not used ### Changed diff --git a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java index c42b4a7a..6f1fd9e3 100644 --- a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java +++ b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java @@ -550,7 +550,7 @@ void testGCI94() { @Test void testGCI1044_1() { - String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java"; + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedAttributeQueryNonCompliant.java"; String ruleId= "creedengo-java:GCI1044"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{41}; @@ -559,7 +559,7 @@ void testGCI1044_1() { @Test void testGCI1044_2() { - String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java"; + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedLitteralQueryNonCompliant.java"; String ruleId= "creedengo-java:GCI1044"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{40}; @@ -568,7 +568,7 @@ void testGCI1044_2() { @Test void testGCI1044_3() { - String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java"; + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java"; String ruleId= "creedengo-java:GCI1044"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{45}; @@ -577,7 +577,7 @@ void testGCI1044_3() { @Test void testGCI1044_4() { - String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java"; + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java"; String ruleId= "creedengo-java:GCI1044"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{42}; @@ -586,7 +586,7 @@ void testGCI1044_4() { @Test void testGCI1044_5() { - String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java"; + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java"; String ruleId= "creedengo-java:GCI1044"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{40}; diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedAttributeQueryNonCompliant.java similarity index 96% rename from src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java rename to src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedAttributeQueryNonCompliant.java index 5aa34f74..652e6824 100644 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AttributeQueryNonCompliant.java +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedAttributeQueryNonCompliant.java @@ -27,7 +27,7 @@ * This is the nominal test case, where the SQL query is an attribute of the class. * One field is not accesed, so an issue is raised */ -public class AttributeQueryNonCompliant { +public class UseEveryColumnQueriedAttributeQueryNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedLitteralQueryNonCompliant.java similarity index 96% rename from src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java rename to src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedLitteralQueryNonCompliant.java index 565cf2ad..df56b09f 100644 --- a/src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedLitteralQueryNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, the query is a litteral string directly inserted in the method * One field is not accesed, so an issue is raised */ -public class LitteralQueryNonCompliant { +public class UseEveryColumnQueriedLitteralQueryNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java similarity index 97% rename from src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java rename to src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java index 337b20c4..fcffb3b0 100644 --- a/src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, multiple queries are done using the same Statement Object. * One field is not accesed, so an issue is raised */ -public class MultipleQueriesNonCompliant { +public class UseEveryColumnQueriedMultipleQueriesNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java similarity index 96% rename from src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java rename to src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java index 7ea85086..797887cd 100644 --- a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, columns are accesed by IDs and names, some of them being in final variables * One field is not accesed, so an issue is raised */ -public class UseColumnIdsAndNameAttributesNonCompliant { +public class UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java similarity index 97% rename from src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java rename to src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java index 8888fbd3..d4a6c534 100644 --- a/src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, the ResultSet is passed through a method * One field is not accesed, so an issue is raised */ -public class UseMethodNonCompliant { +public class UseEveryColumnQueriedUseMethodNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java b/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java index 66dfb67f..ccb7b946 100644 --- a/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java @@ -123,9 +123,6 @@ public void visitNode(Tree tree) { // get the ResultSet object and check it's validity Symbol resultSet = getResultSetNode(methodInvocationTree); - if (resultSet == null) { - return; - } if(isResultSetInvalid(resultSet)){ return; } @@ -296,7 +293,7 @@ private static MethodInvocationTree getMethodInvocationFromTree(IdentifierTree t * - the ResultSet object being reassigned */ private static boolean isResultSetInvalid(Symbol resultSet) { - return isObjectUsedInMethodParameters(resultSet) + return resultSet==null || isObjectUsedInMethodParameters(resultSet) || isObjectReassigned(resultSet); } diff --git a/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryCompliant.java similarity index 97% rename from src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryCompliant.java index 8f948f07..307e09e4 100644 --- a/src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryCompliant.java @@ -27,7 +27,7 @@ * This is the nominal test case, where the SQL query is an attribute of the class. * All Fields are accessed, so no issue is raised */ -public class AttributeQueryCompliant { +public class UseEveryColumnQueriedAttributeQueryCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryNonCompliant.java similarity index 96% rename from src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryNonCompliant.java index 5aa34f74..652e6824 100644 --- a/src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryNonCompliant.java @@ -27,7 +27,7 @@ * This is the nominal test case, where the SQL query is an attribute of the class. * One field is not accesed, so an issue is raised */ -public class AttributeQueryNonCompliant { +public class UseEveryColumnQueriedAttributeQueryNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryCompliant.java similarity index 96% rename from src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryCompliant.java index e0d4a767..a901d613 100644 --- a/src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryCompliant.java @@ -27,7 +27,7 @@ * In this test case, the query is a litteral string directly inserted in the method * All Fields are accessed, so no issue is raised */ -public class LitteralQueryCompliant { +public class UseEveryColumnQueriedLitteralQueryCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryNonCompliant.java similarity index 96% rename from src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryNonCompliant.java index 565cf2ad..df56b09f 100644 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/LitteralQueryNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, the query is a litteral string directly inserted in the method * One field is not accesed, so an issue is raised */ -public class LitteralQueryNonCompliant { +public class UseEveryColumnQueriedLitteralQueryNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesCompliant.java similarity index 97% rename from src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesCompliant.java index 4f0bc885..8222c383 100644 --- a/src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesCompliant.java @@ -27,7 +27,7 @@ * In this test case, multiple queries are done using the same Statement Object. * All Fields are accessed, so no issue is raised */ -public class MultipleQueriesCompliant { +public class UseEveryColumnQueriedMultipleQueriesCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java similarity index 97% rename from src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java index 337b20c4..fcffb3b0 100644 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/MultipleQueriesNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, multiple queries are done using the same Statement Object. * One field is not accesed, so an issue is raised */ -public class MultipleQueriesNonCompliant { +public class UseEveryColumnQueriedMultipleQueriesNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/SelectStar.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedSelectStar.java similarity index 97% rename from src/test/files/UseEveryColumnQueried/SelectStar.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedSelectStar.java index df607bfc..829f147e 100644 --- a/src/test/files/UseEveryColumnQueried/SelectStar.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedSelectStar.java @@ -28,7 +28,7 @@ * Since we can't know what columns exist, we can't know if all columns are being used, no issue is raised * "select * from" is bad practice but is already covered by EC74 */ -public class SelectStar { +public class UseEveryColumnQueriedSelectStar { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesCompliant.java similarity index 96% rename from src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesCompliant.java index 01589e9e..d4c0a460 100644 --- a/src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesCompliant.java @@ -27,7 +27,7 @@ * In this test case, columns are accesed by IDs and names, some of them being in final variables * All Fields are accessed, so no issue is raised */ -public class UseColumnIdsAndNameAttributesCompliant { +public class UseEveryColumnQueriedUseColumnIdsAndNameAttributesCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java similarity index 96% rename from src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java index 7ea85086..797887cd 100644 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseColumnIdsAndNameAttributesNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, columns are accesed by IDs and names, some of them being in final variables * One field is not accesed, so an issue is raised */ -public class UseColumnIdsAndNameAttributesNonCompliant { +public class UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodCompliant.java similarity index 97% rename from src/test/files/UseEveryColumnQueried/UseMethodCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodCompliant.java index d65418e8..b01db542 100644 --- a/src/test/files/UseEveryColumnQueried/UseMethodCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodCompliant.java @@ -27,7 +27,7 @@ * In this test case, the ResultSet is passed through a method * All Fields are accessed, so no issue is raised */ -public class UseMethodCompliant { +public class UseEveryColumnQueriedUseMethodCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java similarity index 97% rename from src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java rename to src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java index 8888fbd3..d4a6c534 100644 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseMethodNonCompliant.java +++ b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java @@ -27,7 +27,7 @@ * In this test case, the ResultSet is passed through a method * One field is not accesed, so an issue is raised */ -public class UseMethodNonCompliant { +public class UseEveryColumnQueriedUseMethodNonCompliant { private static final String DB_URL = "jdbc:mysql://localhost/TEST"; private static final String USER = "guest"; diff --git a/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java index 10f46431..4e4c206f 100644 --- a/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java @@ -28,7 +28,7 @@ class UseEveryColumnQueriedTest { @Test - void testExtractSelectedSQLColumns(){ + void UseEveryColumnQueriedTestExtractSelectedSQLColumns(){ String query = "\"SELECT id AS registration_id,\tfirst, last as Final, AGE FROM Registration\""; List columns = UseEveryColumnQueried.extractSelectedSQLColumns(query); assertEquals(4, columns.size()); @@ -39,92 +39,92 @@ void testExtractSelectedSQLColumns(){ } @Test - void testHasIssues1() { + void UseEveryColumnQueriedAttributeQueryNonCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/AttributeQueryNonCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test - void testHasIssues2() { + void UseEveryColumnQueriedLitteralQueryNonCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/LitteralQueryNonCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test - void testHasIssues3() { + void UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesNonCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test @Disabled // case not handled (multiple queries with the same ResultSet object) - void testHasIssues4() { + void UseEveryColumnQueriedMultipleQueriesNonCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/MultipleQueriesNonCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test @Disabled // case not handled (usage of a method) - void testHasIssues5() { + void UseEveryColumnQueriedUseMethodNonCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/UseMethodNonCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyIssues(); } @Test - void testHasNoIssues1() { + void UseEveryColumnQueriedAttributeQueryCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/AttributeQueryCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedAttributeQueryCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @Test - void testHasNoIssues2() { + void UseEveryColumnQueriedLitteralQueryCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/LitteralQueryCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedLitteralQueryCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @Test - void testHasNoIssues3() { + void UseEveryColumnQueriedUseColumnIdsAndNameAttributesCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/UseColumnIdsAndNameAttributesCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseColumnIdsAndNameAttributesCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @Test - void testHasNoIssues4() { + void UseEveryColumnQueriedMultipleQueriesCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/MultipleQueriesCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @Test - void testHasNoIssues5() { + void UseEveryColumnQueriedUseMethodCompliant() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/UseMethodCompliant.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodCompliant.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } @Test - void testHasNoIssues6() { + void UseEveryColumnQueriedSelectStar() { CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/SelectStar.java") + .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedSelectStar.java") .withCheck(new UseEveryColumnQueried()) .verifyNoIssues(); } From 1b8b696fb092bbc8568502ec01f42887143cf127 Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Wed, 21 May 2025 10:27:39 +0200 Subject: [PATCH 23/25] removed disabled tests --- .../java/integration/tests/GCIRulesIT.java | 17 ----- ...umnQueriedMultipleQueriesNonCompliant.java | 68 ------------------- ...eryColumnQueriedUseMethodNonCompliant.java | 56 --------------- ...umnQueriedMultipleQueriesNonCompliant.java | 68 ------------------- ...eryColumnQueriedUseMethodNonCompliant.java | 56 --------------- .../checks/UseEveryColumnQueriedTest.java | 19 ------ 6 files changed, 284 deletions(-) delete mode 100644 src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java delete mode 100644 src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java delete mode 100644 src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java delete mode 100644 src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java diff --git a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java index 6f1fd9e3..71e710a1 100644 --- a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java +++ b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java @@ -568,15 +568,6 @@ void testGCI1044_2() { @Test void testGCI1044_3() { - String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java"; - String ruleId= "creedengo-java:GCI1044"; - String ruleMsg = "Avoid querying SQL columns that are not used"; - int[] startLines = new int[]{45}; - int[] endLines = new int[]{45}; - } - - @Test - void testGCI1044_4() { String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java"; String ruleId= "creedengo-java:GCI1044"; String ruleMsg = "Avoid querying SQL columns that are not used"; @@ -584,12 +575,4 @@ void testGCI1044_4() { int[] endLines = new int[]{42}; } - @Test - void testGCI1044_5() { - String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java"; - String ruleId= "creedengo-java:GCI1044"; - String ruleMsg = "Avoid querying SQL columns that are not used"; - int[] startLines = new int[]{40}; - int[] endLines = new int[]{40}; - } } diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java deleted file mode 100644 index fcffb3b0..00000000 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedMultipleQueriesNonCompliant.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs - * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.greencodeinitiative.creedengo.java.checks; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -/** - * In this test case, multiple queries are done using the same Statement Object. - * One field is not accesed, so an issue is raised - */ -public class UseEveryColumnQueriedMultipleQueriesNonCompliant { - - private static final String DB_URL = "jdbc:mysql://localhost/TEST"; - private static final String USER = "guest"; - private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; - private static final String QUERY2 = "SELECT id, first, last, age FROM Registration2"; - - - public void callJdbc() { - - try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); - Statement stmt = conn.createStatement(); - ) { - - ResultSet rs = stmt.executeQuery(QUERY); // Noncompliant {{Avoid querying SQL columns that are not used}} - while (rs.next()) { - // Display values - System.out.print("ID: " + rs.getInt("id")); - System.out.print(", First: " + rs.getString("first")); - System.out.println(", Last: " + rs.getString("last")); - } - rs = stmt.executeQuery(QUERY2); - - while (rs.next()) { - // Display values - System.out.print("Age: " + rs.getInt("age")); - System.out.print("ID: " + rs.getInt("id")); - System.out.print(", First: " + rs.getString("first")); - System.out.println(", Last: " + rs.getString("last")); - } - - - } catch (SQLException e) { - e.printStackTrace(); - } - - } -} diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java deleted file mode 100644 index d4a6c534..00000000 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseMethodNonCompliant.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs - * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.greencodeinitiative.creedengo.java.checks; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -/** - * In this test case, the ResultSet is passed through a method - * One field is not accesed, so an issue is raised - */ -public class UseEveryColumnQueriedUseMethodNonCompliant { - - private static final String DB_URL = "jdbc:mysql://localhost/TEST"; - private static final String USER = "guest"; - private static final String PASS = "guest123"; - - public void callJdbc() { - - try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { // Noncompliant {{Avoid querying SQL columns that are not used}} - extractGet(rs); - } catch (SQLException e) { - e.printStackTrace(); - } - - } - - private void extractGet(ResultSet rs) throws SQLException { - while (rs.next()) { - // Display values - System.out.print("ID: " + rs.getInt("id")); - System.out.print(", First: " + rs.getString("first")); - System.out.println(", Last: " + rs.getString("last")); - } - } -} diff --git a/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java deleted file mode 100644 index fcffb3b0..00000000 --- a/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs - * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.greencodeinitiative.creedengo.java.checks; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -/** - * In this test case, multiple queries are done using the same Statement Object. - * One field is not accesed, so an issue is raised - */ -public class UseEveryColumnQueriedMultipleQueriesNonCompliant { - - private static final String DB_URL = "jdbc:mysql://localhost/TEST"; - private static final String USER = "guest"; - private static final String PASS = "guest123"; - private static final String QUERY = "SELECT id, first, last, age FROM Registration"; - private static final String QUERY2 = "SELECT id, first, last, age FROM Registration2"; - - - public void callJdbc() { - - try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); - Statement stmt = conn.createStatement(); - ) { - - ResultSet rs = stmt.executeQuery(QUERY); // Noncompliant {{Avoid querying SQL columns that are not used}} - while (rs.next()) { - // Display values - System.out.print("ID: " + rs.getInt("id")); - System.out.print(", First: " + rs.getString("first")); - System.out.println(", Last: " + rs.getString("last")); - } - rs = stmt.executeQuery(QUERY2); - - while (rs.next()) { - // Display values - System.out.print("Age: " + rs.getInt("age")); - System.out.print("ID: " + rs.getInt("id")); - System.out.print(", First: " + rs.getString("first")); - System.out.println(", Last: " + rs.getString("last")); - } - - - } catch (SQLException e) { - e.printStackTrace(); - } - - } -} diff --git a/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java b/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java deleted file mode 100644 index d4a6c534..00000000 --- a/src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs - * Copyright © 2023 Green Code Initiative (https://www.ecocode.io) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.greencodeinitiative.creedengo.java.checks; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -/** - * In this test case, the ResultSet is passed through a method - * One field is not accesed, so an issue is raised - */ -public class UseEveryColumnQueriedUseMethodNonCompliant { - - private static final String DB_URL = "jdbc:mysql://localhost/TEST"; - private static final String USER = "guest"; - private static final String PASS = "guest123"; - - public void callJdbc() { - - try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT id, first, last, age FROM Registration");) { // Noncompliant {{Avoid querying SQL columns that are not used}} - extractGet(rs); - } catch (SQLException e) { - e.printStackTrace(); - } - - } - - private void extractGet(ResultSet rs) throws SQLException { - while (rs.next()) { - // Display values - System.out.print("ID: " + rs.getInt("id")); - System.out.print(", First: " + rs.getString("first")); - System.out.println(", Last: " + rs.getString("last")); - } - } -} diff --git a/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java b/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java index 4e4c206f..33e7355c 100644 --- a/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java +++ b/src/test/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedTest.java @@ -62,25 +62,6 @@ void UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant() { .verifyIssues(); } - @Test - @Disabled // case not handled (multiple queries with the same ResultSet object) - void UseEveryColumnQueriedMultipleQueriesNonCompliant() { - CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedMultipleQueriesNonCompliant.java") - .withCheck(new UseEveryColumnQueried()) - .verifyIssues(); - } - - @Test - @Disabled // case not handled (usage of a method) - void UseEveryColumnQueriedUseMethodNonCompliant() { - CheckVerifier.newVerifier() - .onFile("src/test/files/UseEveryColumnQueried/UseEveryColumnQueriedUseMethodNonCompliant.java") - .withCheck(new UseEveryColumnQueried()) - .verifyIssues(); - } - - @Test void UseEveryColumnQueriedAttributeQueryCompliant() { CheckVerifier.newVerifier() From 46c9bcab051d949270e468dad4dcecb6112c5191 Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Wed, 21 May 2025 10:41:04 +0200 Subject: [PATCH 24/25] give real number to GCI95 --- .../creedengo/java/integration/tests/GCIRulesIT.java | 12 ++++++------ .../creedengo/java/checks/UseEveryColumnQueried.java | 2 +- .../creedengo/java/creedengo_way_profile.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java index 71e710a1..d228a036 100644 --- a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java +++ b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java @@ -549,27 +549,27 @@ void testGCI94() { } @Test - void testGCI1044_1() { + void testGCI95_1() { String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedAttributeQueryNonCompliant.java"; - String ruleId= "creedengo-java:GCI1044"; + String ruleId= "creedengo-java:GCI95"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{41}; int[] endLines = new int[]{41}; } @Test - void testGCI1044_2() { + void testGCI95_2() { String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedLitteralQueryNonCompliant.java"; - String ruleId= "creedengo-java:GCI1044"; + String ruleId= "creedengo-java:GCI95"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{40}; int[] endLines = new int[]{40}; } @Test - void testGCI1044_3() { + void testGCI95_3() { String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueriedUseColumnIdsAndNameAttributesNonCompliant.java"; - String ruleId= "creedengo-java:GCI1044"; + String ruleId= "creedengo-java:GCI95"; String ruleMsg = "Avoid querying SQL columns that are not used"; int[] startLines = new int[]{42}; int[] endLines = new int[]{42}; diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java b/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java index ccb7b946..2acdbf21 100644 --- a/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java +++ b/src/main/java/org/greencodeinitiative/creedengo/java/checks/UseEveryColumnQueried.java @@ -42,7 +42,7 @@ import org.sonar.plugins.java.api.tree.VariableTree; import org.sonar.plugins.java.api.tree.Tree.Kind; -@Rule(key = "GCI1044") +@Rule(key = "GCI95") public class UseEveryColumnQueried extends IssuableSubscriptionVisitor { protected static final String MESSAGERULE = "Avoid querying SQL columns that are not used"; diff --git a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json index 3cc33d32..06725ccf 100644 --- a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json +++ b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json @@ -19,6 +19,6 @@ "GCI79", "GCI82", "GCI94", - "GCI1044" + "GCI95" ] } From 49ba3226eed511ff6aeea50c773a1381886bf99e Mon Sep 17 00:00:00 2001 From: Maxime DANIEL Date: Wed, 21 May 2025 10:51:37 +0200 Subject: [PATCH 25/25] correct changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68dcd399..01598cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- [#63](https://github.com/green-code-initiative/ecoCode-java/pull/63) [GCI1044] [Java] Avoid querying SQL columns that are not used +- [#63](https://github.com/green-code-initiative/ecoCode-java/pull/63) [GCI95] [Java] Avoid querying SQL columns that are not used ### Changed