1
+ import Tokenizer from "sql-formatter/lib/src/lexer/Tokenizer" ;
1
2
import SQLTokeniser , { NameTypes } from "./tokens" ;
2
3
import { CTEReference , CallableReference , ClauseType , ClauseTypeWord , IRange , ObjectRef , QualifiedObject , StatementType , StatementTypeWord , Token } from "./types" ;
3
4
@@ -8,6 +9,15 @@ const tokenIs = (token: Token|undefined, type: string, value?: string) => {
8
9
export default class Statement {
9
10
public type : StatementType = StatementType . Unknown ;
10
11
private label : string | undefined ;
12
+ private _blockTokens : Token [ ] | undefined ;
13
+
14
+ private get blockTokens ( ) {
15
+ if ( ! this . _blockTokens ) {
16
+ this . _blockTokens = SQLTokeniser . createBlocks ( this . tokens . slice ( 0 ) ) ;
17
+ }
18
+
19
+ return this . _blockTokens ;
20
+ }
11
21
12
22
constructor ( public tokens : Token [ ] , public range : IRange ) {
13
23
this . tokens = this . tokens . filter ( newToken => newToken . type !== `newline` ) ;
@@ -101,62 +111,34 @@ export default class Statement {
101
111
return currentClause ;
102
112
}
103
113
104
- getBlockRangeAt ( offset : number ) {
105
- let start = - 1 ;
106
- let end = - 1 ;
107
-
108
- // Get the current token for the provided offset
109
- let i = this . tokens . findIndex ( ( token , i ) => ( offset >= token . range . start && offset <= token . range . end ) || ( offset > token . range . end && this . tokens [ i + 1 ] && offset < this . tokens [ i + 1 ] . range . start ) ) ;
110
-
111
- let depth = 0 ;
112
-
113
- if ( tokenIs ( this . tokens [ i ] , `closebracket` ) ) {
114
- i -- ;
115
- }
116
-
117
- if ( tokenIs ( this . tokens [ i ] , `openbracket` ) ) {
118
- start = i + 1 ;
119
- i ++ ;
120
- } else {
121
- for ( let x = i ; x >= 0 ; x -- ) {
122
- if ( tokenIs ( this . tokens [ x ] , `openbracket` ) ) {
123
- if ( depth === 0 ) {
124
- start = x + 1 ;
125
- break ;
114
+ getBlockRangeAt ( offset : number ) : IRange | undefined {
115
+ const blockContainsOffset = ( cOffset : number , block : Token [ ] ) : IRange | undefined => {
116
+ const tokenInOffset = block . find ( token => cOffset >= token . range . start && cOffset <= token . range . end ) ;
117
+ if ( tokenInOffset ) {
118
+ if ( tokenInOffset . type === `block` ) {
119
+ if ( tokenInOffset . block ! . length > 0 ) {
120
+ return blockContainsOffset ( cOffset , tokenInOffset . block ! ) ;
126
121
} else {
127
- depth -- ;
122
+ const rawEnd = this . tokens . findIndex ( token => token . range . end === tokenInOffset . range . end ) ;
123
+ return {
124
+ start : rawEnd ,
125
+ end : rawEnd
126
+ }
128
127
}
129
- } else
130
- if ( tokenIs ( this . tokens [ x ] , `closebracket` ) ) {
131
- depth ++ ;
132
- }
133
- }
134
- }
135
-
136
- depth = 0 ;
137
-
138
- for ( let x = i ; x <= this . tokens . length ; x ++ ) {
139
- if ( tokenIs ( this . tokens [ x ] , `openbracket` ) ) {
140
- depth ++ ;
141
- } else
142
- if ( tokenIs ( this . tokens [ x ] , `closebracket` ) ) {
143
- if ( depth === 0 ) {
144
- end = x ;
145
- break ;
146
128
} else {
147
- depth -- ;
129
+ const rawStart = this . tokens . findIndex ( token => token . range . start === block [ 0 ] . range . start ) ;
130
+ const rawEnd = this . tokens . findIndex ( token => token . range . end === block [ block . length - 1 ] . range . end ) ;
131
+ return {
132
+ start : rawStart ,
133
+ end : rawEnd + 1
134
+ } ;
148
135
}
149
136
}
150
- }
151
137
152
- if ( start === - 1 || end === - 1 ) {
153
138
return undefined ;
154
- } else {
155
- return {
156
- start,
157
- end
158
- }
159
139
}
140
+
141
+ return blockContainsOffset ( offset , this . blockTokens ) ;
160
142
}
161
143
162
144
getCallableDetail ( offset : number , withBlocks = false ) : CallableReference {
@@ -176,13 +158,22 @@ export default class Statement {
176
158
}
177
159
178
160
getBlockAt ( offset : number ) : Token [ ] {
179
- const range = this . getBlockRangeAt ( offset ) ;
161
+ const expandBlock = ( tokens : Token [ ] ) : Token [ ] => {
162
+ const block = tokens . filter ( token => token . type === `block` ) ;
163
+ if ( block . length > 0 ) {
164
+ return block . reduce ( ( acc , token ) => acc . concat ( expandBlock ( token . block ! ) ) , tokens ) ;
165
+ }
180
166
181
- if ( range ) {
182
- return this . tokens . slice ( range . start , range . end )
183
- } else {
184
- return [ ]
167
+ return tokens ;
168
+ }
169
+
170
+ let blockRange = this . getBlockRangeAt ( offset ) ;
171
+
172
+ if ( blockRange ) {
173
+ return expandBlock ( this . tokens . slice ( blockRange . start , blockRange . end ) ) ;
185
174
}
175
+
176
+ return [ ] ;
186
177
}
187
178
188
179
getReferenceByOffset ( offset : number ) {
@@ -227,7 +218,7 @@ export default class Statement {
227
218
getCTEReferences ( ) : CTEReference [ ] {
228
219
if ( this . type !== StatementType . With ) return [ ] ;
229
220
230
- const withBlocks = SQLTokeniser . createBlocks ( this . tokens . slice ( 0 ) ) ;
221
+ const withBlocks = this . blockTokens ;
231
222
232
223
let cteList : CTEReference [ ] = [ ] ;
233
224
@@ -324,11 +315,16 @@ export default class Statement {
324
315
325
316
}
326
317
318
+ private static readonly BANNED_NAMES = [ `VALUES` ] ;
327
319
getObjectReferences ( ) : ObjectRef [ ] {
328
320
let list : ObjectRef [ ] = [ ] ;
329
321
330
322
const doAdd = ( ref ?: ObjectRef ) => {
331
- if ( ref ) list . push ( ref ) ;
323
+ if ( ref ) {
324
+ if ( ref . object . name && ! Statement . BANNED_NAMES . includes ( ref . object . name . toUpperCase ( ) ) ) {
325
+ list . push ( ref ) ;
326
+ }
327
+ }
332
328
}
333
329
334
330
const basicQueryFinder = ( startIndex : number ) : void => {
@@ -365,6 +361,14 @@ export default class Statement {
365
361
i += 3 ; //For the brackets
366
362
}
367
363
}
364
+ } else if ( currentClause === `from` && tokenIs ( this . tokens [ i ] , `function` ) ) {
365
+ const sqlObj = this . getRefAtToken ( i , { includeParameters : true } ) ;
366
+ if ( sqlObj ) {
367
+ i += sqlObj . tokens . length ;
368
+ if ( sqlObj . isUDTF || sqlObj . fromLateral ) {
369
+ i += 3 ; //For the brackets
370
+ }
371
+ }
368
372
}
369
373
}
370
374
}
@@ -538,18 +542,17 @@ export default class Statement {
538
542
return list ;
539
543
}
540
544
541
- private getRefAtToken ( i : number , options : { withSystemName ?: boolean } = { } ) : ObjectRef | undefined {
545
+ private getRefAtToken ( i : number , options : { withSystemName ?: boolean , includeParameters ?: boolean } = { } ) : ObjectRef | undefined {
542
546
let sqlObj : ObjectRef ;
543
547
544
548
let nextIndex = i ;
545
549
let nextToken = this . tokens [ i ] ;
546
550
547
551
let endIndex = i ;
548
552
549
- const isUDTF = tokenIs ( nextToken , `function` , `TABLE` ) ;
550
- const isLateral = tokenIs ( nextToken , `function` , `LATERAL` ) ;
553
+ const isSubSelect = tokenIs ( nextToken , `function` , `TABLE` ) || tokenIs ( nextToken , `function` , `LATERAL` ) || ( options . includeParameters && tokenIs ( nextToken , `function` ) ) ;
551
554
552
- if ( isUDTF ) {
555
+ if ( isSubSelect ) {
553
556
sqlObj = this . getRefAtToken ( i + 2 ) ;
554
557
if ( sqlObj ) {
555
558
sqlObj . isUDTF = true ;
@@ -563,14 +566,6 @@ export default class Statement {
563
566
nextIndex = - 1 ;
564
567
nextToken = undefined ;
565
568
}
566
- } else if ( isLateral ) {
567
- const blockTokens = this . getBlockAt ( nextToken . range . end + 1 ) ;
568
- const newStatement = new Statement ( blockTokens , { start : nextToken . range . start , end : blockTokens [ blockTokens . length - 1 ] . range . end } ) ;
569
- [ sqlObj ] = newStatement . getObjectReferences ( ) ;
570
-
571
- sqlObj . fromLateral = true ;
572
- nextIndex = i + 2 + blockTokens . length ;
573
- nextToken = this . tokens [ nextIndex ] ;
574
569
575
570
} else {
576
571
if ( nextToken && NameTypes . includes ( nextToken . type ) ) {
@@ -618,7 +613,7 @@ export default class Statement {
618
613
}
619
614
}
620
615
621
- if ( ! isUDTF ) {
616
+ if ( ! isSubSelect && ! sqlObj . isUDTF ) {
622
617
sqlObj . tokens = this . tokens . slice ( i , endIndex + 1 ) ;
623
618
}
624
619
0 commit comments