5
5
6
6
'use strict'
7
7
8
- const logger = require ( '../../logger' ) . child ( { component : 'sql_query_parser' } )
8
+ const defaultLogger = require ( '../../logger' ) . child ( { component : 'sql_query_parser' } )
9
9
const stringify = require ( 'json-stringify-safe' )
10
10
11
+ /**
12
+ * In a query like `select * from (select * from foo)`, extract the subquery
13
+ * as the statement to retrieve the target identifier from.
14
+ *
15
+ * @type {RegExp }
16
+ */
17
+ const selectSubquery = / f r o m \s + ?\( (?< subquery > s e l e c t .* ?) \) (?: \s + ?) ? / gi
18
+
11
19
/**
12
20
* Parses a SQL statement into the parts we want to report as metadata in
13
21
* database transactions.
14
22
*
15
23
* @param {string } sql The statement to parse.
24
+ * @param {object } [deps] A set of optional dependencies.
25
+ * @param {object } [deps.logger] A logger instance.
16
26
*
17
27
* @returns {{query: string, collection: null|string, operation: string} } Parsed
18
28
* metadata.
19
29
*/
20
- module . exports = function parseSql ( sql ) {
30
+ module . exports = function parseSql ( sql , { logger = defaultLogger } = { } ) {
21
31
// Sometimes we get an object here from MySQL. We have been unable to
22
32
// reproduce it, so we'll just log what that object is and return a statement
23
33
// type of `other`.
@@ -27,7 +37,7 @@ module.exports = function parseSql(sql) {
27
37
if ( typeof sql !== 'string' ) {
28
38
if ( logger . traceEnabled ( ) ) {
29
39
try {
30
- logger . trace ( 'parseSQL got an a non-string sql that looks like: %s' , stringify ( sql ) )
40
+ logger . trace ( 'parseSQL got a non-string sql that looks like: %s' , stringify ( sql ) )
31
41
} catch ( err ) {
32
42
logger . debug ( err , 'Unable to stringify SQL' )
33
43
}
@@ -166,10 +176,17 @@ function parseStatement(statement, kind = 'insert') {
166
176
}
167
177
168
178
case 'select' : {
179
+ const subqueryMatch = selectSubquery . exec ( statement )
180
+ if ( subqueryMatch !== null ) {
181
+ statement = subqueryMatch . groups . subquery
182
+ }
183
+
169
184
if ( / \b f r o m \b / i. test ( statement ) === false ) {
170
185
// Statement does not specify a table. We don't need further processing.
186
+ // E.g., we have a statement like `select 1 + 1 as added`.
171
187
return { collection : 'unknown' , table : 'unknown' }
172
188
}
189
+
173
190
splitter = / \s * ?\b f r o m \b \s * ?/ i
174
191
break
175
192
}
@@ -240,7 +257,6 @@ function normalizeTableName(tableIdentifier) {
240
257
if ( parts . length === 1 ) {
241
258
return parts [ 0 ]
242
259
}
243
- return '(subquery)'
244
260
}
245
261
246
262
const parenPos = tableIdentifier . indexOf ( '(' )
0 commit comments