Skip to content

Commit 8c1a245

Browse files
committed
feat: add placeholderRule setting
1 parent b68ba44 commit 8c1a245

File tree

8 files changed

+76
-8
lines changed

8 files changed

+76
-8
lines changed

.README/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,13 @@ npm install eslint-plugin-sql --save-dev
5757

5858
## Settings
5959

60-
N/A
60+
### `placeholderRule`
61+
62+
A regex used to ignore placeholders or other fragments of the query that'd make it invalid SQL query, e.g.
63+
64+
If you are using `?` placeholders in your queries, you must ignore `\?` pattern as otherwise the string is not going to be recognized as a valid SQL query.
65+
66+
This configuration is relevant for `sql/no-unsafe-query` to match queries containing placeholders as well as for `sql/format` when used with `{ignoreTagless: false}` configuration.
6167

6268
## Rules
6369

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ SQL linting rules for ESLint.
1414
* [Installation](#eslint-plugin-sql-installation)
1515
* [Configuration](#eslint-plugin-sql-configuration)
1616
* [Settings](#eslint-plugin-sql-settings)
17+
* [`placeholderRule`](#eslint-plugin-sql-settings-placeholderrule)
1718
* [Rules](#eslint-plugin-sql-rules)
1819
* [`format`](#eslint-plugin-sql-rules-format)
1920
* [`no-unsafe-query`](#eslint-plugin-sql-rules-no-unsafe-query)
@@ -68,7 +69,14 @@ npm install eslint-plugin-sql --save-dev
6869
<a name="eslint-plugin-sql-settings"></a>
6970
## Settings
7071

71-
N/A
72+
<a name="eslint-plugin-sql-settings-placeholderrule"></a>
73+
### <code>placeholderRule</code>
74+
75+
A regex used to ignore placeholders or other fragments of the query that'd make it invalid SQL query, e.g.
76+
77+
If you are using `?` placeholders in your queries, you must ignore `\?` pattern as otherwise the string is not going to be recognized as a valid SQL query.
78+
79+
This configuration is relevant for `sql/no-unsafe-query` to match queries containing placeholders as well as for `sql/format` when used with `{ignoreTagless: false}` configuration.
7280

7381
<a name="eslint-plugin-sql-rules"></a>
7482
## Rules
@@ -180,6 +188,9 @@ The following patterns are considered problems:
180188

181189
foo`SELECT ${'bar'}`
182190
// Message: Use "sql" tag
191+
192+
`SELECT ?`
193+
// Message: Use "sql" tag
183194
```
184195

185196
The following patterns are not considered problems:

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
},
77
"dependencies": {
88
"astring": "^1.0.2",
9+
"debug": "^2.6.8",
910
"lodash": "^4.17.4",
1011
"pg-formatter": "^1.1.0",
1112
"sql-parse": "^0.1.5"
@@ -53,7 +54,7 @@
5354
"documentation-add-assertions": "babel-node ./bin/readmeAssertions",
5455
"lint": "eslint ./src ./test",
5556
"precommit": "npm run lint && npm run test",
56-
"test": "mocha --compilers js:babel-register ./test/rules/index.js"
57+
"test": "mocha --compilers js:babel-register ./test/**/*.js"
5758
},
5859
"version": "1.0.1"
5960
}

src/rules/format.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
import isSqlQuery from '../utilities/isSqlQuery';
1010

1111
export default (context) => {
12+
const placeholderRule = context.settings.sql && context.settings.sql.placeholderRule;
13+
1214
const pluginOptions = context.options && context.options[0] || {};
1315

1416
const ignoreExpressions = pluginOptions.ignoreExpressions === true;
@@ -35,7 +37,7 @@ export default (context) => {
3537
})
3638
.join(magic);
3739

38-
if (!sqlTagIsPresent && !isSqlQuery(literal)) {
40+
if (!sqlTagIsPresent && !isSqlQuery(literal, placeholderRule)) {
3941
return;
4042
}
4143

src/rules/noUnsafeQuery.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
// @flow
22

3+
import createDebug from 'debug';
34
import isSqlQuery from '../utilities/isSqlQuery';
45

6+
const debug = createDebug('eslint-plugin-sql:rule:no-unsafe-query');
7+
58
export default (context) => {
9+
const placeholderRule = context.settings.sql && context.settings.sql.placeholderRule;
10+
611
const allowLiteral = context.options && context.options[0] && context.options[0].allowLiteral;
712

813
return {
@@ -15,9 +20,15 @@ export default (context) => {
1520
.map((quasi) => {
1621
return quasi.value.raw;
1722
})
18-
.join('1');
23+
.join('foo');
24+
25+
debug('input', literal);
26+
27+
const recognizedAsQuery = isSqlQuery(literal, placeholderRule);
28+
29+
debug('recognized as a query', recognizedAsQuery);
1930

20-
if (!isSqlQuery(literal)) {
31+
if (!recognizedAsQuery) {
2132
return;
2233
}
2334

src/utilities/isSqlQuery.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
import parser from 'sql-parse';
44

5-
export default (literal: string): boolean => {
5+
export default (literal: string, ignorePattern: string): boolean => {
66
if (!literal) {
77
return false;
88
}
99

10+
let maybeSql = literal;
11+
12+
if (ignorePattern) {
13+
maybeSql = maybeSql.replace(new RegExp(ignorePattern, 'g'), 'foo');
14+
}
15+
1016
try {
11-
parser.parse(literal);
17+
parser.parse(maybeSql);
1218
} catch (error) {
1319
return false;
1420
}

test/rules/assertions/noUnsafeQuery.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ export default {
2525
message: 'Use "sql" tag'
2626
}
2727
]
28+
},
29+
{
30+
code: '`SELECT ?`',
31+
errors: [
32+
{
33+
message: 'Use "sql" tag'
34+
}
35+
],
36+
settings: {
37+
sql: {
38+
placeholderRule: '\\?'
39+
}
40+
}
2841
}
2942
],
3043
valid: [

test/utilities/isSqlQuery.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* global describe */
2+
/* global it */
3+
4+
import assert from 'assert';
5+
import isSqlQuery from '../../src/utilities/isSqlQuery';
6+
7+
describe('isSqlQuery', () => {
8+
it('recognizes SQL input', () => {
9+
assert(isSqlQuery('SELECT 1'));
10+
});
11+
it('recognizes SQL input after ignoring defined patterns', () => {
12+
assert(isSqlQuery('SELECT ? FROM bar', '\\?'));
13+
});
14+
it('distinguishes from non-SQL input', () => {
15+
assert(!isSqlQuery('foo bar'));
16+
assert(!isSqlQuery('foo SELECT FROM bar'));
17+
});
18+
});

0 commit comments

Comments
 (0)