Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions ecocode-rules-specifications/src/main/rules/EC475/EC475.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"title": "Optimize Database Queries",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, for me title is quite wrong, like class names above, because you deal with database queries only for relational databases (and not NOSql databases for example). Thus, could you add "SQL" word in title, class names, descriptions, documentation, etc ... please

also, there are some lacks if you check DoD list https://github.com/green-code-initiative/ecoCode-common/blob/main/doc/starter-pack.md#definition-of-done-of-a-pr - example :

  • update RULES.md
  • update CHANGELOG.md
  • add test in real test repository
  • ...

"type": "CODE_SMELL",
"status": "ready",
"tags": [
"performance",
"eco-design",
"ecocode"
],
"defaultSeverity": "Minor"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Databases are typically essential application components. As many queries are used to retrieve and store data, they end up having a significant impact on the solution's resource use when executed frequently.

With this in mind, it is important to pay attention for these queries and ensure, at least for the most expensive ones that they are well optimized.

The most common optimization tips are:

- Use less data and limit it to the bare minimum. For example, the LIMIT clause limits the number of result rows in relational databases. When possible, using the 'LIMIT' clause reduces the amount of transferred data. Performance gains will be even more significant if records contain a large number of voluminous fields.

- Only use necessary fields in the tables or documents in order to avoid needlessly transferring data that will not be used and to avoid using database server and application server resources to manipulate them.

- Add indexes on fields used as keys in your model. Adding them can completely change queries performance. Be careful: adding indexes makes writing longer as it must be updated for each added, modified or deleted document. This must be done if there are more reads than writes or if reads are particularly expensive.

- Use database management system tools to analyze queries in order to identify improvement areas, such as EXPLAIN for RDBMS.

- Cache the most expensive queries results as well as data that changes little or never (reference data).

- Optionally, modifying data models to be able to access information more easily without joins (denormalization)


## Noncompliant Code Example

```java
String sql = "SELECT user FROM myTable";
```

## Compliant Code Example

```java
String sql = "SELECT user FROM myTable LIMIT 50";
```

### Index creation example

```sql
CREATE INDEX idx_people_lastname_firstname ON people(lastname, firstname)
```
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import fr.greencodeinitiative.java.checks.OptimizeReadFileExceptions;
import fr.greencodeinitiative.java.checks.UnnecessarilyAssignValuesToVariables;
import fr.greencodeinitiative.java.checks.UseCorrectForLoop;
import fr.greencodeinitiative.java.checks.OptimizeDatabaseQueries;
import org.sonar.plugins.java.api.CheckRegistrar;
import org.sonar.plugins.java.api.JavaCheck;
import org.sonarsource.api.sonarlint.SonarLintSide;
Expand Down Expand Up @@ -69,7 +70,8 @@ public class JavaCheckRegistrar implements CheckRegistrar {
AvoidUsingGlobalVariablesCheck.class,
AvoidSetConstantInBatchUpdate.class,
FreeResourcesOfAutoCloseableInterface.class,
AvoidMultipleIfElseStatement.class
AvoidMultipleIfElseStatement.class,
OptimizeDatabaseQueries.class
);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package fr.greencodeinitiative.java.checks;

import org.sonar.check.Rule;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.tree.LiteralTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.Tree.Kind;

import java.util.List;
import java.util.function.Predicate;

import static java.util.Collections.singletonList;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
import static java.util.regex.Pattern.compile;

@Rule(key = "EC475")
public class OptimizeDatabaseQueries extends IssuableSubscriptionVisitor{
public static final String MESSAGE_RULE = "Optimize Database Queries (Clause LIMIT)";
private static final Predicate<String> LIMIT_REGEXP =
compile("limit", CASE_INSENSITIVE).asPredicate();
private static final Predicate<String> SELECT_REGEXP =
compile("select", CASE_INSENSITIVE).asPredicate();
private static final Predicate<String> FROM_REGEXP =
compile("from", CASE_INSENSITIVE).asPredicate();

@Override
public List<Kind> nodesToVisit() {
return singletonList(Kind.STRING_LITERAL);
}

@Override
public void visitNode(Tree tree) {
String value = ((LiteralTree) tree).value();
if (SELECT_REGEXP.test(value) && FROM_REGEXP.test(value) && !LIMIT_REGEXP.test(value)) {
reportIssue(tree, MESSAGE_RULE);
}
}
}
21 changes: 21 additions & 0 deletions java-plugin/src/test/files/OptimizeDatabaseQueries.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class OptimizeDatabaseQueries {

OptimizeDatabaseQueries(OptimizeDatabaseQueries mc) {
}

public void literalSQLrequest() {
dummyCall("SELECT user FROM myTable"); // Noncompliant
dummyCall("SELECT user FROM myTable LIMIT 50"); // Compliant
}

@Query("select t from Todo t where t.status != 'COMPLETED'") // Noncompliant
@Query("select t from Todo t where t.status != 'COMPLETED' LIMIT 25") // Compliant

private void callQuery() {
String sql1 = "SELECT user FROM myTable"; // Noncompliant
String sql2 = "SELECT user FROM myTable LIMIT 50"; // Compliant
}

private void dummyCall(String request) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void checkNumberRules() {
final JavaCheckRegistrar registrar = new JavaCheckRegistrar();
registrar.register(context);

assertThat(context.checkClasses()).hasSize(19);
assertThat(context.checkClasses()).hasSize(20);
assertThat(context.testCheckClasses()).isEmpty();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ void testMetadata() {
assertThat(repository.name()).isEqualTo("ecoCode");
assertThat(repository.language()).isEqualTo("java");
assertThat(repository.key()).isEqualTo("ecocode-java");
assertThat(repository.rules()).hasSize(19);
assertThat(repository.rules()).hasSize(20);
}

@Test
void testRegistredRules() {
assertThat(repository.rules()).hasSize(19);
assertThat(repository.rules()).hasSize(20);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package fr.greencodeinitiative.java.checks;

import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;

class OptimizeDatabaseQueriesTest {

@Test
void test() {
CheckVerifier.newVerifier()
.onFile("src/test/files/OptimizeDatabaseQueries.java")
.withCheck(new OptimizeDatabaseQueries())
.verifyIssues();
}

}