Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- [#69](https://github.com/green-code-initiative/creedengo-javascript/pull/69) Only support string literals in GCI11
- [#69](https://github.com/green-code-initiative/creedengo-javascript/pull/69) Only support string literals (GCI11)
- [#70](https://github.com/green-code-initiative/creedengo-javascript/pull/70) Only support SQL queries within standard methods (GCI24)

## [2.0.0] - 2025-01-22

Expand Down
18 changes: 16 additions & 2 deletions eslint-plugin/docs/rules/limit-db-query-results.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,27 @@ If you store data about customers, you certainly don’t need to retrieve inform
the table will be, the more elements the query will return.

```js
const query = "SELECT * FROM customers"; // Non-compliant
// Non-compliant: Direct SQL query without LIMIT
const mysql = require("mysql2");
const connection = mysql.createConnection({ host: "localhost", user: "root" });

connection.query("SELECT * FROM users", (err, results) => {
if (err) throw err;
console.log(results);
});
```

It may therefore be a good idea to limit the results and use pagination, for example.

```js
const query = "SELECT id,name,email FROM customers FETCH FIRST 10 ROWS ONLY"; // Compliant
// Compliant: SQL query with LIMIT clause
const mysql = require("mysql2");
const connection = mysql.createConnection({ host: "localhost", user: "root" });

connection.query("SELECT * FROM users LIMIT 10", (err, results) => {
if (err) throw err;
console.log(results);
});
```

## Resources
Expand Down
19 changes: 15 additions & 4 deletions eslint-plugin/lib/rules/limit-db-query-results.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ module.exports = {
"FETCH FIRST",
"WHERE",
];

// List of known SQL client methods or functions
const sqlClientMethods = ["query", "execute", "run"];

return {
// Detect SQL queries in string literals
Literal: function (node) {
if (typeof node.value == "string") {
const query = node.value.toUpperCase();
Expand All @@ -51,10 +56,16 @@ module.exports = {
query.includes("FROM") &&
!limitingClauses.some((clause) => query.includes(clause))
) {
context.report({
node: node,
messageId: "LimitTheNumberOfReturns",
});
// Check if the query is used within a SQL client
const parent = node.parent;

if (
parent?.type === "CallExpression" &&
parent.callee.type === "MemberExpression" &&
sqlClientMethods.includes(parent.callee.property.name)
) {
context.report({ node, messageId: "LimitTheNumberOfReturns" });
}
}
}
},
Expand Down
24 changes: 18 additions & 6 deletions eslint-plugin/tests/lib/rules/limit-db-query-results.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const ruleTester = new RuleTester({
sourceType: "module",
},
});

const expectedError = {
messageId: "LimitTheNumberOfReturns",
type: "Literal",
Expand All @@ -42,25 +43,36 @@ const expectedError = {
ruleTester.run("limit-db-query-results", rule, {
valid: [
`
const query = "SELECT id, name, email FROM customers LIMIT 10;";
sqlClient.query("SELECT id, name, email FROM customers LIMIT 10;");
`,
`
sqlClient.query("SELECT TOP 5 * FROM products;");
`,
`
const query = "SELECT TOP 5 * FROM products;";
sqlClient.query("SELECT id, name, email FROM customers WHERE id = 1");
`,
`
const query = "SELECT id, name, email FROM customers WHERE id = 1;";
sqlClient.query("SELECT * FROM orders FETCH FIRST 20 ROWS ONLY");
`,
`
const query = "SELECT * FROM orders FETCH FIRST 20 ROWS ONLY;";
sqlClient.query("WITH numbered_customers AS (SELECT *, ROW_NUMBER() OVER (ORDER BY customer_id) AS row_num FROM customers) SELECT * FROM numbered_customers WHERE row_num <= 50");
`,
`
const query = "WITH numbered_customers AS (SELECT *, ROW_NUMBER() OVER (ORDER BY customer_id) AS row_num FROM customers) SELECT * FROM numbered_customers WHERE row_num <= 50;";
console.log("SELECT id, name, email FROM customers WHERE id = 1");
`,
],

invalid: [
{
code: `const query = "SELECT * FROM bikes;";`,
code: `sqlClient.query("SELECT * FROM bikes");`,
errors: [expectedError],
},
{
code: `sqlClient.run("SELECT id, departure, arrival FROM flights");`,
errors: [expectedError],
},
{
code: `sqlClient.execute("SELECT * FROM cars");`,
errors: [expectedError],
},
],
Expand Down